commit 064d78b562c9670751c48673c6d1d171aff51a42 Author: Andrew J. Schorr Date: Tue Nov 14 14:28:48 2017 -0500 Fix field corruption when $0 is reassigned with open $n references. diff --git a/ChangeLog b/ChangeLog index a3e53e9..27adc35 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,15 @@ +2017-11-14 Andrew J. Schorr + + Fix corruption when $0 is reassigned while other NODEs have open + references to $n. Thanks to Eric Pruitt for + the bug report. + * field.c (purge_record): For each $n field variable, if valref > 1 + and it has not already been malloced, make a copy of the string, since + $0 is about to be reset. + * interpret.h (Op_store_field): We must call the assign function + before unref, since we must copy any non-malloced $n string values + before freeing $0. + 2017-11-09 Arnold D. Robbins * main.c (usage): Add a note to not post bugs in comp.lang.awk. diff --git a/field.c b/field.c index 5ab718d..5263cc6 100644 --- a/field.c +++ b/field.c @@ -341,14 +341,20 @@ static void purge_record() { int i; - NODE *n; NF = -1; for (i = 1; i <= parse_high_water; i++) { - assert((fields_arr[i]->flags & MALLOC) == 0 - ? fields_arr[i]->valref == 1 - : true); - unref(fields_arr[i]); + NODE *n; + NODE *r = fields_arr[i]; + if ((r->flags & MALLOC) == 0 && r->valref > 1) { + /* This can and does happen. We must copy the string! */ + const char *save = r->stptr; + emalloc(r->stptr, char *, r->stlen + 1, "purge_record"); + memcpy(r->stptr, save, r->stlen); + r->stptr[r->stlen] = '\0'; + r->flags |= MALLOC; + } + unref(r); getnode(n); *n = *Null_field; fields_arr[i] = n; diff --git a/interpret.h b/interpret.h index 166a11e..4b140c2 100644 --- a/interpret.h +++ b/interpret.h @@ -699,11 +699,16 @@ mod: lhs = r_get_field(t1, & assign, false); decr_sp(); DEREF(t1); + /* + * N.B. We must call assign() before unref, since + * we may need to copy $n values before freeing the + * $0 buffer. + */ + assert(assign != NULL); + assign(); unref(*lhs); r = POP_SCALAR(); UNFIELD(*lhs, r); - assert(assign != NULL); - assign(); } break; diff --git a/test/ChangeLog b/test/ChangeLog index 4586d02..1e1cf7e 100644 --- a/test/ChangeLog +++ b/test/ChangeLog @@ -1,3 +1,10 @@ +2017-11-14 Andrew J. Schorr + + * Makefile.am (EXTRA_DIST): Add new tests setrec0 and setrec1. + (BASIC_TESTS): Add setrec0 and setrec1. + * setrec0.awk, setrec0.in, setrec0.ok: New files. + * setrec1.awk, setrec1.ok: New files. + 2017-11-10 Arnold D. Robbins * badargs.ok: Updated after code change. diff --git a/test/Makefile.am b/test/Makefile.am index cb17f9d..1438e08 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -1003,6 +1003,11 @@ EXTRA_DIST = \ sclforin.ok \ sclifin.awk \ sclifin.ok \ + setrec0.awk \ + setrec0.in \ + setrec0.ok \ + setrec1.awk \ + setrec1.ok \ shadow.awk \ shadow.ok \ shadowbuiltin.awk \ @@ -1229,7 +1234,8 @@ BASIC_TESTS = \ regexpbrack regexpbrack2 regexprange regrange reindops reparse resplit \ rri1 rs rscompat rsnul1nl rsnulbig rsnulbig2 rstest1 rstest2 rstest3 \ rstest4 rstest5 rswhite \ - scalar sclforin sclifin sigpipe1 sortempty sortglos splitargv splitarr \ + scalar sclforin sclifin setrec0 setrec1 \ + sigpipe1 sortempty sortglos splitargv splitarr \ splitdef splitvar splitwht status-close strcat1 strnum1 strnum2 strtod \ subamp subback subi18n subsepnm subslash substr swaplns synerr1 synerr2 \ tradanch tweakfld \ diff --git a/test/Makefile.in b/test/Makefile.in index 091927c..5a190d7 100644 --- a/test/Makefile.in +++ b/test/Makefile.in @@ -1261,6 +1261,11 @@ EXTRA_DIST = \ sclforin.ok \ sclifin.awk \ sclifin.ok \ + setrec0.awk \ + setrec0.in \ + setrec0.ok \ + setrec1.awk \ + setrec1.ok \ shadow.awk \ shadow.ok \ shadowbuiltin.awk \ @@ -1486,7 +1491,8 @@ BASIC_TESTS = \ regexpbrack regexpbrack2 regexprange regrange reindops reparse resplit \ rri1 rs rscompat rsnul1nl rsnulbig rsnulbig2 rstest1 rstest2 rstest3 \ rstest4 rstest5 rswhite \ - scalar sclforin sclifin sigpipe1 sortempty sortglos splitargv splitarr \ + scalar sclforin sclifin setrec0 setrec1 \ + sigpipe1 sortempty sortglos splitargv splitarr \ splitdef splitvar splitwht status-close strcat1 strnum1 strnum2 strtod \ subamp subback subi18n subsepnm subslash substr swaplns synerr1 synerr2 \ tradanch tweakfld \ @@ -3755,6 +3761,16 @@ sclifin: @AWKPATH="$(srcdir)" $(AWK) -f address@hidden >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ @-$(CMP) "$(srcdir)"/address@hidden _$@ && rm -f _$@ +setrec0: + @echo $@ + @AWKPATH="$(srcdir)" $(AWK) -f address@hidden < "$(srcdir)"/address@hidden >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) "$(srcdir)"/address@hidden _$@ && rm -f _$@ + +setrec1: + @echo $@ + @AWKPATH="$(srcdir)" $(AWK) -f address@hidden >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) "$(srcdir)"/address@hidden _$@ && rm -f _$@ + sigpipe1: @echo $@ @AWKPATH="$(srcdir)" $(AWK) -f address@hidden >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ diff --git a/test/Maketests b/test/Maketests index 8ae3f97..f64b864 100644 --- a/test/Maketests +++ b/test/Maketests @@ -887,6 +887,16 @@ sclifin: @AWKPATH="$(srcdir)" $(AWK) -f address@hidden >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ @-$(CMP) "$(srcdir)"/address@hidden _$@ && rm -f _$@ +setrec0: + @echo $@ + @AWKPATH="$(srcdir)" $(AWK) -f address@hidden < "$(srcdir)"/address@hidden >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) "$(srcdir)"/address@hidden _$@ && rm -f _$@ + +setrec1: + @echo $@ + @AWKPATH="$(srcdir)" $(AWK) -f address@hidden >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ + @-$(CMP) "$(srcdir)"/address@hidden _$@ && rm -f _$@ + sigpipe1: @echo $@ @AWKPATH="$(srcdir)" $(AWK) -f address@hidden >_$@ 2>&1 || echo EXIT CODE: $$? >>_$@ diff --git a/test/setrec0.awk b/test/setrec0.awk new file mode 100644 index 0000000..8d978aa --- /dev/null +++ b/test/setrec0.awk @@ -0,0 +1,8 @@ +function reassign(x, y) { + $0 = x + print y +} + +{ + reassign("larry", $1) +} diff --git a/test/setrec0.in b/test/setrec0.in new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/test/setrec0.in @@ -0,0 +1 @@ +hello diff --git a/test/setrec0.ok b/test/setrec0.ok new file mode 100644 index 0000000..ce01362 --- /dev/null +++ b/test/setrec0.ok @@ -0,0 +1 @@ +hello diff --git a/test/setrec1.awk b/test/setrec1.awk new file mode 100644 index 0000000..3da1aa1 --- /dev/null +++ b/test/setrec1.awk @@ -0,0 +1,9 @@ +function reassign(x, y) { + $0 = x + print y +} + +BEGIN { + $0 = substr("geronimo", 5, 3) + reassign(" 52", $1) +} diff --git a/test/setrec1.ok b/test/setrec1.ok new file mode 100644 index 0000000..6bb3f86 --- /dev/null +++ b/test/setrec1.ok @@ -0,0 +1 @@ +nim