Re: lib/60517: fnmatch(2) does not implement FNM_PATHNAME correctly

"Luke Mewburn via gnats" <[email protected]>
Newsgroups gmane.os.netbsd.bugs
Message-ID <[email protected]>
The following reply was made to PR lib/60517; it has been noted by GNATS.

From: Luke Mewburn <[email protected]>
To: [email protected], [email protected]
Cc: [email protected], [email protected],
	[email protected]
Subject: Re: lib/60517: fnmatch(2) does not implement FNM_PATHNAME correctly
Date: Wed, 5 Aug 2026 18:32:02 +1000

 On 26-07-29 17:35, [email protected] via gnats wrote:
   | >Description:
   | fnmatch(3) does not implement FNM_PATHNAME correctly in rangematch,
   | causing some patterns to not match correctly.
   | 
   | >How-To-Repeat:
   | The following program reproduces the issue:
   | 
   | #include <fnmatch.h>
   | #include <stdio.h>
   | 
   | int main(int argc, char *argv[]) {
   | 	int match = fnmatch(argv[1], argv[2], 0);
   | 	printf("%d\n", match);
   | 	return 0;
   | }
   | 
   | Run:
   | 
   | ./fnmatch '[\\/$]*' '/usr/lib'
   | 
   | And it should print 0.
 
 (+ Christos)
 
 Hi Drew,
 
 tl;dr: I think you have identified a bug with fnmatch without FNM_PATHNAME,
 even if I think your test case pattern was a bit tricky at first :)
 
 Unless Christos (or someone else) disagrees with your report
 and my analysis, I think this should be committed.
 
 
 Details:
 
 When I first analyzed your report it wasn't clear what the fault was:
 1. Your pattern was a bit tricky. Now I see that the intention is to
    match any string starting with `\', `/', or `$', relying upon
    `\\' to become `\` because you're not using FNM_NOESCAPE.
 2. You're not using FNM_PATHNAME.
 
 I've dug into the commit that caused the issue, and read multiple
 versions of the relevant POSIX pages, and I now understand your bug
 report and think your proposed fix is correct.
 
 The commit to lib/libc/gen/fnmatch.c 1.26 on 2014-10-12 was by Christos,
 with commit message:
 	Fix fnmatch issues according to POSIX.
 	http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_13_01
 	1. A [...] pattern containing a slash is not a pattern; the [ ]'s become regular
 	   characters
 	2. A [] or a [!] is not an empty pattern, why would it? The first would never
 	   match and the second would always match which makes it equivalent to ?
 	   In those cases the ] is taken as a literal character and does not have
 	   special meaning.
 
 Reading that POSIX (issue 6) "Shell Command Language" page linked in item 1,
 I don't actually see the text relating to item 1 in section 2.13.1.
 There's related text in section 2.13.3 "Patterns Used for Filename Expansion".
 
 However, the POSIX (issue 6) fnmatch page at
   https://pubs.opengroup.org/onlinepubs/009695399/functions/fnmatch.html 
 doesn't cross-reference back to this shell section 2.13.3 about filename
 expansion.  It seems to me that fnmatch FNM_PATHNAME has a limited and
 different implementation than the shell.
 
 The relevant text in fnmatch for slash without FNM_PATHNAME is:
 	If the FNM_PATHNAME flag is not set, the slash character shall be
 	treated as an ordinary character.
 
 The latest POSIX (issue 8) page for fnmatch at
   https://pubs.opengroup.org/onlinepubs/9799919799/functions/fnmatch.html
 has similar text:
 	If the FNM_PATHNAME flag is not set, the <slash> character shall
 	be treated as an ordinary character.
 
 Regards,
 Luke.
 
 
 
   | >Fix:
   | --- src.orig/lib/libc/gen/fnmatch.c	2014-10-13 00:32:33.000000000 +0200
   | +++ src/lib/libc/gen/fnmatch.c	2026-07-29 15:25:45.271817024 +0200
   | @@ -91,7 +91,7 @@
   |  	need = 1;
   |  	for (ok = 0; (c = FOLDCASE(*pattern++, flags)) != ']' || need;) {
   |  		need = 0;
   | -		if (c == '/')
   | +		if (c == '/' && (flags & FNM_PATHNAME))
   |  			return (void *)-1;
   |  		if (c == '\\' && !(flags & FNM_NOESCAPE))
   |  			c = FOLDCASE(*pattern++, flags);
   | --- src.orig/tests/lib/libc/gen/t_fnmatch.c	2016-10-31 06:08:53.000000000 +0100
   | +++ src/tests/lib/libc/gen/t_fnmatch.c	2026-07-29 19:25:10.723264988 +0200
   | @@ -125,6 +125,8 @@
   |  	ATF_CHECK(fnmatch("???x", "xxxx", FNM_PATHNAME) == 0);
   |  	ATF_CHECK(fnmatch("*/xxx", "/xxx", FNM_PATHNAME) == 0);
   |  	ATF_CHECK(fnmatch("x/*.y", "x/z.y", FNM_PATHNAME) == 0);
   | +
   | +	ATF_CHECK(fnmatch("[\\\\/$]*", "/usr/lib", 0) == 0);
   |  }
   |
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.