Skip to content

Commit 42cb447

Browse files
yangerkuntytso
authored andcommitted
ext4: fix potential infinite loop in ext4_dx_readdir()
When ext4_htree_fill_tree() fails, ext4_dx_readdir() can run into an infinite loop since if info->last_pos != ctx->pos this will reset the directory scan and reread the failing entry. For example: 1. a dx_dir which has 3 block, block 0 as dx_root block, block 1/2 as leaf block which own the ext4_dir_entry_2 2. block 1 read ok and call_filldir which will fill the dirent and update the ctx->pos 3. block 2 read fail, but we has already fill some dirent, so we will return back to userspace will a positive return val(see ksys_getdents64) 4. the second ext4_dx_readdir will reset the world since info->last_pos != ctx->pos, and will also init the curr_hash which pos to block 1 5. So we will read block1 too, and once block2 still read fail, we can only fill one dirent because the hash of the entry in block1(besides the last one) won't greater than curr_hash 6. this time, we forget update last_pos too since the read for block2 will fail, and since we has got the one entry, ksys_getdents64 can return success 7. Latter we will trapped in a loop with step 4~6 Cc: [email protected] Signed-off-by: yangerkun <[email protected]> Reviewed-by: Jan Kara <[email protected]> Signed-off-by: Theodore Ts'o <[email protected]> Link: https://lore.kernel.org/r/[email protected]
1 parent bb9464e commit 42cb447

File tree

1 file changed

+3
-3
lines changed

1 file changed

+3
-3
lines changed

fs/ext4/dir.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -551,7 +551,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
551551
struct dir_private_info *info = file->private_data;
552552
struct inode *inode = file_inode(file);
553553
struct fname *fname;
554-
int ret;
554+
int ret = 0;
555555

556556
if (!info) {
557557
info = ext4_htree_create_dir_info(file, ctx->pos);
@@ -599,7 +599,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
599599
info->curr_minor_hash,
600600
&info->next_hash);
601601
if (ret < 0)
602-
return ret;
602+
goto finished;
603603
if (ret == 0) {
604604
ctx->pos = ext4_get_htree_eof(file);
605605
break;
@@ -630,7 +630,7 @@ static int ext4_dx_readdir(struct file *file, struct dir_context *ctx)
630630
}
631631
finished:
632632
info->last_pos = ctx->pos;
633-
return 0;
633+
return ret < 0 ? ret : 0;
634634
}
635635

636636
static int ext4_release_dir(struct inode *inode, struct file *filp)

0 commit comments

Comments
 (0)