make(1): document undefined behavior in Parse_IsVar Sigh. If only C could be compiled in strict mode that detects these out-of-bounds memory accesses.diff -r1.420 -r1.421 src/usr.bin/make/parse.c
(rillig)
--- src/usr.bin/make/parse.c 2020/11/01 00:24:57 1.420
+++ src/usr.bin/make/parse.c 2020/11/02 22:44:29 1.421
@@ -1,14 +1,14 @@ | @@ -1,14 +1,14 @@ | |||
1 | /* $NetBSD: parse.c,v 1.420 2020/11/01 00:24:57 rillig Exp $ */ | 1 | /* $NetBSD: parse.c,v 1.421 2020/11/02 22:44:29 rillig Exp $ */ | |
2 | 2 | |||
3 | /* | 3 | /* | |
4 | * Copyright (c) 1988, 1989, 1990, 1993 | 4 | * Copyright (c) 1988, 1989, 1990, 1993 | |
5 | * The Regents of the University of California. All rights reserved. | 5 | * The Regents of the University of California. All rights reserved. | |
6 | * | 6 | * | |
7 | * This code is derived from software contributed to Berkeley by | 7 | * This code is derived from software contributed to Berkeley by | |
8 | * Adam de Boor. | 8 | * Adam de Boor. | |
9 | * | 9 | * | |
10 | * Redistribution and use in source and binary forms, with or without | 10 | * Redistribution and use in source and binary forms, with or without | |
11 | * modification, are permitted provided that the following conditions | 11 | * modification, are permitted provided that the following conditions | |
12 | * are met: | 12 | * are met: | |
13 | * 1. Redistributions of source code must retain the above copyright | 13 | * 1. Redistributions of source code must retain the above copyright | |
14 | * notice, this list of conditions and the following disclaimer. | 14 | * notice, this list of conditions and the following disclaimer. | |
@@ -107,27 +107,27 @@ | @@ -107,27 +107,27 @@ | |||
107 | #ifndef MAP_FILE | 107 | #ifndef MAP_FILE | |
108 | #define MAP_FILE 0 | 108 | #define MAP_FILE 0 | |
109 | #endif | 109 | #endif | |
110 | #ifndef MAP_COPY | 110 | #ifndef MAP_COPY | |
111 | #define MAP_COPY MAP_PRIVATE | 111 | #define MAP_COPY MAP_PRIVATE | |
112 | #endif | 112 | #endif | |
113 | 113 | |||
114 | #include "make.h" | 114 | #include "make.h" | |
115 | #include "dir.h" | 115 | #include "dir.h" | |
116 | #include "job.h" | 116 | #include "job.h" | |
117 | #include "pathnames.h" | 117 | #include "pathnames.h" | |
118 | 118 | |||
119 | /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ | 119 | /* "@(#)parse.c 8.3 (Berkeley) 3/19/94" */ | |
120 | MAKE_RCSID("$NetBSD: parse.c,v 1.420 2020/11/01 00:24:57 rillig Exp $"); | 120 | MAKE_RCSID("$NetBSD: parse.c,v 1.421 2020/11/02 22:44:29 rillig Exp $"); | |
121 | 121 | |||
122 | /* types and constants */ | 122 | /* types and constants */ | |
123 | 123 | |||
124 | /* | 124 | /* | |
125 | * Structure for a file being read ("included file") | 125 | * Structure for a file being read ("included file") | |
126 | */ | 126 | */ | |
127 | typedef struct IFile { | 127 | typedef struct IFile { | |
128 | char *fname; /* name of file */ | 128 | char *fname; /* name of file */ | |
129 | Boolean fromForLoop; /* simulated .include by the .for loop */ | 129 | Boolean fromForLoop; /* simulated .include by the .for loop */ | |
130 | int lineno; /* current line number in file */ | 130 | int lineno; /* current line number in file */ | |
131 | int first_lineno; /* line number of start of text */ | 131 | int first_lineno; /* line number of start of text */ | |
132 | unsigned int cond_depth; /* 'if' nesting when file opened */ | 132 | unsigned int cond_depth; /* 'if' nesting when file opened */ | |
133 | Boolean depending; /* state of doing_depend on EOF */ | 133 | Boolean depending; /* state of doing_depend on EOF */ | |
@@ -1840,26 +1840,28 @@ Parse_IsVar(const char *p, VarAssign *ou | @@ -1840,26 +1840,28 @@ Parse_IsVar(const char *p, VarAssign *ou | |||
1840 | p++; | 1840 | p++; | |
1841 | 1841 | |||
1842 | /* During parsing, the '+' of the '+=' operator is initially parsed | 1842 | /* During parsing, the '+' of the '+=' operator is initially parsed | |
1843 | * as part of the variable name. It is later corrected, as is the ':sh' | 1843 | * as part of the variable name. It is later corrected, as is the ':sh' | |
1844 | * modifier. Of these two (nameEnd and op), the earlier one determines the | 1844 | * modifier. Of these two (nameEnd and op), the earlier one determines the | |
1845 | * actual end of the variable name. */ | 1845 | * actual end of the variable name. */ | |
1846 | pvar.nameStart = p; | 1846 | pvar.nameStart = p; | |
1847 | #ifdef CLEANUP | 1847 | #ifdef CLEANUP | |
1848 | pvar.nameEnd = NULL; | 1848 | pvar.nameEnd = NULL; | |
1849 | pvar.eq = NULL; | 1849 | pvar.eq = NULL; | |
1850 | #endif | 1850 | #endif | |
1851 | 1851 | |||
1852 | /* Scan for one of the assignment operators outside a variable expansion */ | 1852 | /* Scan for one of the assignment operators outside a variable expansion */ | |
1853 | /* FIXME: undefined behavior. In unit-tests/varname.mk:try1, at the end | |||
1854 | * of the loop, p already points to the next line. */ | |||
1853 | while ((ch = *p++) != 0) { | 1855 | while ((ch = *p++) != 0) { | |
1854 | if (ch == '(' || ch == '{') { | 1856 | if (ch == '(' || ch == '{') { | |
1855 | level++; | 1857 | level++; | |
1856 | continue; | 1858 | continue; | |
1857 | } | 1859 | } | |
1858 | if (ch == ')' || ch == '}') { | 1860 | if (ch == ')' || ch == '}') { | |
1859 | level--; | 1861 | level--; | |
1860 | continue; | 1862 | continue; | |
1861 | } | 1863 | } | |
1862 | 1864 | |||
1863 | if (level != 0) | 1865 | if (level != 0) | |
1864 | continue; | 1866 | continue; | |
1865 | 1867 |
--- src/usr.bin/make/unit-tests/varname.exp 2020/11/02 22:29:48 1.5
+++ src/usr.bin/make/unit-tests/varname.exp 2020/11/02 22:44:29 1.6
@@ -1,26 +1,26 @@ | @@ -1,26 +1,26 @@ | |||
1 | Global:VAR{{{}}} = 3 braces | 1 | Global:VAR{{{}}} = 3 braces | |
2 | Var_Parse: ${VAR{{{}}}}" != "3 braces" with VARE_WANTRES | 2 | Var_Parse: ${VAR{{{}}}}" != "3 braces" with VARE_WANTRES | |
3 | Global:VARNAME = VAR((( | 3 | Global:VARNAME = VAR((( | |
4 | Var_Parse: ${VARNAME} with VARE_WANTRES | 4 | Var_Parse: ${VARNAME} with VARE_WANTRES | |
5 | Global:VAR((( = 3 open parentheses | 5 | Global:VAR((( = 3 open parentheses | |
6 | Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" with VARE_WANTRES | 6 | Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" with VARE_WANTRES | |
7 | Var_Parse: ${:UVAR(((}= try1 with VARE_UNDEFERR|VARE_WANTRES | 7 | Var_Parse: ${:UVAR(((}= try1 with VARE_UNDEFERR|VARE_WANTRES | |
8 | Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) | 8 | Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) | |
9 | Result of ${:UVAR(((} is "VAR(((" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) | 9 | Result of ${:UVAR(((} is "VAR(((" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) | |
10 | Global:.ALLTARGETS = VAR(((=) | 10 | Global:.ALLTARGETS = VAR(((=) | |
11 | No closing parenthesis in archive specification | 11 | No closing parenthesis in archive specification | |
12 | make: "varname.mk" line 26: Error in archive specification: "VAR" | 12 | make: "varname.mk" line 29: Error in archive specification: "VAR" | |
13 | Var_Parse: ${:UVAR\(\(\(}= try2 with VARE_UNDEFERR|VARE_WANTRES | 13 | Var_Parse: ${:UVAR\(\(\(}= try2 with VARE_UNDEFERR|VARE_WANTRES | |
14 | Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) | 14 | Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) | |
15 | Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) | 15 | Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) | |
16 | Global:.ALLTARGETS = VAR(((=) VAR\(\(\(= | 16 | Global:.ALLTARGETS = VAR(((=) VAR\(\(\(= | |
17 | make: "varname.mk" line 27: Need an operator | 17 | make: "varname.mk" line 30: Need an operator | |
18 | Var_Parse: ${:UVAR\(\(\(}= try3 with VARE_UNDEFERR|VARE_WANTRES | 18 | Var_Parse: ${:UVAR\(\(\(}= try3 with VARE_UNDEFERR|VARE_WANTRES | |
19 | Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) | 19 | Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) | |
20 | Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) | 20 | Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) | |
21 | make: "varname.mk" line 28: Need an operator | 21 | make: "varname.mk" line 31: Need an operator | |
22 | Global:.MAKEFLAGS = -r -k -d v -d | 22 | Global:.MAKEFLAGS = -r -k -d v -d | |
23 | Global:.MAKEFLAGS = -r -k -d v -d 0 | 23 | Global:.MAKEFLAGS = -r -k -d v -d 0 | |
24 | make: Fatal errors encountered -- cannot continue | 24 | make: Fatal errors encountered -- cannot continue | |
25 | make: stopped in unit-tests | 25 | make: stopped in unit-tests | |
26 | exit status 1 | 26 | exit status 1 |
--- src/usr.bin/make/unit-tests/varname.mk 2020/11/02 22:29:48 1.6
+++ src/usr.bin/make/unit-tests/varname.mk 2020/11/02 22:44:29 1.7
@@ -1,32 +1,36 @@ | @@ -1,32 +1,36 @@ | |||
1 | # $NetBSD: varname.mk,v 1.6 2020/11/02 22:29:48 rillig Exp $ | 1 | # $NetBSD: varname.mk,v 1.7 2020/11/02 22:44:29 rillig Exp $ | |
2 | # | 2 | # | |
3 | # Tests for special variables, such as .MAKE or .PARSEDIR. | 3 | # Tests for special variables, such as .MAKE or .PARSEDIR. | |
4 | # And for variable names in general. | 4 | # And for variable names in general. | |
5 | 5 | |||
6 | .MAKEFLAGS: -dv | 6 | .MAKEFLAGS: -dv | |
7 | 7 | |||
8 | # In variable names, braces are allowed, but they must be balanced. | 8 | # In variable names, braces are allowed, but they must be balanced. | |
9 | # Parentheses and braces may be mixed. | 9 | # Parentheses and braces may be mixed. | |
10 | VAR{{{}}}= 3 braces | 10 | VAR{{{}}}= 3 braces | |
11 | .if "${VAR{{{}}}}" != "3 braces" | 11 | .if "${VAR{{{}}}}" != "3 braces" | |
12 | . error | 12 | . error | |
13 | .endif | 13 | .endif | |
14 | 14 | |||
15 | # In variable expressions, the parser works differently. It doesn't treat | 15 | # In variable expressions, the parser works differently. It doesn't treat | |
16 | # braces and parentheses equally, therefore the first closing brace already | 16 | # braces and parentheses equally, therefore the first closing brace already | |
17 | # marks the end of the variable name. | 17 | # marks the end of the variable name. | |
18 | VARNAME= VAR((( | 18 | VARNAME= VAR((( | |
19 | ${VARNAME}= 3 open parentheses | 19 | ${VARNAME}= 3 open parentheses | |
20 | .if "${VAR(((}}}}" != "3 open parentheses}}}" | 20 | .if "${VAR(((}}}}" != "3 open parentheses}}}" | |
21 | . error | 21 | . error | |
22 | .endif | 22 | .endif | |
23 | 23 | |||
24 | # In the above test, the variable name is constructed indirectly. Neither | 24 | # In the above test, the variable name is constructed indirectly. Neither | |
25 | # of the following expressions produces the intended effect. | 25 | # of the following expressions produces the intended effect. | |
26 | # | |||
27 | # This is not a variable assignment since the parentheses and braces are not | |||
28 | # balanced. At the end of the line, there are still 3 levels open, which | |||
29 | # means the variable name is not finished. | |||
26 | ${:UVAR(((}= try1 | 30 | ${:UVAR(((}= try1 | |
27 | ${:UVAR\(\(\(}= try2 | 31 | ${:UVAR\(\(\(}= try2 | |
28 | ${:UVAR\(\(\(}= try3 | 32 | ${:UVAR\(\(\(}= try3 | |
29 | 33 | |||
30 | .MAKEFLAGS: -d0 | 34 | .MAKEFLAGS: -d0 | |
31 | 35 | |||
32 | all: | 36 | all: |