diff options
author | Tavian Barnes <tavianator@tavianator.com> | 2022-02-28 14:35:39 -0500 |
---|---|---|
committer | Tavian Barnes <tavianator@tavianator.com> | 2022-02-28 14:35:39 -0500 |
commit | 517a303e425b606cde3b70a1481ff557bba17462 (patch) | |
tree | cdb4100bab98bbd335aa192128aff949850f3ffe | |
parent | e14f04e52f792d12f3798b3c69bd0279ac7d3150 (diff) | |
download | bfs-517a303e425b606cde3b70a1481ff557bba17462.tar.xz |
parse: Check for globs with unescaped trailing backslashes
Both macOS and musl fail to fail on an unescaped backslash, so check for
it ourselves.
Link: https://pubs.opengroup.org/onlinepubs/9699919799/functions/fnmatch.html
Link: https://github.com/void-linux/void-packages/pull/35836
Link: https://www.openwall.com/lists/musl/2022/02/25/2
Link: https://www.austingroupbugs.net/view.php?id=806
-rw-r--r-- | parse.c | 25 | ||||
-rwxr-xr-x | tests.sh | 3 |
2 files changed, 23 insertions, 5 deletions
@@ -1686,11 +1686,14 @@ static struct expr *parse_fnmatch(const struct parser_state *state, struct expr return NULL; } + const char *arg = expr->argv[0]; + const char *pattern = expr->sdata; + if (casefold) { #ifdef FNM_CASEFOLD expr->idata = FNM_CASEFOLD; #else - parse_error(state, "${blu}%s${rs} is missing platform support.\n", expr->argv[0]); + parse_error(state, "${blu}%s${rs} is missing platform support.\n", arg); free_expr(expr); return NULL; #endif @@ -1698,9 +1701,27 @@ static struct expr *parse_fnmatch(const struct parser_state *state, struct expr expr->idata = 0; } + // POSIX says, about fnmatch(): + // + // If pattern ends with an unescaped <backslash>, fnmatch() shall + // return a non-zero value (indicating either no match or an error). + // + // But not all implementations obey this, so check for it ourselves. + size_t i, len = strlen(pattern); + for (i = 0; i < len; ++i) { + if (pattern[len - i - 1] != '\\') { + break; + } + } + if (i % 2 != 0) { + parse_warning(state, "${blu}%s${rs} ${bld}%s${rs}: Unescaped trailing backslash.\n\n", arg, pattern); + free_expr(expr); + return &expr_false; + } + expr->cost = 400.0; - if (strchr(expr->sdata, '*')) { + if (strchr(pattern, '*')) { expr->probability = 0.5; } else { expr->probability = 0.1; @@ -1338,9 +1338,6 @@ function test_name_bracket() { } function test_name_backslash() { - # fnmatch() is broken on macOS - skip_if test "$UNAME" = "Darwin" - # An unescaped \ doesn't match bfs_diff weirdnames -name '\' } |