Module Name: src
Committed By: rillig
Date: Mon May 8 10:24:07 UTC 2023
Modified Files:
src/usr.bin/make: for.c
src/usr.bin/make/unit-tests: directive-for-errors.exp
directive-for-errors.mk directive-for-escape.exp
directive-for-escape.mk directive-for.exp directive-for.mk
Log Message:
make: disallow characters like '$' in variable names in .for loops
Fixes PR 53146.
To generate a diff of this commit:
cvs rdiff -u -r1.172 -r1.173 src/usr.bin/make/for.c
cvs rdiff -u -r1.2 -r1.3 src/usr.bin/make/unit-tests/directive-for-errors.exp
cvs rdiff -u -r1.3 -r1.4 src/usr.bin/make/unit-tests/directive-for-errors.mk
cvs rdiff -u -r1.17 -r1.18 \
src/usr.bin/make/unit-tests/directive-for-escape.exp \
src/usr.bin/make/unit-tests/directive-for.mk
cvs rdiff -u -r1.16 -r1.17 \
src/usr.bin/make/unit-tests/directive-for-escape.mk
cvs rdiff -u -r1.14 -r1.15 src/usr.bin/make/unit-tests/directive-for.exp
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/usr.bin/make/for.c
diff -u src/usr.bin/make/for.c:1.172 src/usr.bin/make/for.c:1.173
--- src/usr.bin/make/for.c:1.172 Mon May 8 09:01:20 2023
+++ src/usr.bin/make/for.c Mon May 8 10:24:07 2023
@@ -1,4 +1,4 @@
-/* $NetBSD: for.c,v 1.172 2023/05/08 09:01:20 rillig Exp $ */
+/* $NetBSD: for.c,v 1.173 2023/05/08 10:24:07 rillig Exp $ */
/*
* Copyright (c) 1992, The Regents of the University of California.
@@ -58,7 +58,7 @@
#include "make.h"
/* "@(#)for.c 8.1 (Berkeley) 6/6/93" */
-MAKE_RCSID("$NetBSD: for.c,v 1.172 2023/05/08 09:01:20 rillig Exp $");
+MAKE_RCSID("$NetBSD: for.c,v 1.173 2023/05/08 10:24:07 rillig Exp $");
typedef struct ForLoop {
@@ -139,6 +139,13 @@ ForLoop_Details(ForLoop *f)
}
static bool
+IsValidInVarname(char c)
+{
+ return c != '$' && c != ':' && c != '\\' &&
+ c != '(' && c != '{' && c != ')' && c != '}';
+}
+
+static bool
ForLoop_ParseVarnames(ForLoop *f, const char **pp)
{
const char *p = *pp;
@@ -152,12 +159,15 @@ ForLoop_ParseVarnames(ForLoop *f, const
return false;
}
- /*
- * XXX: This allows arbitrary variable names;
- * see directive-for.mk.
- */
- for (len = 1; p[len] != '\0' && !ch_isspace(p[len]); len++)
- continue;
+ for (len = 0; p[len] != '\0' && !ch_isspace(p[len]); len++) {
+ if (!IsValidInVarname(p[len])) {
+ Parse_Error(PARSE_FATAL,
+ "invalid character '%c' "
+ "in .for loop variable name",
+ p[len]);
+ return false;
+ }
+ }
if (len == 2 && p[0] == 'i' && p[1] == 'n') {
p += 2;
Index: src/usr.bin/make/unit-tests/directive-for-errors.exp
diff -u src/usr.bin/make/unit-tests/directive-for-errors.exp:1.2 src/usr.bin/make/unit-tests/directive-for-errors.exp:1.3
--- src/usr.bin/make/unit-tests/directive-for-errors.exp:1.2 Tue Feb 23 15:19:41 2021
+++ src/usr.bin/make/unit-tests/directive-for-errors.exp Mon May 8 10:24:07 2023
@@ -4,19 +4,20 @@ make: "directive-for-errors.mk" line 9:
make: "directive-for-errors.mk" line 19: Unknown directive "for"
make: "directive-for-errors.mk" line 20: warning:
make: "directive-for-errors.mk" line 21: for-less endfor
-make: "directive-for-errors.mk" line 37: Dollar $ 1 1 and backslash 2 2 2.
-make: "directive-for-errors.mk" line 37: Dollar $ 3 3 and backslash 4 4 4.
-make: "directive-for-errors.mk" line 43: no iteration variables in for
-make: "directive-for-errors.mk" line 47: warning: Should not be reached.
-make: "directive-for-errors.mk" line 48: for-less endfor
-make: "directive-for-errors.mk" line 53: Wrong number of words (5) in .for substitution list with 3 variables
-make: "directive-for-errors.mk" line 64: missing `in' in for
-make: "directive-for-errors.mk" line 66: warning: Should not be reached.
-make: "directive-for-errors.mk" line 67: for-less endfor
-make: "directive-for-errors.mk" line 73: Unknown modifier "Z"
-make: "directive-for-errors.mk" line 74: warning: Should not be reached.
-make: "directive-for-errors.mk" line 74: warning: Should not be reached.
-make: "directive-for-errors.mk" line 74: warning: Should not be reached.
+make: "directive-for-errors.mk" line 34: invalid character '$' in .for loop variable name
+make: "directive-for-errors.mk" line 35: Dollar $ and backslash backslash backslash backslash.
+make: "directive-for-errors.mk" line 36: for-less endfor
+make: "directive-for-errors.mk" line 41: no iteration variables in for
+make: "directive-for-errors.mk" line 45: warning: Should not be reached.
+make: "directive-for-errors.mk" line 46: for-less endfor
+make: "directive-for-errors.mk" line 51: Wrong number of words (5) in .for substitution list with 3 variables
+make: "directive-for-errors.mk" line 63: missing `in' in for
+make: "directive-for-errors.mk" line 65: warning: Should not be reached.
+make: "directive-for-errors.mk" line 66: for-less endfor
+make: "directive-for-errors.mk" line 72: Unknown modifier "Z"
+make: "directive-for-errors.mk" line 73: warning: Should not be reached.
+make: "directive-for-errors.mk" line 73: warning: Should not be reached.
+make: "directive-for-errors.mk" line 73: warning: Should not be reached.
make: Fatal errors encountered -- cannot continue
make: stopped in unit-tests
exit status 1
Index: src/usr.bin/make/unit-tests/directive-for-errors.mk
diff -u src/usr.bin/make/unit-tests/directive-for-errors.mk:1.3 src/usr.bin/make/unit-tests/directive-for-errors.mk:1.4
--- src/usr.bin/make/unit-tests/directive-for-errors.mk:1.3 Sun Apr 4 10:13:09 2021
+++ src/usr.bin/make/unit-tests/directive-for-errors.mk Mon May 8 10:24:07 2023
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-errors.mk,v 1.3 2021/04/04 10:13:09 rillig Exp $
+# $NetBSD: directive-for-errors.mk,v 1.4 2023/05/08 10:24:07 rillig Exp $
#
# Tests for error handling in .for loops.
@@ -20,17 +20,15 @@
. warning ${i}
.endfor
-# As of 2020-12-31, the variable name can be an arbitrary word, it just needs
-# to be separated by whitespace. Even '$' and '\' are valid variable names,
-# which is not useful in practice.
+# Before for.c 1.173 from 2023-05-08, the variable name could be an arbitrary
+# word, it only needed to be separated by whitespace. Even '$' and '\' were
+# valid variable names, which was not useful in practice.
#
-# The '$$' is not replaced with the values '1' or '3' from the .for loop,
-# instead it is kept as-is, and when the .info directive expands its argument,
-# each '$$' gets replaced with a single '$'. The "long variable expression"
-# ${$} gets replaced though, even though this would be a parse error everywhere
-# outside a .for loop.
-#
-# The '\' on the other hand is treated as a normal variable name.
+# The '$$' was not replaced with the values '1' or '3' from the .for loop,
+# instead it was kept as-is, and when the .info directive expanded its
+# argument, each '$$' got replaced with a single '$'. The "long variable
+# expression" ${$} got replaced though, even though this would be a parse
+# error everywhere outside a .for loop.
${:U\$}= dollar # see whether the "variable" '$' is local
${:U\\}= backslash # see whether the "variable" '\' is local
.for $ \ in 1 2 3 4
@@ -38,8 +36,8 @@ ${:U\\}= backslash # see whether the "va
.endfor
# If there are no variables, there is no point in expanding the .for loop
-# since this would end up in an endless loop, each time consuming 0 of the
-# 3 values.
+# since this would end up in an endless loop, consuming 0 of the 3 values in
+# each iteration.
.for in 1 2 3
# XXX: This should not be reached. It should be skipped, as already done
# when the number of values is not a multiple of the number of variables,
@@ -49,7 +47,7 @@ ${:U\\}= backslash # see whether the "va
# There are 3 variables and 5 values. These 5 values cannot be split evenly
# among the variables, therefore the loop is not expanded at all, it is
-# rather skipped.
+# skipped instead.
.for a b c in 1 2 3 4 5
. warning Should not be reached.
.endfor
@@ -61,7 +59,8 @@ ${:U\\}= backslash # see whether the "va
.endfor
# A missing 'in' should parse the .for loop but skip the body.
-.for i : k
+# expect+1: missing `in' in for
+.for i over k
# XXX: As of 2020-12-31, this line is reached once.
. warning Should not be reached.
.endfor
Index: src/usr.bin/make/unit-tests/directive-for-escape.exp
diff -u src/usr.bin/make/unit-tests/directive-for-escape.exp:1.17 src/usr.bin/make/unit-tests/directive-for-escape.exp:1.18
--- src/usr.bin/make/unit-tests/directive-for-escape.exp:1.17 Sun Jun 12 16:09:21 2022
+++ src/usr.bin/make/unit-tests/directive-for-escape.exp Mon May 8 10:24:07 2023
@@ -49,14 +49,12 @@ For: end for 1
For: loop body:
. info ${:U\$}
make: "directive-for-escape.mk" line 121: $
-For: end for 1
-For: loop body:
-. info ${NUMBERS} ${:Ureplaced}
-make: "directive-for-escape.mk" line 129: one two three replaced
-For: end for 1
-For: loop body:
-. info ${:Ureplaced}
-make: "directive-for-escape.mk" line 139: replaced
+make: "directive-for-escape.mk" line 129: invalid character ':' in .for loop variable name
+make: "directive-for-escape.mk" line 130: one two three one three
+make: "directive-for-escape.mk" line 131: for-less endfor
+make: "directive-for-escape.mk" line 139: invalid character '}' in .for loop variable name
+make: "directive-for-escape.mk" line 140: one.c
+make: "directive-for-escape.mk" line 141: for-less endfor
For: end for 1
For: loop body:
. info . $$i: ${:Uinner}
@@ -69,46 +67,44 @@ For: loop body:
. info . $${i2}: ${i2}
. info . $${i,}: ${i,}
. info . adjacent: ${:Uinner}${:Uinner}${:Uinner:M*}${:Uinner}
-make: "directive-for-escape.mk" line 147: . $i: inner
-make: "directive-for-escape.mk" line 148: . ${i}: inner
-make: "directive-for-escape.mk" line 149: . ${i:M*}: inner
-make: "directive-for-escape.mk" line 150: . $(i): inner
-make: "directive-for-escape.mk" line 151: . $(i:M*): inner
-make: "directive-for-escape.mk" line 152: . ${i${:U}}: outer
-make: "directive-for-escape.mk" line 153: . ${i\}}: inner}
-make: "directive-for-escape.mk" line 154: . ${i2}: two
-make: "directive-for-escape.mk" line 155: . ${i,}: comma
-make: "directive-for-escape.mk" line 156: . adjacent: innerinnerinnerinner
-For: end for 1
-For: loop body:
-. info eight $$$$$$$$ and no cents.
-. info eight ${:Udollar}${:Udollar}${:Udollar}${:Udollar} and no cents.
-make: "directive-for-escape.mk" line 164: eight $$$$ and no cents.
-make: "directive-for-escape.mk" line 165: eight dollardollardollardollar and no cents.
-make: "directive-for-escape.mk" line 174: eight and no cents.
+make: "directive-for-escape.mk" line 148: . $i: inner
+make: "directive-for-escape.mk" line 149: . ${i}: inner
+make: "directive-for-escape.mk" line 150: . ${i:M*}: inner
+make: "directive-for-escape.mk" line 151: . $(i): inner
+make: "directive-for-escape.mk" line 152: . $(i:M*): inner
+make: "directive-for-escape.mk" line 153: . ${i${:U}}: outer
+make: "directive-for-escape.mk" line 154: . ${i\}}: inner}
+make: "directive-for-escape.mk" line 155: . ${i2}: two
+make: "directive-for-escape.mk" line 156: . ${i,}: comma
+make: "directive-for-escape.mk" line 157: . adjacent: innerinnerinnerinner
+make: "directive-for-escape.mk" line 165: invalid character '$' in .for loop variable name
+make: "directive-for-escape.mk" line 166: eight $$$$ and no cents.
+make: "directive-for-escape.mk" line 167: eight and no cents.
+make: "directive-for-escape.mk" line 168: for-less endfor
+make: "directive-for-escape.mk" line 176: eight and no cents.
For: end for 1
-make: "directive-for-escape.mk" line 181: newline in .for value
-make: "directive-for-escape.mk" line 181: newline in .for value
+make: "directive-for-escape.mk" line 183: newline in .for value
+make: "directive-for-escape.mk" line 183: newline in .for value
For: loop body:
. info short: ${:U" "}
. info long: ${:U" "}
-make: "directive-for-escape.mk" line 182: short: " "
-make: "directive-for-escape.mk" line 183: long: " "
+make: "directive-for-escape.mk" line 184: short: " "
+make: "directive-for-escape.mk" line 185: long: " "
For: end for 1
For: loop body:
For: end for 1
-Parse_PushInput: .for loop in directive-for-escape.mk, line 196
-make: "directive-for-escape.mk" line 196: newline in .for value
- in .for loop from directive-for-escape.mk:196 with i = "
+Parse_PushInput: .for loop in directive-for-escape.mk, line 198
+make: "directive-for-escape.mk" line 198: newline in .for value
+ in .for loop from directive-for-escape.mk:198 with i = "
"
For: loop body:
: ${:U" "}
SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk'
-Parsing line 197: : ${:U" "}
+Parsing line 199: : ${:U" "}
ParseDependency(: " ")
-ParseEOF: returning to file directive-for-escape.mk, line 199
+ParseEOF: returning to file directive-for-escape.mk, line 201
SetFilenameVars: ${.PARSEDIR} = <some-dir> ${.PARSEFILE} = `directive-for-escape.mk'
-Parsing line 199: .MAKEFLAGS: -d0
+Parsing line 201: .MAKEFLAGS: -d0
ParseDependency(.MAKEFLAGS: -d0)
For: end for 1
For: loop body:
Index: src/usr.bin/make/unit-tests/directive-for.mk
diff -u src/usr.bin/make/unit-tests/directive-for.mk:1.17 src/usr.bin/make/unit-tests/directive-for.mk:1.18
--- src/usr.bin/make/unit-tests/directive-for.mk:1.17 Mon May 8 09:24:42 2023
+++ src/usr.bin/make/unit-tests/directive-for.mk Mon May 8 10:24:07 2023
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for.mk,v 1.17 2023/05/08 09:24:42 rillig Exp $
+# $NetBSD: directive-for.mk,v 1.18 2023/05/08 10:24:07 rillig Exp $
#
# Tests for the .for directive.
#
@@ -144,18 +144,18 @@ EXPANSION${plus}= value
# except for whitespace. This allows for creative side effects. Hopefully
# nobody is misusing this "feature".
var= outer
+# expect+1: invalid character ':' in .for loop variable name
.for var:Q in value "quoted"
-. info ${var} ${var:Q} ${var:Q:Q}
+. info <${var}> <${var:Q}> <${var:Q:Q}>
.endfor
# The short expression '$$' is preserved, the long expressions are
# substituted.
-# expect+2: $ value value
+# expect+1: invalid character '$' in .for loop variable name
.for $ in value
-. info $$ ${$} $($)
+. info <$$> <${$}> <$($)>
.endfor
# From https://gnats.netbsd.org/53146.
-# expect+3: <> <> <a>
-# expect+2: <> <> <b>
+# expect+1: invalid character '$' in .for loop variable name
.for $(FOO) in a b
. info <$(FOO)> <$(foo)> <$($(FOO))>
.endfor
Index: src/usr.bin/make/unit-tests/directive-for-escape.mk
diff -u src/usr.bin/make/unit-tests/directive-for-escape.mk:1.16 src/usr.bin/make/unit-tests/directive-for-escape.mk:1.17
--- src/usr.bin/make/unit-tests/directive-for-escape.mk:1.16 Sun Jun 12 16:09:21 2022
+++ src/usr.bin/make/unit-tests/directive-for-escape.mk Mon May 8 10:24:07 2023
@@ -1,4 +1,4 @@
-# $NetBSD: directive-for-escape.mk,v 1.16 2022/06/12 16:09:21 rillig Exp $
+# $NetBSD: directive-for-escape.mk,v 1.17 2023/05/08 10:24:07 rillig Exp $
#
# Test escaping of special characters in the iteration values of a .for loop.
# These values get expanded later using the :U variable modifier, and this
@@ -121,20 +121,21 @@ VALUES= begin<$${UNDEF:Ufallback:N{{{}}
. info ${i}
.endfor
-# As of 2020-12-31, the name of the iteration variable can even contain
-# colons, which then affects variable expressions having this exact modifier.
-# This is clearly an unintended side effect of the implementation.
+# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
+# could contain colons, which affected variable expressions having this exact
+# modifier. This possibility was neither intended nor documented.
NUMBERS= one two three
+# expect+1: invalid character ':' in .for loop variable name
.for NUMBERS:M*e in replaced
. info ${NUMBERS} ${NUMBERS:M*e}
.endfor
-# As of 2020-12-31, the name of the iteration variable can contain braces,
-# which gets even more surprising than colons, since it allows to replace
-# sequences of variable expressions. There is no practical use case for
-# this, though.
+# Before for.c 1.173 from 2023-05-08, the name of the iteration variable
+# could contain braces, which allowed to replace sequences of variable
+# expressions. This possibility was neither intended nor documented.
BASENAME= one
EXT= .c
+# expect+1: invalid character '}' in .for loop variable name
.for BASENAME}${EXT in replaced
. info ${BASENAME}${EXT}
.endfor
@@ -156,10 +157,11 @@ i,= comma
. info . adjacent: $i${i}${i:M*}$i
.endfor
-# The variable name can be a single '$' since there is no check on valid
-# variable names. ForLoop_SubstVarShort skips "stupid" variable names though,
-# but ForLoop_SubstVarLong naively parses the body of the loop, substituting
-# each '${$}' with an actual 'dollar'.
+# Before for.c 1.173 from 2023-05-08, the variable name could be a single '$'
+# since there was no check on valid variable names. ForLoop_SubstVarShort
+# skipped "stupid" variable names though, but ForLoop_SubstVarLong naively
+# parsed the body of the loop, substituting each '${$}' with an actual
+# '${:Udollar}'.
.for $ in dollar
. info eight $$$$$$$$ and no cents.
. info eight ${$}${$}${$}${$} and no cents.
Index: src/usr.bin/make/unit-tests/directive-for.exp
diff -u src/usr.bin/make/unit-tests/directive-for.exp:1.14 src/usr.bin/make/unit-tests/directive-for.exp:1.15
--- src/usr.bin/make/unit-tests/directive-for.exp:1.14 Mon May 8 09:24:42 2023
+++ src/usr.bin/make/unit-tests/directive-for.exp Mon May 8 10:24:07 2023
@@ -14,11 +14,15 @@ make: "directive-for.mk" line 140: {{}}
make: "directive-for.mk" line 140: )( )( )(
make: "directive-for.mk" line 140: ][ ][ ][
make: "directive-for.mk" line 140: }{ }{ }{
-make: "directive-for.mk" line 148: outer value value
-make: "directive-for.mk" line 148: outer "quoted" \"quoted\"
-make: "directive-for.mk" line 154: $ value value
-make: "directive-for.mk" line 160: <> <> <a>
-make: "directive-for.mk" line 160: <> <> <b>
+make: "directive-for.mk" line 148: invalid character ':' in .for loop variable name
+make: "directive-for.mk" line 149: <outer> <outer> <outer>
+make: "directive-for.mk" line 150: for-less endfor
+make: "directive-for.mk" line 154: invalid character '$' in .for loop variable name
+make: "directive-for.mk" line 155: <$> <> <>
+make: "directive-for.mk" line 156: for-less endfor
+make: "directive-for.mk" line 159: invalid character '$' in .for loop variable name
+make: "directive-for.mk" line 160: <> <> <>
+make: "directive-for.mk" line 161: for-less endfor
make: "directive-for.mk" line 166: Unknown modifier "Z"
make: "directive-for.mk" line 167: XXX: Not reached word1
make: "directive-for.mk" line 167: XXX: Not reached word3