Skip to content

Commit

Permalink
Merge branch 'gix-glob-fix'
Browse files Browse the repository at this point in the history
  • Loading branch information
Byron committed Dec 21, 2023
2 parents 7d21ce9 + dab926d commit c1e4c62
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 11 deletions.
23 changes: 19 additions & 4 deletions gix-glob/src/wildmatch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ pub(crate) mod function {
NoMatch,
AbortAll,
AbortToStarStar,
RecursionLimitReached,
}

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

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

fn match_recursive(pattern: &BStr, text: &BStr, mode: Mode) -> Result {
fn match_recursive(pattern: &BStr, text: &BStr, mode: Mode, depth: usize) -> Result {
if depth == RECURSION_LIMIT {
return RecursionLimitReached;
}
use self::Result::*;
let possibly_lowercase = |c: &u8| {
if mode.contains(Mode::IGNORE_CASE) {
Expand Down Expand Up @@ -89,7 +95,12 @@ pub(crate) mod function {
})
{
if next.map_or(NoMatch, |(idx, _)| {
match_recursive(pattern[idx + 1..].as_bstr(), text[t_idx..].as_bstr(), mode)
match_recursive(
pattern[idx + 1..].as_bstr(),
text[t_idx..].as_bstr(),
mode,
depth + 1,
)
}) == Match
{
return Match;
Expand Down Expand Up @@ -152,7 +163,7 @@ pub(crate) mod function {
return NoMatch;
}
}
let res = match_recursive(pattern[p_idx..].as_bstr(), text[t_idx..].as_bstr(), mode);
let res = match_recursive(pattern[p_idx..].as_bstr(), text[t_idx..].as_bstr(), mode, depth + 1);
if res != NoMatch {
if !match_slash || res != AbortToStarStar {
return res;
Expand Down Expand Up @@ -352,6 +363,10 @@ pub(crate) mod function {
///
/// `mode` can be used to adjust the way the matching is performed.
pub fn wildmatch(pattern: &BStr, value: &BStr, mode: Mode) -> bool {
match_recursive(pattern, value, mode) == Result::Match
let res = match_recursive(pattern, value, mode, 0);
if res == Result::RecursionLimitReached {
gix_features::trace::error!("Recursion limit of {} reached for pattern '{pattern}'", RECURSION_LIMIT);
}
res == Result::Match
}
}
1 change: 1 addition & 0 deletions gix-glob/tests/fixtures/fuzzed/many-stars.pattern

Large diffs are not rendered by default.

13 changes: 7 additions & 6 deletions gix-glob/tests/pattern/matching.rs
Original file line number Diff line number Diff line change
Expand Up @@ -326,15 +326,16 @@ fn single_paths_match_anywhere() {
}

#[test]
fn exponential_runaway_denial_of_service() {
fn fuzzed_exponential_runaway_denial_of_service() {
// 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]"
// reduced: "[[:[:]"
for pattern in [
"*?[wxxxxxx\0!t[:rt]\u{14}*",
"?[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",
"[[:digit]ab]",
"[[:]ab]",
"[[:[:x]",
include_bytes!("../fixtures/fuzzed/many-stars.pattern"),
"*?[wxxxxxx\0!t[:rt]\u{14}*".as_bytes(),
"?[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(),
b"[[:digit]ab]",
b"[[:]ab]",
b"[[:[:x]",
] {
let pat = pat(pattern);
match_file(&pat, "relative/path", Case::Sensitive);
Expand Down
2 changes: 1 addition & 1 deletion gix-url/tests/fuzzed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn fuzzed() {
let start = std::time::Instant::now();
gix_url::parse(url.as_bstr()).ok();
assert!(
start.elapsed() < Duration::from_millis(100),
start.elapsed() < Duration::from_millis(250),
"URL at '{}' parsed too slowly, took {:.00}s",
location.display(),
start.elapsed().as_secs_f32()
Expand Down

0 comments on commit c1e4c62

Please sign in to comment.