Mon Nov 2 22:44:29 2020 UTC ()
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.


(rillig)
diff -r1.420 -r1.421 src/usr.bin/make/parse.c
diff -r1.5 -r1.6 src/usr.bin/make/unit-tests/varname.exp
diff -r1.6 -r1.7 src/usr.bin/make/unit-tests/varname.mk

cvs diff -r1.420 -r1.421 src/usr.bin/make/parse.c (expand / switch to unified diff)

--- 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" */
120MAKE_RCSID("$NetBSD: parse.c,v 1.420 2020/11/01 00:24:57 rillig Exp $"); 120MAKE_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 */
127typedef struct IFile { 127typedef 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

cvs diff -r1.5 -r1.6 src/usr.bin/make/unit-tests/varname.exp (expand / switch to unified diff)

--- 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 @@
1Global:VAR{{{}}} = 3 braces 1Global:VAR{{{}}} = 3 braces
2Var_Parse: ${VAR{{{}}}}" != "3 braces" with VARE_WANTRES 2Var_Parse: ${VAR{{{}}}}" != "3 braces" with VARE_WANTRES
3Global:VARNAME = VAR((( 3Global:VARNAME = VAR(((
4Var_Parse: ${VARNAME} with VARE_WANTRES 4Var_Parse: ${VARNAME} with VARE_WANTRES
5Global:VAR((( = 3 open parentheses 5Global:VAR((( = 3 open parentheses
6Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" with VARE_WANTRES 6Var_Parse: ${VAR(((}}}}" != "3 open parentheses}}}" with VARE_WANTRES
7Var_Parse: ${:UVAR(((}= try1 with VARE_UNDEFERR|VARE_WANTRES 7Var_Parse: ${:UVAR(((}= try1 with VARE_UNDEFERR|VARE_WANTRES
8Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) 8Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF)
9Result of ${:UVAR(((} is "VAR(((" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) 9Result of ${:UVAR(((} is "VAR(((" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
10Global:.ALLTARGETS = VAR(((=) 10Global:.ALLTARGETS = VAR(((=)
11No closing parenthesis in archive specification 11No closing parenthesis in archive specification
12make: "varname.mk" line 26: Error in archive specification: "VAR" 12make: "varname.mk" line 29: Error in archive specification: "VAR"
13Var_Parse: ${:UVAR\(\(\(}= try2 with VARE_UNDEFERR|VARE_WANTRES 13Var_Parse: ${:UVAR\(\(\(}= try2 with VARE_UNDEFERR|VARE_WANTRES
14Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) 14Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF)
15Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) 15Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
16Global:.ALLTARGETS = VAR(((=) VAR\(\(\(= 16Global:.ALLTARGETS = VAR(((=) VAR\(\(\(=
17make: "varname.mk" line 27: Need an operator 17make: "varname.mk" line 30: Need an operator
18Var_Parse: ${:UVAR\(\(\(}= try3 with VARE_UNDEFERR|VARE_WANTRES 18Var_Parse: ${:UVAR\(\(\(}= try3 with VARE_UNDEFERR|VARE_WANTRES
19Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF) 19Applying ${:U...} to "" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF)
20Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF) 20Result of ${:UVAR\(\(\(} is "VAR\(\(\(" (VARE_UNDEFERR|VARE_WANTRES, none, VEF_UNDEF|VEF_DEF)
21make: "varname.mk" line 28: Need an operator 21make: "varname.mk" line 31: Need an operator
22Global:.MAKEFLAGS = -r -k -d v -d 22Global:.MAKEFLAGS = -r -k -d v -d
23Global:.MAKEFLAGS = -r -k -d v -d 0 23Global:.MAKEFLAGS = -r -k -d v -d 0
24make: Fatal errors encountered -- cannot continue 24make: Fatal errors encountered -- cannot continue
25make: stopped in unit-tests 25make: stopped in unit-tests
26exit status 1 26exit status 1

cvs diff -r1.6 -r1.7 src/usr.bin/make/unit-tests/varname.mk (expand / switch to unified diff)

--- 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.
10VAR{{{}}}= 3 braces 10VAR{{{}}}= 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.
18VARNAME= VAR((( 18VARNAME= 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
32all: 36all: