summaryrefslogtreecommitdiff
path: root/shell/hush.c
diff options
context:
space:
mode:
authorDenis Vlasenko <vda.linux@googlemail.com>2007-10-01 10:02:25 +0000
committerDenis Vlasenko <vda.linux@googlemail.com>2007-10-01 10:02:25 +0000
commitd65ea39ffc7503807fa95e8840c012a80c83e4f3 (patch)
treeff05ca5f98996c7bd3207c9499bbb2818505a6a2 /shell/hush.c
parentff0976248ac8759af1f1de32677887c4c9866865 (diff)
downloadbusybox-w32-d65ea39ffc7503807fa95e8840c012a80c83e4f3.tar.gz
busybox-w32-d65ea39ffc7503807fa95e8840c012a80c83e4f3.tar.bz2
busybox-w32-d65ea39ffc7503807fa95e8840c012a80c83e4f3.zip
hush: fix glob() abuse. Code was making unfounded assumptions how
glob() works, and it broke horribly on specific uclibc config.
Diffstat (limited to 'shell/hush.c')
-rw-r--r--shell/hush.c189
1 files changed, 120 insertions, 69 deletions
diff --git a/shell/hush.c b/shell/hush.c
index e73432a89..90ed15547 100644
--- a/shell/hush.c
+++ b/shell/hush.c
@@ -246,7 +246,7 @@ struct redir_struct {
246 redir_type type; /* type of redirection */ 246 redir_type type; /* type of redirection */
247 int fd; /* file descriptor being redirected */ 247 int fd; /* file descriptor being redirected */
248 int dup; /* -1, or file descriptor being duplicated */ 248 int dup; /* -1, or file descriptor being duplicated */
249 glob_t glob_word; /* *word.gl_pathv is the filename */ 249 char **glob_word; /* *word.gl_pathv is the filename */
250}; 250};
251 251
252struct child_prog { 252struct child_prog {
@@ -256,7 +256,7 @@ struct child_prog {
256 smallint subshell; /* flag, non-zero if group must be forked */ 256 smallint subshell; /* flag, non-zero if group must be forked */
257 smallint is_stopped; /* is the program currently running? */ 257 smallint is_stopped; /* is the program currently running? */
258 struct redir_struct *redirects; /* I/O redirections */ 258 struct redir_struct *redirects; /* I/O redirections */
259 glob_t glob_result; /* result of parameter globbing */ 259 char **glob_result; /* result of parameter globbing */
260 struct pipe *family; /* pointer back to the child's parent pipe */ 260 struct pipe *family; /* pointer back to the child's parent pipe */
261 //sp counting seems to be broken... so commented out, grep for '//sp:' 261 //sp counting seems to be broken... so commented out, grep for '//sp:'
262 //sp: int sp; /* number of SPECIAL_VAR_SYMBOL */ 262 //sp: int sp; /* number of SPECIAL_VAR_SYMBOL */
@@ -503,9 +503,9 @@ static void pseudo_exec_argv(char **argv) ATTRIBUTE_NORETURN;
503static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN; 503static void pseudo_exec(struct child_prog *child) ATTRIBUTE_NORETURN;
504static int run_pipe_real(struct pipe *pi); 504static int run_pipe_real(struct pipe *pi);
505/* extended glob support: */ 505/* extended glob support: */
506static int globhack(const char *src, int flags, glob_t *pglob); 506static char **globhack(const char *src, char **strings);
507static int glob_needed(const char *s); 507static int glob_needed(const char *s);
508static int xglob(o_string *dest, int flags, glob_t *pglob); 508static int xglob(o_string *dest, char ***pglob);
509/* variable assignment: */ 509/* variable assignment: */
510static int is_assignment(const char *s); 510static int is_assignment(const char *s);
511/* data structure manipulation: */ 511/* data structure manipulation: */
@@ -548,6 +548,58 @@ static struct variable *get_local_var(const char *name);
548static int set_local_var(char *str, int flg_export); 548static int set_local_var(char *str, int flg_export);
549static void unset_local_var(const char *name); 549static void unset_local_var(const char *name);
550 550
551
552static char **add_strings_to_strings(int need_xstrdup, char **strings, char **add)
553{
554 int i;
555 unsigned count1;
556 unsigned count2;
557 char **v;
558
559 v = strings;
560 count1 = 0;
561 if (v) {
562 while (*v) {
563 count1++;
564 v++;
565 }
566 }
567 count2 = 0;
568 v = add;
569 while (*v) {
570 count2++;
571 v++;
572 }
573 v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*));
574 v[count1 + count2] = NULL;
575 i = count2;
576 while (--i >= 0)
577 v[count1 + i] = need_xstrdup ? xstrdup(add[i]) : add[i];
578 return v;
579}
580
581/* 'add' should be a malloced pointer */
582static char **add_string_to_strings(char **strings, char *add)
583{
584 char *v[2];
585
586 v[0] = add;
587 v[1] = NULL;
588
589 return add_strings_to_strings(0, strings, v);
590}
591
592static void free_strings(char **strings)
593{
594 if (strings) {
595 char **v = strings;
596 while (*v)
597 free(*v++);
598 free(strings);
599 }
600}
601
602
551/* Table of built-in functions. They can be forked or not, depending on 603/* Table of built-in functions. They can be forked or not, depending on
552 * context: within pipes, they fork. As simple commands, they do not. 604 * context: within pipes, they fork. As simple commands, they do not.
553 * When used in non-forking context, they can change global variables 605 * When used in non-forking context, they can change global variables
@@ -1067,16 +1119,14 @@ static void b_reset(o_string *o)
1067{ 1119{
1068 o->length = 0; 1120 o->length = 0;
1069 o->nonnull = 0; 1121 o->nonnull = 0;
1070 if (o->data != NULL) 1122 if (o->data)
1071 *o->data = '\0'; 1123 o->data[0] = '\0';
1072} 1124}
1073 1125
1074static void b_free(o_string *o) 1126static void b_free(o_string *o)
1075{ 1127{
1076 b_reset(o);
1077 free(o->data); 1128 free(o->data);
1078 o->data = NULL; 1129 memset(o, 0, sizeof(*o));
1079 o->maxlen = 0;
1080} 1130}
1081 1131
1082/* My analysis of quoting semantics tells me that state information 1132/* My analysis of quoting semantics tells me that state information
@@ -1256,13 +1306,13 @@ static int setup_redirects(struct child_prog *prog, int squirrel[])
1256 struct redir_struct *redir; 1306 struct redir_struct *redir;
1257 1307
1258 for (redir = prog->redirects; redir; redir = redir->next) { 1308 for (redir = prog->redirects; redir; redir = redir->next) {
1259 if (redir->dup == -1 && redir->glob_word.gl_pathv == NULL) { 1309 if (redir->dup == -1 && redir->glob_word == NULL) {
1260 /* something went wrong in the parse. Pretend it didn't happen */ 1310 /* something went wrong in the parse. Pretend it didn't happen */
1261 continue; 1311 continue;
1262 } 1312 }
1263 if (redir->dup == -1) { 1313 if (redir->dup == -1) {
1264 mode = redir_table[redir->type].mode; 1314 mode = redir_table[redir->type].mode;
1265 openfd = open_or_warn(redir->glob_word.gl_pathv[0], mode); 1315 openfd = open_or_warn(redir->glob_word[0], mode);
1266 if (openfd < 0) { 1316 if (openfd < 0) {
1267 /* this could get lost if stderr has been redirected, but 1317 /* this could get lost if stderr has been redirected, but
1268 bash and ash both lose it as well (though zsh doesn't!) */ 1318 bash and ash both lose it as well (though zsh doesn't!) */
@@ -2024,6 +2074,7 @@ static int run_list_real(struct pipe *pi)
2024#if ENABLE_HUSH_LOOPS 2074#if ENABLE_HUSH_LOOPS
2025 if (rword == RES_FOR && pi->num_progs) { 2075 if (rword == RES_FOR && pi->num_progs) {
2026 if (!for_lcur) { 2076 if (!for_lcur) {
2077 /* first loop through for */
2027 /* if no variable values after "in" we skip "for" */ 2078 /* if no variable values after "in" we skip "for" */
2028 if (!pi->next->progs->argv) 2079 if (!pi->next->progs->argv)
2029 continue; 2080 continue;
@@ -2036,17 +2087,16 @@ static int run_list_real(struct pipe *pi)
2036 } 2087 }
2037 free(pi->progs->argv[0]); 2088 free(pi->progs->argv[0]);
2038 if (!*for_lcur) { 2089 if (!*for_lcur) {
2090 /* for loop is over, clean up */
2039 free(for_list); 2091 free(for_list);
2040 for_lcur = NULL; 2092 for_lcur = NULL;
2041 flag_rep = 0; 2093 flag_rep = 0;
2042 pi->progs->argv[0] = for_varname; 2094 pi->progs->argv[0] = for_varname;
2043 pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0];
2044 continue; 2095 continue;
2045 } 2096 }
2046 /* insert next value from for_lcur */ 2097 /* insert next value from for_lcur */
2047 /* vda: does it need escaping? */ 2098 /* vda: does it need escaping? */
2048 pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++); 2099 pi->progs->argv[0] = xasprintf("%s=%s", for_varname, *for_lcur++);
2049 pi->progs->glob_result.gl_pathv[0] = pi->progs->argv[0];
2050 } 2100 }
2051 if (rword == RES_IN) 2101 if (rword == RES_IN)
2052 continue; 2102 continue;
@@ -2149,8 +2199,8 @@ static int free_pipe(struct pipe *pi, int indent)
2149 for (a = 0, p = child->argv; *p; a++, p++) { 2199 for (a = 0, p = child->argv; *p; a++, p++) {
2150 debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p); 2200 debug_printf_clean("%s argv[%d] = %s\n", indenter(indent), a, *p);
2151 } 2201 }
2152 globfree(&child->glob_result); 2202 free_strings(child->glob_result);
2153 child->argv = NULL; 2203 child->glob_result = NULL;
2154 } else if (child->group) { 2204 } else if (child->group) {
2155 debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), child->subshell); 2205 debug_printf_clean("%s begin group (subshell:%d)\n", indenter(indent), child->subshell);
2156 ret_code = free_pipe_list(child->group, indent+3); 2206 ret_code = free_pipe_list(child->group, indent+3);
@@ -2162,9 +2212,10 @@ static int free_pipe(struct pipe *pi, int indent)
2162 debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->type].descrip); 2212 debug_printf_clean("%s redirect %d%s", indenter(indent), r->fd, redir_table[r->type].descrip);
2163 if (r->dup == -1) { 2213 if (r->dup == -1) {
2164 /* guard against the case >$FOO, where foo is unset or blank */ 2214 /* guard against the case >$FOO, where foo is unset or blank */
2165 if (r->glob_word.gl_pathv) { 2215 if (r->glob_word) {
2166 debug_printf_clean(" %s\n", r->glob_word.gl_pathv[0]); 2216 debug_printf_clean(" %s\n", r->glob_word[0]);
2167 globfree(&r->glob_word); 2217 free_strings(r->glob_word);
2218 r->glob_word = NULL;
2168 } 2219 }
2169 } else { 2220 } else {
2170 debug_printf_clean("&%d\n", r->dup); 2221 debug_printf_clean("&%d\n", r->dup);
@@ -2224,80 +2275,79 @@ static int run_list(struct pipe *pi)
2224 * string into the output structure, removing non-backslashed backslashes. 2275 * string into the output structure, removing non-backslashed backslashes.
2225 * If someone can prove me wrong, by performing this function within the 2276 * If someone can prove me wrong, by performing this function within the
2226 * original glob(3) api, feel free to rewrite this routine into oblivion. 2277 * original glob(3) api, feel free to rewrite this routine into oblivion.
2227 * Return code (0 vs. GLOB_NOSPACE) matches glob(3).
2228 * XXX broken if the last character is '\\', check that before calling. 2278 * XXX broken if the last character is '\\', check that before calling.
2229 */ 2279 */
2230static int globhack(const char *src, int flags, glob_t *pglob) 2280static char **globhack(const char *src, char **strings)
2231{ 2281{
2232 int cnt = 0, pathc; 2282 int cnt;
2233 const char *s; 2283 const char *s;
2234 char *dest; 2284 char *v, *dest;
2285
2235 for (cnt = 1, s = src; s && *s; s++) { 2286 for (cnt = 1, s = src; s && *s; s++) {
2236 if (*s == '\\') s++; 2287 if (*s == '\\') s++;
2237 cnt++; 2288 cnt++;
2238 } 2289 }
2239 dest = xmalloc(cnt); 2290 v = dest = xmalloc(cnt);
2240 if (!(flags & GLOB_APPEND)) {
2241 pglob->gl_pathv = NULL;
2242 pglob->gl_pathc = 0;
2243 pglob->gl_offs = 0;
2244 pglob->gl_offs = 0;
2245 }
2246 pathc = ++pglob->gl_pathc;
2247 pglob->gl_pathv = xrealloc(pglob->gl_pathv, (pathc+1) * sizeof(*pglob->gl_pathv));
2248 pglob->gl_pathv[pathc-1] = dest;
2249 pglob->gl_pathv[pathc] = NULL;
2250 for (s = src; s && *s; s++, dest++) { 2291 for (s = src; s && *s; s++, dest++) {
2251 if (*s == '\\') s++; 2292 if (*s == '\\') s++;
2252 *dest = *s; 2293 *dest = *s;
2253 } 2294 }
2254 *dest = '\0'; 2295 *dest = '\0';
2255 return 0; 2296
2297 return add_string_to_strings(strings, v);
2256} 2298}
2257 2299
2258/* XXX broken if the last character is '\\', check that before calling */ 2300/* XXX broken if the last character is '\\', check that before calling */
2259static int glob_needed(const char *s) 2301static int glob_needed(const char *s)
2260{ 2302{
2261 for (; *s; s++) { 2303 for (; *s; s++) {
2262 if (*s == '\\') s++; 2304 if (*s == '\\')
2263 if (strchr("*[?", *s)) return 1; 2305 s++;
2306 if (strchr("*[?", *s))
2307 return 1;
2264 } 2308 }
2265 return 0; 2309 return 0;
2266} 2310}
2267 2311
2268static int xglob(o_string *dest, int flags, glob_t *pglob) 2312static int xglob(o_string *dest, char ***pglob)
2269{ 2313{
2270 int gr;
2271
2272 /* short-circuit for null word */ 2314 /* short-circuit for null word */
2273 /* we can code this better when the debug_printf's are gone */ 2315 /* we can code this better when the debug_printf's are gone */
2274 if (dest->length == 0) { 2316 if (dest->length == 0) {
2275 if (dest->nonnull) { 2317 if (dest->nonnull) {
2276 /* bash man page calls this an "explicit" null */ 2318 /* bash man page calls this an "explicit" null */
2277 gr = globhack(dest->data, flags, pglob); 2319 *pglob = globhack(dest->data, *pglob);
2278 debug_printf("globhack returned %d\n", gr);
2279 } else {
2280 return 0;
2281 } 2320 }
2282 } else if (glob_needed(dest->data)) { 2321 return 0;
2283 gr = glob(dest->data, flags, NULL, pglob); 2322 }
2323
2324 if (glob_needed(dest->data)) {
2325 glob_t globdata;
2326 int gr;
2327
2328 memset(&globdata, 0, sizeof(globdata));
2329 gr = glob(dest->data, 0, NULL, &globdata);
2284 debug_printf("glob returned %d\n", gr); 2330 debug_printf("glob returned %d\n", gr);
2331 if (gr == GLOB_NOSPACE)
2332 bb_error_msg_and_die("out of memory during glob");
2285 if (gr == GLOB_NOMATCH) { 2333 if (gr == GLOB_NOMATCH) {
2286 /* quote removal, or more accurately, backslash removal */
2287 gr = globhack(dest->data, flags, pglob);
2288 debug_printf("globhack returned %d\n", gr); 2334 debug_printf("globhack returned %d\n", gr);
2335 /* quote removal, or more accurately, backslash removal */
2336 *pglob = globhack(dest->data, *pglob);
2337 return 0;
2289 } 2338 }
2290 } else { 2339 if (gr != 0) { /* GLOB_ABORTED ? */
2291 gr = globhack(dest->data, flags, pglob); 2340 bb_error_msg("glob(3) error %d", gr);
2292 debug_printf("globhack returned %d\n", gr); 2341 }
2293 } 2342 if (globdata.gl_pathv && globdata.gl_pathv[0])
2294 if (gr == GLOB_NOSPACE) 2343 *pglob = add_strings_to_strings(1, *pglob, globdata.gl_pathv);
2295 bb_error_msg_and_die("out of memory during glob"); 2344 /* globprint(glob_target); */
2296 if (gr != 0) { /* GLOB_ABORTED ? */ 2345 globfree(&globdata);
2297 bb_error_msg("glob(3) error %d", gr); 2346 return gr;
2298 } 2347 }
2299 /* globprint(glob_target); */ 2348
2300 return gr; 2349 *pglob = globhack(dest->data, *pglob);
2350 return 0;
2301} 2351}
2302 2352
2303/* expand_strvec_to_strvec() takes a list of strings, expands 2353/* expand_strvec_to_strvec() takes a list of strings, expands
@@ -2775,7 +2825,7 @@ static int setup_redirect(struct p_context *ctx, int fd, redir_type style,
2775 } 2825 }
2776 redir = xzalloc(sizeof(struct redir_struct)); 2826 redir = xzalloc(sizeof(struct redir_struct));
2777 /* redir->next = NULL; */ 2827 /* redir->next = NULL; */
2778 /* redir->glob_word.gl_pathv = NULL; */ 2828 /* redir->glob_word = NULL; */
2779 if (last_redir) { 2829 if (last_redir) {
2780 last_redir->next = redir; 2830 last_redir->next = redir;
2781 } else { 2831 } else {
@@ -2919,8 +2969,8 @@ static int reserved_word(o_string *dest, struct p_context *ctx)
2919static int done_word(o_string *dest, struct p_context *ctx) 2969static int done_word(o_string *dest, struct p_context *ctx)
2920{ 2970{
2921 struct child_prog *child = ctx->child; 2971 struct child_prog *child = ctx->child;
2922 glob_t *glob_target; 2972 char ***glob_target;
2923 int gr, flags = 0; 2973 int gr;
2924 2974
2925 debug_printf_parse("done_word entered: '%s' %p\n", dest->data, child); 2975 debug_printf_parse("done_word entered: '%s' %p\n", dest->data, child);
2926 if (dest->length == 0 && !dest->nonnull) { 2976 if (dest->length == 0 && !dest->nonnull) {
@@ -2942,11 +2992,9 @@ static int done_word(o_string *dest, struct p_context *ctx)
2942 return (ctx->res_w == RES_SNTX); 2992 return (ctx->res_w == RES_SNTX);
2943 } 2993 }
2944 } 2994 }
2945 glob_target = &child->glob_result; 2995 glob_target = &child->argv;
2946 if (child->argv)
2947 flags |= GLOB_APPEND;
2948 } 2996 }
2949 gr = xglob(dest, flags, glob_target); 2997 gr = xglob(dest, glob_target);
2950 if (gr != 0) { 2998 if (gr != 0) {
2951 debug_printf_parse("done_word return 1: xglob returned %d\n", gr); 2999 debug_printf_parse("done_word return 1: xglob returned %d\n", gr);
2952 return 1; 3000 return 1;
@@ -2954,14 +3002,17 @@ static int done_word(o_string *dest, struct p_context *ctx)
2954 3002
2955 b_reset(dest); 3003 b_reset(dest);
2956 if (ctx->pending_redirect) { 3004 if (ctx->pending_redirect) {
2957 ctx->pending_redirect = NULL; 3005 if (ctx->pending_redirect->glob_word
2958 if (glob_target->gl_pathc != 1) { 3006 && ctx->pending_redirect->glob_word[0]
3007 && ctx->pending_redirect->glob_word[1]
3008 ) {
3009 /* more than one word resulted from globbing redir */
3010 ctx->pending_redirect = NULL;
2959 bb_error_msg("ambiguous redirect"); 3011 bb_error_msg("ambiguous redirect");
2960 debug_printf_parse("done_word return 1: ambiguous redirect\n"); 3012 debug_printf_parse("done_word return 1: ambiguous redirect\n");
2961 return 1; 3013 return 1;
2962 } 3014 }
2963 } else { 3015 ctx->pending_redirect = NULL;
2964 child->argv = glob_target->gl_pathv;
2965 } 3016 }
2966#if ENABLE_HUSH_LOOPS 3017#if ENABLE_HUSH_LOOPS
2967 if (ctx->res_w == RES_FOR) { 3018 if (ctx->res_w == RES_FOR) {
@@ -3006,7 +3057,7 @@ static int done_command(struct p_context *ctx)
3006 /*child->argv = NULL;*/ 3057 /*child->argv = NULL;*/
3007 /*child->is_stopped = 0;*/ 3058 /*child->is_stopped = 0;*/
3008 /*child->group = NULL;*/ 3059 /*child->group = NULL;*/
3009 /*child->glob_result.gl_pathv = NULL;*/ 3060 /*child->glob_result = NULL;*/
3010 child->family = pi; 3061 child->family = pi;
3011 //sp: /*child->sp = 0;*/ 3062 //sp: /*child->sp = 0;*/
3012 //pt: child->parse_type = ctx->parse_type; 3063 //pt: child->parse_type = ctx->parse_type;