public inbox for libc-stable@sourceware.org
 help / color / mirror / Atom feed
* [COMMITTED 2.31 1/7] support: Add create_temp_file_in_dir
@ 2022-08-18 10:02 Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 2/7] Add xchdir to libsupport Aurelien Jarno
                   ` (5 more replies)
  0 siblings, 6 replies; 7+ messages in thread
From: Aurelien Jarno @ 2022-08-18 10:02 UTC (permalink / raw)
  To: libc-stable

From: Adhemerval Zanella <adhemerval.zanella@linaro.org>

It allows created a temporary file in a specified directory.

(cherry picked from commit 60854f40ea2d420867ed2f0f052ee7fca661dbff)
---
 support/temp_file.c | 12 ++++++++----
 support/temp_file.h |  7 +++++++
 2 files changed, 15 insertions(+), 4 deletions(-)

diff --git a/support/temp_file.c b/support/temp_file.c
index 277c5e0cf1..98bd235526 100644
--- a/support/temp_file.c
+++ b/support/temp_file.c
@@ -60,14 +60,12 @@ add_temp_file (const char *name)
 }
 
 int
-create_temp_file (const char *base, char **filename)
+create_temp_file_in_dir (const char *base, const char *dir, char **filename)
 {
   char *fname;
   int fd;
 
-  fname = (char *) xmalloc (strlen (test_dir) + 1 + strlen (base)
-			    + sizeof ("XXXXXX"));
-  strcpy (stpcpy (stpcpy (stpcpy (fname, test_dir), "/"), base), "XXXXXX");
+  fname = xasprintf ("%s/%sXXXXXX", dir, base);
 
   fd = mkstemp (fname);
   if (fd == -1)
@@ -86,6 +84,12 @@ create_temp_file (const char *base, char **filename)
   return fd;
 }
 
+int
+create_temp_file (const char *base, char **filename)
+{
+  return create_temp_file_in_dir (base, test_dir, filename);
+}
+
 char *
 support_create_temp_directory (const char *base)
 {
diff --git a/support/temp_file.h b/support/temp_file.h
index 8b6303a6e4..ac61105428 100644
--- a/support/temp_file.h
+++ b/support/temp_file.h
@@ -32,6 +32,13 @@ void add_temp_file (const char *name);
    *FILENAME.  */
 int create_temp_file (const char *base, char **filename);
 
+/* Create a temporary file in directory DIR.  Return the opened file
+   descriptor on success, or -1 on failure.  Write the file name to
+   *FILENAME if FILENAME is not NULL.  In this case, the caller is
+   expected to free *FILENAME.  */
+int create_temp_file_in_dir (const char *base, const char *dir,
+			     char **filename);
+
 /* Create a temporary directory and schedule it for deletion.  BASE is
    used as a prefix for the unique directory name, which the function
    returns.  The caller should free this string.  */
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [COMMITTED 2.31 2/7] Add xchdir to libsupport.
  2022-08-18 10:02 [COMMITTED 2.31 1/7] support: Add create_temp_file_in_dir Aurelien Jarno
@ 2022-08-18 10:02 ` Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 3/7] support: Add xclone Aurelien Jarno
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Aurelien Jarno @ 2022-08-18 10:02 UTC (permalink / raw)
  To: libc-stable; +Cc: Alexandra Hájková

From: Alexandra Hájková <ahajkova@redhat.com>

(cherry picked from commit a7e9dbb7742954814643a8562dcad09abb0b0e5d)
---
 support/Makefile  |  1 +
 support/xchdir.c  | 28 ++++++++++++++++++++++++++++
 support/xunistd.h |  1 +
 3 files changed, 30 insertions(+)
 create mode 100644 support/xchdir.c

diff --git a/support/Makefile b/support/Makefile
index 3325feb790..a40a2f5bca 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -83,6 +83,7 @@ libsupport-routines = \
   xasprintf \
   xbind \
   xcalloc \
+  xchdir \
   xchroot \
   xclock_gettime \
   xclose \
diff --git a/support/xchdir.c b/support/xchdir.c
new file mode 100644
index 0000000000..beb4feff72
--- /dev/null
+++ b/support/xchdir.c
@@ -0,0 +1,28 @@
+/* chdir with error checking.
+   Copyright (C) 2020 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 <support/check.h>
+#include <support/xunistd.h>
+#include <unistd.h>
+
+void
+xchdir (const char *path)
+{
+  if (chdir (path) != 0)
+    FAIL_EXIT1 ("chdir (\"%s\"): %m", path);
+}
diff --git a/support/xunistd.h b/support/xunistd.h
index 96f498f2e5..43799d92c5 100644
--- a/support/xunistd.h
+++ b/support/xunistd.h
@@ -44,6 +44,7 @@ long xsysconf (int name);
 long long xlseek (int fd, long long offset, int whence);
 void xftruncate (int fd, long long length);
 void xsymlink (const char *target, const char *linkpath);
+void xchdir (const char *path);
 
 /* Equivalent of "mkdir -p".  */
 void xmkdirp (const char *, mode_t);
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [COMMITTED 2.31 3/7] support: Add xclone
  2022-08-18 10:02 [COMMITTED 2.31 1/7] support: Add create_temp_file_in_dir Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 2/7] Add xchdir to libsupport Aurelien Jarno
