Skip to content

Commit c1e4c62

Browse files
committed
Merge branch 'gix-glob-fix'
2 parents 7d21ce9 + dab926d commit c1e4c62

File tree

4 files changed

+28
-11
lines changed

4 files changed

+28
-11
lines changed

gix-glob/src/wildmatch.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ pub(crate) mod function {
2222
NoMatch,
2323
AbortAll,
2424
AbortToStarStar,
25+
RecursionLimitReached,
2526
}
2627

2728
const STAR: u8 = b'*';
@@ -32,8 +33,13 @@ pub(crate) mod function {
3233
const COLON: u8 = b':';
3334

3435
const NEGATE_CLASS: u8 = b'!';
36+
/// Setting this limit to something reasonable means less compute time spent on unnecessarily complex patterns, or malicious ones.
37+
const RECURSION_LIMIT: usize = 64;
3538

36-
fn match_recursive(pattern: &BStr, text: &BStr, mode: Mode) -> Result {
39+
fn match_recursive(pattern: &BStr, text: &BStr, mode: Mode, depth: usize) -> Result {
40+
if depth == RECURSION_LIMIT {
41+
return RecursionLimitReached;
42+
}
3743
use self::Result::*;
3844
let possibly_lowercase = |c: &u8| {
3945
if mode.contains(Mode::IGNORE_CASE) {
@@ -89,7 +95,12 @@ pub(crate) mod function {
8995
})
9096
{
9197
if next.map_or(NoMatch, |(idx, _)| {
92-
match_recursive(pattern[idx + 1..].as_bstr(), text[t_idx..].as_bstr(), mode)
98+
match_recursive(
99+
pattern[idx + 1..].as_bstr(),
100+
text[t_idx..].as_bstr(),
101+
mode,
102+
depth + 1,
103+
)
93104
}) == Match
94105
{
95106
return Match;
@@ -152,7 +163,7 @@ pub(crate) mod function {
152163
return NoMatch;
153164
}
154165
}
155-
let res = match_recursive(pattern[p_idx..].as_bstr(), text[t_idx..].as_bstr(), mode);
166+
let res = match_recursive(pattern[p_idx..].as_bstr(), text[t_idx..].as_bstr(), mode, depth + 1);
156167
if res != NoMatch {
157168
if !match_slash || res != AbortToStarStar {
158169
return res;
@@ -352,6 +363,10 @@ pub(crate) mod function {
352363
///
353364
/// `mode` can be used to adjust the way the matching is performed.
354365
pub fn wildmatch(pattern: &BStr, value: &BStr, mode: Mode) -> bool {
355-
match_recursive(pattern, value, mode) == Result::Match
366+
let res = match_recursive(pattern, value, mode, 0);
367+
if res == Result::RecursionLimitReached {
368+
gix_features::trace::error!("Recursion limit of {} reached for pattern '{pattern}'", RECURSION_LIMIT);
369+
}
370+
res == Result::Match
356371
}
357372
}

gix-glob/tests/fixtures/fuzzed/many-stars.pattern

Lines changed: 1 addition & 0 deletions
Large diffs are not rendered by default.

gix-glob/tests/pattern/matching.rs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -326,15 +326,16 @@ fn single_paths_match_anywhere() {
326326
}
327327

328328
#[test]
329-
fn exponential_runaway_denial_of_service() {
329+
fn fuzzed_exponential_runaway_denial_of_service() {
330330
// original: "?[at(/\u{1d}\0\u{4}\u{14}\0[[[[:[\0\0\0\0\0\0\0\0Wt(/\u{1d}\0\u{4}\u{14}\0[[[[:[\0\0\0\0\0\0\0\0\0\0\0]"
331331
// reduced: "[[:[:]"
332332
for pattern in [
333-
"*?[wxxxxxx\0!t[:rt]\u{14}*",
334-
"?[at(/\u{1d}\0\u{4}\u{14}\0[[[[:[\0\0\0\0\0\0\0\0\0\0/s\0\0\0*\0\0\0\0\0\0\0\0]\0\0\0\0\0\0\0\0\0",
335-
"[[:digit]ab]",
336-
"[[:]ab]",
337-
"[[:[:x]",
333+
include_bytes!("../fixtures/fuzzed/many-stars.pattern"),
334+
"*?[wxxxxxx\0!t[:rt]\u{14}*".as_bytes(),
335+
"?[at(/\u{1d}\0\u{4}\u{14}\0[[[[:[\0\0\0\0\0\0\0\0\0\0/s\0\0\0*\0\0\0\0\0\0\0\0]\0\0\0\0\0\0\0\0\0".as_bytes(),
336+
b"[[:digit]ab]",
337+
b"[[:]ab]",
338+
b"[[:[:x]",
338339
] {
339340
let pat = pat(pattern);
340341
match_file(&pat, "relative/path", Case::Sensitive);

gix-url/tests/fuzzed.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ fn fuzzed() {
3737
let start = std::time::Instant::now();
3838
gix_url::parse(url.as_bstr()).ok();
3939
assert!(
40-
start.elapsed() < Duration::from_millis(100),
40+
start.elapsed() < Duration::from_millis(250),
4141
"URL at '{}' parsed too slowly, took {:.00}s",
4242
location.display(),
4343
start.elapsed().as_secs_f32()

0 commit comments

Comments
 (0)