diff options
| author | Denys Vlasenko <vda.linux@googlemail.com> | 2021-07-04 01:25:34 +0200 |
|---|---|---|
| committer | Denys Vlasenko <vda.linux@googlemail.com> | 2021-07-04 01:25:34 +0200 |
| commit | e2e3802987266c98df0efdf40ad5da4b07df0113 (patch) | |
| tree | 8065733d6843453779fbc7927c95edcee805a9bb | |
| parent | 08ca313d7edb99687068b93b5d2435b59f3db23a (diff) | |
| download | busybox-w32-e2e3802987266c98df0efdf40ad5da4b07df0113.tar.gz busybox-w32-e2e3802987266c98df0efdf40ad5da4b07df0113.tar.bz2 busybox-w32-e2e3802987266c98df0efdf40ad5da4b07df0113.zip | |
awk: fix printf buffer overflow
function old new delta
awk_printf 468 546 +78
fmt_num 239 247 +8
getvar_s 125 111 -14
evaluate 3343 3329 -14
------------------------------------------------------------------------------
(add/remove: 0/0 grow/shrink: 2/2 up/down: 86/-28) Total: 58 bytes
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
| -rw-r--r-- | editors/awk.c | 94 |
1 files changed, 55 insertions, 39 deletions
diff --git a/editors/awk.c b/editors/awk.c index cd135ef64..a440a6234 100644 --- a/editors/awk.c +++ b/editors/awk.c | |||
| @@ -904,25 +904,23 @@ static double my_strtod(char **pp) | |||
| 904 | 904 | ||
| 905 | /* -------- working with variables (set/get/copy/etc) -------- */ | 905 | /* -------- working with variables (set/get/copy/etc) -------- */ |
| 906 | 906 | ||
| 907 | static int fmt_num(char *b, int size, const char *format, double n, int int_as_int) | 907 | static void fmt_num(const char *format, double n) |
| 908 | { | 908 | { |
| 909 | int r = 0; | 909 | if (n == (long long)n) { |
| 910 | char c; | 910 | snprintf(g_buf, MAXVARFMT, "%lld", (long long)n); |
| 911 | const char *s = format; | ||
| 912 | |||
| 913 | if (int_as_int && n == (long long)n) { | ||
| 914 | r = snprintf(b, size, "%lld", (long long)n); | ||
| 915 | } else { | 911 | } else { |
| 912 | const char *s = format; | ||
| 913 | char c; | ||
| 914 | |||
| 916 | do { c = *s; } while (c && *++s); | 915 | do { c = *s; } while (c && *++s); |
| 917 | if (strchr("diouxX", c)) { | 916 | if (strchr("diouxX", c)) { |
| 918 | r = snprintf(b, size, format, (int)n); | 917 | snprintf(g_buf, MAXVARFMT, format, (int)n); |
| 919 | } else if (strchr("eEfFgGaA", c)) { | 918 | } else if (strchr("eEfFgGaA", c)) { |
| 920 | r = snprintf(b, size, format, n); | 919 | snprintf(g_buf, MAXVARFMT, format, n); |
| 921 | } else { | 920 | } else { |
| 922 | syntax_error(EMSG_INV_FMT); | 921 | syntax_error(EMSG_INV_FMT); |
| 923 | } | 922 | } |
| 924 | } | 923 | } |
| 925 | return r; | ||
| 926 | } | 924 | } |
| 927 | 925 | ||
| 928 | static xhash *iamarray(var *a) | 926 | static xhash *iamarray(var *a) |
| @@ -999,7 +997,7 @@ static const char *getvar_s(var *v) | |||
| 999 | { | 997 | { |
| 1000 | /* if v is numeric and has no cached string, convert it to string */ | 998 | /* if v is numeric and has no cached string, convert it to string */ |
| 1001 | if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) { | 999 | if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) { |
| 1002 | fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[CONVFMT]), v->number, TRUE); | 1000 | fmt_num(getvar_s(intvar[CONVFMT]), v->number); |
| 1003 | v->string = xstrdup(g_buf); | 1001 | v->string = xstrdup(g_buf); |
| 1004 | v->type |= VF_CACHED; | 1002 | v->type |= VF_CACHED; |
| 1005 | } | 1003 | } |
| @@ -2315,12 +2313,9 @@ static int awk_getline(rstream *rsm, var *v) | |||
| 2315 | #endif | 2313 | #endif |
| 2316 | static char *awk_printf(node *n, int *len) | 2314 | static char *awk_printf(node *n, int *len) |
| 2317 | { | 2315 | { |
| 2318 | char *b = NULL; | 2316 | char *b; |
| 2319 | char *fmt, *s, *f; | 2317 | char *fmt, *f; |
| 2320 | const char *s1; | 2318 | int i; |
| 2321 | int i, j, incr, bsize; | ||
| 2322 | char c, c1; | ||
| 2323 | var *arg; | ||
| 2324 | 2319 | ||
| 2325 | //tmpvar = nvalloc(1); | 2320 | //tmpvar = nvalloc(1); |
| 2326 | #define TMPVAR (&G.awk_printf__tmpvar) | 2321 | #define TMPVAR (&G.awk_printf__tmpvar) |
| @@ -2333,8 +2328,14 @@ static char *awk_printf(node *n, int *len) | |||
| 2333 | // to evaluate() potentially recursing into another awk_printf() can't | 2328 | // to evaluate() potentially recursing into another awk_printf() can't |
| 2334 | // mangle the value. | 2329 | // mangle the value. |
| 2335 | 2330 | ||
| 2331 | b = NULL; | ||
| 2336 | i = 0; | 2332 | i = 0; |
| 2337 | while (*f) { | 2333 | while (*f) { /* "print one format spec" loop */ |
| 2334 | char *s; | ||
| 2335 | char c; | ||
| 2336 | char sv; | ||
| 2337 | var *arg; | ||
| 2338 | |||
| 2338 | s = f; | 2339 | s = f; |
| 2339 | while (*f && (*f != '%' || *++f == '%')) | 2340 | while (*f && (*f != '%' || *++f == '%')) |
| 2340 | f++; | 2341 | f++; |
| @@ -2343,40 +2344,55 @@ static char *awk_printf(node *n, int *len) | |||
| 2343 | syntax_error("%*x formats are not supported"); | 2344 | syntax_error("%*x formats are not supported"); |
| 2344 | f++; | 2345 | f++; |
| 2345 | } | 2346 | } |
| 2346 | |||
| 2347 | incr = (f - s) + MAXVARFMT; | ||
| 2348 | b = qrealloc(b, incr + i, &bsize); | ||
| 2349 | c = *f; | 2347 | c = *f; |
| 2350 | if (c != '\0') | 2348 | if (!c) { |
| 2351 | f++; | 2349 | /* Tail of fmt with no percent chars, |
| 2352 | c1 = *f; | 2350 | * or "....%" (percent seen, but no format specifier char found) |
| 2351 | */ | ||
| 2352 | goto tail; | ||
| 2353 | } | ||
| 2354 | sv = *++f; | ||
| 2353 | *f = '\0'; | 2355 | *f = '\0'; |
| 2354 | arg = evaluate(nextarg(&n), TMPVAR); | 2356 | arg = evaluate(nextarg(&n), TMPVAR); |
| 2355 | 2357 | ||
| 2356 | j = i; | 2358 | /* Result can be arbitrarily long. Example: |
| 2357 | if (c == 'c' || !c) { | 2359 | * printf "%99999s", "BOOM" |
| 2358 | i += sprintf(b+i, s, is_numeric(arg) ? | 2360 | */ |
| 2361 | if (c == 'c') { | ||
| 2362 | s = xasprintf(s, is_numeric(arg) ? | ||
| 2359 | (char)getvar_i(arg) : *getvar_s(arg)); | 2363 | (char)getvar_i(arg) : *getvar_s(arg)); |
| 2360 | } else if (c == 's') { | 2364 | } else if (c == 's') { |
| 2361 | s1 = getvar_s(arg); | 2365 | s = xasprintf(s, getvar_s(arg)); |
| 2362 | b = qrealloc(b, incr+i+strlen(s1), &bsize); | ||
| 2363 | i += sprintf(b+i, s, s1); | ||
| 2364 | } else { | 2366 | } else { |
| 2365 | i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE); | 2367 | double d = getvar_i(arg); |
| 2368 | if (strchr("diouxX", c)) { | ||
| 2369 | //TODO: make it wider here (%x -> %llx etc)? | ||
| 2370 | s = xasprintf(s, (int)d); | ||
| 2371 | } else if (strchr("eEfFgGaA", c)) { | ||
| 2372 | s = xasprintf(s, d); | ||
| 2373 | } else { | ||
| 2374 | syntax_error(EMSG_INV_FMT); | ||
| 2375 | } | ||
| 2366 | } | 2376 | } |
| 2367 | *f = c1; | 2377 | *f = sv; |
| 2368 | 2378 | ||
| 2369 | /* if there was an error while sprintf, return value is negative */ | 2379 | if (i == 0) { |
| 2370 | if (i < j) | 2380 | b = s; |
| 2371 | i = j; | 2381 | i = strlen(b); |
| 2382 | continue; | ||
| 2383 | } | ||
| 2384 | tail: | ||
| 2385 | b = xrealloc(b, i + strlen(s) + 1); | ||
| 2386 | i = stpcpy(b + i, s) - b; | ||
| 2387 | if (!c) /* tail? */ | ||
| 2388 | break; | ||
| 2389 | free(s); | ||
| 2372 | } | 2390 | } |
| 2373 | 2391 | ||
| 2374 | free(fmt); | 2392 | free(fmt); |
| 2375 | //nvfree(tmpvar, 1); | 2393 | //nvfree(tmpvar, 1); |
| 2376 | #undef TMPVAR | 2394 | #undef TMPVAR |
| 2377 | 2395 | ||
| 2378 | b = xrealloc(b, i + 1); | ||
| 2379 | b[i] = '\0'; | ||
| 2380 | #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS | 2396 | #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS |
| 2381 | if (len) | 2397 | if (len) |
| 2382 | *len = i; | 2398 | *len = i; |
| @@ -2936,8 +2952,8 @@ static var *evaluate(node *op, var *res) | |||
| 2936 | for (;;) { | 2952 | for (;;) { |
| 2937 | var *v = evaluate(nextarg(&op1), TMPVAR0); | 2953 | var *v = evaluate(nextarg(&op1), TMPVAR0); |
| 2938 | if (v->type & VF_NUMBER) { | 2954 | if (v->type & VF_NUMBER) { |
| 2939 | fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[OFMT]), | 2955 | fmt_num(getvar_s(intvar[OFMT]), |
| 2940 | getvar_i(v), TRUE); | 2956 | getvar_i(v)); |
| 2941 | fputs(g_buf, F); | 2957 | fputs(g_buf, F); |
| 2942 | } else { | 2958 | } else { |
| 2943 | fputs(getvar_s(v), F); | 2959 | fputs(getvar_s(v), F); |
