Module Name: src Committed By: rillig Date: Sun Nov 1 14:36:25 UTC 2020
Modified Files: src/usr.bin/make: var.c src/usr.bin/make/unit-tests: moderrs.exp moderrs.mk varmod-range.exp varmod-range.mk varmod-to-separator.exp varmod-to-separator.mk Log Message: make(1): treat malformed :range, :ts and :[...] as errors Before, integer overflow in the :[1..2] modifier had not been detected, and the actual behavior varied between ILP64 and LP64I32 machines. Before, the :ts modifier accepted character literals like \012345 and \x1F600, which don't fit in a single character and were thus truncated. Before, the :range modifier issued an "Unknown modifier" error message for :range=x, which was not quite correct. The error message in this case is now "Invalid number". To generate a diff of this commit: cvs rdiff -u -r1.634 -r1.635 src/usr.bin/make/var.c cvs rdiff -u -r1.22 -r1.23 src/usr.bin/make/unit-tests/moderrs.exp cvs rdiff -u -r1.23 -r1.24 src/usr.bin/make/unit-tests/moderrs.mk cvs rdiff -u -r1.4 -r1.5 src/usr.bin/make/unit-tests/varmod-range.exp \ src/usr.bin/make/unit-tests/varmod-to-separator.exp cvs rdiff -u -r1.6 -r1.7 src/usr.bin/make/unit-tests/varmod-range.mk cvs rdiff -u -r1.5 -r1.6 src/usr.bin/make/unit-tests/varmod-to-separator.mk 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/var.c diff -u src/usr.bin/make/var.c:1.634 src/usr.bin/make/var.c:1.635 --- src/usr.bin/make/var.c:1.634 Sun Nov 1 13:55:31 2020 +++ src/usr.bin/make/var.c Sun Nov 1 14:36:25 2020 @@ -1,4 +1,4 @@ -/* $NetBSD: var.c,v 1.634 2020/11/01 13:55:31 rillig Exp $ */ +/* $NetBSD: var.c,v 1.635 2020/11/01 14:36:25 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990, 1993 @@ -130,7 +130,7 @@ #include "metachar.h" /* "@(#)var.c 8.3 (Berkeley) 3/19/94" */ -MAKE_RCSID("$NetBSD: var.c,v 1.634 2020/11/01 13:55:31 rillig Exp $"); +MAKE_RCSID("$NetBSD: var.c,v 1.635 2020/11/01 14:36:25 rillig Exp $"); #define VAR_DEBUG1(fmt, arg1) DEBUG1(VAR, fmt, arg1) #define VAR_DEBUG2(fmt, arg1, arg2) DEBUG2(VAR, fmt, arg1, arg2) @@ -2006,6 +2006,66 @@ ModMatchEq(const char *mod, const char * (mod[n] == endc || mod[n] == ':' || mod[n] == '='); } +static Boolean +TryParseIntBase0(const char **pp, int *out_num) +{ + char *end; + long n; + + errno = 0; + n = strtol(*pp, &end, 0); + if ((n == LONG_MIN || n == LONG_MAX) && errno == ERANGE) + return FALSE; + if (n < INT_MIN || n > INT_MAX) + return FALSE; + + *pp = end; + *out_num = (int)n; + return TRUE; +} + +static Boolean +TryParseSize(const char **pp, size_t *out_num) +{ + char *end; + unsigned long n; + + if (!ch_isdigit(**pp)) + return FALSE; + + errno = 0; + n = strtoul(*pp, &end, 10); + if (n == ULONG_MAX && errno == ERANGE) + return FALSE; + if (n > SIZE_MAX) + return FALSE; + + *pp = end; + *out_num = (size_t)n; + return TRUE; +} + +static Boolean +TryParseChar(const char **pp, int base, char *out_ch) +{ + char *end; + unsigned long n; + + if (!ch_isalnum(**pp)) + return FALSE; + + errno = 0; + n = strtoul(*pp, &end, base); + if (n == ULONG_MAX && errno == ERANGE) + return FALSE; + if (n > UCHAR_MAX) + return FALSE; + + *pp = end; + *out_ch = (char)n; + return TRUE; +} + /* :@var@...${var}...@ */ static ApplyModifierResult ApplyModifier_Loop(const char **pp, ApplyModifiersState *st) @@ -2263,9 +2323,12 @@ ApplyModifier_Range(const char **pp, App return AMR_UNKNOWN; if (mod[5] == '=') { - char *ep; - n = (size_t)strtoul(mod + 6, &ep, 10); - *pp = ep; + const char *p = mod + 6; + if (!TryParseSize(&p, &n)) { + Parse_Error(PARSE_FATAL, "Invalid number: %s\n", mod + 6); + return AMR_CLEANUP; + } + *pp = p; } else { n = 0; *pp = mod + 5; @@ -2562,24 +2625,27 @@ ApplyModifier_ToSep(const char **pp, App /* ":ts\x40" or ":ts\100" */ { - const char *numStart = sep + 1; + const char *p = sep + 1; int base = 8; /* assume octal */ - char *end; if (sep[1] == 'x') { base = 16; - numStart++; + p++; } else if (!ch_isdigit(sep[1])) { (*pp)++; /* just for backwards compatibility */ return AMR_BAD; /* ":ts<backslash><unrecognised>". */ } - st->sep = (char)strtoul(numStart, &end, base); - if (*end != ':' && *end != st->endc) { + if (!TryParseChar(&p, base, &st->sep)) { + Parse_Error(PARSE_FATAL, "Invalid character number: %s\n", p); + return AMR_CLEANUP; + } + if (*p != ':' && *p != st->endc) { (*pp)++; /* just for backwards compatibility */ return AMR_BAD; } - *pp = end; + + *pp = p; } ok: @@ -2653,9 +2719,9 @@ static ApplyModifierResult ApplyModifier_Words(const char **pp, ApplyModifiersState *st) { char *estr; - char *ep; int first, last; VarParseResult res; + const char *p; (*pp)++; /* skip the '[' */ res = ParseModifierPart(pp, ']', st->eflags, st, @@ -2705,18 +2771,17 @@ ApplyModifier_Words(const char **pp, App * We expect estr to contain a single integer for :[N], or two integers * separated by ".." for :[start..end]. */ - first = (int)strtol(estr, &ep, 0); - if (ep == estr) /* Found junk instead of a number */ - goto bad_modifier; + p = estr; + if (!TryParseIntBase0(&p, &first)) + goto bad_modifier; /* Found junk instead of a number */ - if (ep[0] == '\0') { /* Found only one integer in :[N] */ + if (p[0] == '\0') { /* Found only one integer in :[N] */ last = first; - } else if (ep[0] == '.' && ep[1] == '.' && ep[2] != '\0') { + } else if (p[0] == '.' && p[1] == '.' && p[2] != '\0') { /* Expecting another integer after ".." */ - ep += 2; - last = (int)strtol(ep, &ep, 0); - if (ep[0] != '\0') /* Found junk after ".." */ - goto bad_modifier; + p += 2; + if (!TryParseIntBase0(&p, &last) || *p != '\0') + goto bad_modifier; /* Found junk after ".." */ } else goto bad_modifier; /* Found junk instead of ".." */ Index: src/usr.bin/make/unit-tests/moderrs.exp diff -u src/usr.bin/make/unit-tests/moderrs.exp:1.22 src/usr.bin/make/unit-tests/moderrs.exp:1.23 --- src/usr.bin/make/unit-tests/moderrs.exp:1.22 Sun Nov 1 10:56:08 2020 +++ src/usr.bin/make/unit-tests/moderrs.exp Sun Nov 1 14:36:25 2020 @@ -45,7 +45,8 @@ want: Unfinished modifier for UNDEF (']' make: Unfinished modifier for UNDEF (']' missing) 13= -12345=ok +make: Bad modifier `:[123451234512345123451234512345]' for UNDEF +12345=S,^ok,:S,^3ok,} exclam: want: Unfinished modifier for VARNAME ('!' missing) Index: src/usr.bin/make/unit-tests/moderrs.mk diff -u src/usr.bin/make/unit-tests/moderrs.mk:1.23 src/usr.bin/make/unit-tests/moderrs.mk:1.24 --- src/usr.bin/make/unit-tests/moderrs.mk:1.23 Sun Nov 1 10:56:08 2020 +++ src/usr.bin/make/unit-tests/moderrs.mk Sun Nov 1 14:36:25 2020 @@ -1,4 +1,4 @@ -# $NetBSD: moderrs.mk,v 1.23 2020/11/01 10:56:08 rillig Exp $ +# $NetBSD: moderrs.mk,v 1.24 2020/11/01 14:36:25 rillig Exp $ # # various modifier error tests @@ -74,15 +74,21 @@ words: print-header print-footer # Word index out of bounds. # - # On LP64I32, strtol returns LONG_MAX, - # which is then truncated to int (undefined behavior), - # typically resulting in -1. - # This -1 is interpreted as "the last word". + # Until 2020-11-01, the behavior in this case depended upon the size + # of unsigned long. # - # On ILP32, strtol returns LONG_MAX, - # which is a large number. - # This results in a range from LONG_MAX - 1 to 3, - # which is empty. + # On LP64I32, strtol returns LONG_MAX, which was then truncated to + # int (undefined behavior), typically resulting in -1. This -1 was + # interpreted as "the last word". + # + # On ILP32, strtol returns LONG_MAX, which is a large number. This + # resulted in a range from LONG_MAX - 1 to 3, which was empty. + # + # Since 2020-11-01, the numeric overflow is detected and generates an + # error. In the remainder of the text, the '$,' is no longer parsed + # as part of a variable modifier, where it would have been interpreted + # as an anchor to the :S modifier, but as a normal variable named ','. + # That variable is undefined, resulting in an empty string. @echo 12345=${UNDEF:U1 2 3:[123451234512345123451234512345]:S,^$,ok,:S,^3$,ok,} exclam: print-header print-footer Index: src/usr.bin/make/unit-tests/varmod-range.exp diff -u src/usr.bin/make/unit-tests/varmod-range.exp:1.4 src/usr.bin/make/unit-tests/varmod-range.exp:1.5 --- src/usr.bin/make/unit-tests/varmod-range.exp:1.4 Sun Nov 1 13:55:31 2020 +++ src/usr.bin/make/unit-tests/varmod-range.exp Sun Nov 1 14:36:25 2020 @@ -1,13 +1,14 @@ +make: "varmod-range.mk" line 53: Invalid number: x}Rest" != "Rest" + +make: "varmod-range.mk" line 53: Malformed conditional ("${:U:range=x}Rest" != "Rest") make: Unknown modifier 'x' -make: "varmod-range.mk" line 49: Malformed conditional ("${:U:range=x}Rest" != "Rest") -make: Unknown modifier 'x' -make: "varmod-range.mk" line 58: Malformed conditional ("${:U:range=0x0}Rest" != "Rest") +make: "varmod-range.mk" line 62: Malformed conditional ("${:U:range=0x0}Rest" != "Rest") make: Unknown modifier 'r' -make: "varmod-range.mk" line 74: Malformed conditional ("${a b c:L:rang}Rest" != "Rest") +make: "varmod-range.mk" line 78: Malformed conditional ("${a b c:L:rang}Rest" != "Rest") make: Unknown modifier 'r' -make: "varmod-range.mk" line 81: Malformed conditional ("${a b c:L:rango}Rest" != "Rest") +make: "varmod-range.mk" line 85: Malformed conditional ("${a b c:L:rango}Rest" != "Rest") make: Unknown modifier 'r' -make: "varmod-range.mk" line 88: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest") +make: "varmod-range.mk" line 92: Malformed conditional ("${a b c:L:ranger}Rest" != "Rest") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 Index: src/usr.bin/make/unit-tests/varmod-to-separator.exp diff -u src/usr.bin/make/unit-tests/varmod-to-separator.exp:1.4 src/usr.bin/make/unit-tests/varmod-to-separator.exp:1.5 --- src/usr.bin/make/unit-tests/varmod-to-separator.exp:1.4 Sun Nov 1 13:28:50 2020 +++ src/usr.bin/make/unit-tests/varmod-to-separator.exp Sun Nov 1 14:36:25 2020 @@ -1,17 +1,21 @@ -make: "varmod-to-separator.mk" line 106: warning: The separator \400 is accepted even though it is out of bounds. -make: "varmod-to-separator.mk" line 118: warning: The separator \x100 is accepted even though it is out of bounds. +make: "varmod-to-separator.mk" line 107: Invalid character number: 400:tu} + +make: "varmod-to-separator.mk" line 107: Malformed conditional (${WORDS:[1..3]:ts\400:tu}) +make: "varmod-to-separator.mk" line 121: Invalid character number: 100:tu} + +make: "varmod-to-separator.mk" line 121: Malformed conditional (${WORDS:[1..3]:ts\x100:tu}) make: Bad modifier `:ts\-300' for WORDS -make: "varmod-to-separator.mk" line 124: Malformed conditional (${WORDS:[1..3]:ts\-300:tu}) +make: "varmod-to-separator.mk" line 128: Malformed conditional (${WORDS:[1..3]:ts\-300:tu}) make: Bad modifier `:ts\8' for 1 2 3 -make: "varmod-to-separator.mk" line 132: Malformed conditional (${1 2 3:L:ts\8:tu}) +make: "varmod-to-separator.mk" line 136: Malformed conditional (${1 2 3:L:ts\8:tu}) make: Bad modifier `:ts\100L' for 1 2 3 -make: "varmod-to-separator.mk" line 139: Malformed conditional (${1 2 3:L:ts\100L}) +make: "varmod-to-separator.mk" line 143: Malformed conditional (${1 2 3:L:ts\100L}) make: Bad modifier `:ts\x40g' for 1 2 3 -make: "varmod-to-separator.mk" line 146: Malformed conditional (${1 2 3:L:ts\x40g}) +make: "varmod-to-separator.mk" line 150: Malformed conditional (${1 2 3:L:ts\x40g}) make: Bad modifier `:tx' for WORDS -make: "varmod-to-separator.mk" line 154: Malformed conditional (${WORDS:tx} != "anything") +make: "varmod-to-separator.mk" line 158: Malformed conditional (${WORDS:tx} != "anything") make: Bad modifier `:t\X' for WORDS -make: "varmod-to-separator.mk" line 161: Malformed conditional (${WORDS:t\X} != "anything") +make: "varmod-to-separator.mk" line 165: Malformed conditional (${WORDS:t\X} != "anything") make: Fatal errors encountered -- cannot continue make: stopped in unit-tests exit status 1 Index: src/usr.bin/make/unit-tests/varmod-range.mk diff -u src/usr.bin/make/unit-tests/varmod-range.mk:1.6 src/usr.bin/make/unit-tests/varmod-range.mk:1.7 --- src/usr.bin/make/unit-tests/varmod-range.mk:1.6 Sun Nov 1 13:55:31 2020 +++ src/usr.bin/make/unit-tests/varmod-range.mk Sun Nov 1 14:36:25 2020 @@ -1,4 +1,4 @@ -# $NetBSD: varmod-range.mk,v 1.6 2020/11/01 13:55:31 rillig Exp $ +# $NetBSD: varmod-range.mk,v 1.7 2020/11/01 14:36:25 rillig Exp $ # # Tests for the :range variable modifier, which generates sequences # of integers from the given range. @@ -43,9 +43,13 @@ #.endif # The :range modifier requires a number as parameter. -# As of 2020-11-01, the parser tries to read the 'x' as a number, fails and -# stops there. It then tries to parse the next modifier at that point, -# which fails with the message "Unknown modifier". +# +# Until 2020-11-01, the parser tried to read the 'x' as a number, failed and +# stopped there. It then tried to parse the next modifier at that point, +# which failed with the message "Unknown modifier". +# +# Since 2020-11-01, the parser issues a more precise "Invalid number" error +# instead. .if "${:U:range=x}Rest" != "Rest" . error .else Index: src/usr.bin/make/unit-tests/varmod-to-separator.mk diff -u src/usr.bin/make/unit-tests/varmod-to-separator.mk:1.5 src/usr.bin/make/unit-tests/varmod-to-separator.mk:1.6 --- src/usr.bin/make/unit-tests/varmod-to-separator.mk:1.5 Sun Nov 1 13:28:50 2020 +++ src/usr.bin/make/unit-tests/varmod-to-separator.mk Sun Nov 1 14:36:25 2020 @@ -1,4 +1,4 @@ -# $NetBSD: varmod-to-separator.mk,v 1.5 2020/11/01 13:28:50 rillig Exp $ +# $NetBSD: varmod-to-separator.mk,v 1.6 2020/11/01 14:36:25 rillig Exp $ # # Tests for the :ts variable modifier, which joins the words of the variable # using an arbitrary character as word separator. @@ -102,6 +102,8 @@ WORDS= one two three four five six # The value of the separator character must not be outside the value space # for an unsigned character though. +# +# Since 2020-11-01, these out-of-bounds values are rejected. .if ${WORDS:[1..3]:ts\400:tu} . warning The separator \400 is accepted even though it is out of bounds. .else @@ -114,6 +116,8 @@ WORDS= one two three four five six .endif # The hexadecimal number must be in the range of an unsigned char. +# +# Since 2020-11-01, these out-of-bounds values are rejected. .if ${WORDS:[1..3]:ts\x100:tu} . warning The separator \x100 is accepted even though it is out of bounds. .else