[glibc] posix: Move the glob home directory lookups out of __glob
Adhemerval Zanella via Glibc-cvs <[email protected]>
| Newsgroups | gmane.comp.lib.glibc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://sourceware.org/git/gitweb.cgi?p=glibc.git;h=c31848f01b792e7069276e7cae9b417352a89e18 commit c31848f01b792e7069276e7cae9b417352a89e18 Author: Adhemerval Zanella <[email protected]> Date: Fri Jul 31 16:35:53 2026 +0000 posix: Move the glob home directory lookups out of __glob Expanding "~" or "~user" needs a struct scratch_buffer to call getpwnam_r through, where the code might reserve extra stack in every glob frame (around 1224 bytes on x86_64). Even though the lookups only run when the caller passed GLOB_TILDE or GLOB_TILDE_CHECK. Move the two lookups into glob_current_home_dir and glob_user_home_dir, which return the directory as a malloc'ed string. The frame of each glob call drops to around 184 bytes. This also fixes a small leak: the ~user path returned GLOB_NOSPACE without freeing user_name when scratch_buffer_grow failed. Checked on x86_64-linux-gnu, aarch64-linux-gnu, and i686-linux-gnu. Reviewed-by: Collin Funk <[email protected]> Diff: --- posix/glob.c | 175 +++++++++++++++++++++++++++++++------------------ posix/tst-glob-tilde.c | 19 ++++++ 2 files changed, 132 insertions(+), 62 deletions(-) diff --git a/posix/glob.c b/posix/glob.c index a81a2285b8..0e9bf02bb4 100644 --- a/posix/glob.c +++ b/posix/glob.c @@ -285,6 +285,101 @@ next_brace_sub (const char *cp, int flags) return *cp != '\0' ? cp : NULL; } +#ifndef WINDOWS32 +static char * +glob_dup_pw_dir (const struct passwd *p, bool *nospace) +{ + char *result = NULL; + + if (p != NULL) + { + result = strdup (p->pw_dir); + if (result == NULL) + *nospace = true; + } + return result; +} + +/* Return the home directory of the current user as a malloc'ed string, or + NULL if it cannot be determined. Set *NOSPACE if the failure was caused + by an allocation failure. */ +static char * +glob_current_home_dir (bool *nospace) +{ + struct passwd *p; +# if defined HAVE_GETPWNAM_R || defined _LIBC + struct passwd pwbuf; +# endif + int err; + + *nospace = false; + + struct scratch_buffer s; + scratch_buffer_init (&s); + while (true) + { + p = NULL; + err = __getlogin_r (s.data, s.length); + if (err == 0) + { +# if defined HAVE_GETPWNAM_R || defined _LIBC + size_t ssize = strlen (s.data) + 1; + char *sdata = s.data; + err = getpwnam_r (sdata, &pwbuf, sdata + ssize, s.length - ssize, + &p); +# else + p = getpwnam (s.data); + if (p == NULL) + err = errno; +# endif + } + if (err != ERANGE) + break; + if (!scratch_buffer_grow (&s)) + { + /* scratch_buffer_grow has already released the buffer. */ + *nospace = true; + return NULL; + } + } + + char *result = err == 0 ? glob_dup_pw_dir (p, nospace) : NULL; + scratch_buffer_free (&s); + return result; +} + +/* Likewise, for the home directory of the user named USER_NAME. */ +static char * +glob_user_home_dir (const char *user_name, bool *nospace) +{ + struct passwd *p; + struct scratch_buffer pwtmpbuf; + char *result; + + *nospace = false; + scratch_buffer_init (&pwtmpbuf); + +# if defined HAVE_GETPWNAM_R || defined _LIBC + struct passwd pwbuf; + + while (getpwnam_r (user_name, &pwbuf, pwtmpbuf.data, pwtmpbuf.length, &p) + == ERANGE) + if (!scratch_buffer_grow (&pwtmpbuf)) + { + /* scratch_buffer_grow has already released the buffer. */ + *nospace = true; + return NULL; + } +# else + p = getpwnam (user_name); +# endif + + result = glob_dup_pw_dir (p, nospace); + scratch_buffer_free (&pwtmpbuf); + return result; +} +#endif /* !WINDOWS32 */ + #ifndef GLOB_ATTRIBUTE # define GLOB_ATTRIBUTE #endif @@ -642,43 +737,12 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), else home_dir = "c:/users/default"; /* poor default */ #else - int err; - struct passwd *p; - struct passwd pwbuf; - struct scratch_buffer s; - scratch_buffer_init (&s); - while (true) - { - p = NULL; - err = __getlogin_r (s.data, s.length); - if (err == 0) - { -# if defined HAVE_GETPWNAM_R || defined _LIBC - size_t ssize = strlen (s.data) + 1; - char *sdata = s.data; - err = getpwnam_r (sdata, &pwbuf, sdata + ssize, - s.length - ssize, &p); -# else - p = getpwnam (s.data); - if (p == NULL) - err = errno; -# endif - } - if (err != ERANGE) - break; - if (!scratch_buffer_grow (&s)) - { - retval = GLOB_NOSPACE; - goto out; - } - } - if (err == 0) - { - home_dir = strdup (p->pw_dir); - malloc_home_dir = 1; - } - scratch_buffer_free (&s); - if (err == 0 && home_dir == NULL) + bool nospace; + + home_dir = glob_current_home_dir (&nospace); + if (home_dir != NULL) + malloc_home_dir = 1; + else if (nospace) { retval = GLOB_NOSPACE; goto out; @@ -809,34 +873,22 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), /* Look up specific user's home directory. */ { - struct passwd *p; - struct scratch_buffer pwtmpbuf; - scratch_buffer_init (&pwtmpbuf); + bool nospace; + char *home_dir = glob_user_home_dir (user_name, &nospace); -# if defined HAVE_GETPWNAM_R || defined _LIBC - struct passwd pwbuf; + if (__glibc_unlikely (malloc_user_name)) + free (user_name); - while (getpwnam_r (user_name, &pwbuf, - pwtmpbuf.data, pwtmpbuf.length, &p) - == ERANGE) + if (__glibc_unlikely (nospace)) { - if (!scratch_buffer_grow (&pwtmpbuf)) - { - retval = GLOB_NOSPACE; - goto out; - } + retval = GLOB_NOSPACE; + goto out; } -# else - p = getpwnam (user_name); -# endif - - if (__glibc_unlikely (malloc_user_name)) - free (user_name); /* If we found a home directory use this. */ - if (p != NULL) + if (home_dir != NULL) { - size_t home_len = strlen (p->pw_dir); + size_t home_len = strlen (home_dir); size_t rest_len = end_name == NULL ? 0 : strlen (end_name); /* dirname contains end_name; we can't free it now. */ char *prev_dirname = @@ -849,17 +901,18 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), if (dirname == NULL) { free (prev_dirname); - scratch_buffer_free (&pwtmpbuf); + free (home_dir); retval = GLOB_NOSPACE; goto out; } malloc_dirname = 1; - d = mempcpy (dirname, p->pw_dir, home_len); + d = mempcpy (dirname, home_dir, home_len); if (end_name != NULL) d = mempcpy (d, end_name, rest_len); *d = '\0'; free (prev_dirname); + free (home_dir); dirlen = home_len + rest_len; dirname_modified = 1; @@ -870,12 +923,10 @@ __glob (const char *pattern, int flags, int (*errfunc) (const char *, int), { /* We have to regard it as an error if we cannot find the home directory. */ - scratch_buffer_free (&pwtmpbuf); retval = GLOB_NOMATCH; goto out; } } - scratch_buffer_free (&pwtmpbuf); } #else /* WINDOWS32 */ /* On native Windows, access to a user's home directory diff --git a/posix/tst-glob-tilde.c b/posix/tst-glob-tilde.c index c893df6800..83d05faa11 100644 --- a/posix/tst-glob-tilde.c +++ b/posix/tst-glob-tilde.c @@ -135,6 +135,25 @@ do_test (void) one_test ("*/~", user_name, "/a/b"); } + /* Exercise the current-user expansion (getlogin_r followed by getpwnam_r) + rather than the ~user lookup, with HOME unset and empty. */ + do_onlydir = 0; + do_mark = 0; + do_noescape = 0; + unsetenv ("HOME"); + for (do_nocheck = 0; do_nocheck < 2; ++do_nocheck) + { + one_test ("~", "", ""); + one_test ("~", "", "/"); + one_test ("~", "", "/a/b"); + } + setenv ("HOME", "", 1); + for (do_nocheck = 0; do_nocheck < 2; ++do_nocheck) + { + one_test ("~", "", ""); + one_test ("~", "", "/a/b"); + } + free (repeat); return 0;