fix bad character checking in wordexp
[musl] / src / misc / wordexp.c
1 #include <wordexp.h>
2 #include <unistd.h>
3 #include <stdio.h>
4 #include <string.h>
5 #include <limits.h>
6 #include <stdint.h>
7 #include <stdlib.h>
8 #include <sys/wait.h>
9 #include <signal.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include "pthread_impl.h"
13
14 static void reap(pid_t pid)
15 {
16         int status;
17         for (;;) {
18                 if (waitpid(pid, &status, 0) < 0) {
19                         if (errno != EINTR) return;
20                 } else {
21                         if (WIFEXITED(status)) return;
22                 }
23         }
24 }
25
26 static char *getword(FILE *f)
27 {
28         char *s = 0;
29         return getdelim(&s, (size_t [1]){0}, 0, f) < 0 ? 0 : s;
30 }
31
32 static int do_wordexp(const char *s, wordexp_t *we, int flags)
33 {
34         size_t i, l;
35         int sq=0, dq=0;
36         size_t np=0;
37         char *w, **tmp;
38         char *redir = (flags & WRDE_SHOWERR) ? "" : "2>/dev/null";
39         int err = 0;
40         FILE *f;
41         size_t wc = 0;
42         char **wv = 0;
43         int p[2];
44         pid_t pid;
45         sigset_t set;
46
47         if (flags & WRDE_REUSE) wordfree(we);
48
49         if (flags & WRDE_NOCMD) for (i=0; s[i]; i++) switch (s[i]) {
50         case '\\':
51                 if (!sq) i++;
52                 break;
53         case '\'':
54                 if (!dq) sq^=1;
55                 break;
56         case '"':
57                 if (!sq) dq^=1;
58                 break;
59         case '(':
60                 if (np) {
61                         np++;
62                         break;
63                 }
64         case ')':
65                 if (np) {
66                         np--;
67                         break;
68                 }
69         case '\n':
70         case '|':
71         case '&':
72         case ';':
73         case '<':
74         case '>':
75         case '{':
76         case '}':
77                 if (!(sq|dq|np)) return WRDE_BADCHAR;
78                 break;
79         case '$':
80                 if (sq) break;
81                 if (s[i+1]=='(' && s[i+2]=='(') {
82                         i += 2;
83                         np += 2;
84                         break;
85                 } else if (s[i+1] != '(') break;
86         case '`':
87                 if (sq) break;
88                 return WRDE_CMDSUB;
89         }
90
91         if (flags & WRDE_APPEND) {
92                 wc = we->we_wordc;
93                 wv = we->we_wordv;
94         }
95
96         i = wc;
97         if (flags & WRDE_DOOFFS) {
98                 if (we->we_offs > SIZE_MAX/sizeof(void *)/4)
99                         goto nospace;
100                 i += we->we_offs;
101         } else {
102                 we->we_offs = 0;
103         }
104
105         if (pipe2(p, O_CLOEXEC) < 0) goto nospace;
106         __block_all_sigs(&set);
107         pid = fork();
108         __restore_sigs(&set);
109         if (pid < 0) {
110                 close(p[0]);
111                 close(p[1]);
112                 goto nospace;
113         }
114         if (!pid) {
115                 if (p[1] == 1) fcntl(1, F_SETFD, 0);
116                 else dup2(p[1], 1);
117                 execl("/bin/sh", "sh", "-c",
118                         "eval \"printf %s\\\\\\\\0 x $1 $2\"",
119                         "sh", s, redir, (char *)0);
120                 _exit(1);
121         }
122         close(p[1]);
123         
124         f = fdopen(p[0], "r");
125         if (!f) {
126                 close(p[0]);
127                 kill(pid, SIGKILL);
128                 reap(pid);
129                 goto nospace;
130         }
131
132         l = wv ? i+1 : 0;
133
134         free(getword(f));
135         if (feof(f)) {
136                 fclose(f);
137                 reap(pid);
138                 return WRDE_SYNTAX;
139         }
140
141         while ((w = getword(f))) {
142                 if (i+1 >= l) {
143                         l += l/2+10;
144                         tmp = realloc(wv, l*sizeof(char *));
145                         if (!tmp) break;
146                         wv = tmp;
147                 }
148                 wv[i++] = w;
149                 wv[i] = 0;
150         }
151         if (!feof(f)) err = WRDE_NOSPACE;
152
153         fclose(f);
154         reap(pid);
155
156         if (!wv) wv = calloc(i+1, sizeof *wv);
157
158         we->we_wordv = wv;
159         we->we_wordc = i;
160
161         if (flags & WRDE_DOOFFS) {
162                 if (wv) for (i=we->we_offs; i; i--)
163                         we->we_wordv[i-1] = 0;
164                 we->we_wordc -= we->we_offs;
165         }
166         return err;
167
168 nospace:
169         if (!(flags & WRDE_APPEND)) {
170                 we->we_wordc = 0;
171                 we->we_wordv = 0;
172         }
173         return WRDE_NOSPACE;
174 }
175
176 int wordexp(const char *restrict s, wordexp_t *restrict we, int flags)
177 {
178         int r, cs;
179         pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cs);
180         r = do_wordexp(s, we, flags);
181         pthread_setcancelstate(cs, 0);
182         return r;
183 }
184
185 void wordfree(wordexp_t *we)
186 {
187         size_t i;
188         if (!we->we_wordv) return;
189         for (i=0; i<we->we_wordc; i++) free(we->we_wordv[we->we_offs+i]);
190         free(we->we_wordv);
191         we->we_wordv = 0;
192         we->we_wordc = 0;
193 }