about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTulio Magno Quites Machado Filho <tuliom@linux.ibm.com>2018-04-19 15:21:54 -0300
committerTulio Magno Quites Machado Filho <tuliom@linux.ibm.com>2018-04-19 15:21:54 -0300
commit70ae2309a84496e287d721cf88a1c916c2a2c971 (patch)
treedb32edc81ee3df28aed67a192a6a0c5def226942
parent15a05fafa4d7f94ae5002803ad8384519479469b (diff)
parent49a0c33ead1b1eea5b414e9e2574a4fd96291203 (diff)
downloadglibc-70ae2309a84496e287d721cf88a1c916c2a2c971.tar.gz
glibc-70ae2309a84496e287d721cf88a1c916c2a2c971.tar.xz
glibc-70ae2309a84496e287d721cf88a1c916c2a2c971.zip
Merge branch 'release/2.22/master' into ibm/2.22/master ibm/2.22/master
-rw-r--r--ChangeLog80
-rw-r--r--NEWS9
-rw-r--r--posix/Makefile14
-rw-r--r--posix/flexmember.h45
-rw-r--r--posix/glob.c785
-rw-r--r--posix/glob64.c6
-rw-r--r--posix/glob_internal.h57
-rw-r--r--posix/glob_pattern_p.c33
-rw-r--r--posix/globfree.c41
-rw-r--r--posix/globfree64.c31
-rw-r--r--posix/tst-glob-tilde.c136
-rw-r--r--sysdeps/gnu/glob64.c3
-rw-r--r--sysdeps/gnu/globfree64.c10
-rw-r--r--sysdeps/unix/sysv/linux/Makefile2
-rw-r--r--sysdeps/unix/sysv/linux/alpha/glob.c4
-rw-r--r--sysdeps/unix/sysv/linux/alpha/globfree.c37
-rw-r--r--sysdeps/unix/sysv/linux/i386/glob64.c36
-rw-r--r--sysdeps/unix/sysv/linux/mips/mips64/n64/globfree64.c1
-rw-r--r--sysdeps/unix/sysv/linux/oldglob.c42
-rw-r--r--sysdeps/unix/sysv/linux/wordsize-64/globfree64.c2
-rw-r--r--sysdeps/unix/sysv/linux/x86_64/x32/globfree.c1
-rw-r--r--sysdeps/wordsize-64/glob.c2
-rw-r--r--sysdeps/wordsize-64/globfree.c5
-rw-r--r--sysdeps/wordsize-64/globfree64.c1
24 files changed, 917 insertions, 466 deletions
diff --git a/ChangeLog b/ChangeLog
index 9869f6054c..34ab625d0b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,83 @@
+2017-10-22  Paul Eggert <eggert@cs.ucla.edu>
+
+	[BZ #22332]
+	* posix/glob.c (__glob): Fix buffer overflow during GLOB_TILDE
+	unescaping.
+
+2017-10-21  Florian Weimer  <fweimer@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 <eggert@cs.ucla.edu>
+
+	[BZ #22320]
+	CVE-2017-15670
+	* posix/glob.c (__glob): Fix one-byte overflow.
+
+2017-09-08  Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+
+	[BZ #1062]
+	* 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.
+
 2018-01-18  Arjun Shankar  <arjun@redhat.com>
 
 	[BZ #22343]
diff --git a/NEWS b/NEWS
index db81e9e6b1..f67abd6af0 100644
--- a/NEWS
+++ b/NEWS
@@ -95,6 +95,15 @@ Version 2.22.1
 * CVE-2018-6551: The malloc function, when called with an object size near
   the value of SIZE_MAX, would return a pointer to a buffer which is too
   small, instead of NULL.
+
+* 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).
+
+* 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.
+
 
 Version 2.22
 
diff --git a/posix/Makefile b/posix/Makefile
index 15e8818787..29b44e5204 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  \
@@ -87,7 +87,8 @@ tests		:= tstgetopt testfnm runtests runptests	     \
 		   bug-getopt1 bug-getopt2 bug-getopt3 bug-getopt4 \
 		   bug-getopt5 tst-getopt_long1 bug-regex34 bug-regex35 \
 		   tst-pathconf tst-getaddrinfo4 tst-rxspencer-no-utf8 \
-		   tst-fnmatch3 bug-regex36 tst-getaddrinfo5
+		   tst-fnmatch3 bug-regex36 tst-getaddrinfo5 \
+		   tst-glob-tilde
 xtests		:= bug-ga2
 ifeq (yes,$(build-shared))
 test-srcs	:= globtest
@@ -130,7 +131,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
 
@@ -307,6 +309,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 60fa6c5bfd..944b77b4dd 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)
+/* Initialize and return type member of struct readdir_result.  */
+static dirent_type
+readdir_result_type (struct readdir_result d)
 {
-  return d.type == DT_DIR || readdir_result_might_be_symlink (d);
-}
-# else /* defined _DIRENT_HAVE_D_TYPE || defined HAVE_STRUCT_DIRENT_D_TYPE */
-#  define D_TYPE_TO_RESULT(source)
-
-/* If we do not have type information, symbolic links and directories
-   are always a possibility.  */
-
-static bool
-readdir_result_might_be_symlink (struct readdir_result d)
-{
-  return true;
+#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,10 +192,48 @@ 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
+
+/* 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.  */
 
-#ifndef attribute_hidden
-# define attribute_hidden
+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),
@@ -236,7 +241,6 @@ static int glob_in_dir (const char *pattern, const char *directory,
 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
@@ -295,9 +298,7 @@ glob (pattern, flags, errfunc, pglob)
   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)
     {
@@ -311,7 +312,7 @@ glob (pattern, flags, errfunc, pglob)
     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;
 
@@ -353,14 +354,12 @@ glob (pattern, flags, errfunc, pglob)
 	  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)
 		{
 		  if (!(flags & GLOB_APPEND))
@@ -380,11 +379,9 @@ glob (pattern, flags, errfunc, pglob)
 	  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);
 	      return glob (pattern, flags & ~GLOB_BRACE, errfunc, pglob);
 	    }
@@ -432,9 +429,7 @@ glob (pattern, flags, errfunc, pglob)
 	      /* 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))
 		    {
@@ -453,9 +448,7 @@ glob (pattern, flags, errfunc, pglob)
 	      assert (next != NULL);
 	    }
 
-#ifdef _LIBC
 	  if (__glibc_unlikely (!alloca_onealt))
-#endif
 	    free (onealt);
 
 	  if (pglob->gl_pathc != firstc)
@@ -492,14 +485,16 @@ glob (pattern, flags, errfunc, pglob)
 
   /* 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)
     {
@@ -524,11 +519,7 @@ glob (pattern, flags, errfunc, pglob)
 	    }
 
 	  filename = pattern;
-#ifdef _AMIGA
-	  dirname = (char *) "";
-#else
 	  dirname = (char *) ".";
-#endif
 	  dirlen = 0;
 	}
     }
@@ -552,22 +543,21 @@ glob (pattern, flags, errfunc, pglob)
 	  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)
@@ -578,14 +568,17 @@ glob (pattern, flags, errfunc, pglob)
       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] == '\\')
@@ -618,7 +611,6 @@ glob (pattern, flags, errfunc, pglob)
 	}
     }
 
-#ifndef VMS
   if ((flags & (GLOB_TILDE|GLOB_TILDE_CHECK)) && dirname[0] == '~')
     {
       if (dirname[1] == '\0' || dirname[1] == '/'
@@ -628,100 +620,127 @@ glob (pattern, flags, errfunc, pglob)
 	  /* 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
@@ -736,26 +755,32 @@ glob (pattern, flags, errfunc, pglob)
 			      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')
 	    {
@@ -770,8 +795,7 @@ glob (pattern, flags, errfunc, pglob)
 	    {
 	      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
@@ -795,12 +819,15 @@ glob (pattern, flags, errfunc, pglob)
 	      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;
@@ -822,7 +849,7 @@ glob (pattern, flags, errfunc, pglob)
 	  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
 		{
@@ -839,11 +866,11 @@ glob (pattern, flags, errfunc, pglob)
 		  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
@@ -859,7 +886,7 @@ glob (pattern, flags, errfunc, pglob)
 		  *p = '\0';
 		}
 	      else
-		*((char *) mempcpy (newp, dirname + 1, end_name - dirname))
+		*((char *) mempcpy (newp, dirname + 1, end_name - dirname - 1))
 		  = '\0';
 	      user_name = newp;
 	    }
@@ -867,20 +894,21 @@ glob (pattern, flags, errfunc, pglob)
 	  /* 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
 	      {
@@ -893,32 +921,32 @@ glob (pattern, flags, errfunc, pglob)
 		    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);
 	      }
@@ -939,7 +967,7 @@ glob (pattern, flags, errfunc, pglob)
 		  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
@@ -947,8 +975,7 @@ glob (pattern, flags, errfunc, pglob)
 		    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;
 		      }
@@ -960,24 +987,24 @@ glob (pattern, flags, errfunc, pglob)
 		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.  */
@@ -996,19 +1023,18 @@ glob (pattern, flags, errfunc, pglob)
 	  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;
@@ -1022,12 +1048,19 @@ glob (pattern, flags, errfunc, pglob)
 	      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;
@@ -1037,7 +1070,8 @@ glob (pattern, flags, errfunc, pglob)
 	}
 
       /* Not found.  */
