summaryrefslogtreecommitdiff
path: root/src/misc
diff options
context:
space:
mode:
authorRich Felker <dalias@aerifal.cx>2013-11-22 15:29:14 -0500
committerRich Felker <dalias@aerifal.cx>2013-11-22 15:29:14 -0500
commitd8f1908b821098f7a2ff03fbf6b152fe13023057 (patch)
treef792a251a087b299aaa32d10a99333fc49918d5c /src/misc
parenta516077feba96ec24d1ca8a9cbb8490bfdd066f1 (diff)
downloadmusl-d8f1908b821098f7a2ff03fbf6b152fe13023057.tar.gz
improve robustness of wordexp and fix handling of 0-word case
avoid using exit status to determine if a shell error occurred, since broken programs may install SIGCHLD handlers which reap all zombies, including ones that don't belong to them. using clone and __WCLONE does not seem to work for avoiding this problem since exec resets the exit signal to SIGCHLD. instead, the new code uses a dummy word at the beginning of the shell's output, which is ignored, to determine whether the command was executed successfully. this also fixes a corner case where a word string containing zero words was interpreted as a single zero-length word rather than no words at all. POSIX does not seem to require this case to be supported anyway, though. in addition, the new code uses the correct retry idiom for waitpid to ensure that spurious STOP/CONT signals in the child and/or EINTR in the parent do not prevent successful wait for the child, and blocks signals in the child.
Diffstat (limited to 'src/misc')
-rw-r--r--src/misc/wordexp.c27
1 files changed, 16 insertions, 11 deletions
diff --git a/src/misc/wordexp.c b/src/misc/wordexp.c
index 617706e5..7a358686 100644
--- a/src/misc/wordexp.c
+++ b/src/misc/wordexp.c
@@ -7,7 +7,8 @@
#include <stdlib.h>
#include <sys/wait.h>
#include <signal.h>
-#include <pthread.h>
+#include <errno.h>
+#include "pthread_impl.h"
static char *getword(FILE *f)
{
@@ -28,6 +29,7 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags)
char **wv = 0;
int p[2];
pid_t pid;
+ sigset_t set;
if (flags & WRDE_REUSE) wordfree(we);
@@ -87,7 +89,9 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags)
}
if (pipe(p) < 0) return WRDE_NOSPACE;
+ __block_all_sigs(&set);
pid = fork();
+ __restore_sigs(&set);
if (pid < 0) {
close(p[0]);
close(p[1]);
@@ -98,7 +102,7 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags)
close(p[0]);
close(p[1]);
execl("/bin/sh", "sh", "-c",
- "eval \"printf %s\\\\\\\\0 $1 $2\"",
+ "eval \"printf %s\\\\\\\\0 x $1 $2\"",
"sh", s, redir, (char *)0);
_exit(1);
}
@@ -114,6 +118,14 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags)
l = wv ? i+1 : 0;
+ free(getword(f));
+ if (feof(f)) {
+ fclose(f);
+ while ((waitpid(pid, &status, 0) < 0 && errno == EINTR)
+ || !WIFEXITED(status));
+ return WRDE_SYNTAX;
+ }
+
while ((w = getword(f))) {
if (i+1 >= l) {
l += l/2+10;
@@ -127,15 +139,8 @@ static int do_wordexp(const char *s, wordexp_t *we, int flags)
if (!feof(f)) err = WRDE_NOSPACE;
fclose(f);
- waitpid(pid, &status, 0);
- if (WEXITSTATUS(status)) {
- if (!(flags & WRDE_APPEND)) {
- free(wv);
- return WRDE_SYNTAX;
- } else if (wv==we->we_wordv) {
- return WRDE_SYNTAX;
- }
- }
+ while ((waitpid(pid, &status, 0) < 0 && errno == EINTR)
+ || !WIFEXITED(status));
we->we_wordv = wv;
we->we_wordc = i;