ref: 1ced7968ec649e8726ae82bfc98d71a92f11dcae
parent: a3b68e649d2da36d19d78106134d1114f06b56e1
author: Todd C. Miller <Todd.Miller@sudo.ws>
date: Wed May 1 10:36:53 EDT 2024
Store the Cell containing ARGV instead of the ARGV table itself. The underlying table (array) may change due to, for example, "delete ARGV". However, the Cell that stores ARGV should not change. Also remove the ENVtab global which is never used outside of envinit(). Fixes issue #229.
--- a/lib.c
+++ b/lib.c
@@ -335,14 +335,16 @@
char *getargv(int n) /* get ARGV[n] */
{+ Array *ap;
Cell *x;
char *s, temp[50];
- extern Array *ARGVtab;
+ extern Cell *ARGVcell;
+ ap = (Array *)ARGVcell->sval;
snprintf(temp, sizeof(temp), "%d", n);
- if (lookup(temp, ARGVtab) == NULL)
+ if (lookup(temp, ap) == NULL)
return NULL;
- x = setsymtab(temp, "", 0.0, STR, ARGVtab);
+ x = setsymtab(temp, "", 0.0, STR, ap);
s = getsval(x);
DPRINTF("getargv(%d) returns |%s|\n", n, s);return s;
--- a/testdir/T.argv
+++ b/testdir/T.argv
@@ -148,3 +148,17 @@
printf("ARGV[%d] is %s\n", i, ARGV[i])}' >foo2
diff foo1 foo2 || echo 'BAD: T.argv delete ARGV[2]'
+
+# deleting ARGV used to trigger a use-after-free crash when awk
+# iterates over it to read files.
+printf '' >foo1
+$awk 'BEGIN {+ delete ARGV
+ ARGV[0] = "awk"
+ ARGV[1] = "/dev/null"
+ ARGC = 2
+} {+ # this should not be executed
+ print "bad"
+}' foo bar baz >foo2
+diff foo1 foo2 || echo 'BAD: T.argv delete ARGV'
--- a/tran.c
+++ b/tran.c
@@ -57,8 +57,7 @@
Cell *ofsloc; /* OFS */
Cell *orsloc; /* ORS */
Cell *rsloc; /* RS */
-Array *ARGVtab; /* symbol table containing ARGV[...] */
-Array *ENVtab; /* symbol table containing ENVIRON[...] */
+Cell *ARGVcell; /* cell with symbol table containing ARGV[...] */
Cell *rstartloc; /* RSTART */
Cell *rlengthloc; /* RLENGTH */
Cell *subseploc; /* SUBSEP */
@@ -107,6 +106,7 @@
void arginit(int ac, char **av) /* set up ARGV and ARGC */
{+ Array *ap;
Cell *cp;
int i;
char temp[50];
@@ -113,30 +113,32 @@
ARGC = &setsymtab("ARGC", "", (Awkfloat) ac, NUM, symtab)->fval; cp = setsymtab("ARGV", "", 0.0, ARR, symtab);- ARGVtab = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
+ ap = makesymtab(NSYMTAB); /* could be (int) ARGC as well */
free(cp->sval);
- cp->sval = (char *) ARGVtab;
+ cp->sval = (char *) ap;
for (i = 0; i < ac; i++) {double result;
sprintf(temp, "%d", i);
if (is_number(*av, & result))
- setsymtab(temp, *av, result, STR|NUM, ARGVtab);
+ setsymtab(temp, *av, result, STR|NUM, ap);
else
- setsymtab(temp, *av, 0.0, STR, ARGVtab);
+ setsymtab(temp, *av, 0.0, STR, ap);
av++;
}
+ ARGVcell = cp;
}
void envinit(char **envp) /* set up ENVIRON variable */
{+ Array *ap;
Cell *cp;
char *p;
cp = setsymtab("ENVIRON", "", 0.0, ARR, symtab);- ENVtab = makesymtab(NSYMTAB);
+ ap = makesymtab(NSYMTAB);
free(cp->sval);
- cp->sval = (char *) ENVtab;
+ cp->sval = (char *) ap;
for ( ; *envp; envp++) {double result;
@@ -146,9 +148,9 @@
continue;
*p++ = 0; /* split into two strings at = */
if (is_number(p, & result))
- setsymtab(*envp, p, result, STR|NUM, ENVtab);
+ setsymtab(*envp, p, result, STR|NUM, ap);
else
- setsymtab(*envp, p, 0.0, STR, ENVtab);
+ setsymtab(*envp, p, 0.0, STR, ap);
p[-1] = '='; /* restore in case env is passed down to a shell */
}
}
--
⑨