Message ID | 20220118090728.1825487-2-siddhesh@sourceware.org |
---|---|
State | New |
Headers | show |
Series | Fixes for CVE-2021-3998 and CVE-2021-3999 | expand |
On 18/01/2022 06:07, Siddhesh Poyarekar wrote: > 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. > > Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org> > --- > support/temp_file.c | 157 +++++++++++++++++++++++++++++++++++++++++--- > support/temp_file.h | 11 ++++ > 2 files changed, 160 insertions(+), 8 deletions(-) > > diff --git a/support/temp_file.c b/support/temp_file.c > index e7bb8aadb9..c3e83912ac 100644 > --- a/support/temp_file.c > +++ b/support/temp_file.c > @@ -36,14 +36,31 @@ 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]; > + > +/* Return the maximum size of path on the target. */ > +static inline size_t > +get_path_max (void) > +{ > +#ifdef PATH_MAX > + return PATH_MAX; > +#else > + size_t path_max = pathconf ("/", _PC_PATH_MAX); > + return (path_max < 0 ? 1024 > + : path_max <= PTRDIFF_MAX ? path_max : PTRDIFF_MAX); > +#endif > +} > + > +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 +70,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 +114,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 +123,124 @@ 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_subdir_initialized (void) > +{ > + for (size_t i = 0; i < NAME_MAX; i++) > + if (toolong_subdir[i] != 'X') > + { > + printf ("uninitialized toolong directory tree\n"); > + exit (1); > + } > +} Maybe use FAIL_EXIT1 here? > + > +char * > +support_create_and_chdir_toolong_temp_directory (const char *basename) > +{ > + size_t path_max = get_path_max (); > + > + char *base = create_temp_directory_internal (basename, true); > + > + if (chdir (base) != 0) > + { > + printf ("error creating toolong base: chdir (\"%s\"): %m", base); > + exit (1); > + } > + Use xchdir. > + memset (toolong_subdir, 'X', sizeof (toolong_subdir) - 1); > + toolong_subdir[NAME_MAX] = '\0'; > + > + /* Create directories and descend into them so that the final path is larger > + than PATH_MAX. */ > + for (size_t i = 0; i <= path_max / sizeof (toolong_subdir); i++) > + { > + if (mkdir (toolong_subdir, S_IRWXU) != 0) > + { > + printf ("error creating toolong subdir: mkdir (\"%s\"): %m", > + toolong_subdir); > + exit (1); > + } Use xmkdir. > + if (chdir (toolong_subdir) != 0) Use xchdir. > + { > + printf ("error creating toolong subdir: chdir (\"%s\"): %m", > + toolong_subdir); > + exit (1); > + } > + } > + return base; > +} > > void > -support_set_test_dir (const char *path) > +support_chdir_toolong_temp_directory (const char *base) > { > - test_dir = path; > + size_t path_max = get_path_max (); > + ensure_toolong_subdir_initialized (); > + > + if (chdir (base) != 0) > + { > + printf ("error: chdir (\"%s\"): %m", base); > + exit (1); > + } > + > + for (size_t i = 0; i <= path_max / sizeof (toolong_subdir); i++) > + if (chdir (toolong_subdir) != 0) Use xchdir. > + { > + printf ("error subdir: chdir (\"%s\"): %m", toolong_subdir); > + exit (1); > + } > +} > + > +/* Helper functions called by the test skeleton follow. */ > + > +static void > +remove_toolong_subdirs (const char *base) > +{ > + size_t path_max = get_path_max (); > + > + ensure_toolong_subdir_initialized (); > + > + if (chdir (base) != 0) > + { > + printf ("warning: toolong cleanup base failed: chdir (\"%s\"): %m\n", > + base); > + return; > + } > + > + /* Descend. */ > + int levels = 0; > + for (levels = 0; levels <= path_max / sizeof (toolong_subdir); levels++) > + if (chdir (toolong_subdir) != 0) > + { > + printf ("warning: toolong cleanup failed: chdir (\"%s\"): %m\n", > + toolong_subdir); > + return; > + } > + > + /* 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; > + } > + } > } Throwing an warning should be fine in such cases. > > 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 50a443abe4..de01fbbceb 100644 > --- a/support/temp_file.h > +++ b/support/temp_file.h > @@ -19,6 +19,8 @@ > #ifndef SUPPORT_TEMP_FILE_H > #define SUPPORT_TEMP_FILE_H > > +#include <limits.h> > +#include <unistd.h> > #include <sys/cdefs.h> > > __BEGIN_DECLS > @@ -44,6 +46,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 */
On 19/01/2022 01:12, Adhemerval Zanella wrote: >> + { >> + printf ("error subdir: chdir (\"%s\"): %m", toolong_subdir); >> + exit (1); >> + } >> +} >> + >> +/* Helper functions called by the test skeleton follow. */ >> + >> +static void >> +remove_toolong_subdirs (const char *base) >> +{ >> + size_t path_max = get_path_max (); >> + >> + ensure_toolong_subdir_initialized (); >> + >> + if (chdir (base) != 0) >> + { >> + printf ("warning: toolong cleanup base failed: chdir (\"%s\"): %m\n", >> + base); >> + return; >> + } >> + >> + /* Descend. */ >> + int levels = 0; >> + for (levels = 0; levels <= path_max / sizeof (toolong_subdir); levels++) >> + if (chdir (toolong_subdir) != 0) >> + { >> + printf ("warning: toolong cleanup failed: chdir (\"%s\"): %m\n", >> + toolong_subdir); >> + return; >> + } >> + >> + /* 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; >> + } >> + } >> } > > Throwing an warning should be fine in such cases. > You mean by using the warning() function? It looks like printf is the common way to do this in support/* Thanks, Siddhesh
diff --git a/support/temp_file.c b/support/temp_file.c index e7bb8aadb9..c3e83912ac 100644 --- a/support/temp_file.c +++ b/support/temp_file.c @@ -36,14 +36,31 @@ 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]; + +/* Return the maximum size of path on the target. */ +static inline size_t +get_path_max (void) +{ +#ifdef PATH_MAX + return PATH_MAX; +#else + size_t path_max = pathconf ("/", _PC_PATH_MAX); + return (path_max < 0 ? 1024 + : path_max <= PTRDIFF_MAX ? path_max : PTRDIFF_MAX); +#endif +} + +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 +70,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 +114,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 +123,124 @@ 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_subdir_initialized (void) +{ + for (size_t i = 0; i < NAME_MAX; i++) + if (toolong_subdir[i] != 'X') + { + printf ("uninitialized toolong directory tree\n"); + exit (1); + } +} + +char * +support_create_and_chdir_toolong_temp_directory (const char *basename) +{ + size_t path_max = get_path_max (); + + char *base = create_temp_directory_internal (basename, true); + + if (chdir (base) != 0) + { + printf ("error creating toolong base: chdir (\"%s\"): %m", base); + exit (1); + } + + memset (toolong_subdir, 'X', sizeof (toolong_subdir) - 1); + toolong_subdir[NAME_MAX] = '\0'; + + /* Create directories and descend into them so that the final path is larger + than PATH_MAX. */ + for (size_t i = 0; i <= path_max / sizeof (toolong_subdir); i++) + { + if (mkdir (toolong_subdir, S_IRWXU) != 0) + { + printf ("error creating toolong subdir: mkdir (\"%s\"): %m", + toolong_subdir); + exit (1); + } + if (chdir (toolong_subdir) != 0) + { + printf ("error creating toolong subdir: chdir (\"%s\"): %m", + toolong_subdir); + exit (1); + } + } + return base; +} void -support_set_test_dir (const char *path) +support_chdir_toolong_temp_directory (const char *base) { - test_dir = path; + size_t path_max = get_path_max (); + ensure_toolong_subdir_initialized (); + + if (chdir (base) != 0) + { + printf ("error: chdir (\"%s\"): %m", base); + exit (1); + } + + for (size_t i = 0; i <= path_max / sizeof (toolong_subdir); i++) + if (chdir (toolong_subdir) != 0) + { + printf ("error subdir: chdir (\"%s\"): %m", toolong_subdir); + exit (1); + } +} + +/* Helper functions called by the test skeleton follow. */ + +static void +remove_toolong_subdirs (const char *base) +{ + size_t path_max = get_path_max (); + + ensure_toolong_subdir_initialized (); + + if (chdir (base) != 0) + { + printf ("warning: toolong cleanup base failed: chdir (\"%s\"): %m\n", + base); + return; + } + + /* Descend. */ + int levels = 0; + for (levels = 0; levels <= path_max / sizeof (toolong_subdir); levels++) + if (chdir (toolong_subdir) != 0) + { + printf ("warning: toolong cleanup failed: chdir (\"%s\"): %m\n", + toolong_subdir); + return; + } + + /* 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 50a443abe4..de01fbbceb 100644 --- a/support/temp_file.h +++ b/support/temp_file.h @@ -19,6 +19,8 @@ #ifndef SUPPORT_TEMP_FILE_H #define SUPPORT_TEMP_FILE_H +#include <limits.h> +#include <unistd.h> #include <sys/cdefs.h> __BEGIN_DECLS @@ -44,6 +46,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 */
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. Signed-off-by: Siddhesh Poyarekar <siddhesh@sourceware.org> --- support/temp_file.c | 157 +++++++++++++++++++++++++++++++++++++++++--- support/temp_file.h | 11 ++++ 2 files changed, 160 insertions(+), 8 deletions(-)