-      return GLOB_NOMATCH;
+      retval = GLOB_NOMATCH;
+      goto out;
     }
 
   meta = __glob_pattern_type (dirname, !(flags & GLOB_NOESCAPE));
@@ -1083,7 +1117,10 @@ glob (pattern, flags, errfunc, pglob)
       if (status != 0)
 	{
 	  if ((flags & GLOB_NOCHECK) == 0 || status != GLOB_NOMATCH)
-	    return status;
+	    {
+	      retval = status;
+	      goto out;
+	    }
 	  goto no_matches;
 	}
 
@@ -1094,19 +1131,6 @@ glob (pattern, flags, errfunc, pglob)
 	{
 	  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)
@@ -1121,7 +1145,8 @@ glob (pattern, flags, errfunc, pglob)
 	      globfree (&dirs);
 	      globfree (pglob);
 	      pglob->gl_pathc = 0;
-	      return status;
+	      retval = status;
+	      goto out;
 	    }
 
 	  /* Stick the directory on the front of each name.  */
@@ -1132,13 +1157,14 @@ glob (pattern, flags, errfunc, pglob)
 	      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)
@@ -1150,28 +1176,28 @@ glob (pattern, flags, errfunc, pglob)
 	      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;
@@ -1183,7 +1209,8 @@ glob (pattern, flags, errfunc, pglob)
 	  else
 	    {
 	      globfree (&dirs);
-	      return GLOB_NOMATCH;
+	      retval = GLOB_NOMATCH;
+	      goto out;
 	    }
 	}
 
