[PATCH] erofs-utils: fsck: fix stack-overflow due to directory loops

Gao Xiang hsiangkao at linux.alibaba.com
Thu Feb 27 13:56:39 AEDT 2025


Just record all parent directories to address this issue as
a trivial solution for now.

Closes: https://github.com/erofs/erofs-utils/issues/15
Signed-off-by: Gao Xiang <hsiangkao at linux.alibaba.com>
---
 fsck/main.c | 17 +++++++++++++++--
 lib/dir.c   |  2 +-
 2 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/fsck/main.c b/fsck/main.c
index 372fee6..0e8b944 100644
--- a/fsck/main.c
+++ b/fsck/main.c
@@ -20,7 +20,13 @@
 
 static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid);
 
+struct erofsfsck_dirstack {
+	erofs_nid_t dirs[PATH_MAX];
+	int top;
+};
+
 struct erofsfsck_cfg {
+	struct erofsfsck_dirstack dirstack;
 	u64 physical_blocks;
 	u64 logical_blocks;
 	char *extract_path;
@@ -967,7 +973,7 @@ verify:
 
 static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
 {
-	int ret;
+	int ret, i;
 	struct erofs_inode inode;
 
 	erofs_dbg("check inode: nid(%llu)", nid | 0ULL);
@@ -999,7 +1005,6 @@ static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
 			return ret;
 	}
 
-	/* XXXX: the dir depth should be restricted in order to avoid loops */
 	if (S_ISDIR(inode.i_mode)) {
 		struct erofs_dir_context ctx = {
 			.flags = EROFS_READDIR_VALID_PNID,
@@ -1008,7 +1013,15 @@ static int erofsfsck_check_inode(erofs_nid_t pnid, erofs_nid_t nid)
 			.cb = erofsfsck_dirent_iter,
 		};
 
+		/* XXX: support the deeper cases later */
+		if (fsckcfg.dirstack.top >= ARRAY_SIZE(fsckcfg.dirstack.dirs))
+			return -ENAMETOOLONG;
+		for (i = 0; i < fsckcfg.dirstack.top; ++i)
+			if (inode.nid == fsckcfg.dirstack.dirs[i])
+				return -ELOOP;
+		fsckcfg.dirstack.dirs[fsckcfg.dirstack.top++] = pnid;
 		ret = erofs_iterate_dir(&ctx, true);
+		--fsckcfg.dirstack.top;
 	}
 
 	if (!ret && !erofs_is_packed_inode(&inode))
diff --git a/lib/dir.c b/lib/dir.c
index 1223cbc..d786a5b 100644
--- a/lib/dir.c
+++ b/lib/dir.c
@@ -179,7 +179,7 @@ int erofs_iterate_dir(struct erofs_dir_context *ctx, bool fsck)
 	}
 
 	if (fsck && (ctx->flags & EROFS_READDIR_ALL_SPECIAL_FOUND) !=
-			EROFS_READDIR_ALL_SPECIAL_FOUND) {
+			EROFS_READDIR_ALL_SPECIAL_FOUND && !err) {
 		erofs_err("`.' or `..' dirent is missing @ nid %llu",
 			  dir->nid | 0ULL);
 		return -EFSCORRUPTED;
-- 
2.43.5



More information about the Linux-erofs mailing list