Module Name: src Committed By: rillig Date: Sun Dec 5 15:20:13 UTC 2021
Modified Files: src/distrib/sets/lists/tests: mi src/usr.bin/make: var.c src/usr.bin/make/unit-tests: Makefile varmod-loop.exp varmod-loop.mk Added Files: src/usr.bin/make/unit-tests: varmod-loop-delete.exp varmod-loop-delete.mk Log Message: make: fix use-after-free in modifier ':@' Without memory allocator debugging, the newly added test doesn't show any obvious failure. With memory allocator debugging enabled, all make versions since 2016.02.27.16.20.06 crash with a segmentation fault. To generate a diff of this commit: cvs rdiff -u -r1.1173 -r1.1174 src/distrib/sets/lists/tests/mi cvs rdiff -u -r1.962 -r1.963 src/usr.bin/make/var.c cvs rdiff -u -r1.285 -r1.286 src/usr.bin/make/unit-tests/Makefile cvs rdiff -u -r0 -r1.1 src/usr.bin/make/unit-tests/varmod-loop-delete.exp \ src/usr.bin/make/unit-tests/varmod-loop-delete.mk cvs rdiff -u -r1.12 -r1.13 src/usr.bin/make/unit-tests/varmod-loop.exp cvs rdiff -u -r1.17 -r1.18 src/usr.bin/make/unit-tests/varmod-loop.mk Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/distrib/sets/lists/tests/mi diff -u src/distrib/sets/lists/tests/mi:1.1173 src/distrib/sets/lists/tests/mi:1.1174 --- src/distrib/sets/lists/tests/mi:1.1173 Sun Nov 28 16:20:13 2021 +++ src/distrib/sets/lists/tests/mi Sun Dec 5 15:20:13 2021 @@ -1,4 +1,4 @@ -# $NetBSD: mi,v 1.1173 2021/11/28 16:20:13 rillig Exp $ +# $NetBSD: mi,v 1.1174 2021/12/05 15:20:13 rillig Exp $ # # Note: don't delete entries from here - mark them as "obsolete" instead. # @@ -5985,6 +5985,8 @@ ./usr/tests/usr.bin/make/unit-tests/varmod-l-name-to-value.mk tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/make/unit-tests/varmod-localtime.exp tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/make/unit-tests/varmod-localtime.mk tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/make/unit-tests/varmod-loop-delete.exp tests-usr.bin-tests compattestfile,atf +./usr/tests/usr.bin/make/unit-tests/varmod-loop-delete.mk tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/make/unit-tests/varmod-loop-varname.exp tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/make/unit-tests/varmod-loop-varname.mk tests-usr.bin-tests compattestfile,atf ./usr/tests/usr.bin/make/unit-tests/varmod-loop.exp tests-usr.bin-tests compattestfile,atf Index: src/usr.bin/make/var.c diff -u src/usr.bin/make/var.c:1.962 src/usr.bin/make/var.c:1.963 --- src/usr.bin/make/var.c:1.962 Sun Dec 5 12:17:49 2021 +++ src/usr.bin/make/var.c Sun Dec 5 15:20:13 2021 @@ -1,4 +1,4 @@ -/* $NetBSD: var.c,v 1.962 2021/12/05 12:17:49 rillig Exp $ */ +/* $NetBSD: var.c,v 1.963 2021/12/05 15:20:13 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -140,7 +140,7 @@ #include "metachar.h" /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: var.c,v 1.962 2021/12/05 12:17:49 rillig Exp $"); +MAKE_RCSID("$NetBSD: var.c,v 1.963 2021/12/05 15:20:13 rillig Exp $"); /* * Variables are defined using one of the VAR=value assignments. Their @@ -496,6 +496,12 @@ Var_Delete(GNode *scope, const char *var DEBUG2(VAR, "%s:delete %s\n", scope->name, varname); v = he->value; + if (v->inUse) { + Parse_Error(PARSE_FATAL, + "Cannot delete variable \"%s\" while it is used.", + v->name.str); + return; + } if (v->exported) unsetenv(v->name.str); if (strcmp(v->name.str, MAKE_EXPORTED) == 0) Index: src/usr.bin/make/unit-tests/Makefile diff -u src/usr.bin/make/unit-tests/Makefile:1.285 src/usr.bin/make/unit-tests/Makefile:1.286 --- src/usr.bin/make/unit-tests/Makefile:1.285 Sun Dec 5 14:57:36 2021 +++ src/usr.bin/make/unit-tests/Makefile Sun Dec 5 15:20:13 2021 @@ -1,4 +1,4 @@ -# $NetBSD: Makefile,v 1.285 2021/12/05 14:57:36 rillig Exp $ +# $NetBSD: Makefile,v 1.286 2021/12/05 15:20:13 rillig Exp $ # # Unit tests for make(1) # @@ -351,6 +351,7 @@ TESTS+= varmod-indirect TESTS+= varmod-l-name-to-value TESTS+= varmod-localtime TESTS+= varmod-loop +TESTS+= varmod-loop-delete TESTS+= varmod-loop-varname TESTS+= varmod-match TESTS+= varmod-match-escape Index: src/usr.bin/make/unit-tests/varmod-loop.exp diff -u src/usr.bin/make/unit-tests/varmod-loop.exp:1.12 src/usr.bin/make/unit-tests/varmod-loop.exp:1.13 --- src/usr.bin/make/unit-tests/varmod-loop.exp:1.12 Sun Dec 5 15:01:04 2021 +++ src/usr.bin/make/unit-tests/varmod-loop.exp Sun Dec 5 15:20:13 2021 @@ -13,5 +13,4 @@ mod-loop-dollar:$3$: mod-loop-dollar:$${word}$$: mod-loop-dollar:$$5$$: mod-loop-dollar:$$${word}$$$: -: all: ' rest of the value' exit status 0 Index: src/usr.bin/make/unit-tests/varmod-loop.mk diff -u src/usr.bin/make/unit-tests/varmod-loop.mk:1.17 src/usr.bin/make/unit-tests/varmod-loop.mk:1.18 --- src/usr.bin/make/unit-tests/varmod-loop.mk:1.17 Sun Dec 5 15:01:04 2021 +++ src/usr.bin/make/unit-tests/varmod-loop.mk Sun Dec 5 15:20:13 2021 @@ -1,4 +1,4 @@ -# $NetBSD: varmod-loop.mk,v 1.17 2021/12/05 15:01:04 rillig Exp $ +# $NetBSD: varmod-loop.mk,v 1.18 2021/12/05 15:20:13 rillig Exp $ # # Tests for the :@var@...${var}...@ variable modifier. @@ -186,32 +186,4 @@ CMDLINE= global # needed for deleting t . error # 'CMDLINE' is gone now from all scopes .endif - -# A side effect of the modifier ':@' is that the loop variable is created as -# an actual variable in the current evaluation scope (Command/Global/target), -# and at the end of the loop, this variable is deleted. Before var.c 1.TODO -# from 2021-12-05, a variable could be deleted while it was in use, leading to -# a use-after-free bug. -# -# See Var_Parse, comment 'the value of the variable must not change'. - -# Set up the variable that deletes itself when it is evaluated. -VAR= ${:U:@VAR@@} rest of the value - -# In an assignment, the scope is 'Global'. Since the variable 'VAR' is -# defined in the global scope, it deletes itself. -EVAL:= ${:U rest of the value} #${VAR} # FIXME: use-after-free -.if ${EVAL} != " rest of the value" -. error -.endif - -VAR= ${:U:@VAR@@} rest of the value all: .PHONY - # In the command that is associated with a target, the scope is the - # one from the target. That scope only contains a few variables like - # '.TARGET', '.ALLSRC', '.IMPSRC'. Make does not expect that these - # variables get modified from the outside. - # - # There is no variable named 'VAR' in the local scope, so nothing - # happens. - : $@: '${VAR}' Added files: Index: src/usr.bin/make/unit-tests/varmod-loop-delete.exp diff -u /dev/null src/usr.bin/make/unit-tests/varmod-loop-delete.exp:1.1 --- /dev/null Sun Dec 5 15:20:13 2021 +++ src/usr.bin/make/unit-tests/varmod-loop-delete.exp Sun Dec 5 15:20:13 2021 @@ -0,0 +1,4 @@ +make: "varmod-loop-delete.mk" line 19: Cannot delete variable "VAR" while it is used. +make: Fatal errors encountered -- cannot continue +make: stopped in unit-tests +exit status 1 Index: src/usr.bin/make/unit-tests/varmod-loop-delete.mk diff -u /dev/null src/usr.bin/make/unit-tests/varmod-loop-delete.mk:1.1 --- /dev/null Sun Dec 5 15:20:13 2021 +++ src/usr.bin/make/unit-tests/varmod-loop-delete.mk Sun Dec 5 15:20:13 2021 @@ -0,0 +1,33 @@ +# $NetBSD: varmod-loop-delete.mk,v 1.1 2021/12/05 15:20:13 rillig Exp $ +# +# Tests for the variable modifier ':@', which as a side effect allows to +# delete an arbitrary variable. + +# A side effect of the modifier ':@' is that the loop variable is created as +# an actual variable in the current evaluation scope (Command/Global/target), +# and at the end of the loop, this variable is deleted. Before var.c 1.963 +# from 2021-12-05, a variable could be deleted while it was in use, leading to +# a use-after-free bug. +# +# See Var_Parse, comment 'the value of the variable must not change'. + +# Set up the variable that deletes itself when it is evaluated. +VAR= ${:U:@VAR@@} rest of the value + +# In an assignment, the scope is 'Global'. Since the variable 'VAR' is +# defined in the global scope, it deletes itself. +EVAL:= ${VAR} +.if ${EVAL} != " rest of the value" +. error +.endif + +VAR= ${:U:@VAR@@} rest of the value +all: .PHONY + # In the command that is associated with a target, the scope is the + # one from the target. That scope only contains a few variables like + # '.TARGET', '.ALLSRC', '.IMPSRC'. Make does not expect that these + # variables get modified from the outside. + # + # There is no variable named 'VAR' in the local scope, so nothing + # happens. + : $@: '${VAR}'