@@ -1229,7 +1256,8 @@ glob (pattern, flags, errfunc, pglob)
 	      flags = orig_flags;
 	      goto no_matches;
 	    }
-	  return status;
+	  retval = status;
+	  goto out;
 	}
 
       if (dirlen > 0)
@@ -1241,7 +1269,8 @@ glob (pattern, flags, errfunc, pglob)
 	    {
 	      globfree (pglob);
 	      pglob->gl_pathc = 0;
-	      return GLOB_NOSPACE;
+	      retval = GLOB_NOSPACE;
+	      goto out;
 	    }
 	}
     }
@@ -1266,7 +1295,8 @@ glob (pattern, flags, errfunc, pglob)
 	      {
 		globfree (pglob);
 		pglob->gl_pathc = 0;
-		return GLOB_NOSPACE;
+		retval = GLOB_NOSPACE;
+		goto out;
 	      }
 	    strcpy (&new[len - 2], "/");
 	    pglob->gl_pathv[i] = new;
@@ -1292,33 +1322,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 (pglob)
-     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;
@@ -1339,28 +1348,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
@@ -1368,7 +1373,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)
@@ -1378,7 +1383,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]);
@@ -1388,107 +1393,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 (pattern, quote)
-     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 (pattern, quote)
-     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).  */
@@ -1499,25 +1454,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)))
@@ -1537,14 +1492,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;
 	}
@@ -1552,9 +1509,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;
@@ -1576,16 +1535,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)
@@ -1605,19 +1558,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))
 		    {
@@ -1625,10 +1583,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))
@@ -1644,6 +1605,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;
 		    }
 		}
 	    }
@@ -1654,29 +1617,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:
@@ -1692,7 +1653,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;
@@ -1718,7 +1679,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 56ede77073..96e58c36c4 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/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.
+
+   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 <stdlib.h>
+
+/* 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;
+    }
+}
+#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.
+
+   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 <stdlib.h>
+
+/* Free storage allocated in PGLOB by a previous `glob' call.  */
+void
+globfree64 (glob64_t *pglob)
+{
+}
+libc_hidden_def (globfree64)
diff --git a/posix/tst-glob-tilde.c b/posix/tst-glob-tilde.c
new file mode 100644
index 0000000000..9518b4a6f8
--- /dev/null
+++ b/posix/tst-glob-tilde.c
@@ -0,0 +1,136 @@
+/* Check for GLOB_TIDLE heap allocation issues (bug 22320, bug 22325).
+   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;
+
+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;
+  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 (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");
+              }
+
+            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/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/unix/sysv/linux/Makefile b/sysdeps/unix/sysv/linux/Makefile
index bfbabd4b4b..07cf9bf5e1 100644
--- a/sysdeps/unix/sysv/linux/Makefile
+++ b/sysdeps/unix/sysv/linux/Makefile
@@ -137,7 +137,7 @@ endif
 ifeq ($(subdir),posix)
 sysdep_headers += bits/initspin.h
 
-sysdep_routines += sched_getcpu
+sysdep_routines += sched_getcpu oldglob
 
 tests += tst-getcpu
 
diff --git a/sysdeps/unix/sysv/linux/alpha/glob.c b/sysdeps/unix/sysv/linux/alpha/glob.c
index b0df24b346..639e949ae0 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/i386/glob64.c b/sysdeps/unix/sysv/linux/i386/glob64.c
index 802c957d6c..c2cc85741f 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/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/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/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 */