Skip to content

Commit db86fba

Browse files
committed
Merge branch 'url-fuzz'
2 parents 48e8932 + 8d4bf40 commit db86fba

File tree

3 files changed

+36
-2
lines changed

3 files changed

+36
-2
lines changed

gix-path/src/realpath.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
pub enum Error {
55
#[error("The maximum allowed number {} of symlinks in path is exceeded", .max_symlinks)]
66
MaxSymlinksExceeded { max_symlinks: u8 },
7+
#[error("Cannot resolve symlinks in path with more than {max_symlink_checks} components (takes too long)")]
8+
ExcessiveComponentCount { max_symlink_checks: usize },
79
#[error(transparent)]
810
ReadLink(std::io::Error),
911
#[error(transparent)]
@@ -57,6 +59,8 @@ pub(crate) mod function {
5759
let mut num_symlinks = 0;
5860
let mut path_backing: PathBuf;
5961
let mut components = path.components();
62+
const MAX_SYMLINK_CHECKS: usize = 2048;
63+
let mut symlink_checks = 0;
6064
while let Some(component) = components.next() {
6165
match component {
6266
part @ (RootDir | Prefix(_)) => real_path.push(part),
@@ -68,6 +72,7 @@ pub(crate) mod function {
6872
}
6973
Normal(part) => {
7074
real_path.push(part);
75+
symlink_checks += 1;
7176
if real_path.is_symlink() {
7277
num_symlinks += 1;
7378
if num_symlinks > max_symlinks {
@@ -83,6 +88,11 @@ pub(crate) mod function {
8388
path_backing = link_destination;
8489
components = path_backing.components();
8590
}
91+
if symlink_checks > MAX_SYMLINK_CHECKS {
92+
return Err(Error::ExcessiveComponentCount {
93+
max_symlink_checks: MAX_SYMLINK_CHECKS,
94+
});
95+
}
8696
}
8797
}
8898
}

gix-path/tests/fixtures/fuzzed/54k-path-components.path

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

gix-path/tests/realpath/mod.rs

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,31 @@
1-
use std::path::Path;
1+
use std::path::{Path, PathBuf};
2+
use std::time::Duration;
23

3-
use gix_path::{realpath::Error, realpath_opts};
4+
use bstr::ByteVec;
45
use tempfile::tempdir;
56

7+
use gix_path::{realpath::Error, realpath_opts};
8+
9+
#[test]
10+
fn fuzzed_timeout() -> crate::Result {
11+
let path = PathBuf::from(std::fs::read("tests/fixtures/fuzzed/54k-path-components.path")?.into_string()?);
12+
assert_eq!(path.components().count(), 54862);
13+
let start = std::time::Instant::now();
14+
assert!(matches!(
15+
gix_path::realpath_opts(&path, Path::new("/cwd"), gix_path::realpath::MAX_SYMLINKS).unwrap_err(),
16+
gix_path::realpath::Error::ExcessiveComponentCount {
17+
max_symlink_checks: 2048
18+
}
19+
));
20+
assert!(
21+
start.elapsed() < Duration::from_millis(500),
22+
"took too long: {:.02} , we can't take too much time for this, and should keep the amount of work reasonable\
23+
as paths can be part of URls which sometimes are canonicalized",
24+
start.elapsed().as_secs_f32()
25+
);
26+
Ok(())
27+
}
28+
629
#[test]
730
fn assorted() -> crate::Result {
831
let cwd = tempdir()?;

0 commit comments

Comments
 (0)