This is an automated email from the git hooks/post-receive script. aurel32 pushed a commit to branch sid in repository glibc.
commit edb4b06a022b194efbf1b7b3a72e2de1cb302035 Author: Aurelien Jarno <aurel...@aurel32.net> Date: Sat Dec 2 11:05:46 2017 +0100 debian/patches/git-updates.diff: update from upstream stable branch: * debian/patches/git-updates.diff: update from upstream stable branch: - Fix buffer overflow in glob with GLOB_TILDE (CVE-2017-15670). Closes: #879501. - Fix memory leak in glob with GLOB_TILDE (CVE-2017-15671). Closes: #879500. - Fix a buffer overflow in glob with GLOB_TILDE in unescaping (CVE-2017-15804). Closes: #879955. --- debian/changelog | 6 + debian/patches/git-updates.diff | 2486 +++++++++++++++++++++++++++++++++++++-- 2 files changed, 2393 insertions(+), 99 deletions(-) diff --git a/debian/changelog b/debian/changelog index d133153..e072fe9 100644 --- a/debian/changelog +++ b/debian/changelog @@ -5,6 +5,12 @@ glibc (2.25-3) UNRELEASED; urgency=medium - Fix assertion failure in posix_spawn(). Closes: #882794. - Fix missing posix_fadvise64 from static mips64el build. Closes: #883186. + - Fix buffer overflow in glob with GLOB_TILDE (CVE-2017-15670). Closes: + #879501. + - Fix memory leak in glob with GLOB_TILDE (CVE-2017-15671). Closes: + #879500. + - Fix a buffer overflow in glob with GLOB_TILDE in unescaping + (CVE-2017-15804). Closes: #879955. * debian/patches/any/local-dlfptr.diff: remove, it's not used anymore by HPPA and causes issues on IA64. Closes: #882874. * debian/patches/submitted-ldconfig-c-collation.diff: New patch to process diff --git a/debian/patches/git-updates.diff b/debian/patches/git-updates.diff index 93e71d6..17a99ed 100644 --- a/debian/patches/git-updates.diff +++ b/debian/patches/git-updates.diff @@ -1,10 +1,104 @@ GIT update of git://sourceware.org/git/glibc.git/release/2.25/master from glibc-2.25 diff --git a/ChangeLog b/ChangeLog -index f140ee67de..ad563057d3 100644 +index f140ee67de..f85bf022b9 100644 --- a/ChangeLog +++ b/ChangeLog -@@ -1,3 +1,615 @@ +@@ -1,3 +1,709 @@ ++2017-11-02 Florian Weimer <fwei...@redhat.com> ++ ++ [BZ #22332] ++ * posix/tst-glob-tilde.c (do_noescape): New variable. ++ (one_test): Process it. ++ (do_test): Set do_noescape. Add unescaping test case. ++ ++2017-10-22 Paul Eggert <egg...@cs.ucla.edu> ++ ++ [BZ #22332] ++ * posix/glob.c (__glob): Fix buffer overflow during GLOB_TILDE ++ unescaping. ++ ++2017-10-21 Florian Weimer <fwei...@redhat.com> ++ ++ * posix/Makefile (tests): Add tst-glob-tilde. ++ (tests-special): Add tst-glob-tilde-mem.out ++ (tst-glob-tilde-ENV): Set MALLOC_TRACE. ++ (tst-glob-tilde-mem.out): Add mtrace check. ++ * posix/tst-glob-tilde.c: New file. ++ ++2017-10-20 Paul Eggert <egg...@cs.ucla.edu> ++ ++ [BZ #22320] ++ CVE-2017-15670 ++ * posix/glob.c (__glob): Fix one-byte overflow. ++ ++2017-09-08 Adhemerval Zanella <adhemerval.zane...@linaro.org> ++ ++ [BZ #1062] ++ CVE-2017-15671 ++ * posix/Makefile (routines): Add globfree, globfree64, and ++ glob_pattern_p. ++ * posix/flexmember.h: New file. ++ * posix/glob_internal.h: Likewise. ++ * posix/glob_pattern_p.c: Likewise. ++ * posix/globfree.c: Likewise. ++ * posix/globfree64.c: Likewise. ++ * sysdeps/gnu/globfree64.c: Likewise. ++ * sysdeps/unix/sysv/linux/alpha/globfree.c: Likewise. ++ * sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c: Likewise. ++ * sysdeps/unix/sysv/linux/oldglob.c: Likewise. ++ * sysdeps/unix/sysv/linux/wordsize-64/globfree64.c: Likewise. ++ * sysdeps/unix/sysv/linux/x86_64/x32/globfree.c: Likewise. ++ * sysdeps/wordsize-64/globfree.c: Likewise. ++ * sysdeps/wordsize-64/globfree64.c: Likewise. ++ * posix/glob.c (HAVE_CONFIG_H): Use !_LIBC instead. ++ [NDEBUG): Remove comments. ++ (GLOB_ONLY_P, _AMIGA, VMS): Remove define. ++ (dirent_type): New type. Use uint_fast8_t not ++ uint8_t, as C99 does not require uint8_t. ++ (DT_UNKNOWN, DT_DIR, DT_LNK): New macros. ++ (struct readdir_result): Use dirent_type. Do not define skip_entry ++ unless it is needed; this saves a byte on platforms lacking d_ino. ++ (readdir_result_type, readdir_result_skip_entry): ++ New functions, replacing ... ++ (readdir_result_might_be_symlink, readdir_result_might_be_dir): ++ these functions, which were removed. This makes the callers ++ easier to read. All callers changed. ++ (D_INO_TO_RESULT): Now empty if there is no d_ino. ++ (size_add_wrapv, glob_use_alloca): New static functions. ++ (glob, glob_in_dir): Check for size_t overflow in several places, ++ and fix some size_t checks that were not quite right. ++ Remove old code using SHELL since Bash no longer ++ uses this. ++ (glob, prefix_array): Separate MS code better. ++ (glob_in_dir): Remove old Amiga and VMS code. ++ (globfree, __glob_pattern_type, __glob_pattern_p): Move to ++ separate files. ++ (glob_in_dir): Do not rely on undefined behavior in accessing ++ struct members beyond their bounds. Use a flexible array member ++ instead ++ (link_stat): Rename from link_exists2_p and return -1/0 instead of ++ 0/1. Caller changed. ++ (glob): Fix memory leaks. ++ * posix/glob64 (globfree64): Move to separate file. ++ * sysdeps/gnu/glob64.c (NO_GLOB_PATTERN_P): Remove define. ++ (globfree64): Remove hidden alias. ++ * sysdeps/unix/sysv/linux/Makefile (sysdeps_routines): Add ++ oldglob. ++ * sysdeps/unix/sysv/linux/alpha/glob.c (__new_globfree): Move to ++ separate file. ++ * sysdeps/unix/sysv/linux/i386/glob64.c (NO_GLOB_PATTERN_P): Remove ++ define. ++ Move compat code to separate file. ++ * sysdeps/wordsize-64/glob.c (globfree): Move definitions to ++ separate file. ++ ++2017-08-20 H.J. Lu <hongjiu...@intel.com> ++ ++ [BZ #18822] ++ * sysdeps/unix/sysv/linux/i386/glob64.c (__old_glob64): Add ++ libc_hidden_proto and libc_hidden_def. ++ +2017-03-14 Adhemerval Zanella <adhemerval.zane...@linaro.org> + + [BZ #21232] @@ -724,10 +818,10 @@ index e9194e54cf..43343f03ee 100644 | sed -n -f $< > $@.new test -s $@.new diff --git a/NEWS b/NEWS -index ec15dde761..1879b735e6 100644 +index ec15dde761..0a8f20e371 100644 --- a/NEWS +++ b/NEWS -@@ -5,6 +5,31 @@ See the end for copying conditions. +@@ -5,6 +5,47 @@ See the end for copying conditions. Please send GNU C library bug reports via <http://sourceware.org/bugzilla/> using `glibc' in the "product" field. @@ -738,6 +832,15 @@ index ec15dde761..1879b735e6 100644 +* The DNS stub resolver limits the advertised UDP buffer size to 1200 bytes, + to avoid fragmentation-based spoofing attacks. + ++ CVE-2017-15671: The glob function, when invoked with GLOB_TILDE, ++ would sometimes fail to free memory allocated during ~ operator ++ processing, leading to a memory leak and, potentially, to a denial ++ of service. ++ ++ CVE-2017-15804: The glob function, when invoked with GLOB_TILDE and ++ without GLOB_NOESCAPE, could write past the end of a buffer while ++ unescaping user names. Reported by Tim Rühsen. ++ +The following bugs are resolved with this release: + + [20257] sunrpc: clntudp_call does not enforce timeout when receiving data @@ -755,6 +858,13 @@ index ec15dde761..1879b735e6 100644 + [21778] Robust mutex may deadlock + [21972] assert macro requires operator== (int) for its argument type + [22322] libc: [mips64] wrong bits/long-double.h installed ++ ++Security related changes: ++ ++ CVE-2017-15670: The glob function, when invoked with GLOB_TILDE, suffered ++ from a one-byte overflow during ~ operator processing (either on the stack ++ or the heap, depending on the length of the user name). ++ + Version 2.25 @@ -3072,17 +3182,1827 @@ index 0000000000..8221a61d29 +#include "tst-mutex7.c" diff --git a/nptl/tst-rwlock20.c b/nptl/tst-rwlock20.c new file mode 100644 -index 0000000000..4aeea2b8f5 +index 0000000000..4aeea2b8f5 +--- /dev/null ++++ b/nptl/tst-rwlock20.c +@@ -0,0 +1,116 @@ ++/* Test program for a read-phase / write-phase explicit hand-over. ++ Copyright (C) 2017 Free Software Foundation, Inc. ++ ++ 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; see the file COPYING.LIB. If ++ not, see <http://www.gnu.org/licenses/>. */ ++ ++#include <errno.h> ++#include <error.h> ++#include <pthread.h> ++#include <stdio.h> ++#include <stdlib.h> ++#include <unistd.h> ++#include <stdint.h> ++#include <time.h> ++#include <atomic.h> ++#include <support/xthread.h> ++ ++/* We realy want to set threads to 2 to reproduce this issue. The goal ++ is to have one primary writer and a single reader, and to hit the ++ bug that happens in the interleaving of those two phase transitions. ++ However, on most hardware, adding a second writer seems to help the ++ interleaving happen slightly more often, say 20% of the time. On a ++ 16 core ppc64 machine this fails 100% of the time with an unpatched ++ glibc. On a 8 core x86_64 machine this fails ~93% of the time, but ++ it doesn't fail at all on a 4 core system, so having available ++ unloaded cores makes a big difference in reproducibility. On an 8 ++ core qemu/kvm guest the reproducer reliability drops to ~10%. */ ++#define THREADS 3 ++ ++#define KIND PTHREAD_RWLOCK_PREFER_READER_NP ++ ++static pthread_rwlock_t lock; ++static int done = 0; ++ ++static void* ++tf (void* arg) ++{ ++ while (atomic_load_relaxed (&done) == 0) ++ { ++ int rcnt = 0; ++ int wcnt = 100; ++ if ((uintptr_t) arg == 0) ++ { ++ rcnt = 1; ++ wcnt = 1; ++ } ++ ++ do ++ { ++ if (wcnt) ++ { ++ xpthread_rwlock_wrlock (&lock); ++ xpthread_rwlock_unlock (&lock); ++ wcnt--; ++ } ++ if (rcnt) ++ { ++ xpthread_rwlock_rdlock (&lock); ++ xpthread_rwlock_unlock (&lock); ++ rcnt--; ++ } ++ } ++ while ((atomic_load_relaxed (&done) == 0) && (rcnt + wcnt > 0)); ++ ++ } ++ return NULL; ++} ++ ++ ++ ++static int ++do_test (void) ++{ ++ pthread_t thr[THREADS]; ++ int n; ++ pthread_rwlockattr_t attr; ++ ++ xpthread_rwlockattr_init (&attr); ++ xpthread_rwlockattr_setkind_np (&attr, KIND); ++ ++ xpthread_rwlock_init (&lock, &attr); ++ ++ /* Make standard error the same as standard output. */ ++ dup2 (1, 2); ++ ++ /* Make sure we see all message, even those on stdout. */ ++ setvbuf (stdout, NULL, _IONBF, 0); ++ ++ for (n = 0; n < THREADS; ++n) ++ thr[n] = xpthread_create (NULL, tf, (void *) (uintptr_t) n); ++ ++ struct timespec delay; ++ delay.tv_sec = 10; ++ delay.tv_nsec = 0; ++ nanosleep (&delay, NULL); ++ atomic_store_relaxed (&done, 1); ++ ++ /* Wait for all the threads. */ ++ for (n = 0; n < THREADS; ++n) ++ xpthread_join (thr[n]); ++ ++ return 0; ++} ++ ++#include <support/test-driver.c> +diff --git a/posix/Makefile b/posix/Makefile +index 8f23d647c8..ccbfcf3c48 100644 +--- a/posix/Makefile ++++ b/posix/Makefile +@@ -43,7 +43,7 @@ routines := \ + getpgid setpgid getpgrp bsd-getpgrp setpgrp getsid setsid \ + getresuid getresgid setresuid setresgid \ + pathconf sysconf fpathconf \ +- glob glob64 fnmatch regex \ ++ glob glob64 globfree globfree64 glob_pattern_p fnmatch regex \ + confstr \ + getopt getopt1 getopt_init \ + sched_setp sched_getp sched_sets sched_gets sched_yield sched_primax \ +@@ -91,7 +91,8 @@ tests := tstgetopt testfnm runtests runptests \ + tst-pathconf tst-getaddrinfo4 tst-rxspencer-no-utf8 \ + tst-fnmatch3 bug-regex36 tst-getaddrinfo5 \ + tst-posix_spawn-fd \ +- tst-posix_fadvise tst-posix_fadvise64 ++ tst-posix_fadvise tst-posix_fadvise64 \ ++ tst-glob-tilde + xtests := bug-ga2 + ifeq (yes,$(build-shared)) + test-srcs := globtest +@@ -134,7 +135,8 @@ tests-special += $(objpfx)bug-regex2-mem.out $(objpfx)bug-regex14-mem.out \ + $(objpfx)tst-rxspencer-no-utf8-mem.out $(objpfx)tst-pcre-mem.out \ + $(objpfx)tst-boost-mem.out $(objpfx)tst-getconf.out \ + $(objpfx)bug-glob2-mem.out $(objpfx)tst-vfork3-mem.out \ +- $(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out ++ $(objpfx)tst-fnmatch-mem.out $(objpfx)bug-regex36-mem.out \ ++ $(objpfx)tst-glob-tilde-mem.out + xtests-special += $(objpfx)bug-ga2-mem.out + endif + +@@ -341,6 +343,12 @@ $(objpfx)bug-glob2-mem.out: $(objpfx)bug-glob2.out + $(common-objpfx)malloc/mtrace $(objpfx)bug-glob2.mtrace > $@; \ + $(evaluate-test) + ++tst-glob-tilde-ENV = MALLOC_TRACE=$(objpfx)tst-glob-tilde.mtrace ++ ++$(objpfx)tst-glob-tilde-mem.out: $(objpfx)tst-glob-tilde.out ++ $(common-objpfx)malloc/mtrace $(objpfx)tst-glob-tilde.mtrace > $@; \ ++ $(evaluate-test) ++ + $(inst_libexecdir)/getconf: $(inst_bindir)/getconf \ + $(objpfx)getconf.speclist FORCE + $(addprefix $(..)./scripts/mkinstalldirs ,\ +diff --git a/posix/flexmember.h b/posix/flexmember.h +new file mode 100644 +index 0000000000..107c1f09e9 +--- /dev/null ++++ b/posix/flexmember.h +@@ -0,0 +1,45 @@ ++/* Sizes of structs with flexible array members. ++ ++ Copyright 2016-2017 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 ++ <http://www.gnu.org/licenses/>. ++ ++ Written by Paul Eggert. */ ++ ++#include <stddef.h> ++ ++/* Nonzero multiple of alignment of TYPE, suitable for FLEXSIZEOF below. ++ On older platforms without _Alignof, use a pessimistic bound that is ++ safe in practice even if FLEXIBLE_ARRAY_MEMBER is 1. ++ On newer platforms, use _Alignof to get a tighter bound. */ ++ ++#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112 ++# define FLEXALIGNOF(type) (sizeof (type) & ~ (sizeof (type) - 1)) ++#else ++# define FLEXALIGNOF(type) _Alignof (type) ++#endif ++ ++/* Upper bound on the size of a struct of type TYPE with a flexible ++ array member named MEMBER that is followed by N bytes of other data. ++ This is not simply sizeof (TYPE) + N, since it may require ++ alignment on unusually picky C11 platforms, and ++ FLEXIBLE_ARRAY_MEMBER may be 1 on pre-C11 platforms. ++ Yield a value less than N if and only if arithmetic overflow occurs. */ ++ ++#define FLEXSIZEOF(type, member, n) \ ++ ((offsetof (type, member) + FLEXALIGNOF (type) - 1 + (n)) \ ++ & ~ (FLEXALIGNOF (type) - 1)) +diff --git a/posix/glob.c b/posix/glob.c +index c653809118..b2273ea7bc 100644 +--- a/posix/glob.c ++++ b/posix/glob.c +@@ -15,7 +15,7 @@ + License along with the GNU C Library; if not, see + <http://www.gnu.org/licenses/>. */ + +-#ifdef HAVE_CONFIG_H ++#ifndef _LIBC + # include <config.h> + #endif + +@@ -27,29 +27,15 @@ + #include <stdbool.h> + #include <stddef.h> + #include <stdint.h> +- +-/* Outcomment the following line for production quality code. */ +-/* #define NDEBUG 1 */ + #include <assert.h> ++#include <unistd.h> + +-#include <stdio.h> /* Needed on stupid SunOS for assert. */ +- +-#if !defined _LIBC || !defined GLOB_ONLY_P +-#if defined HAVE_UNISTD_H || defined _LIBC +-# include <unistd.h> +-# ifndef POSIX +-# ifdef _POSIX_VERSION +-# define POSIX +-# endif +-# endif ++#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ ++# define WINDOWS32 + #endif + +-#include <pwd.h> +- +-#if defined HAVE_STDINT_H || defined _LIBC +-# include <stdint.h> +-#elif !defined UINTPTR_MAX +-# define UINTPTR_MAX (~((size_t) 0)) ++#ifndef WINDOWS32 ++# include <pwd.h> + #endif + + #include <errno.h> +@@ -57,24 +43,7 @@ + # define __set_errno(val) errno = (val) + #endif + +-#if defined HAVE_DIRENT_H || defined __GNU_LIBRARY__ +-# include <dirent.h> +-#else +-# define dirent direct +-# ifdef HAVE_SYS_NDIR_H +-# include <sys/ndir.h> +-# endif +-# ifdef HAVE_SYS_DIR_H +-# include <sys/dir.h> +-# endif +-# ifdef HAVE_NDIR_H +-# include <ndir.h> +-# endif +-# ifdef HAVE_VMSDIR_H +-# include "vmsdir.h" +-# endif /* HAVE_VMSDIR_H */ +-#endif +- ++#include <dirent.h> + #include <stdlib.h> + #include <string.h> + #include <alloca.h> +@@ -87,27 +56,29 @@ + # define opendir(name) __opendir (name) + # define readdir(str) __readdir64 (str) + # define getpwnam_r(name, bufp, buf, len, res) \ +- __getpwnam_r (name, bufp, buf, len, res) ++ __getpwnam_r (name, bufp, buf, len, res) + # ifndef __stat64 + # define __stat64(fname, buf) __xstat64 (_STAT_VER, fname, buf) + # endif + # define struct_stat64 struct stat64 ++# define FLEXIBLE_ARRAY_MEMBER + #else /* !_LIBC */ +-# include "getlogin_r.h" +-# include "mempcpy.h" +-# include "stat-macros.h" +-# include "strdup.h" +-# define __stat64(fname, buf) stat (fname, buf) +-# define struct_stat64 struct stat +-# define __stat(fname, buf) stat (fname, buf) +-# define __alloca alloca +-# define __readdir readdir +-# define __readdir64 readdir64 +-# define __glob_pattern_p glob_pattern_p ++# 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 ++# ifndef __MVS__ ++# define __alloca alloca ++# endif ++# define __readdir readdir ++# define COMPILE_GLOB64 + #endif /* _LIBC */ + + #include <fnmatch.h> + ++#include <flexmember.h> ++#include <glob_internal.h> ++ + #ifdef _SC_GETPW_R_SIZE_MAX + # define GETPW_R_SIZE_MAX() sysconf (_SC_GETPW_R_SIZE_MAX) + #else +@@ -121,61 +92,59 @@ + + static const char *next_brace_sub (const char *begin, int flags) __THROWNL; + ++typedef uint_fast8_t dirent_type; ++ ++#if !defined _LIBC && !defined HAVE_STRUCT_DIRENT_D_TYPE ++/* Any distinct values will do here. ++ Undef any existing macros out of the way. */ ++# undef DT_UNKNOWN ++# undef DT_DIR ++# undef DT_LNK ++# define DT_UNKNOWN 0 ++# define DT_DIR 1 ++# define DT_LNK 2 ++#endif ++ + /* 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 ++#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE ++ dirent_type type; ++#endif ++#if defined _LIBC || defined D_INO_IN_DIRENT + bool skip_entry; ++#endif + }; + +-# 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) ++/* Initialize and return type member of struct readdir_result. */ ++static dirent_type ++readdir_result_type (struct readdir_result d) + { +- return true; ++#if defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE ++# define D_TYPE_TO_RESULT(source) (source)->d_type, ++ return d.type; ++#else ++# define D_TYPE_TO_RESULT(source) ++ return DT_UNKNOWN; ++#endif + } + ++/* Initialize and return skip_entry member of struct readdir_result. */ + static bool +-readdir_result_might_be_dir (struct readdir_result d) ++readdir_result_skip_entry (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 ++#if defined _LIBC || defined D_INO_IN_DIRENT ++# define D_INO_TO_RESULT(source) (source)->d_ino == 0, ++ return d.skip_entry; ++#else ++# define D_INO_TO_RESULT(source) ++ return false; ++#endif ++} + + /* Construct an initializer for a struct readdir_result object from a + struct dirent *. No copy of the name is made. */ +@@ -186,8 +155,6 @@ readdir_result_might_be_dir (struct readdir_result d) + D_INO_TO_RESULT (source) \ + } + +-#endif /* !defined _LIBC || !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 +@@ -225,18 +192,55 @@ convert_dirent64 (const struct dirent64 *source) + } + #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, ++ therefore save some unnecessary recursion in fchdir.c and opendir_safer.c. ++ FIXME - if the kernel ever adds support for multi-thread safety for ++ avoiding standard fds, then we should use opendir_safer. */ ++# ifdef GNULIB_defined_opendir ++# undef opendir ++# endif ++# ifdef GNULIB_defined_closedir ++# undef closedir ++# endif + +-#ifndef attribute_hidden +-# define attribute_hidden ++/* 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 + ++/* Set *R = A + B. Return true if the answer is mathematically ++ incorrect due to overflow; in this case, *R is the low order ++ bits of the correct answer. */ ++ ++static bool ++size_add_wrapv (size_t a, size_t b, size_t *r) ++{ ++#if 5 <= __GNUC__ && !defined __ICC ++ return __builtin_add_overflow (a, b, r); ++#else ++ *r = a + b; ++ return *r < a; ++#endif ++} ++ ++static bool ++glob_use_alloca (size_t alloca_used, size_t len) ++{ ++ size_t size; ++ return (!size_add_wrapv (alloca_used, len, &size) ++ && __libc_use_alloca (size)); ++} ++ + 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); + extern int __glob_pattern_type (const char *pattern, int quote) + attribute_hidden; + +-#if !defined _LIBC || !defined 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; + +@@ -265,16 +269,15 @@ next_brace_sub (const char *cp, int flags) + return *cp != '\0' ? cp : NULL; + } + +-#endif /* !defined _LIBC || !defined GLOB_ONLY_P */ + + /* Do glob searching for PATTERN, placing results in PGLOB. + The bits defined above may be set in FLAGS. + If a directory cannot be opened or read and ERRFUNC is not nil, + it is called with the pathname that caused the error, and the +- `errno' value from the failing call; if it returns non-zero +- `glob' returns GLOB_ABORTED; if it returns zero, the error is ignored. ++ 'errno' value from the failing call; if it returns non-zero ++ 'glob' returns GLOB_ABORTED; if it returns zero, the error is ignored. + If memory cannot be allocated for PGLOB, GLOB_NOSPACE is returned. +- Otherwise, `glob' returns zero. */ ++ Otherwise, 'glob' returns zero. */ + int + #ifdef GLOB_ATTRIBUTE + GLOB_ATTRIBUTE +@@ -292,9 +295,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + int malloc_dirname = 0; + glob_t dirs; + int retval = 0; +-#ifdef _LIBC + size_t alloca_used = 0; +-#endif + + if (pattern == NULL || pglob == NULL || (flags & ~__GLOB_FLAGS) != 0) + { +@@ -308,7 +309,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + flags |= GLOB_ONLYDIR; + + if (!(flags & GLOB_DOOFFS)) +- /* Have to do this so `globfree' knows where to start freeing. It ++ /* Have to do this so 'globfree' knows where to start freeing. It + also makes all the code that uses gl_offs simpler. */ + pglob->gl_offs = 0; + +@@ -372,14 +373,12 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + size_t rest_len; + char *onealt; + size_t pattern_len = strlen (pattern) - 1; +-#ifdef _LIBC +- int alloca_onealt = __libc_use_alloca (alloca_used + pattern_len); ++ int alloca_onealt = glob_use_alloca (alloca_used, pattern_len); + if (alloca_onealt) + onealt = alloca_account (pattern_len, alloca_used); + else +-#endif + { +- onealt = (char *) malloc (pattern_len); ++ onealt = malloc (pattern_len); + if (onealt == NULL) + return GLOB_NOSPACE; + } +@@ -392,11 +391,9 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + next = next_brace_sub (begin + 1, flags); + if (next == NULL) + { +- /* It is an illegal expression. */ ++ /* It is an invalid expression. */ + illegal_brace: +-#ifdef _LIBC + if (__glibc_unlikely (!alloca_onealt)) +-#endif + free (onealt); + flags &= ~GLOB_BRACE; + goto no_brace; +@@ -437,9 +434,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + /* If we got an error, return it. */ + if (result && result != GLOB_NOMATCH) + { +-#ifdef _LIBC + if (__glibc_unlikely (!alloca_onealt)) +-#endif + free (onealt); + if (!(flags & GLOB_APPEND)) + { +@@ -458,9 +453,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + assert (next != NULL); + } + +-#ifdef _LIBC + if (__glibc_unlikely (!alloca_onealt)) +-#endif + free (onealt); + + if (pglob->gl_pathc != firstc) +@@ -476,14 +469,16 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + + /* Find the filename. */ + filename = strrchr (pattern, '/'); ++ + #if defined __MSDOS__ || defined WINDOWS32 +- /* The case of "d:pattern". Since `:' is not allowed in ++ /* The case of "d:pattern". Since ':' is not allowed in + file names, we can safely assume that wherever it + happens in pattern, it signals the filename part. This + is so we could some day support patterns like "[a-z]:foo". */ + if (filename == NULL) + filename = strchr (pattern, ':'); + #endif /* __MSDOS__ || WINDOWS32 */ ++ + dirname_modified = 0; + if (filename == NULL) + { +@@ -508,11 +503,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + } + + filename = pattern; +-#ifdef _AMIGA +- dirname = (char *) ""; +-#else + dirname = (char *) "."; +-#endif + dirlen = 0; + } + } +@@ -536,22 +527,21 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + char *drive_spec; + + ++dirlen; +- drive_spec = (char *) __alloca (dirlen + 1); ++ 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 ++ /* 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 +-#ifdef _LIBC +- if (__libc_use_alloca (alloca_used + dirlen + 1)) ++ ++ if (glob_use_alloca (alloca_used, dirlen + 1)) + newp = alloca_account (dirlen + 1, alloca_used); + else +-#endif + { + newp = malloc (dirlen + 1); + if (newp == NULL) +@@ -562,14 +552,17 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + dirname = newp; + ++filename; + +- if (filename[0] == '\0' + #if defined __MSDOS__ || defined WINDOWS32 +- && dirname[dirlen - 1] != ':' +- && (dirlen < 3 || dirname[dirlen - 2] != ':' +- || dirname[dirlen - 1] != '/') ++ bool drive_root = (dirlen > 1 ++ && (dirname[dirlen - 1] == ':' ++ || (dirlen > 2 && dirname[dirlen - 2] == ':' ++ && dirname[dirlen - 1] == '/'))); ++#else ++ bool drive_root = false; + #endif +- && dirlen > 1) +- /* "pattern/". Expand "pattern", appending slashes. */ ++ ++ if (filename[0] == '\0' && dirlen > 1 && !drive_root) ++ /* "pattern/". Expand "pattern", appending slashes. */ + { + int orig_flags = flags; + if (!(flags & GLOB_NOESCAPE) && dirname[dirlen - 1] == '\\') +@@ -602,7 +595,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + } + } + +-#ifndef VMS + if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~') + { + if (dirname[1] == '\0' || dirname[1] == '/' +@@ -612,100 +604,127 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + /* Look up home directory. */ + char *home_dir = getenv ("HOME"); + int malloc_home_dir = 0; +-# ifdef _AMIGA +- if (home_dir == NULL || home_dir[0] == '\0') +- home_dir = "SYS:"; +-# else +-# ifdef WINDOWS32 +- if (home_dir == NULL || home_dir[0] == '\0') +- home_dir = "c:/users/default"; /* poor default */ +-# else + 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 */ ++#else + 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 ++ /* 'sysconf' does not support _SC_LOGIN_NAME_MAX. Try + a moderate value. */ + buflen = 20; +- name = alloca_account (buflen, alloca_used); ++ if (glob_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; + if (success) + { + struct passwd *p; +-# if defined HAVE_GETPWNAM_R || defined _LIBC +- long int pwbuflen = GETPW_R_SIZE_MAX (); ++ char *malloc_pwtmpbuf = NULL; + char *pwtmpbuf; ++# if defined HAVE_GETPWNAM_R || defined _LIBC ++ long int pwbuflenmax = GETPW_R_SIZE_MAX (); ++ size_t pwbuflen = pwbuflenmax; + struct passwd pwbuf; +- int malloc_pwtmpbuf = 0; + int save = errno; + +-# ifndef _LIBC +- if (pwbuflen == -1) +- /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX. ++# ifndef _LIBC ++ if (! (0 < pwbuflenmax && pwbuflenmax <= SIZE_MAX)) ++ /* 'sysconf' does not support _SC_GETPW_R_SIZE_MAX. + Try a moderate value. */ + pwbuflen = 1024; +-# endif +- if (__libc_use_alloca (alloca_used + pwbuflen)) ++# endif ++ if (glob_use_alloca (alloca_used, pwbuflen)) + pwtmpbuf = alloca_account (pwbuflen, alloca_used); + else + { + pwtmpbuf = malloc (pwbuflen); + if (pwtmpbuf == NULL) + { ++ if (__glibc_unlikely (malloc_name)) ++ free (name); + retval = GLOB_NOSPACE; + goto out; + } +- malloc_pwtmpbuf = 1; ++ malloc_pwtmpbuf = pwtmpbuf; + } + + while (getpwnam_r (name, &pwbuf, pwtmpbuf, pwbuflen, &p) + != 0) + { ++ size_t newlen; ++ bool v; + if (errno != ERANGE) + { + p = NULL; + break; + } +- +- if (!malloc_pwtmpbuf +- && __libc_use_alloca (alloca_used +- + 2 * pwbuflen)) ++ v = size_add_wrapv (pwbuflen, pwbuflen, &newlen); ++ if (!v && malloc_pwtmpbuf == NULL ++ && glob_use_alloca (alloca_used, newlen)) + pwtmpbuf = extend_alloca_account (pwtmpbuf, pwbuflen, +- 2 * pwbuflen, +- alloca_used); ++ newlen, alloca_used); + else + { +- char *newp = realloc (malloc_pwtmpbuf +- ? pwtmpbuf : NULL, +- 2 * pwbuflen); ++ char *newp = (v ? NULL ++ : realloc (malloc_pwtmpbuf, newlen)); + if (newp == NULL) + { +- if (__glibc_unlikely (malloc_pwtmpbuf)) +- free (pwtmpbuf); ++ free (malloc_pwtmpbuf); ++ if (__glibc_unlikely (malloc_name)) ++ free (name); + retval = GLOB_NOSPACE; + goto out; + } +- pwtmpbuf = newp; +- pwbuflen = 2 * pwbuflen; +- malloc_pwtmpbuf = 1; ++ malloc_pwtmpbuf = pwtmpbuf = newp; + } ++ pwbuflen = newlen; + __set_errno (save); + } +-# else ++# else + p = getpwnam (name); +-# endif ++# endif ++ if (__glibc_unlikely (malloc_name)) ++ free (name); + if (p != NULL) + { +- if (!malloc_pwtmpbuf) ++ if (malloc_pwtmpbuf == NULL) + 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)) ++ if (glob_use_alloca (alloca_used, home_dir_len)) + home_dir = alloca_account (home_dir_len, + alloca_used); + else +@@ -720,26 +739,32 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + malloc_home_dir = 1; + } + memcpy (home_dir, p->pw_dir, home_dir_len); +- +- free (pwtmpbuf); + } + } ++ free (malloc_pwtmpbuf); + } ++ else ++ { ++ if (__glibc_unlikely (malloc_name)) ++ free (name); ++ } ++#endif /* WINDOWS32 */ + } + if (home_dir == NULL || home_dir[0] == '\0') + { ++ if (__glibc_unlikely (malloc_home_dir)) ++ free (home_dir); + if (flags & GLOB_TILDE_CHECK) + { +- if (__glibc_unlikely (malloc_home_dir)) +- free (home_dir); + retval = GLOB_NOMATCH; + goto out; + } + else +- home_dir = (char *) "~"; /* No luck. */ ++ { ++ home_dir = (char *) "~"; /* No luck. */ ++ malloc_home_dir = 0; ++ } + } +-# endif /* WINDOWS32 */ +-# endif + /* Now construct the full directory. */ + if (dirname[1] == '\0') + { +@@ -754,8 +779,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + { + char *newp; + size_t home_len = strlen (home_dir); +- int use_alloca = __libc_use_alloca (alloca_used +- + home_len + dirlen); ++ int use_alloca = glob_use_alloca (alloca_used, home_len + dirlen); + if (use_alloca) + newp = alloca_account (home_len + dirlen, alloca_used); + else +@@ -779,12 +803,15 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + dirname = newp; + dirlen += home_len - 1; + malloc_dirname = !use_alloca; ++ ++ if (__glibc_unlikely (malloc_home_dir)) ++ free (home_dir); + } + dirname_modified = 1; + } +-# if !defined _AMIGA && !defined WINDOWS32 + else + { ++#ifndef WINDOWS32 + char *end_name = strchr (dirname, '/'); + char *user_name; + int malloc_user_name = 0; +@@ -806,7 +833,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + else + { + char *newp; +- if (__libc_use_alloca (alloca_used + (end_name - dirname))) ++ if (glob_use_alloca (alloca_used, end_name - dirname)) + newp = alloca_account (end_name - dirname, alloca_used); + else + { +@@ -823,11 +850,11 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + char *p = mempcpy (newp, dirname + 1, + unescape - dirname - 1); + char *q = unescape; +- while (*q != '\0') ++ while (q != end_name) + { + if (*q == '\\') + { +- if (q[1] == '\0') ++ if (q + 1 == end_name) + { + /* "~fo\\o\\" unescape to user_name "foo\\", + but "~fo\\o\\/" unescape to user_name +@@ -843,7 +870,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + *p = '\0'; + } + else +- *((char *) mempcpy (newp, dirname + 1, end_name - dirname)) ++ *((char *) mempcpy (newp, dirname + 1, end_name - dirname - 1)) + = '\0'; + user_name = newp; + } +@@ -851,20 +878,21 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + /* Look up specific user's home directory. */ + { + struct passwd *p; ++ char *malloc_pwtmpbuf = NULL; + # if defined HAVE_GETPWNAM_R || defined _LIBC +- long int buflen = GETPW_R_SIZE_MAX (); ++ long int buflenmax = GETPW_R_SIZE_MAX (); ++ size_t buflen = buflenmax; + char *pwtmpbuf; +- int malloc_pwtmpbuf = 0; + struct passwd pwbuf; + int save = errno; + + # ifndef _LIBC +- if (buflen == -1) +- /* `sysconf' does not support _SC_GETPW_R_SIZE_MAX. Try a ++ if (! (0 <= buflenmax && buflenmax <= SIZE_MAX)) ++ /* Perhaps 'sysconf' does not support _SC_GETPW_R_SIZE_MAX. Try a + moderate value. */ + buflen = 1024; + # endif +- if (__libc_use_alloca (alloca_used + buflen)) ++ if (glob_use_alloca (alloca_used, buflen)) + pwtmpbuf = alloca_account (buflen, alloca_used); + else + { +@@ -877,32 +905,32 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + retval = GLOB_NOSPACE; + goto out; + } +- malloc_pwtmpbuf = 1; ++ malloc_pwtmpbuf = pwtmpbuf; + } + + while (getpwnam_r (user_name, &pwbuf, pwtmpbuf, buflen, &p) != 0) + { ++ size_t newlen; ++ bool v; + if (errno != ERANGE) + { + p = NULL; + break; + } +- if (!malloc_pwtmpbuf +- && __libc_use_alloca (alloca_used + 2 * buflen)) ++ v = size_add_wrapv (buflen, buflen, &newlen); ++ if (!v && malloc_pwtmpbuf == NULL ++ && glob_use_alloca (alloca_used, newlen)) + pwtmpbuf = extend_alloca_account (pwtmpbuf, buflen, +- 2 * buflen, alloca_used); ++ newlen, alloca_used); + else + { +- char *newp = realloc (malloc_pwtmpbuf ? pwtmpbuf : NULL, +- 2 * buflen); ++ char *newp = v ? NULL : realloc (malloc_pwtmpbuf, newlen); + if (newp == NULL) + { +- if (__glibc_unlikely (malloc_pwtmpbuf)) +- free (pwtmpbuf); ++ free (malloc_pwtmpbuf); + goto nomem_getpw; + } +- pwtmpbuf = newp; +- malloc_pwtmpbuf = 1; ++ malloc_pwtmpbuf = pwtmpbuf = newp; + } + __set_errno (save); + } +@@ -923,7 +951,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + free (dirname); + malloc_dirname = 0; + +- if (__libc_use_alloca (alloca_used + home_len + rest_len + 1)) ++ if (glob_use_alloca (alloca_used, home_len + rest_len + 1)) + dirname = alloca_account (home_len + rest_len + 1, + alloca_used); + else +@@ -931,8 +959,7 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + dirname = malloc (home_len + rest_len + 1); + if (dirname == NULL) + { +- if (__glibc_unlikely (malloc_pwtmpbuf)) +- free (pwtmpbuf); ++ free (malloc_pwtmpbuf); + retval = GLOB_NOSPACE; + goto out; + } +@@ -944,24 +971,24 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + dirlen = home_len + rest_len; + dirname_modified = 1; + +- if (__glibc_unlikely (malloc_pwtmpbuf)) +- free (pwtmpbuf); ++ free (malloc_pwtmpbuf); + } + else + { +- if (__glibc_unlikely (malloc_pwtmpbuf)) +- free (pwtmpbuf); ++ free (malloc_pwtmpbuf); + + if (flags & GLOB_TILDE_CHECK) +- /* We have to regard it as an error if we cannot find the +- home directory. */ +- return GLOB_NOMATCH; ++ { ++ /* We have to regard it as an error if we cannot find the ++ home directory. */ ++ retval = GLOB_NOMATCH; ++ goto out; ++ } + } + } ++#endif /* !WINDOWS32 */ + } +-# endif /* Not Amiga && not WINDOWS32. */ + } +-#endif /* Not VMS. */ + + /* Now test whether we looked for "~" or "~NAME". In this case we + can give the answer now. */ +@@ -980,19 +1007,18 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + size_t newcount = pglob->gl_pathc + pglob->gl_offs; + char **new_gl_pathv; + +- if (newcount > UINTPTR_MAX - (1 + 1) +- || newcount + 1 + 1 > ~((size_t) 0) / sizeof (char *)) ++ if (newcount > SIZE_MAX / sizeof (char *) - 2) + { + nospace: + free (pglob->gl_pathv); + pglob->gl_pathv = NULL; + pglob->gl_pathc = 0; +- return GLOB_NOSPACE; ++ retval = GLOB_NOSPACE; ++ goto out; + } + +- new_gl_pathv +- = (char **) realloc (pglob->gl_pathv, +- (newcount + 1 + 1) * sizeof (char *)); ++ new_gl_pathv = realloc (pglob->gl_pathv, ++ (newcount + 2) * sizeof (char *)); + if (new_gl_pathv == NULL) + goto nospace; + pglob->gl_pathv = new_gl_pathv; +@@ -1006,12 +1032,19 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + p = mempcpy (pglob->gl_pathv[newcount], dirname, dirlen); + p[0] = '/'; + p[1] = '\0'; ++ if (__glibc_unlikely (malloc_dirname)) ++ free (dirname); + } + else + { +- pglob->gl_pathv[newcount] = strdup (dirname); +- if (pglob->gl_pathv[newcount] == NULL) +- goto nospace; ++ if (__glibc_unlikely (malloc_dirname)) ++ pglob->gl_pathv[newcount] = dirname; ++ else ++ { ++ pglob->gl_pathv[newcount] = strdup (dirname); ++ if (pglob->gl_pathv[newcount] == NULL) ++ goto nospace; ++ } + } + pglob->gl_pathv[++newcount] = NULL; + ++pglob->gl_pathc; +@@ -1021,7 +1054,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + } + + /* Not found. */ +- return GLOB_NOMATCH; ++ retval = GLOB_NOMATCH; ++ goto out; + } + + meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE)); +@@ -1067,7 +1101,10 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + if (status != 0) + { + if ((flags & GLOB_NOCHECK) == 0 || status != GLOB_NOMATCH) +- return status; ++ { ++ retval = status; ++ goto out; ++ } + goto no_matches; + } + +@@ -1078,19 +1115,6 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + { + size_t old_pathc; + +-#ifdef SHELL +- { +- /* Make globbing interruptible in the bash shell. */ +- extern int interrupt_state; +- +- if (interrupt_state) +- { +- globfree (&dirs); +- return GLOB_ABORTED; +- } +- } +-#endif /* SHELL. */ +- + old_pathc = pglob->gl_pathc; + status = glob_in_dir (filename, dirs.gl_pathv[i], + ((flags | GLOB_APPEND) +@@ -1105,7 +1129,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + globfree (&dirs); + globfree (pglob); + pglob->gl_pathc = 0; +- return status; ++ retval = status; ++ goto out; + } + + /* Stick the directory on the front of each name. */ +@@ -1116,13 +1141,14 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + globfree (&dirs); + globfree (pglob); + pglob->gl_pathc = 0; +- return GLOB_NOSPACE; ++ retval = GLOB_NOSPACE; ++ goto out; + } + } + + flags |= GLOB_MAGCHAR; + +- /* We have ignored the GLOB_NOCHECK flag in the `glob_in_dir' calls. ++ /* We have ignored the GLOB_NOCHECK flag in the 'glob_in_dir' calls. + But if we have not found any matching entry and the GLOB_NOCHECK + flag was set we must return the input pattern itself. */ + if (pglob->gl_pathc + pglob->gl_offs == oldcount) +@@ -1134,28 +1160,28 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + size_t newcount = pglob->gl_pathc + pglob->gl_offs; + char **new_gl_pathv; + +- if (newcount > UINTPTR_MAX - 2 +- || newcount + 2 > ~((size_t) 0) / sizeof (char *)) ++ if (newcount > SIZE_MAX / sizeof (char *) - 2) + { + nospace2: + globfree (&dirs); +- return GLOB_NOSPACE; ++ retval = GLOB_NOSPACE; ++ goto out; + } + +- new_gl_pathv = (char **) realloc (pglob->gl_pathv, +- (newcount + 2) +- * sizeof (char *)); ++ 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); ++ pglob->gl_pathv[newcount] = strdup (pattern); + if (pglob->gl_pathv[newcount] == NULL) + { + globfree (&dirs); + globfree (pglob); + pglob->gl_pathc = 0; +- return GLOB_NOSPACE; ++ retval = GLOB_NOSPACE; ++ goto out; + } + + ++pglob->gl_pathc; +@@ -1167,7 +1193,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + else + { + globfree (&dirs); +- return GLOB_NOMATCH; ++ retval = GLOB_NOMATCH; ++ goto out; + } + } + +@@ -1213,7 +1240,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + flags = orig_flags; + goto no_matches; + } +- return status; ++ retval = status; ++ goto out; + } + + if (dirlen > 0) +@@ -1225,7 +1253,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + { + globfree (pglob); + pglob->gl_pathc = 0; +- return GLOB_NOSPACE; ++ retval = GLOB_NOSPACE; ++ goto out; + } + } + } +@@ -1250,7 +1279,8 @@ glob (const char *pattern, int flags, int (*errfunc) (const char *, int), + { + globfree (pglob); + pglob->gl_pathc = 0; +- return GLOB_NOSPACE; ++ retval = GLOB_NOSPACE; ++ goto out; + } + strcpy (&new[len - 2], "/"); + pglob->gl_pathv[i] = new; +@@ -1276,32 +1306,12 @@ libc_hidden_def (glob) + #endif + + +-#if !defined _LIBC || !defined GLOB_ONLY_P +- +-/* Free storage allocated in PGLOB by a previous `glob' call. */ +-void +-globfree (glob_t *pglob) +-{ +- if (pglob->gl_pathv != NULL) +- { +- size_t i; +- for (i = 0; i < pglob->gl_pathc; ++i) +- free (pglob->gl_pathv[pglob->gl_offs + i]); +- free (pglob->gl_pathv); +- pglob->gl_pathv = NULL; +- } +-} +-#if defined _LIBC && !defined globfree +-libc_hidden_def (globfree) +-#endif +- +- + /* Do a collated comparison of A and B. */ + static int + collated_compare (const void *a, const void *b) + { +- const char *const s1 = *(const char *const * const) a; +- const char *const s2 = *(const char *const * const) b; ++ char *const *ps1 = a; char *s1 = *ps1; ++ char *const *ps2 = b; char *s2 = *ps2; + + if (s1 == s2) + return 0; +@@ -1322,28 +1332,24 @@ prefix_array (const char *dirname, char **array, size_t n) + { + size_t i; + size_t dirlen = strlen (dirname); +-#if defined __MSDOS__ || defined WINDOWS32 +- int sep_char = '/'; +-# define DIRSEP_CHAR sep_char +-#else +-# define DIRSEP_CHAR '/' +-#endif ++ char dirsep_char = '/'; + + if (dirlen == 1 && dirname[0] == '/') + /* DIRNAME is just "/", so normal prepending would get us "//foo". + We want "/foo" instead, so don't prepend any chars from DIRNAME. */ + dirlen = 0; ++ + #if defined __MSDOS__ || defined WINDOWS32 +- else if (dirlen > 1) ++ if (dirlen > 1) + { + if (dirname[dirlen - 1] == '/' && dirname[dirlen - 2] == ':') + /* DIRNAME is "d:/". Don't prepend the slash from DIRNAME. */ + --dirlen; + else if (dirname[dirlen - 1] == ':') + { +- /* DIRNAME is "d:". Use `:' instead of `/'. */ ++ /* DIRNAME is "d:". Use ':' instead of '/'. */ + --dirlen; +- sep_char = ':'; ++ dirsep_char = ':'; + } + } + #endif +@@ -1351,7 +1357,7 @@ prefix_array (const char *dirname, char **array, size_t n) + for (i = 0; i < n; ++i) + { + size_t eltlen = strlen (array[i]) + 1; +- char *new = (char *) malloc (dirlen + 1 + eltlen); ++ char *new = malloc (dirlen + 1 + eltlen); + if (new == NULL) + { + while (i > 0) +@@ -1361,7 +1367,7 @@ prefix_array (const char *dirname, char **array, size_t n) + + { + char *endp = mempcpy (new, dirname, dirlen); +- *endp++ = DIRSEP_CHAR; ++ *endp++ = dirsep_char; + mempcpy (endp, array[i], eltlen); + } + free (array[i]); +@@ -1371,103 +1377,57 @@ prefix_array (const char *dirname, char **array, size_t n) + return 0; + } + +- +-/* We must not compile this function twice. */ +-#if !defined _LIBC || !defined NO_GLOB_PATTERN_P +-int +-__glob_pattern_type (const char *pattern, int quote) +-{ +- const char *p; +- int ret = 0; +- +- for (p = pattern; *p != '\0'; ++p) +- switch (*p) +- { +- case '?': +- case '*': +- return 1; +- +- case '\\': +- if (quote) +- { +- if (p[1] != '\0') +- ++p; +- ret |= 2; +- } +- break; +- +- case '[': +- ret |= 4; +- break; +- +- case ']': +- if (ret & 4) +- return 1; +- break; +- } +- +- return ret; +-} +- +-/* Return nonzero if PATTERN contains any metacharacters. +- Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ +-int +-__glob_pattern_p (const char *pattern, int quote) +-{ +- return __glob_pattern_type (pattern, quote) == 1; +-} +-# ifdef _LIBC +-weak_alias (__glob_pattern_p, glob_pattern_p) +-# endif +-#endif +- +-#endif /* !GLOB_ONLY_P */ +- +- + /* We put this in a separate function mainly to allow the memory + allocated with alloca to be recycled. */ +-#if !defined _LIBC || !defined GLOB_ONLY_P + static int + __attribute_noinline__ +-link_exists2_p (const char *dir, size_t dirlen, const char *fname, +- glob_t *pglob +-# ifndef _LIBC +- , int flags ++link_stat (const char *dir, size_t dirlen, const char *fname, ++ glob_t *pglob ++# if !defined _LIBC && !HAVE_FSTATAT ++ , int flags + # endif +- ) ++ ) + { + size_t fnamelen = strlen (fname); +- char *fullname = (char *) __alloca (dirlen + 1 + fnamelen + 1); ++ char *fullname = __alloca (dirlen + 1 + fnamelen + 1); + struct stat st; +-# ifndef _LIBC +- struct_stat64 st64; +-# endif + + mempcpy (mempcpy (mempcpy (fullname, dir, dirlen), "/", 1), + fname, fnamelen + 1); + +-# ifdef _LIBC +- return (*pglob->gl_stat) (fullname, &st) == 0; +-# else +- return ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) +- ? (*pglob->gl_stat) (fullname, &st) +- : __stat64 (fullname, &st64)) == 0); ++# if !defined _LIBC && !HAVE_FSTATAT ++ if (__builtin_expect ((flags & GLOB_ALTDIRFUNC) == 0, 1)) ++ { ++ struct_stat64 st64; ++ return __stat64 (fullname, &st64); ++ } + # endif ++ return (*pglob->gl_stat) (fullname, &st); + } +-# ifdef _LIBC +-# define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \ +- (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) \ +- ? link_exists2_p (dirname, dirnamelen, fname, pglob) \ +- : ({ struct stat64 st64; \ +- __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0) == 0; })) ++ ++/* Return true if DIR/FNAME exists. */ ++static int ++link_exists_p (int dfd, const char *dir, size_t dirlen, const char *fname, ++ glob_t *pglob, int flags) ++{ ++ int status; ++# if defined _LIBC || HAVE_FSTATAT ++ if (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0)) ++ status = link_stat (dir, dirlen, fname, pglob); ++ else ++ { ++ /* dfd cannot be -1 here, because dirfd never returns -1 on ++ glibc, or on hosts that have fstatat. */ ++ struct_stat64 st64; ++ status = __fxstatat64 (_STAT_VER, dfd, fname, &st64, 0); ++ } + # else +-# define link_exists_p(dfd, dirname, dirnamelen, fname, pglob, flags) \ +- link_exists2_p (dirname, dirnamelen, fname, pglob, flags) ++ status = link_stat (dir, dirlen, fname, pglob, flags); + # endif +-#endif +- ++ return status == 0 || errno == EOVERFLOW; ++} + +-/* Like `glob', but PATTERN is a final pathname component, ++/* Like 'glob', but PATTERN is a final pathname component, + and matches are searched for in DIRECTORY. + The GLOB_NOSORT bit in FLAGS is ignored. No sorting is ever done. + The GLOB_APPEND flag is assumed to be set (always appends). */ +@@ -1478,25 +1438,25 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + { + size_t dirlen = strlen (directory); + void *stream = NULL; +- struct globnames +- { +- struct globnames *next; +- size_t count; +- char *name[64]; +- }; +-#define INITIAL_COUNT sizeof (init_names.name) / sizeof (init_names.name[0]) +- struct globnames init_names; +- struct globnames *names = &init_names; +- struct globnames *names_alloca = &init_names; ++# define GLOBNAMES_MEMBERS(nnames) \ ++ struct globnames *next; size_t count; char *name[nnames]; ++ struct globnames { GLOBNAMES_MEMBERS (FLEXIBLE_ARRAY_MEMBER) }; ++ struct { GLOBNAMES_MEMBERS (64) } init_names_buf; ++ struct globnames *init_names = (struct globnames *) &init_names_buf; ++ struct globnames *names = init_names; ++ struct globnames *names_alloca = init_names; + size_t nfound = 0; + size_t cur = 0; + int meta; + int save; ++ int result; + +- alloca_used += sizeof (init_names); ++ alloca_used += sizeof init_names_buf; + +- init_names.next = NULL; +- init_names.count = INITIAL_COUNT; ++ init_names->next = NULL; ++ init_names->count = ((sizeof init_names_buf ++ - offsetof (struct globnames, name)) ++ / sizeof init_names->name[0]); + + meta = __glob_pattern_type (pattern, !(flags & GLOB_NOESCAPE)); + if (meta == 0 && (flags & (GLOB_NOCHECK|GLOB_NOMAGIC))) +@@ -1516,14 +1476,16 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + struct_stat64 st64; + } ust; + size_t patlen = strlen (pattern); +- int alloca_fullname = __libc_use_alloca (alloca_used +- + dirlen + 1 + patlen + 1); ++ size_t fullsize; ++ bool alloca_fullname ++ = (! size_add_wrapv (dirlen + 1, patlen + 1, &fullsize) ++ && glob_use_alloca (alloca_used, fullsize)); + char *fullname; + if (alloca_fullname) +- fullname = alloca_account (dirlen + 1 + patlen + 1, alloca_used); ++ fullname = alloca_account (fullsize, alloca_used); + else + { +- fullname = malloc (dirlen + 1 + patlen + 1); ++ fullname = malloc (fullsize); + if (fullname == NULL) + return GLOB_NOSPACE; + } +@@ -1531,9 +1493,11 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + mempcpy (mempcpy (mempcpy (fullname, directory, dirlen), + "/", 1), + pattern, patlen + 1); +- if ((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) ++ if (((__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? (*pglob->gl_stat) (fullname, &ust.st) +- : __stat64 (fullname, &ust.st64)) == 0) ++ : __stat64 (fullname, &ust.st64)) ++ == 0) ++ || errno == EOVERFLOW) + /* We found this file to be existing. Now tell the rest + of the function to copy this name into the result. */ + flags |= GLOB_NOCHECK; +@@ -1555,16 +1519,10 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + } + else + { +-#ifdef _LIBC + int dfd = (__builtin_expect (flags & GLOB_ALTDIRFUNC, 0) + ? -1 : dirfd ((DIR *) stream)); +-#endif + int fnm_flags = ((!(flags & GLOB_PERIOD) ? FNM_PERIOD : 0) +- | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) +-#if defined _AMIGA || defined VMS +- | FNM_CASEFOLD +-#endif +- ); ++ | ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0)); + flags |= GLOB_MAGCHAR; + + while (1) +@@ -1584,19 +1542,24 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + } + if (d.name == NULL) + break; +- if (d.skip_entry) ++ if (readdir_result_skip_entry (d)) + continue; + + /* If we shall match only directories use the information + provided by the dirent call if possible. */ +- if ((flags & GLOB_ONLYDIR) && !readdir_result_might_be_dir (d)) +- continue; ++ if (flags & GLOB_ONLYDIR) ++ switch (readdir_result_type (d)) ++ { ++ case DT_DIR: case DT_LNK: case DT_UNKNOWN: break; ++ default: continue; ++ } + + 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 (!readdir_result_might_be_symlink (d) ++ dirent_type type = readdir_result_type (d); ++ if (! (type == DT_LNK || type == DT_UNKNOWN) + || link_exists_p (dfd, directory, dirlen, d.name, + pglob, flags)) + { +@@ -1604,10 +1567,13 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + { + struct globnames *newnames; + size_t count = names->count * 2; +- size_t size = (sizeof (struct globnames) +- + ((count - INITIAL_COUNT) +- * sizeof (char *))); +- if (__libc_use_alloca (alloca_used + size)) ++ size_t nameoff = offsetof (struct globnames, name); ++ size_t size = FLEXSIZEOF (struct globnames, name, ++ count * sizeof (char *)); ++ if ((SIZE_MAX - nameoff) / 2 / sizeof (char *) ++ < names->count) ++ goto memory_error; ++ if (glob_use_alloca (alloca_used, size)) + newnames = names_alloca + = alloca_account (size, alloca_used); + else if ((newnames = malloc (size)) +@@ -1623,6 +1589,8 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + goto memory_error; + ++cur; + ++nfound; ++ if (SIZE_MAX - pglob->gl_offs <= nfound) ++ goto memory_error; + } + } + } +@@ -1633,29 +1601,27 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + { + size_t len = strlen (pattern); + nfound = 1; +- names->name[cur] = (char *) malloc (len + 1); ++ names->name[cur] = malloc (len + 1); + if (names->name[cur] == NULL) + goto memory_error; + *((char *) mempcpy (names->name[cur++], pattern, len)) = '\0'; + } + +- int result = GLOB_NOMATCH; ++ result = GLOB_NOMATCH; + if (nfound != 0) + { ++ 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 *))) ++ if (SIZE_MAX / sizeof (char *) - pglob->gl_pathc ++ < pglob->gl_offs + nfound + 1) + goto memory_error; + +- char **new_gl_pathv; + new_gl_pathv +- = (char **) realloc (pglob->gl_pathv, +- (pglob->gl_pathc + pglob->gl_offs + nfound + 1) +- * sizeof (char *)); ++ = realloc (pglob->gl_pathv, ++ (pglob->gl_pathc + pglob->gl_offs + nfound + 1) ++ * sizeof (char *)); ++ + if (new_gl_pathv == NULL) + { + memory_error: +@@ -1671,7 +1637,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + and this is the block assigned to OLD here. */ + if (names == NULL) + { +- assert (old == &init_names); ++ assert (old == init_names); + break; + } + cur = names->count; +@@ -1697,7 +1663,7 @@ glob_in_dir (const char *pattern, const char *directory, int flags, + and this is the block assigned to OLD here. */ + if (names == NULL) + { +- assert (old == &init_names); ++ assert (old == init_names); + break; + } + cur = names->count; +diff --git a/posix/glob64.c b/posix/glob64.c +index 6cb3d654a8..a515a1c12f 100644 +--- a/posix/glob64.c ++++ b/posix/glob64.c +@@ -43,10 +43,4 @@ glob64 (const char *pattern, int flags, + } + libc_hidden_def (glob64) + +-void +-globfree64 (glob64_t *pglob) +-{ +-} +-libc_hidden_def (globfree64) +- + stub_warning (glob64) +diff --git a/posix/glob_internal.h b/posix/glob_internal.h +new file mode 100644 +index 0000000000..12c93660b7 +--- /dev/null ++++ b/posix/glob_internal.h +@@ -0,0 +1,57 @@ ++/* Shared definition for glob and glob_pattern_p. ++ Copyright (C) 2017 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 ++ <http://www.gnu.org/licenses/>. */ ++ ++#ifndef GLOB_INTERNAL_H ++# define GLOB_INTERNAL_H ++ ++static inline int ++__glob_pattern_type (const char *pattern, int quote) ++{ ++ const char *p; ++ int ret = 0; ++ ++ for (p = pattern; *p != '\0'; ++p) ++ switch (*p) ++ { ++ case '?': ++ case '*': ++ return 1; ++ ++ case '\\': ++ if (quote) ++ { ++ if (p[1] != '\0') ++ ++p; ++ ret |= 2; ++ } ++ break; ++ ++ case '[': ++ ret |= 4; ++ break; ++ ++ case ']': ++ if (ret & 4) ++ return 1; ++ break; ++ } ++ ++ return ret; ++} ++ ++#endif /* GLOB_INTERNAL_H */ +diff --git a/posix/glob_pattern_p.c b/posix/glob_pattern_p.c +new file mode 100644 +index 0000000000..a17d337182 +--- /dev/null ++++ b/posix/glob_pattern_p.c +@@ -0,0 +1,33 @@ ++/* Return nonzero if PATTERN contains any metacharacters. ++ Copyright (C) 2017 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 ++ <http://www.gnu.org/licenses/>. */ ++ ++#ifndef _LIBC ++# include <config.h> ++#endif ++ ++#include <glob.h> ++#include "glob_internal.h" ++ ++/* Return nonzero if PATTERN contains any metacharacters. ++ Metacharacters can be quoted with backslashes if QUOTE is nonzero. */ ++int ++__glob_pattern_p (const char *pattern, int quote) ++{ ++ return __glob_pattern_type (pattern, quote) == 1; ++} ++weak_alias (__glob_pattern_p, glob_pattern_p) +diff --git a/posix/globfree.c b/posix/globfree.c +new file mode 100644 +index 0000000000..042e29d9b0 --- /dev/null -+++ b/nptl/tst-rwlock20.c -@@ -0,0 +1,116 @@ -+/* Test program for a read-phase / write-phase explicit hand-over. ++++ b/posix/globfree.c +@@ -0,0 +1,41 @@ ++/* Frees the dynamically allocated storage from an earlier call to glob. + Copyright (C) 2017 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. ++ 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 @@ -3090,108 +5010,69 @@ index 0000000000..4aeea2b8f5 + 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; see the file COPYING.LIB. If -+ not, see <http://www.gnu.org/licenses/>. */ -+ -+#include <errno.h> -+#include <error.h> -+#include <pthread.h> -+#include <stdio.h> -+#include <stdlib.h> -+#include <unistd.h> -+#include <stdint.h> -+#include <time.h> -+#include <atomic.h> -+#include <support/xthread.h> -+ -+/* We realy want to set threads to 2 to reproduce this issue. The goal -+ is to have one primary writer and a single reader, and to hit the -+ bug that happens in the interleaving of those two phase transitions. -+ However, on most hardware, adding a second writer seems to help the -+ interleaving happen slightly more often, say 20% of the time. On a -+ 16 core ppc64 machine this fails 100% of the time with an unpatched -+ glibc. On a 8 core x86_64 machine this fails ~93% of the time, but -+ it doesn't fail at all on a 4 core system, so having available -+ unloaded cores makes a big difference in reproducibility. On an 8 -+ core qemu/kvm guest the reproducer reliability drops to ~10%. */ -+#define THREADS 3 ++ License along with the GNU C Library; if not, see ++ <http://www.gnu.org/licenses/>. */ + -+#define KIND PTHREAD_RWLOCK_PREFER_READER_NP ++#ifndef _LIBC ++# include <config.h> ++#endif + -+static pthread_rwlock_t lock; -+static int done = 0; ++#include <glob.h> ++#include <stdlib.h> + -+static void* -+tf (void* arg) ++/* Free storage allocated in PGLOB by a previous `glob' call. */ ++void ++globfree (glob_t *pglob) +{ -+ while (atomic_load_relaxed (&done) == 0) ++ if (pglob->gl_pathv != NULL) + { -+ int rcnt = 0; -+ int wcnt = 100; -+ if ((uintptr_t) arg == 0) -+ { -+ rcnt = 1; -+ wcnt = 1; -+ } -+ -+ do -+ { -+ if (wcnt) -+ { -+ xpthread_rwlock_wrlock (&lock); -+ xpthread_rwlock_unlock (&lock); -+ wcnt--; -+ } -+ if (rcnt) -+ { -+ xpthread_rwlock_rdlock (&lock); -+ xpthread_rwlock_unlock (&lock); -+ rcnt--; -+ } -+ } -+ while ((atomic_load_relaxed (&done) == 0) && (rcnt + wcnt > 0)); -+ ++ size_t i; ++ for (i = 0; i < pglob->gl_pathc; ++i) ++ free (pglob->gl_pathv[pglob->gl_offs + i]); ++ free (pglob->gl_pathv); ++ pglob->gl_pathv = NULL; + } -+ return NULL; +} ++#ifndef globfree ++libc_hidden_def (globfree) ++#endif +diff --git a/posix/globfree64.c b/posix/globfree64.c +new file mode 100644 +index 0000000000..c9f8908a4e +--- /dev/null ++++ b/posix/globfree64.c +@@ -0,0 +1,31 @@ ++/* Frees the dynamically allocated storage from an earlier call to glob. ++ Copyright (C) 2017 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. + -+static int -+do_test (void) -+{ -+ pthread_t thr[THREADS]; -+ int n; -+ pthread_rwlockattr_t attr; -+ -+ xpthread_rwlockattr_init (&attr); -+ xpthread_rwlockattr_setkind_np (&attr, KIND); -+ -+ xpthread_rwlock_init (&lock, &attr); -+ -+ /* Make standard error the same as standard output. */ -+ dup2 (1, 2); -+ -+ /* Make sure we see all message, even those on stdout. */ -+ setvbuf (stdout, NULL, _IONBF, 0); -+ -+ for (n = 0; n < THREADS; ++n) -+ thr[n] = xpthread_create (NULL, tf, (void *) (uintptr_t) n); ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ <http://www.gnu.org/licenses/>. */ + -+ struct timespec delay; -+ delay.tv_sec = 10; -+ delay.tv_nsec = 0; -+ nanosleep (&delay, NULL); -+ atomic_store_relaxed (&done, 1); ++#ifndef _LIBC ++# include <config.h> ++#endif + -+ /* Wait for all the threads. */ -+ for (n = 0; n < THREADS; ++n) -+ xpthread_join (thr[n]); ++#include <glob.h> ++#include <stdlib.h> + -+ return 0; ++/* Free storage allocated in PGLOB by a previous `glob' call. */ ++void ++globfree64 (glob64_t *pglob) ++{ +} -+ -+#include <support/test-driver.c> ++libc_hidden_def (globfree64) diff --git a/posix/globtest.sh b/posix/globtest.sh index f9cc80b4b5..73f7ae31cc 100755 --- a/posix/globtest.sh @@ -3219,6 +5100,155 @@ index f9cc80b4b5..73f7ae31cc 100755 echo "All OK." > $logfile fi +diff --git a/posix/tst-glob-tilde.c b/posix/tst-glob-tilde.c +new file mode 100644 +index 0000000000..6886f4371f +--- /dev/null ++++ b/posix/tst-glob-tilde.c +@@ -0,0 +1,143 @@ ++/* Check for GLOB_TIDLE heap allocation issues (bugs 22320, 22325, 22332). ++ Copyright (C) 2017 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 ++ <http://www.gnu.org/licenses/>. */ ++ ++#include <glob.h> ++#include <mcheck.h> ++#include <nss.h> ++#include <pwd.h> ++#include <stdlib.h> ++#include <string.h> ++#include <support/check.h> ++#include <support/support.h> ++ ++/* Flag which indicates whether to pass the GLOB_ONLYDIR flag. */ ++static int do_onlydir; ++ ++/* Flag which indicates whether to pass the GLOB_NOCHECK flag. */ ++static int do_nocheck; ++ ++/* Flag which indicates whether to pass the GLOB_MARK flag. */ ++static int do_mark; ++ ++/* Flag which indicates whether to pass the GLOB_NOESCAPE flag. */ ++static int do_noescape; ++ ++static void ++one_test (const char *prefix, const char *middle, const char *suffix) ++{ ++ char *pattern = xasprintf ("%s%s%s", prefix, middle, suffix); ++ int flags = GLOB_TILDE; ++ if (do_onlydir) ++ flags |= GLOB_ONLYDIR; ++ if (do_nocheck) ++ flags |= GLOB_NOCHECK; ++ if (do_mark) ++ flags |= GLOB_MARK; ++ if (do_noescape) ++ flags |= GLOB_NOESCAPE; ++ glob_t gl; ++ /* This glob call might result in crashes or memory leaks. */ ++ if (glob (pattern, flags, NULL, &gl) == 0) ++ globfree (&gl); ++ free (pattern); ++} ++ ++enum ++ { ++ /* The largest base being tested. */ ++ largest_base_size = 500000, ++ ++ /* The actual size is the base size plus a variable whose absolute ++ value is not greater than this. This helps malloc to trigger ++ overflows. */ ++ max_size_skew = 16, ++ ++ /* The maximum string length supported by repeating_string ++ below. */ ++ repeat_size = largest_base_size + max_size_skew, ++ }; ++ ++/* Used to construct strings which repeat a single character 'x'. */ ++static char *repeat; ++ ++/* Return a string of SIZE characters. */ ++const char * ++repeating_string (int size) ++{ ++ TEST_VERIFY (size >= 0); ++ TEST_VERIFY (size <= repeat_size); ++ const char *repeated_shifted = repeat + repeat_size - size; ++ TEST_VERIFY (strlen (repeated_shifted) == size); ++ return repeated_shifted; ++} ++ ++static int ++do_test (void) ++{ ++ /* Avoid network-based NSS modules and initialize nss_files with a ++ dummy lookup. This has to come before mtrace because NSS does ++ not free all memory. */ ++ __nss_configure_lookup ("passwd", "files"); ++ (void) getpwnam ("root"); ++ ++ mtrace (); ++ ++ repeat = xmalloc (repeat_size + 1); ++ memset (repeat, 'x', repeat_size); ++ repeat[repeat_size] = '\0'; ++ ++ /* These numbers control the size of the user name. The values ++ cover the minimum (0), a typical size (8), a large ++ stack-allocated size (100000), and a somewhat large ++ heap-allocated size (largest_base_size). */ ++ static const int base_sizes[] = { 0, 8, 100, 100000, largest_base_size, -1 }; ++ ++ for (do_onlydir = 0; do_onlydir < 2; ++do_onlydir) ++ for (do_nocheck = 0; do_nocheck < 2; ++do_nocheck) ++ for (do_mark = 0; do_mark < 2; ++do_mark) ++ for (do_noescape = 0; do_noescape < 2; ++do_noescape) ++ for (int base_idx = 0; base_sizes[base_idx] >= 0; ++base_idx) ++ { ++ for (int size_skew = -max_size_skew; size_skew <= max_size_skew; ++ ++size_skew) ++ { ++ int size = base_sizes[base_idx] + size_skew; ++ if (size < 0) ++ continue; ++ ++ const char *user_name = repeating_string (size); ++ one_test ("~", user_name, "/a/b"); ++ one_test ("~", user_name, "x\\x\\x////x\\a"); ++ } ++ ++ const char *user_name = repeating_string (base_sizes[base_idx]); ++ one_test ("~", user_name, ""); ++ one_test ("~", user_name, "/"); ++ one_test ("~", user_name, "/a"); ++ one_test ("~", user_name, "/*/*"); ++ one_test ("~", user_name, "\\/"); ++ one_test ("/~", user_name, ""); ++ one_test ("*/~", user_name, "/a/b"); ++ } ++ ++ free (repeat); ++ ++ return 0; ++} ++ ++#include <support/test-driver.c> diff --git a/resolv/Makefile b/resolv/Makefile index fdc37edff1..01086d569f 100644 --- a/resolv/Makefile @@ -8513,6 +10543,38 @@ index a74083786e..5ea8a4a259 100644 "LD_LIBRARY_PATH\0" \ "LD_ORIGIN_PATH\0" \ "LD_PRELOAD\0" \ +diff --git a/sysdeps/gnu/glob64.c b/sysdeps/gnu/glob64.c +index d1e4e6f0d5..52e97e2f6a 100644 +--- a/sysdeps/gnu/glob64.c ++++ b/sysdeps/gnu/glob64.c +@@ -15,11 +15,8 @@ + #undef __stat + #define __stat(file, buf) __xstat64 (_STAT_VER, file, buf) + +-#define NO_GLOB_PATTERN_P 1 +- + #define COMPILE_GLOB64 1 + + #include <posix/glob.c> + + libc_hidden_def (glob64) +-libc_hidden_def (globfree64) +diff --git a/sysdeps/gnu/globfree64.c b/sysdeps/gnu/globfree64.c +new file mode 100644 +index 0000000000..f092d0bf8b +--- /dev/null ++++ b/sysdeps/gnu/globfree64.c +@@ -0,0 +1,10 @@ ++#include <dirent.h> ++#include <glob.h> ++#include <sys/stat.h> ++ ++#define glob_t glob64_t ++#define globfree(pglob) globfree64 (pglob) ++ ++#include <posix/globfree.c> ++ ++libc_hidden_def (globfree64) diff --git a/sysdeps/hppa/__longjmp.c b/sysdeps/hppa/__longjmp.c index a7eefc7ad6..2fedb1d738 100644 --- a/sysdeps/hppa/__longjmp.c @@ -9075,6 +11137,77 @@ index 99c00f493d..0694ac1362 100644 value = map->l_addr; } else +diff --git a/sysdeps/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile +index b3d68665f9..beb1c29111 100644 +--- a/sysdeps/unix/sysv/linux/Makefile ++++ b/sysdeps/unix/sysv/linux/Makefile +@@ -141,7 +141,7 @@ endif + ifeq ($(subdir),posix) + sysdep_headers += bits/initspin.h + +-sysdep_routines += sched_getcpu ++sysdep_routines += sched_getcpu oldglob + + tests += tst-affinity tst-affinity-pid + +diff --git a/sysdeps/unix/sysv/linux/alpha/glob.c b/sysdeps/unix/sysv/linux/alpha/glob.c +index 2d7d287a25..1b813c1cfd 100644 +--- a/sysdeps/unix/sysv/linux/alpha/glob.c ++++ b/sysdeps/unix/sysv/linux/alpha/glob.c +@@ -42,10 +42,6 @@ extern void __new_globfree (glob_t *__pglob); + #undef globfree64 + + versioned_symbol (libc, __new_glob, glob, GLIBC_2_1); +-versioned_symbol (libc, __new_globfree, globfree, GLIBC_2_1); + libc_hidden_ver (__new_glob, glob) +-libc_hidden_ver (__new_globfree, globfree) + + weak_alias (__new_glob, glob64) +-weak_alias (__new_globfree, globfree64) +-libc_hidden_ver (__new_globfree, globfree64) +diff --git a/sysdeps/unix/sysv/linux/alpha/globfree.c b/sysdeps/unix/sysv/linux/alpha/globfree.c +new file mode 100644 +index 0000000000..98cf1c200b +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/alpha/globfree.c +@@ -0,0 +1,37 @@ ++/* Compat globfree. Linux/alpha version. ++ Copyright (C) 2017 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 ++ <http://www.gnu.org/licenses/>. */ ++ ++#define globfree64 __no_globfree64_decl ++#include <sys/types.h> ++#include <glob.h> ++#include <shlib-compat.h> ++ ++#define globfree(pglob) \ ++ __new_globfree (pglob) ++ ++extern void __new_globfree (glob_t *__pglob); ++ ++#include <posix/globfree.c> ++ ++#undef globfree64 ++ ++versioned_symbol (libc, __new_globfree, globfree, GLIBC_2_1); ++libc_hidden_ver (__new_globfree, globfree) ++ ++weak_alias (__new_globfree, globfree64) ++libc_hidden_ver (__new_globfree, globfree64) diff --git a/sysdeps/unix/sysv/linux/alpha/localplt.data b/sysdeps/unix/sysv/linux/alpha/localplt.data index cca17f1e34..1f0e3b494e 100644 --- a/sysdeps/unix/sysv/linux/alpha/localplt.data @@ -9854,6 +11987,63 @@ index d8dd0431a4..c0cd59e9f5 100644 /* We do nothing with the return, except hand it back to someone else */ #undef DO_CALL_NOERRNO +diff --git a/sysdeps/unix/sysv/linux/i386/glob64.c b/sysdeps/unix/sysv/linux/i386/glob64.c +index f68195137e..230f9fc037 100644 +--- a/sysdeps/unix/sysv/linux/i386/glob64.c ++++ b/sysdeps/unix/sysv/linux/i386/glob64.c +@@ -19,6 +19,7 @@ + #include <dirent.h> + #include <glob.h> + #include <sys/stat.h> ++#include <shlib-compat.h> + + #define dirent dirent64 + #define __readdir(dirp) __readdir64 (dirp) +@@ -33,44 +34,9 @@ + #undef __stat + #define __stat(file, buf) __xstat64 (_STAT_VER, file, buf) + +-#define NO_GLOB_PATTERN_P 1 +- + #define COMPILE_GLOB64 1 + + #include <posix/glob.c> + +-#include "shlib-compat.h" +- +-libc_hidden_def (globfree64) +- + versioned_symbol (libc, __glob64, glob64, GLIBC_2_2); + libc_hidden_ver (__glob64, glob64) +- +-#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) +- +-#include <sysdeps/unix/sysv/linux/i386/olddirent.h> +- +-int __old_glob64 (const char *__pattern, int __flags, +- int (*__errfunc) (const char *, int), +- glob64_t *__pglob); +- +-#undef dirent +-#define dirent __old_dirent64 +-#undef GL_READDIR +-# define GL_READDIR(pglob, stream) \ +- ((struct __old_dirent64 *) (pglob)->gl_readdir (stream)) +-#undef __readdir +-#define __readdir(dirp) __old_readdir64 (dirp) +-#undef glob +-#define glob(pattern, flags, errfunc, pglob) \ +- __old_glob64 (pattern, flags, errfunc, pglob) +-#define convert_dirent __old_convert_dirent +-#define glob_in_dir __old_glob_in_dir +-#define GLOB_ATTRIBUTE attribute_compat_text_section +- +-#define GLOB_ONLY_P 1 +- +-#include <posix/glob.c> +- +-compat_symbol (libc, __old_glob64, glob64, GLIBC_2_1); +-#endif diff --git a/sysdeps/unix/sysv/linux/i386/localplt.data b/sysdeps/unix/sysv/linux/i386/localplt.data index 2c2584956d..8ea4333846 100644 --- a/sysdeps/unix/sysv/linux/i386/localplt.data @@ -9867,6 +12057,13 @@ index 2c2584956d..8ea4333846 100644 # The main malloc is interposed into the dynamic linker, for # allocations after the initial link (when dlopen is used). ld.so: malloc + REL R_386_GLOB_DAT +diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c +new file mode 100644 +index 0000000000..abc35fdd2b +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c +@@ -0,0 +1 @@ ++/* glob64 is in globfree64.c */ diff --git a/sysdeps/unix/sysv/linux/mips/mips64/n64/posix_fadvise64.c b/sysdeps/unix/sysv/linux/mips/mips64/n64/posix_fadvise64.c index 9776ee96e5..5a6379613b 100644 --- a/sysdeps/unix/sysv/linux/mips/mips64/n64/posix_fadvise64.c @@ -9879,6 +12076,54 @@ index 9776ee96e5..5a6379613b 100644 +_weak_alias (posix_fadvise, posix_fadvise64); #endif _strong_alias (__posix_fadvise64_l64, posix_fadvise); +diff --git a/sysdeps/unix/sysv/linux/oldglob.c b/sysdeps/unix/sysv/linux/oldglob.c +new file mode 100644 +index 0000000000..8233e57ce9 +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/oldglob.c +@@ -0,0 +1,42 @@ ++#include <shlib-compat.h> ++ ++#if SHLIB_COMPAT(libc, GLIBC_2_1, GLIBC_2_2) ++ ++#include <dirent.h> ++#include <glob.h> ++#include <sys/stat.h> ++ ++#include <sysdeps/unix/sysv/linux/i386/olddirent.h> ++ ++int __old_glob64 (const char *__pattern, int __flags, ++ int (*__errfunc) (const char *, int), ++ glob64_t *__pglob); ++libc_hidden_proto (__old_glob64); ++ ++#define dirent __old_dirent64 ++#define GL_READDIR(pglob, stream) \ ++ ((struct __old_dirent64 *) (pglob)->gl_readdir (stream)) ++#undef __readdir ++#define __readdir(dirp) __old_readdir64 (dirp) ++ ++#define glob_t glob64_t ++#define glob(pattern, flags, errfunc, pglob) \ ++ __old_glob64 (pattern, flags, errfunc, pglob) ++#define globfree(pglob) globfree64(pglob) ++ ++#define convert_dirent __old_convert_dirent ++#define glob_in_dir __old_glob_in_dir ++ ++#undef stat ++#define stat stat64 ++#undef __stat ++#define __stat(file, buf) __xstat64 (_STAT_VER, file, buf) ++ ++#define GLOB_ATTRIBUTE attribute_compat_text_section ++ ++#include <posix/glob.c> ++ ++libc_hidden_def (__old_glob64); ++ ++compat_symbol (libc, __old_glob64, glob64, GLIBC_2_1); ++#endif diff --git a/sysdeps/unix/sysv/linux/s390/pt-longjmp.c b/sysdeps/unix/sysv/linux/s390/pt-longjmp.c index d324237edd..0221ac2cf5 100644 --- a/sysdeps/unix/sysv/linux/s390/pt-longjmp.c @@ -10085,6 +12330,49 @@ index 2daf0c5ef0..ee09fb762b 100644 } else ec = -new_pid; +diff --git a/sysdeps/unix/sysv/linux/wordsize-64/globfree64.c b/sysdeps/unix/sysv/linux/wordsize-64/globfree64.c +new file mode 100644 +index 0000000000..af035e1514 +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/wordsize-64/globfree64.c +@@ -0,0 +1,2 @@ ++/* This file is here so sysdeps/gnu/glob64.c doesn't take precedence. */ ++#include <sysdeps/wordsize-64/globfree64.c> +diff --git a/sysdeps/unix/sysv/linux/x86_64/x32/globfree.c b/sysdeps/unix/sysv/linux/x86_64/x32/globfree.c +new file mode 100644 +index 0000000000..b76a761c17 +--- /dev/null ++++ b/sysdeps/unix/sysv/linux/x86_64/x32/globfree.c +@@ -0,0 +1 @@ ++#include <sysdeps/wordsize-64/globfree.c> +diff --git a/sysdeps/wordsize-64/glob.c b/sysdeps/wordsize-64/glob.c +index 082faf1c70..954e8d37e2 100644 +--- a/sysdeps/wordsize-64/glob.c ++++ b/sysdeps/wordsize-64/glob.c +@@ -4,5 +4,3 @@ + #undef glob64 + #undef globfree64 + weak_alias (glob, glob64) +-weak_alias (globfree, globfree64) +-libc_hidden_ver (globfree, globfree64) +diff --git a/sysdeps/wordsize-64/globfree.c b/sysdeps/wordsize-64/globfree.c +new file mode 100644 +index 0000000000..ec8c35b489 +--- /dev/null ++++ b/sysdeps/wordsize-64/globfree.c +@@ -0,0 +1,5 @@ ++#define globfree64 __no_globfree64_decl ++#include <posix/globfree.c> ++#undef globfree64 ++weak_alias (globfree, globfree64) ++libc_hidden_ver (globfree, globfree64) +diff --git a/sysdeps/wordsize-64/globfree64.c b/sysdeps/wordsize-64/globfree64.c +new file mode 100644 +index 0000000000..a0f57ff4b3 +--- /dev/null ++++ b/sysdeps/wordsize-64/globfree64.c +@@ -0,0 +1 @@ ++/* globfree64 is in globfree.c */ diff --git a/sysdeps/x86/cpu-features-offsets.sym b/sysdeps/x86/cpu-features-offsets.sym index f6739fae81..33dd094e37 100644 --- a/sysdeps/x86/cpu-features-offsets.sym -- Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-glibc/glibc.git