>From 6083d3ed8ca93a69cb4be9d3ea73e4a2d968e344 Mon Sep 17 00:00:00 2001 From: Szabolcs Nagy Date: Sun, 21 Jun 2026 13:33:38 +0000 Subject: [PATCH 3/4] wordexp: implement WRDE_UNDEF use sh -u to detect undef vars wordexp("x $U y", p, WRDE_UNDEF) was "x","y", now fails with WRDE_BADVAL, note that wordexp("$(echo x $U) y", p, WRDE_UNDEF|WRDE_SHOWERR) was "x","y", now "y" and prints an error about U. the echo is not executed, but wordexp succeeds. undef failure is not propagated from a command substitution (but it inherits -u). on failure rerun with sh -n to distinguish unset from syntax errors without side-effects. for the syntax check the string is passed directly as a command which can be parsed differently: wordexp("{ $U", p, 0) wordexp("{ $U", p, WRDE_UNDEF) former is "{" but latter fails with WRDE_SYNTAX, not WRDE_UNDEF. (note: using eval "set -n --;..." does not work on mksh, oksh.) we == NULL marks the rerun (ub for user code). wordexp("$(echo>>x) $U $(echo>>x)", p, WRDE_UNDEF) wordexp("$(echo>>x) $U $(echo>>x", p, WRDE_UNDEF) former returns WRDE_BADVAL and writes one \n to x, latter returns WRDE_SYNTAX and has no side-effect. --- src/misc/wordexp.c | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/misc/wordexp.c b/src/misc/wordexp.c index c4154fad..e65f2381 100644 --- a/src/misc/wordexp.c +++ b/src/misc/wordexp.c @@ -11,10 +11,11 @@ #include #include "pthread_impl.h" -static void reap(pid_t pid) +static int reap(pid_t pid) { int status; while (waitpid(pid, &status, 0) < 0 && errno == EINTR); + return WIFEXITED(status) && WEXITSTATUS(status) == 0; } static char *getword(FILE *f) @@ -36,6 +37,7 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags) int p[2]; pid_t pid; sigset_t set; + int syntax_check = !we; if (flags & WRDE_REUSE) wordfree(we); @@ -91,7 +93,7 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags) if (we->we_offs > SIZE_MAX/sizeof(void *)/4) goto nospace; i += we->we_offs; - } else { + } else if (!syntax_check) { we->we_offs = 0; } @@ -111,9 +113,12 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags) int fd = open("/dev/null", O_WRONLY|O_CLOEXEC); if (fd < 0 || dup2(fd, 2) < 0) close(2); } - execl("/bin/sh", "sh", "-c", - "eval \"set --;printf %s\\\\\\\\0 x $1\"", - "sh", s, (char *)0); + if (syntax_check) + execl("/bin/sh", "sh", "-cn", "--", s, (char *)0); + else + execl("/bin/sh", "sh", flags & WRDE_UNDEF ? "-cu" : "-c", + "eval \"set --;printf %s\\\\\\\\0 x $1\"", + "sh", s, (char *)0); _exit(1); } close(p[1]); @@ -121,9 +126,7 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags) f = fdopen(p[0], "r"); if (!f) { close(p[0]); - kill(pid, SIGKILL); - reap(pid); - goto nospace; + goto killchild; } l = wv ? i+1 : 0; @@ -131,9 +134,19 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags) free(getword(f)); if (feof(f)) { fclose(f); - reap(pid); + int success = reap(pid); + if (syntax_check && success) + return WRDE_BADVAL; + if (flags & WRDE_UNDEF) + /* rerun with set -n */ + return do_wordexp(s, 0, 0); return WRDE_SYNTAX; } + if (syntax_check) { + /* there should not be output. */ + fclose(f); + goto killchild; + } while ((w = getword(f))) { if (i+1 >= l) { @@ -162,8 +175,11 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags) } return err; +killchild: + kill(pid, SIGKILL); + reap(pid); nospace: - if (!(flags & WRDE_APPEND)) { + if (!(flags & WRDE_APPEND) && !syntax_check) { we->we_wordc = 0; we->we_wordv = 0; } -- 2.52.0