Skip to content

Commit 0d8bf87

Browse files
committed
erofs: Bound recursion depth in populate_directory to prevent stack overflow
A crafted EROFS image with directory cycles can cause unbounded recursion in populate_directory(), leading to a stack overflow. Add a depth parameter and enforce a maximum of PATH_MAX / 2 (2048) levels, matching the theoretical limit for valid filesystem paths. Found by cargo-fuzz. Assisted-by: OpenCode (Claude Opus 4) Signed-off-by: Colin Walters <walters@verbum.org>
1 parent f690c47 commit 0d8bf87

1 file changed

Lines changed: 11 additions & 2 deletions

File tree

crates/composefs/src/erofs/reader.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1007,21 +1007,30 @@ fn dir_entries<'a>(
10071007
Ok(entries)
10081008
}
10091009

1010+
/// Maximum directory nesting depth. PATH_MAX is 4096 on Linux, and directory names
1011+
/// must be at least 2 bytes (1 char + separator), so the theoretical max is PATH_MAX / 2.
1012+
const MAX_DIRECTORY_DEPTH: usize = 4096 / 2;
1013+
10101014
/// Recursively populate a `tree::Directory` from an erofs directory inode.
10111015
fn populate_directory<ObjectID: FsVerityHashValue>(
10121016
img: &Image,
10131017
dir_inode: &InodeType,
10141018
dir: &mut tree::Directory<ObjectID>,
10151019
hardlinks: &mut HashMap<u64, Rc<tree::Leaf<ObjectID>>>,
1020+
depth: usize,
10161021
) -> anyhow::Result<()> {
1022+
if depth >= MAX_DIRECTORY_DEPTH {
1023+
return Err(ErofsReaderError::DepthExceeded.into());
1024+
}
1025+
10171026
for (name_bytes, nid) in dir_entries(img, dir_inode)? {
10181027
let name = OsStr::from_bytes(name_bytes);
10191028
let child_inode = img.inode(nid)?;
10201029

10211030
if child_inode.mode().is_dir() {
10221031
let child_stat = stat_from_inode_for_tree(img, &child_inode)?;
10231032
let mut child_dir = tree::Directory::new(child_stat);
1024-
populate_directory(img, &child_inode, &mut child_dir, hardlinks)
1033+
populate_directory(img, &child_inode, &mut child_dir, hardlinks, depth + 1)
10251034
.with_context(|| format!("reading directory {:?}", name))?;
10261035
dir.insert(name, tree::Inode::Directory(Box::new(child_dir)));
10271036
} else {
@@ -1090,7 +1099,7 @@ pub fn erofs_to_filesystem<ObjectID: FsVerityHashValue>(
10901099

10911100
let mut hardlinks: HashMap<u64, Rc<tree::Leaf<ObjectID>>> = HashMap::new();
10921101

1093-
populate_directory(&img, &root_inode, &mut fs.root, &mut hardlinks)
1102+
populate_directory(&img, &root_inode, &mut fs.root, &mut hardlinks, 0)
10941103
.context("reading root directory")?;
10951104

10961105
Ok(fs)

0 commit comments

Comments
 (0)