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

Message ID 20180107133905.GA13846@altlinux.org
State New, archived
Headers

Commit Message

Dmitry V. Levin Jan. 7, 2018, 1:39 p.m. UTC
  As the underlying getcwd syscall, starting with linux commit
v2.6.36-rc1~96^2~2, may succeed without returning an absolute path,
check for the path returned by syscall and fall back to generic_getcwd
if the path is not absolute.

[BZ #22679]
* sysdeps/unix/sysv/linux/getcwd.c (__getcwd): Fall back to
generic_getcwd if the path returned by getcwd syscall is not absolute.
* sysdeps/unix/sysv/linux/tst-getcwd-abspath.c: New test.
* sysdeps/unix/sysv/linux/Makefile (tests): Add tst-getcwd-abspath.
---
 ChangeLog                                    |  8 ++++
 sysdeps/unix/sysv/linux/Makefile             |  2 +-
 sysdeps/unix/sysv/linux/getcwd.c             |  8 ++--
 sysdeps/unix/sysv/linux/tst-getcwd-abspath.c | 59 ++++++++++++++++++++++++++++
 4 files changed, 72 insertions(+), 5 deletions(-)
 create mode 100644 sysdeps/unix/sysv/linux/tst-getcwd-abspath.c
  

Comments

Dmitry V. Levin Jan. 8, 2018, 3:07 p.m. UTC | #1
I think this edition of the fix addresses all comments raised so far.
Is it OK to commit, or does anybody else have any more comments?

On Sun, Jan 07, 2018 at 04:39:05PM +0300, Dmitry V. Levin wrote:
> As the underlying getcwd syscall, starting with linux commit
> v2.6.36-rc1~96^2~2, may succeed without returning an absolute path,
> check for the path returned by syscall and fall back to generic_getcwd
> if the path is not absolute.
> 
> [BZ #22679]
> * sysdeps/unix/sysv/linux/getcwd.c (__getcwd): Fall back to
> generic_getcwd if the path returned by getcwd syscall is not absolute.
> * sysdeps/unix/sysv/linux/tst-getcwd-abspath.c: New test.
> * sysdeps/unix/sysv/linux/Makefile (tests): Add tst-getcwd-abspath.
> ---
>  ChangeLog                                    |  8 ++++
>  sysdeps/unix/sysv/linux/Makefile             |  2 +-
>  sysdeps/unix/sysv/linux/getcwd.c             |  8 ++--
>  sysdeps/unix/sysv/linux/tst-getcwd-abspath.c | 59 ++++++++++++++++++++++++++++
>  4 files changed, 72 insertions(+), 5 deletions(-)
>  create mode 100644 sysdeps/unix/sysv/linux/tst-getcwd-abspath.c
> 
> diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
> index 8f19e0e..34c5c39 100644
> --- a/sysdeps/unix/sysv/linux/Makefile
> +++ b/sysdeps/unix/sysv/linux/Makefile
> @@ -45,7 +45,7 @@ sysdep_headers += sys/mount.h sys/acct.h sys/sysctl.h \
>  tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \
>  	 tst-quota tst-sync_file_range tst-sysconf-iov_max tst-ttyname \
>  	 test-errno-linux tst-memfd_create tst-mlock2 tst-pkey \
> -	 tst-rlimit-infinity
> +	 tst-rlimit-infinity tst-getcwd-abspath
>  
>  # Generate the list of SYS_* macros for the system calls (__NR_*
>  # macros).  The file syscall-names.list contains all possible system
> diff --git a/sysdeps/unix/sysv/linux/getcwd.c b/sysdeps/unix/sysv/linux/getcwd.c
> index f545106..866b9d2 100644
> --- a/sysdeps/unix/sysv/linux/getcwd.c
> +++ b/sysdeps/unix/sysv/linux/getcwd.c
> @@ -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)
> diff --git a/sysdeps/unix/sysv/linux/tst-getcwd-abspath.c b/sysdeps/unix/sysv/linux/tst-getcwd-abspath.c
> new file mode 100644
> index 0000000..ea3562c
> --- /dev/null
> +++ b/sysdeps/unix/sysv/linux/tst-getcwd-abspath.c
> @@ -0,0 +1,59 @@
> +/* BZ #22679 getcwd(3) does 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);
> +  _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>
> -- 
> ldv
  

Patch

diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 8f19e0e..34c5c39 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -45,7 +45,7 @@  sysdep_headers += sys/mount.h sys/acct.h sys/sysctl.h \
 tests += tst-clone tst-clone2 tst-clone3 tst-fanotify tst-personality \
 	 tst-quota tst-sync_file_range tst-sysconf-iov_max tst-ttyname \
 	 test-errno-linux tst-memfd_create tst-mlock2 tst-pkey \
-	 tst-rlimit-infinity
+	 tst-rlimit-infinity tst-getcwd-abspath
 
 # Generate the list of SYS_* macros for the system calls (__NR_*
 # macros).  The file syscall-names.list contains all possible system
diff --git a/sysdeps/unix/sysv/linux/getcwd.c b/sysdeps/unix/sysv/linux/getcwd.c
index f545106..866b9d2 100644
--- a/sysdeps/unix/sysv/linux/getcwd.c
+++ b/sysdeps/unix/sysv/linux/getcwd.c
@@ -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)
diff --git a/sysdeps/unix/sysv/linux/tst-getcwd-abspath.c b/sysdeps/unix/sysv/linux/tst-getcwd-abspath.c
new file mode 100644
index 0000000..ea3562c
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-getcwd-abspath.c
@@ -0,0 +1,59 @@ 
+/* BZ #22679 getcwd(3) does 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);
+  _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>