src/static/http_static_path.c carries a fnmatch shim for Windows, used only
for StaticHandler::hide() patterns. It is recursive with the classic
*-backtracking shape: each * re-invokes the matcher for every remaining
position.
Until #309 that recursion was bounded to one path segment, because * always
stopped at a separator. #309 made the shim honour FNM_PATHNAME, so a pattern
containing ** matches with the flag off and * walks the whole path. A
pattern of the a*a*a*a*a*b/ shape, matched against a long path that does not
match it, then costs exponential time in the number of stars.
Runs of stars are collapsed, which removes the **** case and the form the
documentation encourages. It does not remove the general one.
Bounds, so the shape of the risk is clear:
- The pattern is written by the operator, not by the caller — this is a
careless configuration plus attacker-chosen request paths, not an injection.
- It is time, not stack: recursion depth stays inside the path length, about
4 KiB of frames.
- POSIX
fnmatch uses a non-recursive matcher and does not have the cliff, so
the exposure is Windows-only. A configuration tested on Linux would not show
it.
Options
The standard fix is the iterative two-pointer glob matcher, which keeps one
saved star position and runs in linear time; it handles *, ? and literals,
which is everything this shim handles. Adapting it to stop * at a separator
when FNM_PATHNAME is set is the only part that needs care, and the existing
StaticHide unit table is the check.
The cheaper option is a step budget: count invocations and give up — but giving
up has to mean "hidden", not "served", or the fix opens what it was meant to
close.
Raised by the review of #310, marked there as hardening rather than a defect.
src/static/http_static_path.ccarries afnmatchshim for Windows, used onlyfor
StaticHandler::hide()patterns. It is recursive with the classic*-backtracking shape: each*re-invokes the matcher for every remainingposition.
Until #309 that recursion was bounded to one path segment, because
*alwaysstopped at a separator. #309 made the shim honour
FNM_PATHNAME, so a patterncontaining
**matches with the flag off and*walks the whole path. Apattern of the
a*a*a*a*a*b/shape, matched against a long path that does notmatch it, then costs exponential time in the number of stars.
Runs of stars are collapsed, which removes the
****case and the form thedocumentation encourages. It does not remove the general one.
Bounds, so the shape of the risk is clear:
careless configuration plus attacker-chosen request paths, not an injection.
4 KiB of frames.
fnmatchuses a non-recursive matcher and does not have the cliff, sothe exposure is Windows-only. A configuration tested on Linux would not show
it.
Options
The standard fix is the iterative two-pointer glob matcher, which keeps one
saved star position and runs in linear time; it handles
*,?and literals,which is everything this shim handles. Adapting it to stop
*at a separatorwhen
FNM_PATHNAMEis set is the only part that needs care, and the existingStaticHideunit table is the check.The cheaper option is a step budget: count invocations and give up — but giving
up has to mean "hidden", not "served", or the fix opens what it was meant to
close.
Raised by the review of #310, marked there as hardening rather than a defect.