From 36cc6c33ade715a844662cefd256f8f8fdd8a05d Mon Sep 17 00:00:00 2001 From: Paul Eggert Date: Wed, 4 May 2016 09:57:48 -0700 Subject: [PATCH] glob: merge glibc changes into lib/glob.c * lib/glob.c (glob_in_dir): Sync with glibc/posix/glob.c, dated 2016-05-04 12:09:35 2016 +0200. Here are the changes: 2016-05-04 CVE-2016-1234: glob: Do not copy d_name field of struct dirent [BZ #19779] 2016-04-29 glob: Simplify the interface for the GLOB_ALTDIRFUNC callback gl_readdir 2015-10-20 Convert miscellaneous function definitions to prototype style 2015-10-20 Convert 113 more function definitions to prototype style (files with assertions) 2015-06-12 Fix getlogin_r namespace (bug 18527). 2014-02-10 Use glibc_likely instead __builtin_expect. 2013-10-20 When glob pattern contains a trailing slash match only directories. Fixes bug 10278. 2013-09-04 glob: silence -Wattribute warnings 2013-06-07 Avoid use of "register" as optimization hint. 2012-09-25 Use size_t instead of int for internal variables in glob (bug 14621) 2011-07-20 Check for overflows in expressions 2011-05-28 Remove unused variable 2011-05-22 Add a few more alloca size checks 2010-03-27 Whitespace fixes 2010-03-27 Fix one more issue with the glob patch 2010-03-24 Fix glob with empty pattern 2008-05-27 Remove useless more "if" tests before "free" * modules/glob (Depends-on): Add stdint. --- ChangeLog | 29 ++ lib/glob.c | 794 +++++++++++++++++++++++++++++++++------------------ modules/glob | 1 + 3 files changed, 540 insertions(+), 284 deletions(-) diff --git a/ChangeLog b/ChangeLog index ee9925d424..803a00884f 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,32 @@ +2016-05-04 Paul Eggert + + glob: merge glibc changes into lib/glob.c + * lib/glob.c (glob_in_dir): Sync with glibc/posix/glob.c, + dated 2016-05-04 12:09:35 2016 +0200. Here are the changes: + 2016-05-04 CVE-2016-1234: glob: Do not copy d_name field of + struct dirent [BZ #19779] + 2016-04-29 glob: Simplify the interface for the GLOB_ALTDIRFUNC + callback gl_readdir + 2015-10-20 Convert miscellaneous function definitions to prototype style + 2015-10-20 Convert 113 more function definitions to prototype style + (files with assertions) + 2015-06-12 Fix getlogin_r namespace (bug 18527). + 2014-02-10 Use glibc_likely instead __builtin_expect. + 2013-10-20 When glob pattern contains a trailing slash match only + directories. Fixes bug 10278. + 2013-09-04 glob: silence -Wattribute warnings + 2013-06-07 Avoid use of "register" as optimization hint. + 2012-09-25 Use size_t instead of int for internal variables in glob + (bug 14621) + 2011-07-20 Check for overflows in expressions + 2011-05-28 Remove unused variable + 2011-05-22 Add a few more alloca size checks + 2010-03-27 Whitespace fixes + 2010-03-27 Fix one more issue with the glob patch + 2010-03-24 Fix glob with empty pattern + 2008-05-27 Remove useless more "if" tests before "free" + * modules/glob (Depends-on): Add stdint. + 2016-05-01 Paul Eggert mktime: port to stricter signed overflow checking diff --git a/lib/glob.c b/lib/glob.c index 52290b2255..ff4fee1a41 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -1,18 +1,19 @@ /* Copyright (C) 1991-2016 Free Software Foundation, Inc. This file is part of the GNU C Library. - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation; either version 3 of the License, or - (at your option) any later version. + 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. - This program is distributed in the hope that it will be useful, + 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 General Public License for more details. + 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 General Public License - along with this program. If not, see . */ + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, see + . */ #ifndef _LIBC /* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc @@ -26,17 +27,17 @@ #include #include #include +#include #include +#include /* Outcomment the following line for production quality code. */ /* #define NDEBUG 1 */ #include -#include - #include /* Needed on stupid SunOS for assert. */ -#if !defined _LIBC || !defined GLOB_ONLY_P +#ifndef GLOB_ONLY_P #include #if !defined POSIX && defined _POSIX_VERSION @@ -57,74 +58,8 @@ #endif #include - - -/* In GNU systems, defines this macro for us. */ -#ifndef _D_EXACT_NAMLEN -# define _D_EXACT_NAMLEN(dirent) strlen ((dirent)->d_name) -#endif - -/* When used in the GNU libc the symbol _DIRENT_HAVE_D_TYPE is available - if the 'd_type' member for 'struct dirent' is available. - HAVE_STRUCT_DIRENT_D_TYPE plays the same role in GNULIB. */ -#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE -/* True if the directory entry D must be of type T. */ -# define DIRENT_MUST_BE(d, t) ((d)->d_type == (t)) - -/* True if the directory entry D might be a symbolic link. */ -# define DIRENT_MIGHT_BE_SYMLINK(d) \ - ((d)->d_type == DT_UNKNOWN || (d)->d_type == DT_LNK) - -/* True if the directory entry D might be a directory. */ -# define DIRENT_MIGHT_BE_DIR(d) \ - ((d)->d_type == DT_DIR || DIRENT_MIGHT_BE_SYMLINK (d)) - -#else /* !HAVE_D_TYPE */ -# define DIRENT_MUST_BE(d, t) false -# define DIRENT_MIGHT_BE_SYMLINK(d) true -# define DIRENT_MIGHT_BE_DIR(d) true -#endif /* HAVE_D_TYPE */ - -/* If the system has the 'struct dirent64' type we use it internally. */ -#if defined _LIBC && !defined COMPILE_GLOB64 -# if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ -# define CONVERT_D_INO(d64, d32) -# else -# define CONVERT_D_INO(d64, d32) \ - (d64)->d_ino = (d32)->d_ino; -# endif - -# ifdef _DIRENT_HAVE_D_TYPE -# define CONVERT_D_TYPE(d64, d32) \ - (d64)->d_type = (d32)->d_type; -# else -# define CONVERT_D_TYPE(d64, d32) -# endif - -# define CONVERT_DIRENT_DIRENT64(d64, d32) \ - memcpy ((d64)->d_name, (d32)->d_name, _D_EXACT_NAMLEN (d32) + 1); \ - CONVERT_D_INO (d64, d32) \ - CONVERT_D_TYPE (d64, d32) -#endif - - -#if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ -/* Posix does not require that the d_ino field be present, and some - systems do not provide it. */ -# define REAL_DIR_ENTRY(dp) 1 -#else -# define REAL_DIR_ENTRY(dp) (dp->d_ino != 0) -#endif /* POSIX */ - #include #include - -/* NAME_MAX is usually defined in or . */ -#include -#ifndef NAME_MAX -# define NAME_MAX (sizeof (((struct dirent *) 0)->d_name)) -#endif - #include #ifdef _LIBC @@ -141,12 +76,14 @@ # endif # define struct_stat64 struct stat64 #else /* !_LIBC */ +# define __getlogin_r(buf, len) getlogin_r (buf, len) # define __stat64(fname, buf) stat (fname, buf) # define __fxstatat64(_, d, f, st, flag) fstatat (d, f, st, flag) # define struct_stat64 struct stat # define __alloca alloca # define __readdir readdir # define __glob_pattern_p glob_pattern_p +# define COMPILE_GLOB64 #endif /* _LIBC */ #include @@ -164,7 +101,110 @@ static const char *next_brace_sub (const char *begin, int flags) __THROWNL; -#endif /* !defined _LIBC || !defined GLOB_ONLY_P */ +/* A representation of a directory entry which does not depend on the + layout of struct dirent, or the size of ino_t. */ +struct readdir_result +{ + const char *name; +# if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE + uint8_t type; +# endif + bool skip_entry; +}; + +# if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE +/* Initializer based on the d_type member of struct dirent. */ +# define D_TYPE_TO_RESULT(source) (source)->d_type, + +/* True if the directory entry D might be a symbolic link. */ +static bool +readdir_result_might_be_symlink (struct readdir_result d) +{ + return d.type == DT_UNKNOWN || d.type == DT_LNK; +} + +/* True if the directory entry D might be a directory. */ +static bool +readdir_result_might_be_dir (struct readdir_result d) +{ + return d.type == DT_DIR || readdir_result_might_be_symlink (d); +} +# else /* defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE */ +# define D_TYPE_TO_RESULT(source) + +/* If we do not have type information, symbolic links and directories + are always a possibility. */ + +static bool +readdir_result_might_be_symlink (struct readdir_result d) +{ + return true; +} + +static bool +readdir_result_might_be_dir (struct readdir_result d) +{ + return true; +} + +# endif /* defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE */ + +# if (defined POSIX || defined WINDOWS32) && !defined __GNU_LIBRARY__ +/* Initializer for skip_entry. POSIX does not require that the d_ino + field be present, and some systems do not provide it. */ +# define D_INO_TO_RESULT(source) false, +# else +# define D_INO_TO_RESULT(source) (source)->d_ino == 0, +# endif + +/* Construct an initializer for a struct readdir_result object from a + struct dirent *. No copy of the name is made. */ +#define READDIR_RESULT_INITIALIZER(source) \ + { \ + source->d_name, \ + D_TYPE_TO_RESULT (source) \ + D_INO_TO_RESULT (source) \ + } + +#endif /* !defined GLOB_ONLY_P */ + +/* Call gl_readdir on STREAM. This macro can be overridden to reduce + type safety if an old interface version needs to be supported. */ +#ifndef GL_READDIR +# define GL_READDIR(pglob, stream) ((pglob)->gl_readdir (stream)) +#endif + +/* Extract name and type from directory entry. No copy of the name is + made. If SOURCE is NULL, result name is NULL. Keep in sync with + convert_dirent64 below. */ +static struct readdir_result +convert_dirent (const struct dirent *source) +{ + if (source == NULL) + { + struct readdir_result result = { NULL, }; + return result; + } + struct readdir_result result = READDIR_RESULT_INITIALIZER (source); + return result; +} + +#ifndef COMPILE_GLOB64 +/* Like convert_dirent, but works on struct dirent64 instead. Keep in + sync with convert_dirent above. */ +static struct readdir_result +convert_dirent64 (const struct dirent64 *source) +{ + if (source == NULL) + { + struct readdir_result result = { NULL, }; + return result; + } + struct readdir_result result = READDIR_RESULT_INITIALIZER (source); + return result; +} +#endif + #ifndef attribute_hidden # define attribute_hidden @@ -182,6 +222,10 @@ static const char *next_brace_sub (const char *begin, int flags) __THROWNL; # define __builtin_expect(expr, expected) (expr) #endif +#ifndef __glibc_unlikely +# define __glibc_unlikely(expr) __builtin_expect (expr, 0) +#endif + #ifndef _LIBC /* The results of opendir() in this file are not used with dirfd and fchdir, and we do not leak fds to any single-threaded code that could use stdio, @@ -195,25 +239,20 @@ static const char *next_brace_sub (const char *begin, int flags) __THROWNL; # undef closedir # endif -# if HAVE_ALLOCA -/* The OS usually guarantees only one guard page at the bottom of the stack, - and a page size can be as small as 4096 bytes. So we cannot safely - allocate anything larger than 4096 bytes. Also care for the possibility - of a few compiler-allocated temporary stack slots. */ -# define __libc_use_alloca(n) ((n) < 4032) -# else -/* alloca is implemented with malloc, so just use malloc. */ -# define __libc_use_alloca(n) 0 -# endif +/* Just use malloc. */ +# define __libc_use_alloca(n) false +# define alloca_account(len, avar) ((void) (len), (void) (avar), (void *) 0) +# define extend_alloca_account(buf, len, newlen, avar) \ + ((void) (buf), (void) (len), (void) (newlen), (void) (avar), (void *) 0) #endif static int glob_in_dir (const char *pattern, const char *directory, int flags, int (*errfunc) (const char *, int), - glob_t *pglob); + glob_t *pglob, size_t alloca_used); extern int __glob_pattern_type (const char *pattern, int quote) attribute_hidden; -#if !defined _LIBC || !defined GLOB_ONLY_P +#ifndef GLOB_ONLY_P static int prefix_array (const char *prefix, char **array, size_t n) __THROWNL; static int collated_compare (const void *, const void *) __THROWNL; @@ -222,7 +261,7 @@ static int collated_compare (const void *, const void *) __THROWNL; static const char * next_brace_sub (const char *cp, int flags) { - unsigned int depth = 0; + size_t depth = 0; while (*cp != '\0') if ((flags & GLOB_NOESCAPE) == 0 && *cp == '\\') { @@ -242,7 +281,7 @@ next_brace_sub (const char *cp, int flags) return *cp != '\0' ? cp : NULL; } -#endif /* !defined _LIBC || !defined GLOB_ONLY_P */ +#endif /* !defined GLOB_ONLY_P */ /* Do glob searching for PATTERN, placing results in PGLOB. The bits defined above may be set in FLAGS. @@ -256,20 +295,20 @@ int #ifdef GLOB_ATTRIBUTE GLOB_ATTRIBUTE #endif -glob (pattern, flags, errfunc, pglob) - const char * restrict pattern; - int flags; - int (*errfunc) (const char *, int); - glob_t * restrict pglob; +glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + glob_t *pglob) { const char *filename; - const char *dirname; + char *dirname = NULL; size_t dirlen; int status; size_t oldcount; int meta; int dirname_modified; + int malloc_dirname = 0; glob_t dirs; + int retval = 0; + size_t alloca_used = 0; if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) { @@ -277,6 +316,11 @@ glob (pattern, flags, errfunc, pglob) return -1; } + /* POSIX requires all slashes to be matched. This means that with + a trailing slash we must match only directories. */ + if (pattern[0] && pattern[strlen (pattern) - 1] == '/') + flags |= GLOB_ONLYDIR; + if (!(flags & GLOB_DOOFFS)) /* Have to do this so 'globfree' knows where to start freeing. It also makes all the code that uses gl_offs simpler. */ @@ -318,20 +362,24 @@ glob (pattern, flags, errfunc, pglob) const char *next; const char *rest; size_t rest_len; -#ifdef __GNUC__ - char onealt[strlen (pattern) - 1]; -#else - char *onealt = malloc (strlen (pattern) - 1); - if (onealt == NULL) + char *onealt; + size_t pattern_len = strlen (pattern) - 1; + int alloca_onealt = __libc_use_alloca (alloca_used + pattern_len); + if (alloca_onealt) + onealt = alloca_account (pattern_len, alloca_used); + else { - if (!(flags & GLOB_APPEND)) + onealt = malloc (pattern_len); + if (onealt == NULL) { - pglob->gl_pathc = 0; - pglob->gl_pathv = NULL; + if (!(flags & GLOB_APPEND)) + { + pglob->gl_pathc = 0; + pglob->gl_pathv = NULL; + } + return GLOB_NOSPACE; } - return GLOB_NOSPACE; } -#endif /* We know the prefix for all sub-patterns. */ alt_start = mempcpy (onealt, pattern, begin - pattern); @@ -342,9 +390,9 @@ glob (pattern, flags, errfunc, pglob) if (next == NULL) { /* It is an invalid expression. */ -#ifndef __GNUC__ - free (onealt); -#endif + illegal_brace: + if (__glibc_unlikely (!alloca_onealt)) + free (onealt); return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob); } @@ -354,13 +402,8 @@ glob (pattern, flags, errfunc, pglob) { rest = next_brace_sub (rest + 1, flags); if (rest == NULL) - { - /* It is an invalid expression. */ -#ifndef __GNUC__ - free (onealt); -#endif - return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob); - } + /* It is an illegal expression. */ + goto illegal_brace; } /* Please note that we now can be sure the brace expression is well-formed. */ @@ -396,9 +439,8 @@ glob (pattern, flags, errfunc, pglob) /* If we got an error, return it. */ if (result && result != GLOB_NOMATCH) { -#ifndef __GNUC__ - free (onealt); -#endif + if (__glibc_unlikely (!alloca_onealt)) + free (onealt); if (!(flags & GLOB_APPEND)) { globfree (pglob); @@ -416,9 +458,8 @@ glob (pattern, flags, errfunc, pglob) assert (next != NULL); } -#ifndef __GNUC__ - free (onealt); -#endif + if (__glibc_unlikely (!alloca_onealt)) + free (onealt); if (pglob->gl_pathc != firstc) /* We found some entries. */ @@ -428,6 +469,29 @@ glob (pattern, flags, errfunc, pglob) } } + if (!(flags & GLOB_APPEND)) + { + pglob->gl_pathc = 0; + if (!(flags & GLOB_DOOFFS)) + pglob->gl_pathv = NULL; + else + { + size_t i; + + if (pglob->gl_offs >= ~((size_t) 0) / sizeof (char *)) + return GLOB_NOSPACE; + + pglob->gl_pathv = malloc ((pglob->gl_offs + 1) * sizeof (char *)); + if (pglob->gl_pathv == NULL) + return GLOB_NOSPACE; + + for (i = 0; i <= pglob->gl_offs; ++i) + pglob->gl_pathv[i] = NULL; + } + } + + oldcount = pglob->gl_pathc + pglob->gl_offs; + /* Find the filename. */ filename = strrchr (pattern, '/'); #if defined __MSDOS__ || defined WINDOWS32 @@ -445,7 +509,7 @@ glob (pattern, flags, errfunc, pglob) case is nothing but a notation for a directory. */ if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && pattern[0] == '~') { - dirname = pattern; + dirname = (char *) pattern; dirlen = strlen (pattern); /* Set FILENAME to NULL as a special flag. This is ugly but @@ -455,11 +519,17 @@ glob (pattern, flags, errfunc, pglob) } else { + if (__glibc_unlikely (pattern[0] == '\0')) + { + dirs.gl_pathv = NULL; + goto no_matches; + } + filename = pattern; #ifdef _AMIGA - dirname = ""; + dirname = (char *) ""; #else - dirname = "."; + dirname = (char *) "."; #endif dirlen = 0; } @@ -469,7 +539,7 @@ glob (pattern, flags, errfunc, pglob) && (flags & GLOB_NOESCAPE) == 0)) { /* "/pattern" or "\\/pattern". */ - dirname = "/"; + dirname = (char *) "/"; dirlen = 1; ++filename; } @@ -495,7 +565,15 @@ glob (pattern, flags, errfunc, pglob) from "d:/", since "d:" and "d:/" are not the same.*/ } #endif - newp = __alloca (dirlen + 1); + if (__libc_use_alloca (alloca_used + dirlen + 1)) + newp = alloca_account (dirlen + 1, alloca_used); + else + { + newp = malloc (dirlen + 1); + if (newp == NULL) + return GLOB_NOSPACE; + malloc_dirname = 1; + } *((char *) mempcpy (newp, pattern, dirlen)) = '\0'; dirname = newp; ++filename; @@ -536,29 +614,11 @@ glob (pattern, flags, errfunc, pglob) oldcount = pglob->gl_pathc + pglob->gl_offs; goto no_matches; } - return val; - } - } - - if (!(flags & GLOB_APPEND)) - { - pglob->gl_pathc = 0; - if (!(flags & GLOB_DOOFFS)) - pglob->gl_pathv = NULL; - else - { - size_t i; - pglob->gl_pathv = malloc ((pglob->gl_offs + 1) * sizeof (char *)); - if (pglob->gl_pathv == NULL) - return GLOB_NOSPACE; - - for (i = 0; i <= pglob->gl_offs; ++i) - pglob->gl_pathv[i] = NULL; + retval = val; + goto out; } } - oldcount = pglob->gl_pathc + pglob->gl_offs; - if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~') { if (dirname[1] == '\0' || dirname[1] == '/' @@ -566,7 +626,8 @@ glob (pattern, flags, errfunc, pglob) && (dirname[2] == '\0' || dirname[2] == '/'))) { /* Look up home directory. */ - const char *home_dir = getenv ("HOME"); + char *home_dir = getenv ("HOME"); + int malloc_home_dir = 0; # ifdef _AMIGA if (home_dir == NULL || home_dir[0] == '\0') home_dir = "SYS:"; @@ -597,15 +658,27 @@ glob (pattern, flags, errfunc, pglob) { int success; char *name; + int malloc_name = 0; size_t buflen = GET_LOGIN_NAME_MAX () + 1; if (buflen == 0) /* 'sysconf' does not support _SC_LOGIN_NAME_MAX. Try a moderate value. */ buflen = 20; - name = __alloca (buflen); + if (__libc_use_alloca (alloca_used + buflen)) + name = alloca_account (buflen, alloca_used); + else + { + name = malloc (buflen); + if (name == NULL) + { + retval = GLOB_NOSPACE; + goto out; + } + malloc_name = 1; + } - success = getlogin_r (name, buflen) == 0; + success = __getlogin_r (name, buflen) == 0; if (success) { struct passwd *p; @@ -613,6 +686,7 @@ glob (pattern, flags, errfunc, pglob) long int pwbuflen = GETPW_R_SIZE_MAX (); char *pwtmpbuf; struct passwd pwbuf; + int malloc_pwtmpbuf = 0; int save = errno; # ifndef _LIBC @@ -621,7 +695,18 @@ glob (pattern, flags, errfunc, pglob) Try a moderate value. */ pwbuflen = 1024; # endif - pwtmpbuf = __alloca (pwbuflen); + if (__libc_use_alloca (alloca_used + pwbuflen)) + pwtmpbuf = alloca_account (pwbuflen, alloca_used); + else + { + pwtmpbuf = malloc (pwbuflen); + if (pwtmpbuf == NULL) + { + retval = GLOB_NOSPACE; + goto out; + } + malloc_pwtmpbuf = 1; + } while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p) != 0) @@ -631,46 +716,117 @@ glob (pattern, flags, errfunc, pglob) p = NULL; break; } -# ifdef _LIBC - pwtmpbuf = extend_alloca (pwtmpbuf, pwbuflen, + + if (!malloc_pwtmpbuf + && __libc_use_alloca (alloca_used + + 2 * pwbuflen)) + pwtmpbuf = extend_alloca_account (pwtmpbuf, pwbuflen, + 2 * pwbuflen, + alloca_used); + else + { + char *newp = realloc (malloc_pwtmpbuf + ? pwtmpbuf : NULL, 2 * pwbuflen); -# else - pwbuflen *= 2; - pwtmpbuf = __alloca (pwbuflen); -# endif + if (newp == NULL) + { + if (__glibc_unlikely (malloc_pwtmpbuf)) + free (pwtmpbuf); + retval = GLOB_NOSPACE; + goto out; + } + pwtmpbuf = newp; + pwbuflen = 2 * pwbuflen; + malloc_pwtmpbuf = 1; + } __set_errno (save); } # else p = getpwnam (name); # endif + if (__glibc_unlikely (malloc_name)) + free (name); if (p != NULL) - home_dir = p->pw_dir; + { + if (!malloc_pwtmpbuf) + home_dir = p->pw_dir; + else + { + size_t home_dir_len = strlen (p->pw_dir) + 1; + if (__libc_use_alloca (alloca_used + home_dir_len)) + home_dir = alloca_account (home_dir_len, + alloca_used); + else + { + home_dir = malloc (home_dir_len); + if (home_dir == NULL) + { + free (pwtmpbuf); + retval = GLOB_NOSPACE; + goto out; + } + malloc_home_dir = 1; + } + memcpy (home_dir, p->pw_dir, home_dir_len); + + free (pwtmpbuf); + } + } } } if (home_dir == NULL || home_dir[0] == '\0') { if (flags & GLOB_TILDE_CHECK) - return GLOB_NOMATCH; + { + if (__glibc_unlikely (malloc_home_dir)) + free (home_dir); + retval = GLOB_NOMATCH; + goto out; + } else - home_dir = "~"; /* No luck. */ + home_dir = (char *) "~"; /* No luck. */ } # endif /* WINDOWS32 */ # endif /* Now construct the full directory. */ if (dirname[1] == '\0') { + if (__glibc_unlikely (malloc_dirname)) + free (dirname); + dirname = home_dir; dirlen = strlen (dirname); + malloc_dirname = malloc_home_dir; } else { char *newp; size_t home_len = strlen (home_dir); - newp = __alloca (home_len + dirlen); + int use_alloca = __libc_use_alloca (alloca_used + + home_len + dirlen); + if (use_alloca) + newp = alloca_account (home_len + dirlen, alloca_used); + else + { + newp = malloc (home_len + dirlen); + if (newp == NULL) + { + if (__glibc_unlikely (malloc_home_dir)) + free (home_dir); + retval = GLOB_NOSPACE; + goto out; + } + } + mempcpy (mempcpy (newp, home_dir, home_len), &dirname[1], dirlen); + + if (__glibc_unlikely (malloc_dirname)) + free (dirname); + dirname = newp; dirlen += home_len - 1; + malloc_dirname = !use_alloca; } dirname_modified = 1; } @@ -678,8 +834,8 @@ glob (pattern, flags, errfunc, pglob) else { char *end_name = strchr (dirname, '/'); - const char *user_name; - const char *home_dir; + char *user_name; + int malloc_user_name = 0; char *unescape = NULL; if (!(flags & GLOB_NOESCAPE)) @@ -698,9 +854,18 @@ glob (pattern, flags, errfunc, pglob) else { char *newp; - newp = __alloca (end_name - dirname); - *((char *) mempcpy (newp, dirname + 1, end_name - dirname)) - = '\0'; + if (__libc_use_alloca (alloca_used + (end_name - dirname))) + newp = alloca_account (end_name - dirname, alloca_used); + else + { + newp = malloc (end_name - dirname); + if (newp == NULL) + { + retval = GLOB_NOSPACE; + goto out; + } + malloc_user_name = 1; + } if (unescape != NULL) { char *p = mempcpy (newp, dirname + 1, @@ -737,6 +902,7 @@ glob (pattern, flags, errfunc, pglob) # if defined HAVE_GETPWNAM_R || defined _LIBC long int buflen = GETPW_R_SIZE_MAX (); char *pwtmpbuf; + int malloc_pwtmpbuf = 0; struct passwd pwbuf; int save = errno; @@ -746,7 +912,21 @@ glob (pattern, flags, errfunc, pglob) moderate value. */ buflen = 1024; # endif - pwtmpbuf = __alloca (buflen); + if (__libc_use_alloca (alloca_used + buflen)) + pwtmpbuf = alloca_account (buflen, alloca_used); + else + { + pwtmpbuf = malloc (buflen); + if (pwtmpbuf == NULL) + { + nomem_getpw: + if (__glibc_unlikely (malloc_user_name)) + free (user_name); + retval = GLOB_NOSPACE; + goto out; + } + malloc_pwtmpbuf = 1; + } while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0) { @@ -755,40 +935,77 @@ glob (pattern, flags, errfunc, pglob) p = NULL; break; } -# ifdef _LIBC - pwtmpbuf = extend_alloca (pwtmpbuf, buflen, 2 * buflen); -# else - buflen *= 2; - pwtmpbuf = __alloca (buflen); -# endif + if (!malloc_pwtmpbuf + && __libc_use_alloca (alloca_used + 2 * buflen)) + pwtmpbuf = extend_alloca_account (pwtmpbuf, buflen, + 2 * buflen, alloca_used); + else + { + char *newp = realloc (malloc_pwtmpbuf ? pwtmpbuf : NULL, + 2 * buflen); + if (newp == NULL) + { + if (__glibc_unlikely (malloc_pwtmpbuf)) + free (pwtmpbuf); + goto nomem_getpw; + } + pwtmpbuf = newp; + malloc_pwtmpbuf = 1; + } __set_errno (save); } # else p = getpwnam (user_name); # endif + + if (__glibc_unlikely (malloc_user_name)) + free (user_name); + + /* If we found a home directory use this. */ if (p != NULL) - home_dir = p->pw_dir; + { + size_t home_len = strlen (p->pw_dir); + size_t rest_len = end_name == NULL ? 0 : strlen (end_name); + + if (__glibc_unlikely (malloc_dirname)) + free (dirname); + malloc_dirname = 0; + + if (__libc_use_alloca (alloca_used + home_len + rest_len + 1)) + dirname = alloca_account (home_len + rest_len + 1, + alloca_used); + else + { + dirname = malloc (home_len + rest_len + 1); + if (dirname == NULL) + { + if (__glibc_unlikely (malloc_pwtmpbuf)) + free (pwtmpbuf); + retval = GLOB_NOSPACE; + goto out; + } + malloc_dirname = 1; + } + *((char *) mempcpy (mempcpy (dirname, p->pw_dir, home_len), + end_name, rest_len)) = '\0'; + + dirlen = home_len + rest_len; + dirname_modified = 1; + + if (__glibc_unlikely (malloc_pwtmpbuf)) + free (pwtmpbuf); + } else - home_dir = NULL; + { + if (__glibc_unlikely (malloc_pwtmpbuf)) + free (pwtmpbuf); + + if (flags & GLOB_TILDE_CHECK) + /* We have to regard it as an error if we cannot find the + home directory. */ + return GLOB_NOMATCH; + } } - /* If we found a home directory use this. */ - if (home_dir != NULL) - { - char *newp; - size_t home_len = strlen (home_dir); - size_t rest_len = end_name == NULL ? 0 : strlen (end_name); - newp = __alloca (home_len + rest_len + 1); - *((char *) mempcpy (mempcpy (newp, home_dir, home_len), - end_name, rest_len)) = '\0'; - dirname = newp; - dirlen = home_len + rest_len; - dirname_modified = 1; - } - else - if (flags & GLOB_TILDE_CHECK) - /* We have to regard it as an error if we cannot find the - home directory. */ - return GLOB_NOMATCH; } # endif /* Not Amiga && not WINDOWS32. */ } @@ -807,12 +1024,11 @@ glob (pattern, flags, errfunc, pglob) && S_ISDIR (st.st_mode)) : (__stat64 (dirname, &st64) == 0 && S_ISDIR (st64.st_mode))))) { - int newcount = pglob->gl_pathc + pglob->gl_offs; + size_t newcount = pglob->gl_pathc + pglob->gl_offs; char **new_gl_pathv; - new_gl_pathv - = realloc (pglob->gl_pathv, (newcount + 1 + 1) * sizeof (char *)); - if (new_gl_pathv == NULL) + if (newcount > UINTPTR_MAX - (1 + 1) + || newcount + 1 + 1 > ~((size_t) 0) / sizeof (char *)) { nospace: free (pglob->gl_pathv); @@ -820,6 +1036,11 @@ glob (pattern, flags, errfunc, pglob) pglob->gl_pathc = 0; return GLOB_NOSPACE; } + + new_gl_pathv = realloc (pglob->gl_pathv, + (newcount + 1 + 1) * sizeof (char *)); + if (new_gl_pathv == NULL) + goto nospace; pglob->gl_pathv = new_gl_pathv; if (flags & GLOB_MARK) @@ -873,7 +1094,7 @@ glob (pattern, flags, errfunc, pglob) *(char *) &dirname[--dirlen] = '\0'; } - if (__builtin_expect ((flags & GLOB_ALTDIRFUNC) != 0, 0)) + if (__glibc_unlikely ((flags & GLOB_ALTDIRFUNC) != 0)) { /* Use the alternative access functions also in the recursive call. */ @@ -901,7 +1122,7 @@ glob (pattern, flags, errfunc, pglob) appending the results to PGLOB. */ for (i = 0; i < dirs.gl_pathc; ++i) { - int old_pathc; + size_t old_pathc; #ifdef SHELL { @@ -920,7 +1141,7 @@ glob (pattern, flags, errfunc, pglob) status = glob_in_dir (filename, dirs.gl_pathv[i], ((flags | GLOB_APPEND) & ~(GLOB_NOCHECK | GLOB_NOMAGIC)), - errfunc, pglob); + errfunc, pglob, alloca_used); if (status == GLOB_NOMATCH) /* No matches in this directory. Try the next. */ continue; @@ -956,16 +1177,21 @@ glob (pattern, flags, errfunc, pglob) /* No matches. */ if (flags & GLOB_NOCHECK) { - int newcount = pglob->gl_pathc + pglob->gl_offs; + size_t newcount = pglob->gl_pathc + pglob->gl_offs; char **new_gl_pathv; - new_gl_pathv = realloc (pglob->gl_pathv, - (newcount + 2) * sizeof (char *)); - if (new_gl_pathv == NULL) + if (newcount > UINTPTR_MAX - 2 + || newcount + 2 > ~((size_t) 0) / sizeof (char *)) { + nospace2: globfree (&dirs); return GLOB_NOSPACE; } + + new_gl_pathv = realloc (pglob->gl_pathv, + (newcount + 2) * sizeof (char *)); + if (new_gl_pathv == NULL) + goto nospace2; pglob->gl_pathv = new_gl_pathv; pglob->gl_pathv[newcount] = strdup (pattern); @@ -994,7 +1220,7 @@ glob (pattern, flags, errfunc, pglob) } else { - int old_pathc = pglob->gl_pathc; + size_t old_pathc = pglob->gl_pathc; int orig_flags = flags; if (meta & 2) @@ -1020,7 +1246,8 @@ glob (pattern, flags, errfunc, pglob) } if (dirname_modified) flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC); - status = glob_in_dir (filename, dirname, flags, errfunc, pglob); + status = glob_in_dir (filename, dirname, flags, errfunc, pglob, + alloca_used); if (status != 0) { if (status == GLOB_NOMATCH && flags != orig_flags @@ -1083,26 +1310,28 @@ glob (pattern, flags, errfunc, pglob) sizeof (char *), collated_compare); } - return 0; + out: + if (__glibc_unlikely (malloc_dirname)) + free (dirname); + + return retval; } #if defined _LIBC && !defined glob libc_hidden_def (glob) #endif -#if !defined _LIBC || !defined GLOB_ONLY_P +#ifndef GLOB_ONLY_P /* Free storage allocated in PGLOB by a previous 'glob' call. */ void -globfree (pglob) - register glob_t *pglob; +globfree (glob_t *pglob) { if (pglob->gl_pathv != NULL) { size_t i; for (i = 0; i < pglob->gl_pathc; ++i) - if (pglob->gl_pathv[pglob->gl_offs + i] != NULL) - free (pglob->gl_pathv[pglob->gl_offs + i]); + free (pglob->gl_pathv[pglob->gl_offs + i]); free (pglob->gl_pathv); pglob->gl_pathv = NULL; } @@ -1136,7 +1365,7 @@ collated_compare (const void *a, const void *b) static int prefix_array (const char *dirname, char **array, size_t n) { - register size_t i; + size_t i; size_t dirlen = strlen (dirname); #if defined __MSDOS__ || defined WINDOWS32 int sep_char = '/'; @@ -1189,13 +1418,11 @@ prefix_array (const char *dirname, char **array, size_t n) /* We must not compile this function twice. */ -#if !defined _LIBC || !defined NO_GLOB_PATTERN_P +#ifndef NO_GLOB_PATTERN_P int -__glob_pattern_type (pattern, quote) - const char *pattern; - int quote; +__glob_pattern_type (const char *pattern, int quote) { - register const char *p; + const char *p; int ret = 0; for (p = pattern; *p != '\0'; ++p) @@ -1230,9 +1457,7 @@ __glob_pattern_type (pattern, quote) /* Return nonzero if PATTERN contains any metacharacters. Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ int -__glob_pattern_p (pattern, quote) - const char *pattern; - int quote; +__glob_pattern_p (const char *pattern, int quote) { return __glob_pattern_type (pattern, quote) == 1; } @@ -1241,16 +1466,13 @@ weak_alias (__glob_pattern_p, glob_pattern_p) # endif #endif -#endif /* !GLOB_ONLY_P */ - -#if !defined _LIBC || !defined GLOB_ONLY_P /* We put this in a separate function mainly to allow the memory allocated with alloca to be recycled. */ static int __attribute_noinline__ link_exists2_p (const char *dir, size_t dirlen, const char *fname, - glob_t *pglob + glob_t *pglob # if !defined _LIBC && !HAVE_FSTATAT , int flags # endif @@ -1292,7 +1514,7 @@ link_exists_p (int dfd, const char *dir, size_t dirlen, const char *fname, return link_exists2_p (dir, dirlen, fname, pglob, flags); # endif } -#endif +#endif /* !defined GLOB_ONLY_P */ /* Like 'glob', but PATTERN is a final pathname component, @@ -1302,7 +1524,7 @@ link_exists_p (int dfd, const char *dir, size_t dirlen, const char *fname, static int glob_in_dir (const char *pattern, const char *directory, int flags, int (*errfunc) (const char *, int), - glob_t *pglob) + glob_t *pglob, size_t alloca_used) { size_t dirlen = strlen (directory); void *stream = NULL; @@ -1317,12 +1539,13 @@ glob_in_dir (const char *pattern, const char *directory, int flags, struct globnames *names = &init_names; struct globnames *names_alloca = &init_names; size_t nfound = 0; - size_t allocasize = sizeof (init_names); size_t cur = 0; int meta; int save; int result; + alloca_used += sizeof (init_names); + init_names.next = NULL; init_names.count = INITIAL_COUNT; @@ -1338,20 +1561,36 @@ glob_in_dir (const char *pattern, const char *directory, int flags, { /* Since we use the normal file functions we can also use stat() to verify the file is there. */ - struct stat st; - struct_stat64 st64; + union + { + struct stat st; + struct_stat64 st64; + } ust; size_t patlen = strlen (pattern); - char *fullname = __alloca (dirlen + 1 + patlen + 1); + int alloca_fullname = __libc_use_alloca (alloca_used + + dirlen + 1 + patlen + 1); + char *fullname; + if (alloca_fullname) + fullname = alloca_account (dirlen + 1 + patlen + 1, alloca_used); + else + { + fullname = malloc (dirlen + 1 + patlen + 1); + if (fullname == NULL) + return GLOB_NOSPACE; + } mempcpy (mempcpy (mempcpy (fullname, directory, dirlen), "/", 1), pattern, patlen + 1); if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) - ? (*pglob->gl_stat) (fullname, &st) - : __stat64 (fullname, &st64)) == 0) + ? (*pglob->gl_stat) (fullname, &ust.st) + : __stat64 (fullname, &ust.st64)) == 0) /* We found this file to be existing. Now tell the rest of the function to copy this name into the result. */ flags |= GLOB_NOCHECK; + + if (__glibc_unlikely (!alloca_fullname)) + free (fullname); } else { @@ -1379,56 +1618,36 @@ glob_in_dir (const char *pattern, const char *directory, int flags, while (1) { - const char *name; - size_t len; -#if defined _LIBC && !defined COMPILE_GLOB64 - struct dirent64 *d; - union - { - struct dirent64 d64; - char room [offsetof (struct dirent64, d_name[0]) - + NAME_MAX + 1]; - } - d64buf; - - if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) - { - struct dirent *d32 = (*pglob->gl_readdir) (stream); - if (d32 != NULL) - { - CONVERT_DIRENT_DIRENT64 (&d64buf.d64, d32); - d = &d64buf.d64; - } - else - d = NULL; - } - else - d = __readdir64 (stream); + struct readdir_result d; + { + if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) + d = convert_dirent (GL_READDIR (pglob, stream)); + else + { +#ifdef COMPILE_GLOB64 + d = convert_dirent (__readdir (stream)); #else - struct dirent *d = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) - ? ((struct dirent *) - (*pglob->gl_readdir) (stream)) - : __readdir (stream)); + d = convert_dirent64 (__readdir64 (stream)); #endif - if (d == NULL) + } + } + if (d.name == NULL) break; - if (! REAL_DIR_ENTRY (d)) + if (d.skip_entry) continue; /* If we shall match only directories use the information provided by the dirent call if possible. */ - if ((flags & GLOB_ONLYDIR) && !DIRENT_MIGHT_BE_DIR (d)) + if ((flags & GLOB_ONLYDIR) && !readdir_result_might_be_dir (d)) continue; - name = d->d_name; - - if (fnmatch (pattern, name, fnm_flags) == 0) + if (fnmatch (pattern, d.name, fnm_flags) == 0) { /* If the file we found is a symlink we have to make sure the target file exists. */ - if (!DIRENT_MIGHT_BE_SYMLINK (d) - || link_exists_p (dfd, directory, dirlen, name, pglob, - flags)) + if (!readdir_result_might_be_symlink (d) + || link_exists_p (dfd, directory, dirlen, d.name, + pglob, flags)) { if (cur == names->count) { @@ -1437,9 +1656,9 @@ glob_in_dir (const char *pattern, const char *directory, int flags, size_t size = (sizeof (struct globnames) + ((count - INITIAL_COUNT) * sizeof (char *))); - allocasize += size; - if (__libc_use_alloca (allocasize)) - newnames = names_alloca = __alloca (size); + if (__libc_use_alloca (alloca_used + size)) + newnames = names_alloca + = alloca_account (size, alloca_used); else if ((newnames = malloc (size)) == NULL) goto memory_error; @@ -1448,12 +1667,10 @@ glob_in_dir (const char *pattern, const char *directory, int flags, names = newnames; cur = 0; } - len = _D_EXACT_NAMLEN (d); - names->name[cur] = malloc (len + 1); + names->name[cur] = strdup (d.name); if (names->name[cur] == NULL) goto memory_error; - *((char *) mempcpy (names->name[cur++], name, len)) - = '\0'; + ++cur; ++nfound; } } @@ -1474,11 +1691,20 @@ glob_in_dir (const char *pattern, const char *directory, int flags, result = GLOB_NOMATCH; if (nfound != 0) { - char **new_gl_pathv + char **new_gl_pathv; + result = 0; + + if (pglob->gl_pathc > UINTPTR_MAX - pglob->gl_offs + || pglob->gl_pathc + pglob->gl_offs > UINTPTR_MAX - nfound + || pglob->gl_pathc + pglob->gl_offs + nfound > UINTPTR_MAX - 1 + || (pglob->gl_pathc + pglob->gl_offs + nfound + 1 + > UINTPTR_MAX / sizeof (char *))) + goto memory_error; + + new_gl_pathv = realloc (pglob->gl_pathv, (pglob->gl_pathc + pglob->gl_offs + nfound + 1) * sizeof (char *)); - result = 0; if (new_gl_pathv == NULL) { @@ -1544,7 +1770,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, if (stream != NULL) { save = errno; - if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) + if (__glibc_unlikely (flags & GLOB_ALTDIRFUNC)) (*pglob->gl_closedir) (stream); else closedir (stream); diff --git a/modules/glob b/modules/glob index cef81cc68b..919d7fc0f5 100644 --- a/modules/glob +++ b/modules/glob @@ -25,6 +25,7 @@ mempcpy [test -n "$GLOB_H"] opendir [test -n "$GLOB_H"] readdir [test -n "$GLOB_H"] stdbool [test -n "$GLOB_H"] +stdint [test -n "$GLOB_H"] strdup [test -n "$GLOB_H"] sys_stat [test -n "$GLOB_H"] unistd [test -n "$GLOB_H"] -- 2.39.5