[PATCH 2/6] posix: Move the glob home directory lookups out of __glob
Adhemerval Zanella <[email protected]> Mon, 3 Aug 2026 16:19:16 -0300
| Newsgroups | gmane.comp.lib.glibc.alpha |
|---|---|
| Message-ID | <[email protected]> |
Expanding "~" or "~user" needs a struct scratch_buffer to call
getpwnam_r through, where thecoce 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
tGLOB_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.
---
posix/glob.c | 177 ++++++++++++++++++++++++++---------------
posix/tst-glob-tilde.c | 19 +++++
2 files changed, 132 insertions(+), 64 deletions(-)
diff --git a/posix/glob.c b/posix/glob.c
index a81a2285b89..1b1fd8eda02 100644
--- a/posix/glob.c
+++ b/posix/glob.c
@@ -285,6 +285,99 @@ 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;
+ struct passwd pwbuf;
+ struct scratch_buffer s;
+ char *result;
+ int err;
+
+ *nospace = false;
+ 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;
+ }
+ }
+
+ 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 +735,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 +871,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);
-
-# 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))
- {
- retval = GLOB_NOSPACE;
- goto out;
- }
- }
-# else
- p = getpwnam (user_name);
-# endif
+ bool nospace;
+ char *home_dir = glob_user_home_dir (user_name, &nospace);
if (__glibc_unlikely (malloc_user_name))
free (user_name);
- /* If we found a home directory use this. */
- if (p != NULL)
+ if (__glibc_unlikely (nospace))
{
- size_t home_len = strlen (p->pw_dir);
+ retval = GLOB_NOSPACE;
+ goto out;
+ }
+
+ /* If we found a home directory use this. */
+ if (home_dir != NULL)
+ {
+ 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 +899,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 +921,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 c893df6800b..83d05faa11f 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;
--
2.53.0