The glob function from glibc makes a recursive call for every trailing slash character in the given pattern. This makes it easy to run out of stack space.
The attached patches fix it and add a test in Gnulib. It is based on my patch in glibc, which is just waiting for a final "Reviewed-by" before committing [1]. I'm not expecting to need a v3 patch for glibc, but I will hold off pushing to Gnulib just in case. Collin [1] https://inbox.sourceware.org/libc-alpha/[email protected]/T/#m9e88071d873191ae84a141823b2e310aabf4e6e2
>From b0334ff2173942d282eb85abd9bb2865286f8184 Mon Sep 17 00:00:00 2001 Message-ID: <b0334ff2173942d282eb85abd9bb2865286f8184.1760161417.git.collin.fu...@gmail.com> From: Collin Funk <[email protected]> Date: Fri, 10 Oct 2025 22:34:19 -0700 Subject: [PATCH 1/2] glob: Prevent a stack overflow with many trailing slashes. * lib/glob.c (__glob): Strip trailing slashes before making the recursive call. * m4/glob.m4 (gl_GLOB): Check for the glibc bug. * doc/posix-functions/glob.texi: Mention the bug. --- ChangeLog | 6 +++++ doc/posix-functions/glob.texi | 5 ++++ lib/glob.c | 4 +++ m4/glob.m4 | 50 +++++++++++++++++++++++++++-------- 4 files changed, 54 insertions(+), 11 deletions(-) diff --git a/ChangeLog b/ChangeLog index 07f0503f84..3f1eaf7f24 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,11 @@ 2025-10-10 Collin Funk <[email protected]> + glob: Prevent a stack overflow with many trailing slashes. + * lib/glob.c (__glob): Strip trailing slashes before making the + recursive call. + * m4/glob.m4 (gl_GLOB): Check for the glibc bug. + * doc/posix-functions/glob.texi: Mention the bug. + getline tests: Add a test for the glibc bug. * tests/test-getline.c (main): Check that the buffer is terminated with a NUL character when the first character read is EOF. diff --git a/doc/posix-functions/glob.texi b/doc/posix-functions/glob.texi index c5c423bc1a..705aa1e9d3 100644 --- a/doc/posix-functions/glob.texi +++ b/doc/posix-functions/glob.texi @@ -20,6 +20,11 @@ @node glob On platforms where @code{off_t} is a 32-bit type, this function may not work correctly on huge directories 2 GiB and larger. @xref{Large File Support}. +@item +This function makes a recursive call for every trailing @code{/} +character on some platforms, which can cause the stack to overflow: +@c https://sourceware.org/PR30635 +glibc 2.42. @end itemize Portability problems not fixed by Gnulib: diff --git a/lib/glob.c b/lib/glob.c index 0bd8b4237c..48f10ae0e1 100644 --- a/lib/glob.c +++ b/lib/glob.c @@ -582,6 +582,10 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), flags &= ~(GLOB_NOCHECK | GLOB_NOMAGIC); } } + /* Prevent stack overflows with many trailing '/' characters. */ + for (char *p = &dirname[dirlen - 1]; + p > dirname && p[0] == '/' && p[-1] == '/'; --p) + p[0] = '\0'; int val = __glob (dirname, flags | GLOB_MARK, errfunc, pglob); if (val == 0) pglob->gl_flags = ((pglob->gl_flags & ~GLOB_MARK) diff --git a/m4/glob.m4 b/m4/glob.m4 index ee0320bb10..1bbe6603ca 100644 --- a/m4/glob.m4 +++ b/m4/glob.m4 @@ -1,5 +1,5 @@ # glob.m4 -# serial 30 +# serial 31 dnl Copyright (C) 2005-2007, 2009-2025 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, @@ -23,16 +23,44 @@ AC_DEFUN([gl_GLOB] esac else - AC_CACHE_CHECK([for GNU glob interface version 1 or 2], - [gl_cv_gnu_glob_interface_version_1_2], -[ AC_COMPILE_IFELSE([AC_LANG_SOURCE( -[[#include <gnu-versions.h> -char a[_GNU_GLOB_INTERFACE_VERSION == 1 || _GNU_GLOB_INTERFACE_VERSION == 2 ? 1 : -1];]])], - [gl_cv_gnu_glob_interface_version_1_2=yes], - [gl_cv_gnu_glob_interface_version_1_2=no])]) - if test "$gl_cv_gnu_glob_interface_version_1_2" = "no"; then - REPLACE_GLOB=1 - fi + AC_CACHE_CHECK([whether glob overflows the stack with recursive calls], + [gl_cv_glob_stack_overflow], + [AC_RUN_IFELSE( + [AC_LANG_SOURCE([[ + #include <stddef.h> + #include <stdlib.h> + #include <string.h> + #include <glob.h> + int + main (void) + { + /* Test that glob with many trailing slashes does not overflow + the stack as it did in glibc 2.42 and earlier. */ + char *p = malloc (10000); + glob_t g; + int res; + if (p != NULL) + { + memset (p, '/', 9999); + p[9999] = '\0'; + res = glob (p, 0, NULL, &g); + globfree (&g); + } + return !(res == 0); + }]])], + [gl_cv_glob_overflows_stack=no], + [gl_cv_glob_overflows_stack=yes], + [case "$host_os" in + # Guess yes on glibc systems. + *-gnu* | gnu*) gl_cv_glob_overflows_stack="guessing yes" ;; + esac + ]) + ]) + + case $gl_cv_glob_overflows_stack in + *yes) REPLACE_GLOB=1 ;; + *) REPLACE_GLOB=0 ;; + esac if test $REPLACE_GLOB = 0; then AC_CACHE_CHECK([whether glob lists broken symlinks], -- 2.51.0
>From 0b75106032f73a068ba14ff0a223d4c3312a33ce Mon Sep 17 00:00:00 2001 Message-ID: <0b75106032f73a068ba14ff0a223d4c3312a33ce.1760161418.git.collin.fu...@gmail.com> In-Reply-To: <b0334ff2173942d282eb85abd9bb2865286f8184.1760161417.git.collin.fu...@gmail.com> References: <b0334ff2173942d282eb85abd9bb2865286f8184.1760161417.git.collin.fu...@gmail.com> From: Collin Funk <[email protected]> Date: Fri, 10 Oct 2025 22:36:11 -0700 Subject: [PATCH 2/2] glob tests: Add a test for the glibc bug. * tests/test-glob.c (main): Call glob with a pattern of all slashes to check if the stack overflows due to recursive calls. --- ChangeLog | 4 ++++ tests/test-glob.c | 12 ++++++++++++ 2 files changed, 16 insertions(+) diff --git a/ChangeLog b/ChangeLog index 3f1eaf7f24..9ce420cb64 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,5 +1,9 @@ 2025-10-10 Collin Funk <[email protected]> + glob tests: Add a test for the glibc bug. + * tests/test-glob.c (main): Call glob with a pattern of all slashes to + check if the stack overflows due to recursive calls. + glob: Prevent a stack overflow with many trailing slashes. * lib/glob.c (__glob): Strip trailing slashes before making the recursive call. diff --git a/tests/test-glob.c b/tests/test-glob.c index b064e37b40..52ba191b74 100644 --- a/tests/test-glob.c +++ b/tests/test-glob.c @@ -95,5 +95,17 @@ main () globfree (&g); } + /* Test that glob with many trailing slashes does not overflow the + stack as it did in glibc 2.42 and earlier. */ + char *p = malloc (10000); + if (p != NULL) + { + memset (p, '/', 9999); + p[9999] = '\0'; + res = glob (p, 0, NULL, &g); + ASSERT (res == 0); + globfree (&g); + } + return test_exit_status; } -- 2.51.0
