erofs-utils: fsck: fix stack-overflow due to directory loops
authorGao Xiang <hsiangkao@linux.alibaba.com>
Thu, 27 Feb 2025 02:56:39 +0000 (10:56 +0800)
committerGao Xiang <hsiangkao@linux.alibaba.com>
Thu, 27 Feb 2025 03:06:42 +0000 (11:06 +0800)
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@linux.alibaba.com>
Link: https://lore.kernel.org/r/20250227025639.2160988-1-hsiangkao@linux.alibaba.com
fsck/main.c
lib/dir.c

index 372fee67ad6fbcf3a8fc769097e138c80b734f38..0e8b94417cda0cb81eeb156cd955c095b669f646 100644 (file)
 
 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))
index 1223cbc822d1103787a20301e0672ee226f2adf9..d786a5b9b2e5cdfe958db447f334c9baa2f94928 100644 (file)
--- 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;