awk: fix use-after-free in "$BIGNUM1 $BIGGERNUM2" concat op
Second reference to a field reallocs/moves Fields[] array, but first ref still tries to use the element where it was before move. function old new delta fsrealloc 94 106 +12 Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
This commit is contained in:
parent
83a4967e50
commit
a885ce1af0
@ -1745,12 +1745,22 @@ static char* qrealloc(char *b, int n, int *size)
|
|||||||
/* resize field storage space */
|
/* resize field storage space */
|
||||||
static void fsrealloc(int size)
|
static void fsrealloc(int size)
|
||||||
{
|
{
|
||||||
int i;
|
int i, newsize;
|
||||||
|
|
||||||
if (size >= maxfields) {
|
if (size >= maxfields) {
|
||||||
|
/* Sanity cap, easier than catering for overflows */
|
||||||
|
if (size > 0xffffff)
|
||||||
|
bb_die_memory_exhausted();
|
||||||
|
|
||||||
i = maxfields;
|
i = maxfields;
|
||||||
maxfields = size + 16;
|
maxfields = size + 16;
|
||||||
Fields = xrealloc(Fields, maxfields * sizeof(Fields[0]));
|
|
||||||
|
newsize = maxfields * sizeof(Fields[0]);
|
||||||
|
debug_printf_eval("fsrealloc: xrealloc(%p, %u)\n", Fields, newsize);
|
||||||
|
Fields = xrealloc(Fields, newsize);
|
||||||
|
debug_printf_eval("fsrealloc: Fields=%p..%p\n", Fields, (char*)Fields + newsize - 1);
|
||||||
|
/* ^^^ did Fields[] move? debug aid for L.v getting "upstaged" by R.v in evaluate() */
|
||||||
|
|
||||||
for (; i < maxfields; i++) {
|
for (; i < maxfields; i++) {
|
||||||
Fields[i].type = VF_SPECIAL;
|
Fields[i].type = VF_SPECIAL;
|
||||||
Fields[i].string = NULL;
|
Fields[i].string = NULL;
|
||||||
@ -2614,20 +2624,30 @@ static var *evaluate(node *op, var *res)
|
|||||||
/* execute inevitable things */
|
/* execute inevitable things */
|
||||||
if (opinfo & OF_RES1)
|
if (opinfo & OF_RES1)
|
||||||
L.v = evaluate(op1, v1);
|
L.v = evaluate(op1, v1);
|
||||||
if (opinfo & OF_RES2)
|
|
||||||
R.v = evaluate(op->r.n, v1+1);
|
|
||||||
if (opinfo & OF_STR1) {
|
if (opinfo & OF_STR1) {
|
||||||
L.s = getvar_s(L.v);
|
L.s = getvar_s(L.v);
|
||||||
debug_printf_eval("L.s:'%s'\n", L.s);
|
debug_printf_eval("L.s:'%s'\n", L.s);
|
||||||
}
|
}
|
||||||
if (opinfo & OF_STR2) {
|
|
||||||
R.s = getvar_s(R.v);
|
|
||||||
debug_printf_eval("R.s:'%s'\n", R.s);
|
|
||||||
}
|
|
||||||
if (opinfo & OF_NUM1) {
|
if (opinfo & OF_NUM1) {
|
||||||
L_d = getvar_i(L.v);
|
L_d = getvar_i(L.v);
|
||||||
debug_printf_eval("L_d:%f\n", L_d);
|
debug_printf_eval("L_d:%f\n", L_d);
|
||||||
}
|
}
|
||||||
|
/* NB: Must get string/numeric values of L (done above)
|
||||||
|
* _before_ evaluate()'ing R.v: if both L and R are $NNNs,
|
||||||
|
* and right one is large, then L.v points to Fields[NNN1],
|
||||||
|
* second evaluate() reallocates and moves (!) Fields[],
|
||||||
|
* R.v points to Fields[NNN2] but L.v now points to freed mem!
|
||||||
|
* (Seen trying to evaluate "$444 $44444")
|
||||||
|
*/
|
||||||
|
if (opinfo & OF_RES2) {
|
||||||
|
R.v = evaluate(op->r.n, v1+1);
|
||||||
|
//TODO: L.v may be invalid now, set L.v to NULL to catch bugs?
|
||||||
|
//L.v = NULL;
|
||||||
|
}
|
||||||
|
if (opinfo & OF_STR2) {
|
||||||
|
R.s = getvar_s(R.v);
|
||||||
|
debug_printf_eval("R.s:'%s'\n", R.s);
|
||||||
|
}
|
||||||
|
|
||||||
debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK));
|
debug_printf_eval("switch(0x%x)\n", XC(opinfo & OPCLSMASK));
|
||||||
switch (XC(opinfo & OPCLSMASK)) {
|
switch (XC(opinfo & OPCLSMASK)) {
|
||||||
@ -2636,6 +2656,7 @@ static var *evaluate(node *op, var *res)
|
|||||||
|
|
||||||
/* test pattern */
|
/* test pattern */
|
||||||
case XC( OC_TEST ):
|
case XC( OC_TEST ):
|
||||||
|
debug_printf_eval("TEST\n");
|
||||||
if ((op1->info & OPCLSMASK) == OC_COMMA) {
|
if ((op1->info & OPCLSMASK) == OC_COMMA) {
|
||||||
/* it's range pattern */
|
/* it's range pattern */
|
||||||
if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
|
if ((opinfo & OF_CHECKED) || ptest(op1->l.n)) {
|
||||||
@ -2653,25 +2674,32 @@ static var *evaluate(node *op, var *res)
|
|||||||
|
|
||||||
/* just evaluate an expression, also used as unconditional jump */
|
/* just evaluate an expression, also used as unconditional jump */
|
||||||
case XC( OC_EXEC ):
|
case XC( OC_EXEC ):
|
||||||
|
debug_printf_eval("EXEC\n");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* branch, used in if-else and various loops */
|
/* branch, used in if-else and various loops */
|
||||||
case XC( OC_BR ):
|
case XC( OC_BR ):
|
||||||
|
debug_printf_eval("BR\n");
|
||||||
op = istrue(L.v) ? op->a.n : op->r.n;
|
op = istrue(L.v) ? op->a.n : op->r.n;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* initialize for-in loop */
|
/* initialize for-in loop */
|
||||||
case XC( OC_WALKINIT ):
|
case XC( OC_WALKINIT ):
|
||||||
|
debug_printf_eval("WALKINIT\n");
|
||||||
hashwalk_init(L.v, iamarray(R.v));
|
hashwalk_init(L.v, iamarray(R.v));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
/* get next array item */
|
/* get next array item */
|
||||||
case XC( OC_WALKNEXT ):
|
case XC( OC_WALKNEXT ):
|
||||||
|
debug_printf_eval("WALKNEXT\n");
|
||||||
op = hashwalk_next(L.v) ? op->a.n : op->r.n;
|
op = hashwalk_next(L.v) ? op->a.n : op->r.n;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_PRINT ):
|
case XC( OC_PRINT ):
|
||||||
case XC( OC_PRINTF ): {
|
debug_printf_eval("PRINT /\n");
|
||||||
|
case XC( OC_PRINTF ):
|
||||||
|
debug_printf_eval("PRINTF\n");
|
||||||
|
{
|
||||||
FILE *F = stdout;
|
FILE *F = stdout;
|
||||||
IF_FEATURE_AWK_GNU_EXTENSIONS(int len;)
|
IF_FEATURE_AWK_GNU_EXTENSIONS(int len;)
|
||||||
|
|
||||||
@ -2726,22 +2754,28 @@ static var *evaluate(node *op, var *res)
|
|||||||
/* case XC( OC_DELETE ): - moved to happen before arg evaluation */
|
/* case XC( OC_DELETE ): - moved to happen before arg evaluation */
|
||||||
|
|
||||||
case XC( OC_NEWSOURCE ):
|
case XC( OC_NEWSOURCE ):
|
||||||
|
debug_printf_eval("NEWSOURCE\n");
|
||||||
g_progname = op->l.new_progname;
|
g_progname = op->l.new_progname;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_RETURN ):
|
case XC( OC_RETURN ):
|
||||||
|
debug_printf_eval("RETURN\n");
|
||||||
copyvar(res, L.v);
|
copyvar(res, L.v);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_NEXTFILE ):
|
case XC( OC_NEXTFILE ):
|
||||||
|
debug_printf_eval("NEXTFILE\n");
|
||||||
nextfile = TRUE;
|
nextfile = TRUE;
|
||||||
case XC( OC_NEXT ):
|
case XC( OC_NEXT ):
|
||||||
|
debug_printf_eval("NEXT\n");
|
||||||
nextrec = TRUE;
|
nextrec = TRUE;
|
||||||
case XC( OC_DONE ):
|
case XC( OC_DONE ):
|
||||||
|
debug_printf_eval("DONE\n");
|
||||||
clrvar(res);
|
clrvar(res);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_EXIT ):
|
case XC( OC_EXIT ):
|
||||||
|
debug_printf_eval("EXIT\n");
|
||||||
awk_exit(L_d);
|
awk_exit(L_d);
|
||||||
|
|
||||||
/* -- recursive node type -- */
|
/* -- recursive node type -- */
|
||||||
@ -2761,15 +2795,18 @@ static var *evaluate(node *op, var *res)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_IN ):
|
case XC( OC_IN ):
|
||||||
|
debug_printf_eval("IN\n");
|
||||||
setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
|
setvar_i(res, hash_search(iamarray(R.v), L.s) ? 1 : 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_REGEXP ):
|
case XC( OC_REGEXP ):
|
||||||
|
debug_printf_eval("REGEXP\n");
|
||||||
op1 = op;
|
op1 = op;
|
||||||
L.s = getvar_s(intvar[F0]);
|
L.s = getvar_s(intvar[F0]);
|
||||||
goto re_cont;
|
goto re_cont;
|
||||||
|
|
||||||
case XC( OC_MATCH ):
|
case XC( OC_MATCH ):
|
||||||
|
debug_printf_eval("MATCH\n");
|
||||||
op1 = op->r.n;
|
op1 = op->r.n;
|
||||||
re_cont:
|
re_cont:
|
||||||
{
|
{
|
||||||
@ -2795,6 +2832,7 @@ static var *evaluate(node *op, var *res)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_TERNARY ):
|
case XC( OC_TERNARY ):
|
||||||
|
debug_printf_eval("TERNARY\n");
|
||||||
if ((op->r.n->info & OPCLSMASK) != OC_COLON)
|
if ((op->r.n->info & OPCLSMASK) != OC_COLON)
|
||||||
syntax_error(EMSG_POSSIBLE_ERROR);
|
syntax_error(EMSG_POSSIBLE_ERROR);
|
||||||
res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
|
res = evaluate(istrue(L.v) ? op->r.n->l.n : op->r.n->r.n, res);
|
||||||
@ -2803,6 +2841,7 @@ static var *evaluate(node *op, var *res)
|
|||||||
case XC( OC_FUNC ): {
|
case XC( OC_FUNC ): {
|
||||||
var *vbeg, *v;
|
var *vbeg, *v;
|
||||||
const char *sv_progname;
|
const char *sv_progname;
|
||||||
|
debug_printf_eval("FUNC\n");
|
||||||
|
|
||||||
/* The body might be empty, still has to eval the args */
|
/* The body might be empty, still has to eval the args */
|
||||||
if (!op->r.n->info && !op->r.f->body.first)
|
if (!op->r.n->info && !op->r.f->body.first)
|
||||||
@ -2832,7 +2871,10 @@ static var *evaluate(node *op, var *res)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case XC( OC_GETLINE ):
|
case XC( OC_GETLINE ):
|
||||||
case XC( OC_PGETLINE ): {
|
debug_printf_eval("GETLINE /\n");
|
||||||
|
case XC( OC_PGETLINE ):
|
||||||
|
debug_printf_eval("PGETLINE\n");
|
||||||
|
{
|
||||||
rstream *rsm;
|
rstream *rsm;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@ -2873,6 +2915,7 @@ static var *evaluate(node *op, var *res)
|
|||||||
/* simple builtins */
|
/* simple builtins */
|
||||||
case XC( OC_FBLTIN ): {
|
case XC( OC_FBLTIN ): {
|
||||||
double R_d = R_d; /* for compiler */
|
double R_d = R_d; /* for compiler */
|
||||||
|
debug_printf_eval("FBLTIN\n");
|
||||||
|
|
||||||
switch (opn) {
|
switch (opn) {
|
||||||
case F_in:
|
case F_in:
|
||||||
@ -2986,14 +3029,18 @@ static var *evaluate(node *op, var *res)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case XC( OC_BUILTIN ):
|
case XC( OC_BUILTIN ):
|
||||||
|
debug_printf_eval("BUILTIN\n");
|
||||||
res = exec_builtin(op, res);
|
res = exec_builtin(op, res);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_SPRINTF ):
|
case XC( OC_SPRINTF ):
|
||||||
|
debug_printf_eval("SPRINTF\n");
|
||||||
setvar_p(res, awk_printf(op1, NULL));
|
setvar_p(res, awk_printf(op1, NULL));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_UNARY ): {
|
case XC( OC_UNARY ):
|
||||||
|
debug_printf_eval("UNARY\n");
|
||||||
|
{
|
||||||
double Ld, R_d;
|
double Ld, R_d;
|
||||||
|
|
||||||
Ld = R_d = getvar_i(R.v);
|
Ld = R_d = getvar_i(R.v);
|
||||||
@ -3023,7 +3070,9 @@ static var *evaluate(node *op, var *res)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case XC( OC_FIELD ): {
|
case XC( OC_FIELD ):
|
||||||
|
debug_printf_eval("FIELD\n");
|
||||||
|
{
|
||||||
int i = (int)getvar_i(R.v);
|
int i = (int)getvar_i(R.v);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
syntax_error(EMSG_NEGATIVE_FIELD);
|
syntax_error(EMSG_NEGATIVE_FIELD);
|
||||||
@ -3040,8 +3089,10 @@ static var *evaluate(node *op, var *res)
|
|||||||
|
|
||||||
/* concatenation (" ") and index joining (",") */
|
/* concatenation (" ") and index joining (",") */
|
||||||
case XC( OC_CONCAT ):
|
case XC( OC_CONCAT ):
|
||||||
|
debug_printf_eval("CONCAT /\n");
|
||||||
case XC( OC_COMMA ): {
|
case XC( OC_COMMA ): {
|
||||||
const char *sep = "";
|
const char *sep = "";
|
||||||
|
debug_printf_eval("COMMA\n");
|
||||||
if ((opinfo & OPCLSMASK) == OC_COMMA)
|
if ((opinfo & OPCLSMASK) == OC_COMMA)
|
||||||
sep = getvar_s(intvar[SUBSEP]);
|
sep = getvar_s(intvar[SUBSEP]);
|
||||||
setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s));
|
setvar_p(res, xasprintf("%s%s%s", L.s, sep, R.s));
|
||||||
@ -3049,17 +3100,22 @@ static var *evaluate(node *op, var *res)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case XC( OC_LAND ):
|
case XC( OC_LAND ):
|
||||||
|
debug_printf_eval("LAND\n");
|
||||||
setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
|
setvar_i(res, istrue(L.v) ? ptest(op->r.n) : 0);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_LOR ):
|
case XC( OC_LOR ):
|
||||||
|
debug_printf_eval("LOR\n");
|
||||||
setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
|
setvar_i(res, istrue(L.v) ? 1 : ptest(op->r.n));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case XC( OC_BINARY ):
|
case XC( OC_BINARY ):
|
||||||
case XC( OC_REPLACE ): {
|
debug_printf_eval("BINARY /\n");
|
||||||
|
case XC( OC_REPLACE ):
|
||||||
|
debug_printf_eval("REPLACE\n");
|
||||||
|
{
|
||||||
double R_d = getvar_i(R.v);
|
double R_d = getvar_i(R.v);
|
||||||
debug_printf_eval("BINARY/REPLACE: R_d:%f opn:%c\n", R_d, opn);
|
debug_printf_eval("R_d:%f opn:%c\n", R_d, opn);
|
||||||
switch (opn) {
|
switch (opn) {
|
||||||
case '+':
|
case '+':
|
||||||
L_d += R_d;
|
L_d += R_d;
|
||||||
@ -3095,6 +3151,7 @@ static var *evaluate(node *op, var *res)
|
|||||||
case XC( OC_COMPARE ): {
|
case XC( OC_COMPARE ): {
|
||||||
int i = i; /* for compiler */
|
int i = i; /* for compiler */
|
||||||
double Ld;
|
double Ld;
|
||||||
|
debug_printf_eval("COMPARE\n");
|
||||||
|
|
||||||
if (is_numeric(L.v) && is_numeric(R.v)) {
|
if (is_numeric(L.v) && is_numeric(R.v)) {
|
||||||
Ld = getvar_i(L.v) - getvar_i(R.v);
|
Ld = getvar_i(L.v) - getvar_i(R.v);
|
||||||
|
Loading…
Reference in New Issue
Block a user