Skip to content

Commit 462e5af

Browse files
committed
posix: c_lib_ext: fnmatch: fix escape-oriented regression
A regression in 936d027 introduced a subtle bug in the way that escaped expressions were handled. The regression originated with the assumption that test data (originally adapted from a 3rd-party testsuite) was correct when it was in fact flawed. Specifically, `fnmatch("[[?*\\]", "\\", 0)` should fail, since the "\\" sequence (a single backslash after compilation) escapes the following ']' character, thus leaving the bracket expression incomplete. Since the bracket expression (and therefore the whole pattern) is erroneous, the call should return `FNM_NOMATCH` to indicate failure rather than 0 to indicate success. Added new test cases from #98827 and some commentary for subsequent reviewers. This change does not complete #55186 but is related to it. Signed-off-by: Chris Friedt <cfriedt@tenstorrent.com> Signed-off-by: Jukka Rissanen <jukka.rissanen@nordicsemi.no> Signed-off-by: Keith Packard <keithp@keithp.com>
1 parent 3cf7b20 commit 462e5af

File tree

2 files changed

+23
-5
lines changed

2 files changed

+23
-5
lines changed

lib/posix/c_lib_ext/fnmatch.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,7 @@ static const char *rangematch(const char *pattern, int test, int flags)
9090
}
9191

9292
if (c == '\\' && !(flags & FNM_NOESCAPE)) {
93-
if (*pattern != ']' && *pattern != EOS) {
94-
c = FOLDCASE(*pattern++, flags);
95-
}
93+
c = FOLDCASE(*pattern++, flags);
9694
}
9795

9896
if (c == EOS) {

tests/posix/c_lib_ext/src/fnmatch.c

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,28 @@ ZTEST(posix_c_lib_ext, test_fnmatch)
3030
zassert_equal(fnmatch("a*.c", "a/x.c", FNM_PATHNAME), FNM_NOMATCH);
3131
zassert_ok(fnmatch("*/foo", "/foo", FNM_PATHNAME));
3232
zassert_ok(fnmatch("-O[01]", "-O1", 0));
33-
zassert_ok(fnmatch("[[?*\\]", "\\", 0));
34-
zassert_ok(fnmatch("[]?*\\]", "]", 0));
33+
/* '\' in pattern escapes ']'. bracket expression is incomplete. error */
34+
zassert_equal(fnmatch("[[?*\\]", "\\", 0), FNM_NOMATCH);
35+
/* '\' in pattern does not escape ']'. bracket expression complete. '\' matches input '\' */
36+
zassert_ok(fnmatch("[[?*\\]", "\\", FNM_NOESCAPE));
37+
/* '\' in pattern escapes '\', match '\' */
38+
zassert_ok(fnmatch("[[?*\\\\]", "\\", 0));
39+
/*
40+
* "[]" (empty bracket expression) is an invalid pattern.
41+
* > The ( ']' ) shall lose its special meaning and represent itself in a bracket expression
42+
* > if it occurs first in the list (after an initial ( '^' ), if any)
43+
* https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html#tag_09_03_05
44+
*
45+
* So the next test is (again) and incomplete bracket expression and should return error.
46+
* The two tests that follow it also require the ']' to be treated as a literal character to
47+
* match within the bracket expression.
48+
*/
49+
zassert_equal(fnmatch("[]?*\\]", "]", 0), FNM_NOMATCH);
50+
/* '\' in pattern does not escape. bracket expression complete. ']' matches input ']' */
51+
zassert_ok(fnmatch("[]?*\\]", "]", FNM_NOESCAPE));
52+
/* '\' in pattern escapes '\'. bracket expression complete. ']' matches input ']' */
53+
zassert_ok(fnmatch("[]?*\\\\]", "]", 0));
54+
3555
zassert_ok(fnmatch("[!]a-]", "b", 0));
3656
zassert_ok(fnmatch("[]-_]", "^", 0));
3757
zassert_ok(fnmatch("[!]-_]", "X", 0));

0 commit comments

Comments
 (0)