@ 2022-08-18 10:02 ` Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 4/7] support: Fix xclone build failures on ia64 and hppa Aurelien Jarno
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Aurelien Jarno @ 2022-08-18 10:02 UTC (permalink / raw)
  To: libc-stable

From: Adhemerval Zanella <adhemerval.zanella@linaro.org>

It is a wrapper for Linux clone syscall, to simplify the call to the
use only the most common arguments and remove architecture specific
handling (such as ia64 different name and signature).

(cherry picked from commit de8995a2a04163617c1a233b4b81356ef9f9741f)
---
 support/Makefile |  1 +
 support/xclone.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++
 support/xsched.h | 34 ++++++++++++++++++++++++++++++++
 3 files changed, 85 insertions(+)
 create mode 100644 support/xclone.c
 create mode 100644 support/xsched.h

diff --git a/support/Makefile b/support/Makefile
index a40a2f5bca..05e8c292b7 100644
--- a/support/Makefile
+++ b/support/Makefile
@@ -86,6 +86,7 @@ libsupport-routines = \
   xchdir \
   xchroot \
   xclock_gettime \
+  xclone \
   xclose \
   xconnect \
   xcopy_file_range \
diff --git a/support/xclone.c b/support/xclone.c
new file mode 100644
index 0000000000..924d2b8754
--- /dev/null
+++ b/support/xclone.c
@@ -0,0 +1,50 @@
+/* Auxiliary functions to issue the clone syscall.
+   Copyright (C) 2021 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/>.  */
+
+#ifdef __linux__
+# include <support/check.h>
+# include <stackinfo.h>  /* For _STACK_GROWS_{UP,DOWN}.  */
+# include <xsched.h>
+
+pid_t
+xclone (int (*fn) (void *arg), void *arg, void *stack, size_t stack_size,
+	int flags)
+{
+  pid_t r = -1;
+
+# ifdef __ia64__
+  extern int __clone2 (int (*fn) (void *arg), void *stack, size_t stack_size,
+		       int flags, void *arg, ...);
+  r = __clone2 (f, stack, stack_size, flags, arg, /* ptid */ NULL,
+		/* tls */ NULL, /* ctid  */ ctid);
+# else
+#  if _STACK_GROWS_DOWN
+  r = clone (fn, stack + stack_size, flags, arg, /* ptid */ NULL,
+	     /* tls */ NULL, /* ctid */  NULL);
+#  elif _STACK_GROWS_UP
+  r = clone (fn, stack, flags, arg, /* ptid */ NULL, /* tls */ NULL,
+	     &ctid);
+#  endif
+# endif
+
+  if (r < 0)
+    FAIL_EXIT1 ("clone: %m");
+
+  return r;
+}
+#endif
diff --git a/support/xsched.h b/support/xsched.h
new file mode 100644
index 0000000000..eefd731940
--- /dev/null
+++ b/support/xsched.h
@@ -0,0 +1,34 @@
+/* Wrapper for sched.h functions.
+   Copyright (C) 2021 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/>.  */
+
+#ifndef SUPPORT_XSCHED_H
+#define SUPPORT_XSCHED_H
+
+__BEGIN_DECLS
+
+#include <sched.h>
+#include <sys/types.h>
+
+#ifdef __linux__
+pid_t xclone (int (*fn) (void *arg), void *arg, void *stack,
+	      size_t stack_size, int flags);
+#endif
+
+__END_DECLS
+
+#endif
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [COMMITTED 2.31 4/7] support: Fix xclone build failures on ia64 and hppa
  2022-08-18 10:02 [COMMITTED 2.31 1/7] support: Add create_temp_file_in_dir Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 2/7] Add xchdir to libsupport Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 3/7] support: Add xclone Aurelien Jarno
@ 2022-08-18 10:02 ` Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 5/7] support: Add helpers to create paths longer than PATH_MAX Aurelien Jarno
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 7+ messages in thread
From: Aurelien Jarno @ 2022-08-18 10:02 UTC (permalink / raw)
  To: libc-stable; +Cc: Florian Weimer

From: Florian Weimer <fweimer@redhat.com>

(cherry picked from commit 97ed4749becdc20481688ee074e90507ca3501dd)
---
 support/xclone.c | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/support/xclone.c b/support/xclone.c
