Skip to content

Conversation

@cfriedt
Copy link
Member

@cfriedt cfriedt commented Nov 15, 2025

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 completely fix #55186 but is related to it.

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 zephyrproject-rtos#98827 and some commentary for subsequent
reviewers.

This change does not complete zephyrproject-rtos#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>
@cfriedt cfriedt force-pushed the fix-fnmatch-regression branch from 6f09b19 to 462e5af Compare November 15, 2025 17:28
@cfriedt cfriedt assigned jukkar and cfriedt and unassigned jukkar Nov 15, 2025
@cfriedt cfriedt added bug The issue is a bug, or the PR is fixing a bug backport v4.3-branch Request backport to the v4.3-branch Regression Something, which was working, does not anymore labels Nov 15, 2025
@sonarqubecloud
Copy link

@cfriedt cfriedt added area: POSIX POSIX API Library area: Tests Issues related to a particular existing or missing test backport v4.2-branch Request backport to the v4.2-branch labels Nov 15, 2025
@cfriedt cfriedt marked this pull request as ready for review November 15, 2025 18:12
@cfriedt cfriedt requested a review from ycsin November 15, 2025 18:12
@zephyrbot zephyrbot requested a review from nashif November 15, 2025 18:13
@keith-packard
Copy link
Contributor

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.

The pattern is not erroneous (which would cause the function to return -FNM_NOMATCH). It is a legitimate pattern equivalent to "[[?*]" and not a bracket expression at all. We might want to add a test to verify that the pattern works in this way.

Note that Musl has the same bug, which is why the test was flawed.

@cfriedt
Copy link
Member Author

cfriedt commented Nov 16, 2025

The pattern is not erroneous (which would cause the function to return -FNM_NOMATCH). It is a legitimate pattern equivalent to "[[?*]" and not a bracket expression at all. We might want to add a test to verify that the pattern works in this way.

Ok - sorry - I was proceeding based on your comment here, where you wrote that ] is escaped, which makes sense to me.
#98827 (comment)

Maybe I was reading-in to your comment too far though, and assumed that if the final ] were escaped, that it would not form a proper bracket expression. To me, it would seem that interpreting an escaped ] without an additional non-escaped ] following it would be more of an opportunistic interpretation to force \] to close the bracket expression rather than being a character to match (assuming a following ] would properly close the bracket expression). I have not found any part of the specification that supports that interpretation though, and the specification also states that escaping an ordinary character outside of a bracket expression is undefined.

In either case, when the pattern is invalid or when it is "[[?*]", fnmatch() should return FNM_NOMATCH; in the former interpretation, to indicate an error of some kind, and in the latter interpretation, the \\ in the input would not be matched.

I can update the PR description and / or commentary as you like, but it would be nice to have a reference to the specification to support it.

I've confirmed that Zephyr's implementation recognizes "[[?*\\]" (with 0 flags) as an unterminated (i.e. invalid) bracket expression.

@cfriedt
Copy link
Member Author

cfriedt commented Nov 16, 2025

It would seem that even the glibc agrees with the former interpretation (that there is an incomplete bracket expression).

A sample program I wrote to test

#include <fnmatch.h>
#include <stdio.h>

int main()
{
  const char *pat = "[abc\\]";
  const char *in = "a";
  int ret;

  ret = fnmatch(pat, in, 0);
  if (ret == 0) {
    printf("fnmatch(\"%s\", \"%s\", 0) -> success!\n", pat, in);
  } else {
    printf("fnmatch(\"%s\", \"%s\", 0) -> failed! (%d)\n", pat, in, ret);
  }

  return ret;
}

produces the output below.

$ /tmp/blah 
fnmatch("[abc\]", "a", 0) -> failed! (1)

It fails as well with FNM_NOMATCH when pat = "[[?*\\]" and in = "\\".

@keith-packard
Copy link
Contributor

In either case, when the pattern is invalid or when it is "[[?*]", fnmatch() should return FNM_NOMATCH; in the former interpretation, to indicate an error of some kind, and in the latter interpretation, the \\ in the input would not be matched.

I think you've mis-interpreted my comment -- yes, this is an invalid bracket expression, but https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_13_01 says that you only get a bracket expression when the bracket expression is valid, otherwise you interpret the bytes as not being part of a bracket expression at all. An valid expression which does not match the string returns FNM_NOMATCH, an erroneous expression returns some other non-zero value (glibc, picolibc and Zephyr all return -FNM_NOMATCH).

So, the difference is which return value is expected, in this case the expression is valid (although does not contain a bracket expression), it's just that it doesn't match the string, so it should return FNM_NOMATCH.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: POSIX POSIX API Library area: Tests Issues related to a particular existing or missing test backport v4.2-branch Request backport to the v4.2-branch backport v4.3-branch Request backport to the v4.3-branch bug The issue is a bug, or the PR is fixing a bug Regression Something, which was working, does not anymore

Projects

None yet

Development

Successfully merging this pull request may close these issues.

posix: fnmatch: fix known bugs

3 participants