linux: make getcwd(3) fail if it cannot obtain an absolute path [BZ #22679]

Currently getcwd(3) can succeed without returning an absolute path
because the underlying getcwd syscall, starting with linux commit
v2.6.36-rc1~96^2~2, may succeed without returning an absolute path.

This is a conformance issue because "The getcwd() function shall
place an absolute pathname of the current working directory
in the array pointed to by buf, and return buf".

This is also a security issue because a non-absolute path returned
by getcwd(3) causes a buffer underflow in realpath(3).

Fix this by checking the path returned by getcwd syscall and falling
back to generic_getcwd if the path is not absolute, effectively making
getcwd(3) fail with ENOENT.  The error code is chosen for consistency
with the case when the current directory is unlinked.

[BZ #22679]
CVE-2018-1000001
* sysdeps/unix/sysv/linux/getcwd.c (__getcwd): Fall back to
generic_getcwd if the path returned by getcwd syscall is not absolute.
* io/tst-getcwd-abspath.c: New test.
* io/Makefile (tests): Add tst-getcwd-abspath.
This commit is contained in:
Dmitry V. Levin 2018-01-07 02:03:41 +00:00
parent 249a5895f1
commit 52a713fdd0
5 changed files with 84 additions and 5 deletions

View File

@ -1,3 +1,12 @@
2018-01-12 Dmitry V. Levin <ldv@altlinux.org>
[BZ #22679]
CVE-2018-1000001
* sysdeps/unix/sysv/linux/getcwd.c (__getcwd): Fall back to
generic_getcwd if the path returned by getcwd syscall is not absolute.
* io/tst-getcwd-abspath.c: New test.
* io/Makefile (tests): Add tst-getcwd-abspath.
2018-01-12 Istvan Kurucsai <pistukem@gmail.com>
* malloc/malloc.c (malloc_consolidate): Add size check.

4
NEWS
View File

@ -199,6 +199,10 @@ Security related changes:
for AT_SECURE or SUID binaries could be used to load libraries from the
current directory.
CVE-2018-1000001: Buffer underflow in realpath function when getcwd function
succeeds without returning an absolute path due to unexpected behaviour
of the Linux kernel getcwd syscall. Reported by halfdog.
The following bugs are resolved with this release:
[The release manager will add the list generated by

View File

@ -71,7 +71,7 @@ tests := test-utime test-stat test-stat2 test-lfs tst-getcwd \
tst-mknodat tst-mkfifoat tst-ttyname_r bug-ftw5 \
tst-posix_fallocate tst-posix_fallocate64 \
tst-fts tst-fts-lfs tst-open-tmpfile \
tst-copy_file_range \
tst-copy_file_range tst-getcwd-abspath \
# This test includes the compat implementation of copy_file_range,
# which uses internal, unexported libc functions.

66
io/tst-getcwd-abspath.c Normal file
View File

@ -0,0 +1,66 @@
/* BZ #22679 getcwd(3) should not succeed without returning an absolute path.
Copyright (C) 2018 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <support/check.h>
#include <support/namespace.h>
#include <support/support.h>
#include <support/temp_file.h>
#include <support/test-driver.h>
#include <support/xunistd.h>
#include <unistd.h>
static char *chroot_dir;
/* The actual test. Run it in a subprocess, so that the test harness
can remove the temporary directory in --direct mode. */
static void
getcwd_callback (void *closure)
{
xchroot (chroot_dir);
errno = 0;
char *cwd = getcwd (NULL, 0);
TEST_COMPARE (errno, ENOENT);
TEST_VERIFY (cwd == NULL);
errno = 0;
cwd = realpath (".", NULL);
TEST_COMPARE (errno, ENOENT);
TEST_VERIFY (cwd == NULL);
_exit (0);
}
static int
do_test (void)
{
support_become_root ();
if (!support_can_chroot ())
return EXIT_UNSUPPORTED;
chroot_dir = support_create_temp_directory ("tst-getcwd-abspath-");
support_isolate_in_subprocess (getcwd_callback, NULL);
return 0;
}
#include <support/test-driver.c>

View File

@ -76,7 +76,7 @@ __getcwd (char *buf, size_t size)
int retval;
retval = INLINE_SYSCALL (getcwd, 2, path, alloc_size);
if (retval >= 0)
if (retval > 0 && path[0] == '/')
{
#ifndef NO_ALLOCATION
if (buf == NULL && size == 0)
@ -92,10 +92,10 @@ __getcwd (char *buf, size_t size)
return buf;
}
/* The system call cannot handle paths longer than a page.
Neither can the magic symlink in /proc/self. Just use the
/* The system call either cannot handle paths longer than a page
or can succeed without returning an absolute path. Just use the
generic implementation right away. */
if (errno == ENAMETOOLONG)
if (retval >= 0 || errno == ENAMETOOLONG)
{
#ifndef NO_ALLOCATION
if (buf == NULL && size == 0)