index 924d2b8754..243eee8b23 100644
--- a/support/xclone.c
+++ b/support/xclone.c
@@ -30,15 +30,14 @@ xclone (int (*fn) (void *arg), void *arg, void *stack, size_t stack_size,
 # ifdef __ia64__
   extern int __clone2 (int (*fn) (void *arg), void *stack, size_t stack_size,
 		       int flags, void *arg, ...);
-  r = __clone2 (f, stack, stack_size, flags, arg, /* ptid */ NULL,
-		/* tls */ NULL, /* ctid  */ ctid);
+  r = __clone2 (fn, stack, stack_size, flags, arg, /* ptid */ NULL,
+		/* tls */ NULL, /* ctid  */ NULL);
 # else
 #  if _STACK_GROWS_DOWN
   r = clone (fn, stack + stack_size, flags, arg, /* ptid */ NULL,
 	     /* tls */ NULL, /* ctid */  NULL);
 #  elif _STACK_GROWS_UP
-  r = clone (fn, stack, flags, arg, /* ptid */ NULL, /* tls */ NULL,
-	     &ctid);
+  r = clone (fn, stack, flags, arg, /* ptid */ NULL, /* tls */ NULL, NULL);
 #  endif
 # endif
 
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [COMMITTED 2.31 5/7] support: Add helpers to create paths longer than PATH_MAX
  2022-08-18 10:02 [COMMITTED 2.31 1/7] support: Add create_temp_file_in_dir Aurelien Jarno
                   ` (2 preceding siblings ...)
  2022-08-18 10:02 ` [COMMITTED 2.31 4/7] support: Fix xclone build failures on ia64 and hppa Aurelien Jarno
@ 2022-08-18 10:02 ` Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 6/7] getcwd: Set errno to ERANGE for size == 1 (CVE-2021-3999) Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 7/7] Linux: Detect user namespace support in io/tst-getcwd-smallbuff Aurelien Jarno
  5 siblings, 0 replies; 7+ messages in thread
From: Aurelien Jarno @ 2022-08-18 10:02 UTC (permalink / raw)
  To: libc-stable; +Cc: Siddhesh Poyarekar, Adhemerval Zanella

From: Siddhesh Poyarekar <siddhesh@sourceware.org>

Add new helpers support_create_and_chdir_toolong_temp_directory and
support_chdir_toolong_temp_directory to create and descend into
directory trees longer than PATH_MAX.

Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit fb7bff12e81c677a6622f724edd4d4987dd9d971)
---
 support/support.h   |   1 +
 support/temp_file.c | 161 +++++++++++++++++++++++++++++++++++++++++---
 support/temp_file.h |   9 +++
 3 files changed, 161 insertions(+), 10 deletions(-)

diff --git a/support/support.h b/support/support.h
index 77d68c2aba..0536474c41 100644
--- a/support/support.h
+++ b/support/support.h
@@ -23,6 +23,7 @@
 #ifndef SUPPORT_H
 #define SUPPORT_H
 
+#include <stdbool.h>
 #include <stddef.h>
 #include <sys/cdefs.h>
 /* For mode_t.  */
diff --git a/support/temp_file.c b/support/temp_file.c
index 98bd235526..e41128c2d4 100644
--- a/support/temp_file.c
+++ b/support/temp_file.c
@@ -1,5 +1,6 @@
 /* Temporary file handling for tests.
-   Copyright (C) 1998-2020 Free Software Foundation, Inc.
+   Copyright (C) 1998-2022 Free Software Foundation, Inc.
+   Copyright The GNU Tools Authors.
    This file is part of the GNU C Library.
 
    The GNU C Library is free software; you can redistribute it and/or
@@ -20,15 +21,17 @@
    some 32-bit platforms. */
 #define _FILE_OFFSET_BITS 64
 
+#include <support/check.h>
 #include <support/temp_file.h>
 #include <support/temp_file-internal.h>
 #include <support/support.h>
 
+#include <errno.h>
 #include <paths.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
+#include <xunistd.h>
 
 /* List of temporary files.  */
 static struct temp_name_list
@@ -36,14 +39,20 @@ static struct temp_name_list
   struct temp_name_list *next;
   char *name;
   pid_t owner;
+  bool toolong;
 } *temp_name_list;
 
 /* Location of the temporary files.  Set by the test skeleton via
    support_set_test_dir.  The string is not be freed.  */
 static const char *test_dir = _PATH_TMP;
 
-void
-add_temp_file (const char *name)
+/* Name of subdirectories in a too long temporary directory tree.  */
+static char toolong_subdir[NAME_MAX + 1];
+static bool toolong_initialized;
+static size_t toolong_path_max;
+
+static void
+add_temp_file_internal (const char *name, bool toolong)
 {
   struct temp_name_list *newp
     = (struct temp_name_list *) xcalloc (sizeof (*newp), 1);
@@ -53,12 +62,19 @@ add_temp_file (const char *name)
       newp->name = newname;
       newp->next = temp_name_list;
       newp->owner = getpid ();
+      newp->toolong = toolong;
       temp_name_list = newp;
     }
   else
     free (newp);
 }
 
+void
+add_temp_file (const char *name)
+{
+  add_temp_file_internal (name, false);
+}
+
 int
 create_temp_file_in_dir (const char *base, const char *dir, char **filename)
 {
@@ -90,8 +106,8 @@ create_temp_file (const char *base, char **filename)
   return create_temp_file_in_dir (base, test_dir, filename);
 }
 
-char *
-support_create_temp_directory (const char *base)
+static char *
+create_temp_directory_internal (const char *base, bool toolong)
 {
   char *path = xasprintf ("%s/%sXXXXXX", test_dir, base);
   if (mkdtemp (path) == NULL)
@@ -99,16 +115,132 @@ support_create_temp_directory (const char *base)
       printf ("error: mkdtemp (\"%s\"): %m", path);
       exit (1);
     }
-  add_temp_file (path);
+  add_temp_file_internal (path, toolong);
   return path;
 }
 
-/* Helper functions called by the test skeleton follow.  */
+char *
+support_create_temp_directory (const char *base)
+{
+  return create_temp_directory_internal (base, false);
+}
+
+static void
+ensure_toolong_initialized (void)
+{
+  if (!toolong_initialized)
+    FAIL_EXIT1 ("uninitialized toolong directory tree\n");
+}
+
+static void
+initialize_toolong (const char *base)
+{
+  long name_max = pathconf (base, _PC_NAME_MAX);
+  name_max = (name_max < 0 ? 64
+	      : (name_max < sizeof (toolong_subdir) ? name_max
+		 : sizeof (toolong_subdir) - 1));
+
+  long path_max = pathconf (base, _PC_PATH_MAX);
+  path_max = (path_max < 0 ? 1024
+	      : path_max <= PTRDIFF_MAX ? path_max : PTRDIFF_MAX);
+
+  /* Sanity check to ensure that the test does not create temporary directories
+     in different filesystems because this API doesn't support it.  */
+  if (toolong_initialized)
+    {
+      if (name_max != strlen (toolong_subdir))
+	FAIL_UNSUPPORTED ("name_max: Temporary directories in different"
+			  " filesystems not supported yet\n");
+      if (path_max != toolong_path_max)
+	FAIL_UNSUPPORTED ("path_max: Temporary directories in different"
+			  " filesystems not supported yet\n");
+      return;
+    }
+
+  toolong_path_max = path_max;
+
+  size_t len = name_max;
+  memset (toolong_subdir, 'X', len);
+  toolong_initialized = true;
+}
+
+char *
+support_create_and_chdir_toolong_temp_directory (const char *basename)
+{
+  char *base = create_temp_directory_internal (basename, true);
+  xchdir (base);
+
+  initialize_toolong (base);
+
+  size_t sz = strlen (toolong_subdir);
+
+  /* Create directories and descend into them so that the final path is larger
+     than PATH_MAX.  */
+  for (size_t i = 0; i <= toolong_path_max / sz; i++)
+    {
+      int ret = mkdir (toolong_subdir, S_IRWXU);
+      if (ret != 0 && errno == ENAMETOOLONG)
+	FAIL_UNSUPPORTED ("Filesystem does not support creating too long "
+			  "directory trees\n");
+      else if (ret != 0)
+	FAIL_EXIT1 ("Failed to create directory tree: %m\n");
+      xchdir (toolong_subdir);
+    }
+  return base;
+}
 
 void
-support_set_test_dir (const char *path)
+support_chdir_toolong_temp_directory (const char *base)
 {
-  test_dir = path;
+  ensure_toolong_initialized ();
+
+  xchdir (base);
+
+  size_t sz = strlen (toolong_subdir);
+  for (size_t i = 0; i <= toolong_path_max / sz; i++)
+    xchdir (toolong_subdir);
+}
+
+/* Helper functions called by the test skeleton follow.  */
+
+static void
+remove_toolong_subdirs (const char *base)
+{
+  ensure_toolong_initialized ();
+
+  if (chdir (base) != 0)
+    {
+      printf ("warning: toolong cleanup base failed: chdir (\"%s\"): %m\n",
+	      base);
+      return;
+    }
+
+  /* Descend.  */
+  int levels = 0;
+  size_t sz = strlen (toolong_subdir);
+  for (levels = 0; levels <= toolong_path_max / sz; levels++)
+    if (chdir (toolong_subdir) != 0)
+      {
+	printf ("warning: toolong cleanup failed: chdir (\"%s\"): %m\n",
+		toolong_subdir);
+	break;
+      }
+
+  /* Ascend and remove.  */
+  while (--levels >= 0)
+    {
+      if (chdir ("..") != 0)
+	{
+	  printf ("warning: toolong cleanup failed: chdir (\"..\"): %m\n");
+	  return;
+	}
+      if (remove (toolong_subdir) != 0)
+	{
+	  printf ("warning: could not remove subdirectory: %s: %m\n",
+		  toolong_subdir);
+	  return;
+	}
+    }
 }
 
 void
@@ -123,6 +255,9 @@ support_delete_temp_files (void)
 	 around, to prevent PID reuse.)  */
       if (temp_name_list->owner == pid)
 	{
+	  if (temp_name_list->toolong)
+	    remove_toolong_subdirs (temp_name_list->name);
+
 	  if (remove (temp_name_list->name) != 0)
 	    printf ("warning: could not remove temporary file: %s: %m\n",
 		    temp_name_list->name);
@@ -147,3 +282,9 @@ support_print_temp_files (FILE *f)
       fprintf (f, ")\n");
     }
 }
+
+void
+support_set_test_dir (const char *path)
+{
+  test_dir = path;
+}
diff --git a/support/temp_file.h b/support/temp_file.h
index ac61105428..2598f82136 100644
--- a/support/temp_file.h
+++ b/support/temp_file.h
@@ -44,6 +44,15 @@ int create_temp_file_in_dir (const char *base, const char *dir,
    returns.  The caller should free this string.  */
 char *support_create_temp_directory (const char *base);
 
+/* Create a temporary directory tree that is longer than PATH_MAX and schedule
+   it for deletion.  BASENAME is used as a prefix for the unique directory
+   name, which the function returns.  The caller should free this string.  */
+char *support_create_and_chdir_toolong_temp_directory (const char *basename);
+
+/* Change into the innermost directory of the directory tree BASE, which was
+   created using support_create_and_chdir_toolong_temp_directory.  */
+void support_chdir_toolong_temp_directory (const char *base);
+
 __END_DECLS
 
 #endif /* SUPPORT_TEMP_FILE_H */
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [COMMITTED 2.31 6/7] getcwd: Set errno to ERANGE for size == 1 (CVE-2021-3999)
  2022-08-18 10:02 [COMMITTED 2.31 1/7] support: Add create_temp_file_in_dir Aurelien Jarno
                   ` (3 preceding siblings ...)
  2022-08-18 10:02 ` [COMMITTED 2.31 5/7] support: Add helpers to create paths longer than PATH_MAX Aurelien Jarno
@ 2022-08-18 10:02 ` Aurelien Jarno
  2022-08-18 10:02 ` [COMMITTED 2.31 7/7] Linux: Detect user namespace support in io/tst-getcwd-smallbuff Aurelien Jarno
  5 siblings, 0 replies; 7+ messages in thread
From: Aurelien Jarno @ 2022-08-18 10:02 UTC (permalink / raw)
  To: libc-stable
  Cc: Siddhesh Poyarekar, Andreas Schwab, Adhemerval Zanella,
	Qualys Security Advisory

From: Siddhesh Poyarekar <siddhesh@sourceware.org>

No valid path returned by getcwd would fit into 1 byte, so reject the
size early and return NULL with errno set to ERANGE.  This change is
prompted by CVE-2021-3999, which describes a single byte buffer
underflow and overflow when all of the following conditions are met:

- The buffer size (i.e. the second argument of getcwd) is 1 byte
- The current working directory is too long
- '/' is also mounted on the current working directory

Sequence of events:

- In sysdeps/unix/sysv/linux/getcwd.c, the syscall returns ENAMETOOLONG
  because the linux kernel checks for name length before it checks
  buffer size

- The code falls back to the generic getcwd in sysdeps/posix

- In the generic func, the buf[0] is set to '\0' on line 250

- this while loop on line 262 is bypassed:

    while (!(thisdev == rootdev && thisino == rootino))

  since the rootfs (/) is bind mounted onto the directory and the flow
  goes on to line 449, where it puts a '/' in the byte before the
  buffer.

- Finally on line 458, it moves 2 bytes (the underflowed byte and the
  '\0') to the buf[0] and buf[1], resulting in a 1 byte buffer overflow.

- buf is returned on line 469 and errno is not set.

This resolves BZ #28769.

Reviewed-by: Andreas Schwab <schwab@linux-m68k.org>
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
Signed-off-by: Qualys Security Advisory <qsa@qualys.com>
Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 23e0e8f5f1fb5ed150253d986ecccdc90c2dcd5e)
---
 NEWS                                          |   7 +
 sysdeps/posix/getcwd.c                        |   8 +
 sysdeps/unix/sysv/linux/Makefile              |   2 +-
 .../unix/sysv/linux/tst-getcwd-smallbuff.c    | 241 ++++++++++++++++++
 4 files changed, 257 insertions(+), 1 deletion(-)
 create mode 100644 sysdeps/unix/sysv/linux/tst-getcwd-smallbuff.c

diff --git a/NEWS b/NEWS
index 8b492d48d1..de7bf22aa4 100644
--- a/NEWS
+++ b/NEWS
@@ -44,6 +44,7 @@ The following bugs are resolved with this release:
   [28524] Conversion from ISO-2022-JP-3 with iconv may emit spurious NULs
   [28755] overflow bug in wcsncmp_avx2 and wcsncmp_evex
   [28768] CVE-2022-23218: Buffer overflow in sunrpc svcunix_create
+  [28769] CVE-2021-3999: Off-by-one buffer overflow/underflow in getcwd()
   [28896] strncmp-avx2-rtm and wcsncmp-avx2-rtm fallback on non-rtm
     variants when avoiding overflow
 
@@ -75,6 +76,12 @@ Security related changes:
   CVE-2020-29562: An assertion failure has been fixed in the iconv function
   when invoked with UCS4 input containing an invalid character.
 
+  CVE-2021-3999: Passing a buffer of size exactly 1 byte to the getcwd
+  function may result in an off-by-one buffer underflow and overflow
+  when the current working directory is longer than PATH_MAX and also
+  corresponds to the / directory through an unprivileged mount
+  namespace.  Reported by Qualys.
+
   CVE-2022-23219: Passing an overlong file name to the clnt_create
   legacy function could result in a stack-based buffer overflow when
   using the "unix" protocol.  Reported by Martin Sebor.
diff --git a/sysdeps/posix/getcwd.c b/sysdeps/posix/getcwd.c
index f00b337a13..839d78d7b7 100644
--- a/sysdeps/posix/getcwd.c
+++ b/sysdeps/posix/getcwd.c
@@ -241,6 +241,14 @@ __getcwd (char *buf, size_t size)
   char *path;
 #ifndef NO_ALLOCATION
   size_t allocated = size;
+
+  /* A size of 1 byte is never useful.  */
+  if (allocated == 1)
+    {
+      __set_errno (ERANGE);
+      return NULL;
+    }
+
   if (size == 0)
     {
       if (buf != NULL)
diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index 5fbde369c3..0a0da00151 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -275,7 +275,7 @@ sysdep_routines += xstatconv internal_statvfs internal_statvfs64 \
 
 sysdep_headers += bits/fcntl-linux.h
 
-tests += tst-fallocate tst-fallocate64
+tests += tst-fallocate tst-fallocate64 tst-getcwd-smallbuff
 endif
 
 ifeq ($(subdir),elf)
diff --git a/sysdeps/unix/sysv/linux/tst-getcwd-smallbuff.c b/sysdeps/unix/sysv/linux/tst-getcwd-smallbuff.c
new file mode 100644
index 0000000000..d460d6e766
--- /dev/null
+++ b/sysdeps/unix/sysv/linux/tst-getcwd-smallbuff.c
@@ -0,0 +1,241 @@
+/* Verify that getcwd returns ERANGE for size 1 byte and does not underflow
+   buffer when the CWD is too long and is also a mount target of /.  See bug
+   #28769 or CVE-2021-3999 for more context.
+   Copyright The GNU Toolchain Authors.
+   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 <fcntl.h>
+#include <intprops.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <support/check.h>
+#include <support/temp_file.h>
+#include <support/xsched.h>
+#include <support/xunistd.h>
+
+static char *base;
+#define BASENAME "tst-getcwd-smallbuff"
+#define MOUNT_NAME "mpoint"
+static int sockfd[2];
+
+static void
+do_cleanup (void)
+{
+  support_chdir_toolong_temp_directory (base);
+  TEST_VERIFY_EXIT (rmdir (MOUNT_NAME) == 0);
+  free (base);
+}
+
+static void
+send_fd (const int sock, const int fd)
+{
+  struct msghdr msg = {0};
+  union
+    {
+      struct cmsghdr hdr;
+      char buf[CMSG_SPACE (sizeof (int))];
+    } cmsgbuf = {0};
+  struct cmsghdr *cmsg;
+  struct iovec vec;
+  char ch = 'A';
+  ssize_t n;
+
+  msg.msg_control = &cmsgbuf.buf;
+  msg.msg_controllen = sizeof (cmsgbuf.buf);
+
+  cmsg = CMSG_FIRSTHDR (&msg);
+  cmsg->cmsg_len = CMSG_LEN (sizeof (int));
+  cmsg->cmsg_level = SOL_SOCKET;
+  cmsg->cmsg_type = SCM_RIGHTS;
+  memcpy (CMSG_DATA (cmsg), &fd, sizeof (fd));
+
+  vec.iov_base = &ch;
+  vec.iov_len = 1;
+  msg.msg_iov = &vec;
+  msg.msg_iovlen = 1;
+
+  while ((n = sendmsg (sock, &msg, 0)) == -1 && errno == EINTR);
+
+  TEST_VERIFY_EXIT (n == 1);
+}
+
+static int
+recv_fd (const int sock)
+{
+  struct msghdr msg = {0};
+  union
+    {
+      struct cmsghdr hdr;
+      char buf[CMSG_SPACE(sizeof(int))];
+    } cmsgbuf = {0};
+  struct cmsghdr *cmsg;
+  struct iovec vec;
+  ssize_t n;
+  char ch = '\0';
+  int fd = -1;
+
+  vec.iov_base = &ch;
+  vec.iov_len = 1;
+  msg.msg_iov = &vec;
+  msg.msg_iovlen = 1;
+
+  msg.msg_control = &cmsgbuf.buf;
+  msg.msg_controllen = sizeof (cmsgbuf.buf);
+
+  while ((n = recvmsg (sock, &msg, 0)) == -1 && errno == EINTR);
+  if (n != 1 || ch != 'A')
+    return -1;
+
+  cmsg = CMSG_FIRSTHDR (&msg);
+  if (cmsg == NULL)
+    return -1;
+  if (cmsg->cmsg_type != SCM_RIGHTS)
+    return -1;
+  memcpy (&fd, CMSG_DATA (cmsg), sizeof (fd));
+  if (fd < 0)
+    return -1;
+  return fd;
+}
+
+static int
+child_func (void * const arg)
+{
+  xclose (sockfd[0]);
+  const int sock = sockfd[1];
+  char ch;
+
+  TEST_VERIFY_EXIT (read (sock, &ch, 1) == 1);
+  TEST_VERIFY_EXIT (ch == '1');
+
+  if (mount ("/", MOUNT_NAME, NULL, MS_BIND | MS_REC, NULL))
+    FAIL_EXIT1 ("mount failed: %m\n");
+  const int fd = xopen ("mpoint",
+			O_RDONLY | O_PATH | O_DIRECTORY | O_NOFOLLOW, 0);
+
+  send_fd (sock, fd);
+  xclose (fd);
+
+  TEST_VERIFY_EXIT (read (sock, &ch, 1) == 1);
+  TEST_VERIFY_EXIT (ch == 'a');
+
+  xclose (sock);
+  return 0;
+}
+
+static void
+update_map (char * const mapping, const char * const map_file)
+{
+  const size_t map_len = strlen (mapping);
+
+  const int fd = xopen (map_file, O_WRONLY, 0);
+  xwrite (fd, mapping, map_len);
+  xclose (fd);
+}
+
+static void
+proc_setgroups_write (const long child_pid, const char * const str)
+{
+  const size_t str_len = strlen(str);
+
+  char setgroups_path[sizeof ("/proc//setgroups") + INT_STRLEN_BOUND (long)];
+
+  snprintf (setgroups_path, sizeof (setgroups_path),
+	    "/proc/%ld/setgroups", child_pid);
+
+  const int fd = open (setgroups_path, O_WRONLY);
+
+  if (fd < 0)
+    {
+      TEST_VERIFY_EXIT (errno == ENOENT);
+      FAIL_UNSUPPORTED ("/proc/%ld/setgroups not found\n", child_pid);
+    }
+
+  xwrite (fd, str, str_len);
+  xclose(fd);
+}
+
+static char child_stack[1024 * 1024];
+
+int
+do_test (void)
+{
+  base = support_create_and_chdir_toolong_temp_directory (BASENAME);
+
+  xmkdir (MOUNT_NAME, S_IRWXU);
+  atexit (do_cleanup);
+
+  TEST_VERIFY_EXIT (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd) == 0);
+  pid_t child_pid = xclone (child_func, NULL, child_stack,
+			    sizeof (child_stack),
+			    CLONE_NEWUSER | CLONE_NEWNS | SIGCHLD);
+
+  xclose (sockfd[1]);
+  const int sock = sockfd[0];
+
+  char map_path[sizeof ("/proc//uid_map") + INT_STRLEN_BOUND (long)];
+  char map_buf[sizeof ("0  1") + INT_STRLEN_BOUND (long)];
+
+  snprintf (map_path, sizeof (map_path), "/proc/%ld/uid_map",
+	    (long) child_pid);
+  snprintf (map_buf, sizeof (map_buf), "0 %ld 1", (long) getuid());
+  update_map (map_buf, map_path);
+
+  proc_setgroups_write ((long) child_pid, "deny");
+  snprintf (map_path, sizeof (map_path), "/proc/%ld/gid_map",
+	    (long) child_pid);
+  snprintf (map_buf, sizeof (map_buf), "0 %ld 1", (long) getgid());
+  update_map (map_buf, map_path);
+
+  TEST_VERIFY_EXIT (send (sock, "1", 1, MSG_NOSIGNAL) == 1);
+  const int fd = recv_fd (sock);
+  TEST_VERIFY_EXIT (fd >= 0);
+  TEST_VERIFY_EXIT (fchdir (fd) == 0);
+
+  static char buf[2 * 10 + 1];
+  memset (buf, 'A', sizeof (buf));
+
+  /* Finally, call getcwd and check if it resulted in a buffer underflow.  */
+  char * cwd = getcwd (buf + sizeof (buf) / 2, 1);
+  TEST_VERIFY (cwd == NULL);
+  TEST_VERIFY (errno == ERANGE);
+
+  for (int i = 0; i < sizeof (buf); i++)
+    if (buf[i] != 'A')
+      {
+	printf ("buf[%d] = %02x\n", i, (unsigned int) buf[i]);
+	support_record_failure ();
+      }
+
+  TEST_VERIFY_EXIT (send (sock, "a", 1, MSG_NOSIGNAL) == 1);
+  xclose (sock);
+  TEST_VERIFY_EXIT (xwaitpid (child_pid, NULL, 0) == child_pid);
+
+  return 0;
+}
+
+#define CLEANUP_HANDLER do_cleanup
+#include <support/test-driver.c>
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

* [COMMITTED 2.31 7/7] Linux: Detect user namespace support in io/tst-getcwd-smallbuff
  2022-08-18 10:02 [COMMITTED 2.31 1/7] support: Add create_temp_file_in_dir Aurelien Jarno
                   ` (4 preceding siblings ...)
  2022-08-18 10:02 ` [COMMITTED 2.31 6/7] getcwd: Set errno to ERANGE for size == 1 (CVE-2021-3999) Aurelien Jarno
@ 2022-08-18 10:02 ` Aurelien Jarno
  5 siblings, 0 replies; 7+ messages in thread
From: Aurelien Jarno @ 2022-08-18 10:02 UTC (permalink / raw)
  To: libc-stable; +Cc: Florian Weimer, Siddhesh Poyarekar

From: Florian Weimer <fweimer@redhat.com>

Otherwise the test fails with certain container runtimes.

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
(cherry picked from commit 5b8e7980c5dabd9aaefeba4f0208baa8cf7653ee)
---
 sysdeps/unix/sysv/linux/tst-getcwd-smallbuff.c | 18 ++++++++++++++++++
 1 file changed, 18 insertions(+)

diff --git a/sysdeps/unix/sysv/linux/tst-getcwd-smallbuff.c b/sysdeps/unix/sysv/linux/tst-getcwd-smallbuff.c
index d460d6e766..55362f6060 100644
--- a/sysdeps/unix/sysv/linux/tst-getcwd-smallbuff.c
+++ b/sysdeps/unix/sysv/linux/tst-getcwd-smallbuff.c
@@ -34,6 +34,7 @@
 #include <sys/un.h>
 #include <support/check.h>
 #include <support/temp_file.h>
+#include <support/test-driver.h>
 #include <support/xsched.h>
 #include <support/xunistd.h>
 
@@ -188,6 +189,23 @@ do_test (void)
   xmkdir (MOUNT_NAME, S_IRWXU);
   atexit (do_cleanup);
 
+  /* Check whether user namespaces are supported.  */
+  {
+    pid_t pid = xfork ();
+    if (pid == 0)
+      {
+	if (unshare (CLONE_NEWUSER | CLONE_NEWNS) != 0)
+	  _exit (EXIT_UNSUPPORTED);
+	else
+	  _exit (0);
+      }
+    int status;
+    xwaitpid (pid, &status, 0);
+    TEST_VERIFY_EXIT (WIFEXITED (status));
+    if (WEXITSTATUS (status) != 0)
+      return WEXITSTATUS (status);
+  }
+
   TEST_VERIFY_EXIT (socketpair (AF_UNIX, SOCK_STREAM, 0, sockfd) == 0);
   pid_t child_pid = xclone (child_func, NULL, child_stack,
 			    sizeof (child_stack),
-- 
2.35.1


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2022-08-18 10:02 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-18 10:02 [COMMITTED 2.31 1/7] support: Add create_temp_file_in_dir Aurelien Jarno
2022-08-18 10:02 ` [COMMITTED 2.31 2/7] Add xchdir to libsupport Aurelien Jarno
2022-08-18 10:02 ` [COMMITTED 2.31 3/7] support: Add xclone Aurelien Jarno
2022-08-18 10:02 ` [COMMITTED 2.31 4/7] support: Fix xclone build failures on ia64 and hppa Aurelien Jarno
2022-08-18 10:02 ` [COMMITTED 2.31 5/7] support: Add helpers to create paths longer than PATH_MAX Aurelien Jarno
2022-08-18 10:02 ` [COMMITTED 2.31 6/7] getcwd: Set errno to ERANGE for size == 1 (CVE-2021-3999) Aurelien Jarno
2022-08-18 10:02 ` [COMMITTED 2.31 7/7] Linux: Detect user namespace support in io/tst-getcwd-smallbuff Aurelien Jarno

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).