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}'

Reply via email to