Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 928a4771 authored by OGAWA Hirofumi's avatar OGAWA Hirofumi Committed by Linus Torvalds
Browse files

fat: fix fake_offset handling on error path



For the root directory, .  and ..  are faked (using dir_emit_dots()) and
ctx->pos is reset from 2 to 0.

A corrupted root directory could cause fat_get_entry() to fail, but
->iterate() (fat_readdir()) reports progress to the VFS (with ctx->pos
rewound to 0), so any following calls to ->iterate() continue to return
the same entries again and again.

The result is that userspace will never see the end of the directory,
causing e.g.  'ls' to hang in a getdents() loop.

[hirofumi@mail.parknet.co.jp: cleanup and make sure to correct fake_offset]
Reported-by: default avatarVegard Nossum <vegard.nossum@oracle.com>
Tested-by: default avatarVegard Nossum <vegard.nossum@oracle.com>
Signed-off-by: default avatarRichard Weinberger <richard.weinberger@gmail.com>
Signed-off-by: default avatarOGAWA Hirofumi <hirofumi@mail.parknet.co.jp>
Cc: <stable@vger.kernel.org>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent 1817889e
Loading
Loading
Loading
Loading
+11 −5
Original line number Diff line number Diff line
@@ -610,9 +610,9 @@ static int __fat_readdir(struct inode *inode, struct file *file,
		int status = fat_parse_long(inode, &cpos, &bh, &de,
					    &unicode, &nr_slots);
		if (status < 0) {
			ctx->pos = cpos;
			bh = NULL;
			ret = status;
			goto out;
			goto end_of_dir;
		} else if (status == PARSE_INVALID)
			goto record_end;
		else if (status == PARSE_NOT_LONGNAME)
@@ -654,8 +654,9 @@ static int __fat_readdir(struct inode *inode, struct file *file,
	fill_len = short_len;

start_filldir:
	if (!fake_offset)
	ctx->pos = cpos - (nr_slots + 1) * sizeof(struct msdos_dir_entry);
	if (fake_offset && ctx->pos < 2)
		ctx->pos = 2;

	if (!memcmp(de->name, MSDOS_DOT, MSDOS_NAME)) {
		if (!dir_emit_dot(file, ctx))
@@ -681,7 +682,11 @@ static int __fat_readdir(struct inode *inode, struct file *file,
	fake_offset = 0;
	ctx->pos = cpos;
	goto get_new;

end_of_dir:
	if (fake_offset && cpos < 2)
		ctx->pos = 2;
	else
		ctx->pos = cpos;
fill_failed:
	brelse(bh);
@@ -689,6 +694,7 @@ static int __fat_readdir(struct inode *inode, struct file *file,
		__putname(unicode);
out:
	mutex_unlock(&sbi->s_lock);

	return ret;
}