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); |