diff options
| author | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2020-12-29 17:41:19 -0300 |
|---|---|---|
| committer | Adhemerval Zanella <adhemerval.zanella@linaro.org> | 2021-01-04 08:41:53 -0300 |
| commit | 4883360415f1ed772ba44decc501d59deb17bdf0 (patch) | |
| tree | aab1304b90e68e09acc2b8b90a8f5ec0d8f0fd3b | |
| parent | c2a150d089fa096cb5f9e342da80fb30dc0d1953 (diff) | |
| download | glibc-4883360415f1ed772ba44decc501d59deb17bdf0.tar.xz glibc-4883360415f1ed772ba44decc501d59deb17bdf0.zip | |
posix: Sync glob code with gnulib
It sync with gnulib commit 43ee1a6bf and fixes and use-after-free
bug (gnulib commit 717766da8926e36).
Checked on x86_64-linux-gnu.
| -rw-r--r-- | posix/glob.c | 1748 | ||||
| -rw-r--r-- | posix/glob_pattern_p.c | 2 | ||||
| -rw-r--r-- | posix/globfree.c | 2 |
3 files changed, 886 insertions, 866 deletions
diff --git a/posix/glob.c b/posix/glob.c index 6f24a8e192..32c88e5d15 100644 --- a/posix/glob.c +++ b/posix/glob.c @@ -15,6 +15,16 @@ License along with the GNU C Library; if not, see <https://www.gnu.org/licenses/>. */ +#ifndef _LIBC + +/* Don't use __attribute__ __nonnull__ in this compilation unit. Otherwise gcc + optimizes away the pattern == NULL test below. */ +# define _GL_ARG_NONNULL(params) + +# include <config.h> + +#endif + #include <glob.h> #include <errno.h> @@ -26,7 +36,7 @@ #include <assert.h> #include <unistd.h> -#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ +#if defined _WIN32 && ! defined __CYGWIN__ # define WINDOWS32 #endif @@ -49,14 +59,19 @@ # define readdir(str) __readdir64 (str) # define getpwnam_r(name, bufp, buf, len, res) \ __getpwnam_r (name, bufp, buf, len, res) -# define struct_stat64 struct stat64 +# define struct_stat64 struct stat64 # define FLEXIBLE_ARRAY_MEMBER # include <shlib-compat.h> #else /* !_LIBC */ # define __glob glob # define __getlogin_r(buf, len) getlogin_r (buf, len) # define __lstat64(fname, buf) lstat (fname, buf) +# if defined _WIN32 && !defined __CYGWIN__ + /* Avoid GCC or clang warning. The original __stat64 macro is unused. */ +# undef __stat64 +# endif # define __stat64(fname, buf) stat (fname, buf) +# define __fxstatat64(_, d, f, st, flag) fstatat (d, f, st, flag) # define struct_stat64 struct stat # ifndef __MVS__ # define __alloca alloca @@ -73,7 +88,9 @@ static const char *next_brace_sub (const char *begin, int flags) __THROWNL; -typedef uint_fast8_t dirent_type; +/* The type of ((struct dirent *) 0)->d_type is 'unsigned char' on most + platforms, but 'unsigned int' in the mingw from mingw.org. */ +typedef uint_fast32_t dirent_type; #if !defined _LIBC && !defined HAVE_STRUCT_DIRENT_D_TYPE /* Any distinct values will do here. @@ -112,9 +129,9 @@ readdir_result_type (struct readdir_result d) /* 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) \ + { \ + source->d_name, \ + D_TYPE_TO_RESULT (source) \ } /* Call gl_readdir on STREAM. This macro can be overridden to reduce @@ -204,7 +221,7 @@ glob_lstat (glob_t *pglob, int flags, const char *fullname) static bool size_add_wrapv (size_t a, size_t b, size_t *r) { -#if 5 <= __GNUC__ && !defined __ICC +#if 7 <= __GNUC__ && !defined __ICC return __builtin_add_overflow (a, b, r); #else *r = a + b; @@ -221,8 +238,8 @@ glob_use_alloca (size_t alloca_used, size_t len) } static int glob_in_dir (const char *pattern, const char *directory, - int flags, int (*errfunc) (const char *, int), - glob_t *pglob, size_t alloca_used); + int flags, int (*errfunc) (const char *, int), + glob_t *pglob, size_t alloca_used); static int prefix_array (const char *prefix, char **array, size_t n) __THROWNL; static int collated_compare (const void *, const void *) __THROWNL; @@ -247,17 +264,17 @@ next_brace_sub (const char *cp, int flags) while (*cp != '\0') if ((flags & GLOB_NOESCAPE) == 0 && *cp == '\\') { - if (*++cp == '\0') - break; - ++cp; + if (*++cp == '\0') + break; + ++cp; } else { - if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0)) - break; + if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0)) + break; - if (*cp++ == '{') - depth++; + if (*cp++ == '{') + depth++; } return *cp != '\0' ? cp : NULL; @@ -278,7 +295,7 @@ next_brace_sub (const char *cp, int flags) int GLOB_ATTRIBUTE __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), - glob_t *pglob) + glob_t *pglob) { const char *filename; char *dirname = NULL; @@ -312,22 +329,22 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), { pglob->gl_pathc = 0; if (!(flags & GLOB_DOOFFS)) - pglob->gl_pathv = NULL; + pglob->gl_pathv = NULL; else - { - size_t i; + { + size_t i; - if (pglob->gl_offs >= ~((size_t) 0) / sizeof (char *)) - return GLOB_NOSPACE; + if (pglob->gl_offs >= ~((size_t) 0) / sizeof (char *)) + return GLOB_NOSPACE; - pglob->gl_pathv = (char **) malloc ((pglob->gl_offs + 1) - * sizeof (char *)); - if (pglob->gl_pathv == NULL) - return GLOB_NOSPACE; + pglob->gl_pathv = (char **) 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; - } + for (i = 0; i <= pglob->gl_offs; ++i) + pglob->gl_pathv[i] = NULL; + } } if (flags & GLOB_BRACE) @@ -335,129 +352,129 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), const char *begin; if (flags & GLOB_NOESCAPE) - begin = strchr (pattern, '{'); + begin = strchr (pattern, '{'); else - { - begin = pattern; - while (1) - { - if (*begin == '\0') - { - begin = NULL; - break; - } - - if (*begin == '\\' && begin[1] != '\0') - ++begin; - else if (*begin == '{') - break; - - ++begin; - } - } + { + begin = pattern; + while (1) + { + if (*begin == '\0') + { + begin = NULL; + break; + } + + if (*begin == '\\' && begin[1] != '\0') + ++begin; + else if (*begin == '{') + break; + + ++begin; + } + } if (begin != NULL) - { - /* Allocate working buffer large enough for our work. Note that - we have at least an opening and closing brace. */ - size_t firstc; - char *alt_start; - const char *p; - const char *next; - const char *rest; - size_t rest_len; - char *onealt; - size_t pattern_len = strlen (pattern) - 1; - int alloca_onealt = glob_use_alloca (alloca_used, pattern_len); - if (alloca_onealt) - onealt = alloca_account (pattern_len, alloca_used); - else - { - onealt = malloc (pattern_len); - if (onealt == NULL) - return GLOB_NOSPACE; - } - - /* We know the prefix for all sub-patterns. */ - alt_start = mempcpy (onealt, pattern, begin - pattern); - - /* Find the first sub-pattern and at the same time find the - rest after the closing brace. */ - next = next_brace_sub (begin + 1, flags); - if (next == NULL) - { - /* It is an invalid expression. */ - illegal_brace: - if (__glibc_unlikely (!alloca_onealt)) - free (onealt); - flags &= ~GLOB_BRACE; - goto no_brace; - } - - /* Now find the end of the whole brace expression. */ - rest = next; - while (*rest != '}') - { - rest = next_brace_sub (rest + 1, flags); - if (rest == NULL) - /* It is an illegal expression. */ - goto illegal_brace; - } - /* Please note that we now can be sure the brace expression - is well-formed. */ - rest_len = strlen (++rest) + 1; - - /* We have a brace expression. BEGIN points to the opening {, - NEXT points past the terminator of the first element, and END - points past the final }. We will accumulate result names from - recursive runs for each brace alternative in the buffer using - GLOB_APPEND. */ - firstc = pglob->gl_pathc; - - p = begin + 1; - while (1) - { - int result; - - /* Construct the new glob expression. */ - mempcpy (mempcpy (alt_start, p, next - p), rest, rest_len); - - result = __glob (onealt, - ((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC)) - | GLOB_APPEND), - errfunc, pglob); - - /* If we got an error, return it. */ - if (result && result != GLOB_NOMATCH) - { - if (__glibc_unlikely (!alloca_onealt)) - free (onealt); - if (!(flags & GLOB_APPEND)) - { - globfree (pglob); - pglob->gl_pathc = 0; - } - return result; - } - - if (*next == '}') - /* We saw the last entry. */ - break; - - p = next + 1; - next = next_brace_sub (p, flags); - assert (next != NULL); - } - - if (__glibc_unlikely (!alloca_onealt)) - free (onealt); - - if (pglob->gl_pathc != firstc) - /* We found some entries. */ - return 0; - else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC))) - return GLOB_NOMATCH; - } + { + /* Allocate working buffer large enough for our work. Note that + we have at least an opening and closing brace. */ + size_t firstc; + char *alt_start; + const char *p; + const char *next; + const char *rest; + size_t rest_len; + char *onealt; + size_t pattern_len = strlen (pattern) - 1; + int alloca_onealt = glob_use_alloca (alloca_used, pattern_len); + if (alloca_onealt) + onealt = alloca_account (pattern_len, alloca_used); + else + { + onealt = malloc (pattern_len); + if (onealt == NULL) + return GLOB_NOSPACE; + } + + /* We know the prefix for all sub-patterns. */ + alt_start = mempcpy (onealt, pattern, begin - pattern); + + /* Find the first sub-pattern and at the same time find the + rest after the closing brace. */ + next = next_brace_sub (begin + 1, flags); + if (next == NULL) + { + /* It is an invalid expression. */ + illegal_brace: + if (__glibc_unlikely (!alloca_onealt)) + free (onealt); + flags &= ~GLOB_BRACE; + goto no_brace; + } + + /* Now find the end of the whole brace expression. */ + rest = next; + while (*rest != '}') + { + rest = next_brace_sub (rest + 1, flags); + if (rest == NULL) + /* It is an illegal expression. */ + goto illegal_brace; + } + /* Please note that we now can be sure the brace expression + is well-formed. */ + rest_len = strlen (++rest) + 1; + + /* We have a brace expression. BEGIN points to the opening {, + NEXT points past the terminator of the first element, and END + points past the final }. We will accumulate result names from + recursive runs for each brace alternative in the buffer using + GLOB_APPEND. */ + firstc = pglob->gl_pathc; + + p = begin + 1; + while (1) + { + int result; + + /* Construct the new glob expression. */ + mempcpy (mempcpy (alt_start, p, next - p), rest, rest_len); + + result = __glob (onealt, + ((flags & ~(GLOB_NOCHECK | GLOB_NOMAGIC)) + | GLOB_APPEND), + errfunc, pglob); + + /* If we got an error, return it. */ + if (result && result != GLOB_NOMATCH) + { + if (__glibc_unlikely (!alloca_onealt)) + free (onealt); + if (!(flags & GLOB_APPEND)) + { + globfree (pglob); + pglob->gl_pathc = 0; + } + return result; + } + + if (*next == '}') + /* We saw the last entry. */ + break; + + p = next + 1; + next = next_brace_sub (p, flags); + assert (next != NULL); + } + + if (__glibc_unlikely (!alloca_onealt)) + free (onealt); + + if (pglob->gl_pathc != firstc) + /* We found some entries. */ + return 0; + else if (!(flags & (GLOB_NOCHECK|GLOB_NOMAGIC))) + return GLOB_NOMATCH; + } } no_brace: @@ -479,33 +496,33 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), if (filename == NULL) { /* This can mean two things: a simple name or "~name". The latter - case is nothing but a notation for a directory. */ + case is nothing but a notation for a directory. */ if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && pattern[0] == '~') - { - dirname = (char *) pattern; - dirlen = strlen (pattern); - - /* Set FILENAME to NULL as a special flag. This is ugly but - other solutions would require much more code. We test for - this special case below. */ - filename = NULL; - } + { + dirname = (char *) pattern; + dirlen = strlen (pattern); + + /* Set FILENAME to NULL as a special flag. This is ugly but + other solutions would require much more code. We test for + this special case below. */ + filename = NULL; + } else - { - if (__glibc_unlikely (pattern[0] == '\0')) - { - dirs.gl_pathv = NULL; - goto no_matches; - } - - filename = pattern; - dirname = (char *) "."; - dirlen = 0; - } + { + if (__glibc_unlikely (pattern[0] == '\0')) + { + dirs.gl_pathv = NULL; + goto no_matches; + } + + filename = pattern; + dirname = (char *) "."; + dirlen = 0; + } } else if (filename == pattern - || (filename == pattern + 1 && pattern[0] == '\\' - && (flags & GLOB_NOESCAPE) == 0)) + || (filename == pattern + 1 && pattern[0] == '\\' + && (flags & GLOB_NOESCAPE) == 0)) { /* "/pattern" or "\\/pattern". */ dirname = (char *) "/"; @@ -518,32 +535,32 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), dirlen = filename - pattern; #if defined __MSDOS__ || defined WINDOWS32 if (*filename == ':' - || (filename > pattern + 1 && filename[-1] == ':')) - { - char *drive_spec; - - ++dirlen; - drive_spec = __alloca (dirlen + 1); - *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0'; - /* For now, disallow wildcards in the drive spec, to - prevent infinite recursion in glob. */ - if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE))) - return GLOB_NOMATCH; - /* If this is "d:pattern", we need to copy ':' to DIRNAME - as well. If it's "d:/pattern", don't remove the slash - from "d:/", since "d:" and "d:/" are not the same.*/ - } + || (filename > pattern + 1 && filename[-1] == ':')) + { + char *drive_spec; + + ++dirlen; + drive_spec = __alloca (dirlen + 1); + *((char *) mempcpy (drive_spec, pattern, dirlen)) = '\0'; + /* For now, disallow wildcards in the drive spec, to + prevent infinite recursion in glob. */ + if (__glob_pattern_p (drive_spec, !(flags & GLOB_NOESCAPE))) + return GLOB_NOMATCH; + /* If this is "d:pattern", we need to copy ':' to DIRNAME + as well. If it's "d:/pattern", don't remove the slash + from "d:/", since "d:" and "d:/" are not the same.*/ + } #endif if (glob_use_alloca (alloca_used, dirlen + 1)) - newp = alloca_account (dirlen + 1, alloca_used); + newp = alloca_account (dirlen + 1, alloca_used); else - { - newp = malloc (dirlen + 1); - if (newp == NULL) - return GLOB_NOSPACE; - malloc_dirname = 1; - } + { + newp = malloc (dirlen + 1); + if (newp == NULL) + return GLOB_NOSPACE; + malloc_dirname = 1; + } *((char *) mempcpy (newp, pattern, dirlen)) = '\0'; dirname = newp; ++filename; @@ -559,363 +576,366 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), if (filename[0] == '\0' && dirlen > 1 && !drive_root) /* "pattern/". Expand "pattern", appending slashes. */ - { - int orig_flags = flags; - if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\') - { - /* "pattern\\/". Remove the final backslash if it hasn't - been quoted. */ - char *p = (char *) &dirname[dirlen - 1]; - - while (p > dirname && p[-1] == '\\') --p; - if ((&dirname[dirlen] - p) & 1) - { - *(char *) &dirname[--dirlen] = '\0'; - flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC); - } - } - int val = __glob (dirname, flags | GLOB_MARK, errfunc, pglob); - if (val == 0) - pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK) - | (flags & GLOB_MARK)); - else if (val == GLOB_NOMATCH && flags != orig_flags) - { - /* Make sure globfree (&dirs); is a nop. */ - dirs.gl_pathv = NULL; - flags = orig_flags; - oldcount = pglob->gl_pathc + pglob->gl_offs; - goto no_matches; - } - retval = val; - goto out; - } + { + int orig_flags = flags; + if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\') + { + /* "pattern\\/". Remove the final backslash if it hasn't + been quoted. */ + char *p = (char *) &dirname[dirlen - 1]; + + while (p > dirname && p[-1] == '\\') --p; + if ((&dirname[dirlen] - p) & 1) + { + *(char *) &dirname[--dirlen] = '\0'; + flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC); + } + } + int val = __glob (dirname, flags | GLOB_MARK, errfunc, pglob); + if (val == 0) + pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK) + | (flags & GLOB_MARK)); + else if (val == GLOB_NOMATCH && flags != orig_flags) + { + /* Make sure globfree (&dirs); is a nop. */ + dirs.gl_pathv = NULL; + flags = orig_flags; + oldcount = pglob->gl_pathc + pglob->gl_offs; + goto no_matches; + } + retval = val; + goto out; + } } if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~') { if (dirname[1] == '\0' || dirname[1] == '/' - || (!(flags & GLOB_NOESCAPE) && dirname[1] == '\\' - && (dirname[2] == '\0' || dirname[2] == '/'))) - { - /* Look up home directory. */ - char *home_dir = getenv ("HOME"); - int malloc_home_dir = 0; - if (home_dir == NULL || home_dir[0] == '\0') - { + || (!(flags & GLOB_NOESCAPE) && dirname[1] == '\\' + && (dirname[2] == '\0' || dirname[2] == '/'))) + { + /* Look up home directory. */ + char *home_dir = getenv ("HOME"); + int malloc_home_dir = 0; + if (home_dir == NULL || home_dir[0] == '\0') + { #ifdef WINDOWS32 - /* Windows NT defines HOMEDRIVE and HOMEPATH. But give - preference to HOME, because the user can change HOME. */ - const char *home_drive = getenv ("HOMEDRIVE"); - const char *home_path = getenv ("HOMEPATH"); - - if (home_drive != NULL && home_path != NULL) - { - size_t home_drive_len = strlen (home_drive); - size_t home_path_len = strlen (home_path); - char *mem = alloca (home_drive_len + home_path_len + 1); - - memcpy (mem, home_drive, home_drive_len); - memcpy (mem + home_drive_len, home_path, home_path_len + 1); - home_dir = mem; - } - else - home_dir = "c:/users/default"; /* poor default */ + /* Windows NT defines HOMEDRIVE and HOMEPATH. But give + preference to HOME, because the user can change HOME. */ + const char *home_drive = getenv ("HOMEDRIVE"); + const char *home_path = getenv ("HOMEPATH"); + + if (home_drive != NULL && home_path != NULL) + { + size_t home_drive_len = strlen (home_drive); + size_t home_path_len = strlen (home_path); + char *mem = alloca (home_drive_len + home_path_len + 1); + + memcpy (mem, home_drive, home_drive_len); + memcpy (mem + home_drive_len, home_path, home_path_len + 1); + home_dir = mem; + } + else + home_dir = "c:/users/default"; /* poor default */ #else - int err; - struct passwd *p; - struct passwd pwbuf; - struct scratch_buffer s; - scratch_buffer_init (&s); - while (true) - { - p = NULL; - err = __getlogin_r (s.data, s.length); - if (err == 0) - { + int err; + struct passwd *p; + struct passwd pwbuf; + struct scratch_buffer s; + scratch_buffer_init (&s); + while (true) + { + p = NULL; + err = __getlogin_r (s.data, s.length); + if (err == 0) + { # if defined HAVE_GETPWNAM_R || defined _LIBC - size_t ssize = strlen (s.data) + 1; - char *sdata = s.data; - err = getpwnam_r (sdata, &pwbuf, sdata + ssize, - s.length - ssize, &p); + size_t ssize = strlen (s.data) + 1; + char *sdata = s.data; + err = getpwnam_r (sdata, &pwbuf, sdata + ssize, + s.length - ssize, &p); # else - p = getpwnam (s.data); - if (p == NULL) - err = errno; + p = getpwnam (s.data); + if (p == NULL) + err = errno; # endif - } - if (err != ERANGE) - break; - if (!scratch_buffer_grow (&s)) - { - retval = GLOB_NOSPACE; - goto out; - } - } - if (err == 0) - { - home_dir = strdup (p->pw_dir); - malloc_home_dir = 1; - } - scratch |
