Follow @Openwall on Twitter for new release announcements and other news
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20261111000950.3772888-1-bernie@codewiz.org>
Date: Wed, 11 Nov 2026 09:09:50 +0900
From: Bernie Innocenti <bernie@...ewiz.org>
To: musl@...ts.openwall.com
Cc: Szabolcs Nagy <nsz@...t70.net>,
	Bernie Innocenti <bernie@...ewiz.org>
Subject: [PATCH libc-test] functional/fnmatch: accept either reading of a backslash in brackets

Without FNM_NOESCAPE, POSIX makes a backslash an escape character
inside a bracket expression too: XCU 2.13.1 in POSIX.1-2017 names
fnmatch() among the places where a backslash escapes the next
character, without an exception for brackets, and XCU 2.14.1 in
POSIX.1-2024 leaves it unspecified for the shell only.  Then \] does
not close the bracket, and no bracket expression starts: "[[?*\]" and
"[]?*\]" match "[[" or "[]", then any character, any string and ']',
so neither matches its one-character test string.  glibc reads them
that way; musl takes the backslash as an ordinary character, and both
match.

The two cases now accept a match or FNM_NOMATCH, and two more write the
backslash escaped, "[[?*\\]" and "[]?*\\]", on which both readings
agree.
---
 src/functional/fnmatch.c | 18 +++++++++++++++---
 1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/functional/fnmatch.c b/src/functional/fnmatch.c
index 96b8e06..c75a369 100644
--- a/src/functional/fnmatch.c
+++ b/src/functional/fnmatch.c
@@ -47,6 +47,10 @@ static char *flagstr(const struct xlat *map, int flags)
 	return buf;
 }
 
+/* An expected value for a pattern that POSIX and musl read differently:
+   either a match or FNM_NOMATCH passes.  */
+#define EITHER 0x7fff
+
 /* tests harness adapted from glibc testfnm.c */
 struct {
 	const char *pattern;
@@ -70,8 +74,15 @@ struct {
 	{ "a*.c", "a/x.c", FNM_PATHNAME, FNM_NOMATCH },
 	{ "*/foo", "/foo", FNM_PATHNAME, 0 },
 	{ "-O[01]", "-O1", 0, 0 },
-	{ "[[?*\\]", "\\", 0, 0 },
-	{ "[]?*\\]", "]", 0, 0 },
+	/* Without FNM_NOESCAPE, POSIX makes a backslash an escape inside a
+	   bracket expression too (XCU 2.13.1, 2.14.1 in POSIX.1-2024): the
+	   \] doesn't close the bracket, and these two don't match, as with
+	   glibc.  musl takes the backslash there as an ordinary character, and
+	   they match.  With the backslash escaped, both readings agree.  */
+	{ "[[?*\\]", "\\", 0, EITHER },
+	{ "[]?*\\]", "]", 0, EITHER },
+	{ "[[?*\\\\]", "\\", 0, 0 },
+	{ "[]?*\\\\]", "]", 0, 0 },
 	/* initial right-bracket tests */
 	{ "[!]a-]", "b", 0, 0 },
 	{ "[]-_]", "^", 0, 0 }, /* range: ']', '^', '_' */
@@ -152,7 +163,8 @@ int main(void)
 
 		r = fnmatch(tests[i].pattern, tests[i].string, tests[i].flags);
 		x = tests[i].expected;
-		if (r != x && (r != FNM_NOMATCH || x != -FNM_NOMATCH)) {
+		if (x == EITHER ? r != 0 && r != FNM_NOMATCH :
+		    r != x && (r != FNM_NOMATCH || x != -FNM_NOMATCH)) {
 			t_error("fnmatch(\"%s\", \"%s\", %s) failed, got %d want %d\n",
 				tests[i].pattern, tests[i].string,
 				flagstr(fnmatch_flags, tests[i].flags),
-- 
2.56.0

Powered by blists - more mailing lists

Confused about mailing lists and their use? Read about mailing lists on Wikipedia and check out these guidelines on proper formatting of your messages.