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>
This commit is contained in:
Denys Vlasenko 2021-07-04 01:25:34 +02:00
parent 08ca313d7e
commit e2e3802987

View File

@ -904,25 +904,23 @@ static double my_strtod(char **pp)
/* -------- working with variables (set/get/copy/etc) -------- */ /* -------- working with variables (set/get/copy/etc) -------- */
static int fmt_num(char *b, int size, const char *format, double n, int int_as_int) static void fmt_num(const char *format, double n)
{ {
int r = 0; if (n == (long long)n) {
char c; snprintf(g_buf, MAXVARFMT, "%lld", (long long)n);
const char *s = format;
if (int_as_int && n == (long long)n) {
r = snprintf(b, size, "%lld", (long long)n);
} else { } else {
const char *s = format;
char c;
do { c = *s; } while (c && *++s); do { c = *s; } while (c && *++s);
if (strchr("diouxX", c)) { if (strchr("diouxX", c)) {
r = snprintf(b, size, format, (int)n); snprintf(g_buf, MAXVARFMT, format, (int)n);
} else if (strchr("eEfFgGaA", c)) { } else if (strchr("eEfFgGaA", c)) {
r = snprintf(b, size, format, n); snprintf(g_buf, MAXVARFMT, format, n);
} else { } else {
syntax_error(EMSG_INV_FMT); syntax_error(EMSG_INV_FMT);
} }
} }
return r;
} }
static xhash *iamarray(var *a) static xhash *iamarray(var *a)
@ -999,7 +997,7 @@ static const char *getvar_s(var *v)
{ {
/* if v is numeric and has no cached string, convert it to string */ /* if v is numeric and has no cached string, convert it to string */
if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) { if ((v->type & (VF_NUMBER | VF_CACHED)) == VF_NUMBER) {
fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[CONVFMT]), v->number, TRUE); fmt_num(getvar_s(intvar[CONVFMT]), v->number);
v->string = xstrdup(g_buf); v->string = xstrdup(g_buf);
v->type |= VF_CACHED; v->type |= VF_CACHED;
} }
@ -2315,12 +2313,9 @@ static int awk_getline(rstream *rsm, var *v)
#endif #endif
static char *awk_printf(node *n, int *len) static char *awk_printf(node *n, int *len)
{ {
char *b = NULL; char *b;
char *fmt, *s, *f; char *fmt, *f;
const char *s1; int i;
int i, j, incr, bsize;
char c, c1;
var *arg;
//tmpvar = nvalloc(1); //tmpvar = nvalloc(1);
#define TMPVAR (&G.awk_printf__tmpvar) #define TMPVAR (&G.awk_printf__tmpvar)
@ -2333,8 +2328,14 @@ static char *awk_printf(node *n, int *len)
// to evaluate() potentially recursing into another awk_printf() can't // to evaluate() potentially recursing into another awk_printf() can't
// mangle the value. // mangle the value.
b = NULL;
i = 0; i = 0;
while (*f) { while (*f) { /* "print one format spec" loop */
char *s;
char c;
char sv;
var *arg;
s = f; s = f;
while (*f && (*f != '%' || *++f == '%')) while (*f && (*f != '%' || *++f == '%'))
f++; f++;
@ -2343,40 +2344,55 @@ static char *awk_printf(node *n, int *len)
syntax_error("%*x formats are not supported"); syntax_error("%*x formats are not supported");
f++; f++;
} }
incr = (f - s) + MAXVARFMT;
b = qrealloc(b, incr + i, &bsize);
c = *f; c = *f;
if (c != '\0') if (!c) {
f++; /* Tail of fmt with no percent chars,
c1 = *f; * or "....%" (percent seen, but no format specifier char found)
*/
goto tail;
}
sv = *++f;
*f = '\0'; *f = '\0';
arg = evaluate(nextarg(&n), TMPVAR); arg = evaluate(nextarg(&n), TMPVAR);
j = i; /* Result can be arbitrarily long. Example:
if (c == 'c' || !c) { * printf "%99999s", "BOOM"
i += sprintf(b+i, s, is_numeric(arg) ? */
if (c == 'c') {
s = xasprintf(s, is_numeric(arg) ?
(char)getvar_i(arg) : *getvar_s(arg)); (char)getvar_i(arg) : *getvar_s(arg));
} else if (c == 's') { } else if (c == 's') {
s1 = getvar_s(arg); s = xasprintf(s, getvar_s(arg));
b = qrealloc(b, incr+i+strlen(s1), &bsize);
i += sprintf(b+i, s, s1);
} else { } else {
i += fmt_num(b+i, incr, s, getvar_i(arg), FALSE); double d = getvar_i(arg);
if (strchr("diouxX", c)) {
//TODO: make it wider here (%x -> %llx etc)?
s = xasprintf(s, (int)d);
} else if (strchr("eEfFgGaA", c)) {
s = xasprintf(s, d);
} else {
syntax_error(EMSG_INV_FMT);
}
} }
*f = c1; *f = sv;
/* if there was an error while sprintf, return value is negative */ if (i == 0) {
if (i < j) b = s;
i = j; i = strlen(b);
continue;
}
tail:
b = xrealloc(b, i + strlen(s) + 1);
i = stpcpy(b + i, s) - b;
if (!c) /* tail? */
break;
free(s);
} }
free(fmt); free(fmt);
//nvfree(tmpvar, 1); //nvfree(tmpvar, 1);
#undef TMPVAR #undef TMPVAR
b = xrealloc(b, i + 1);
b[i] = '\0';
#if ENABLE_FEATURE_AWK_GNU_EXTENSIONS #if ENABLE_FEATURE_AWK_GNU_EXTENSIONS
if (len) if (len)
*len = i; *len = i;
@ -2936,8 +2952,8 @@ static var *evaluate(node *op, var *res)
for (;;) { for (;;) {
var *v = evaluate(nextarg(&op1), TMPVAR0); var *v = evaluate(nextarg(&op1), TMPVAR0);
if (v->type & VF_NUMBER) { if (v->type & VF_NUMBER) {
fmt_num(g_buf, MAXVARFMT, getvar_s(intvar[OFMT]), fmt_num(getvar_s(intvar[OFMT]),
getvar_i(v), TRUE); getvar_i(v));
fputs(g_buf, F); fputs(g_buf, F);
} else { } else {
fputs(getvar_s(v), F); fputs(getvar_s(v), F);