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

Commit 738dda85 authored by Gao Xiang's avatar Gao Xiang Committed by Greg Kroah-Hartman
Browse files

staging: erofs: keep corrupted fs from crashing kernel in erofs_readdir()



commit 33bac912840fe64dbc15556302537dc6a17cac63 upstream.

After commit 419d6efc50e9, kernel cannot be crashed in the namei
path. However, corrupted nameoff can do harm in the process of
readdir for scenerios without dm-verity as well. Fix it now.

Fixes: 3aa8ec71 ("staging: erofs: add directory operations")
Cc: <stable@vger.kernel.org> # 4.19+
Signed-off-by: default avatarGao Xiang <gaoxiang25@huawei.com>
Reviewed-by: default avatarChao Yu <yuchao0@huawei.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 83bbd66b
Loading
Loading
Loading
Loading
+25 −20
Original line number Diff line number Diff line
@@ -23,6 +23,21 @@ static const unsigned char erofs_filetype_table[EROFS_FT_MAX] = {
	[EROFS_FT_SYMLINK]	= DT_LNK,
};

static void debug_one_dentry(unsigned char d_type, const char *de_name,
			     unsigned int de_namelen)
{
#ifdef CONFIG_EROFS_FS_DEBUG
	/* since the on-disk name could not have the trailing '\0' */
	unsigned char dbg_namebuf[EROFS_NAME_LEN + 1];

	memcpy(dbg_namebuf, de_name, de_namelen);
	dbg_namebuf[de_namelen] = '\0';

	debugln("found dirent %s de_len %u d_type %d", dbg_namebuf,
		de_namelen, d_type);
#endif
}

static int erofs_fill_dentries(struct dir_context *ctx,
	void *dentry_blk, unsigned *ofs,
	unsigned nameoff, unsigned maxsize)
@@ -33,14 +48,10 @@ static int erofs_fill_dentries(struct dir_context *ctx,
	de = dentry_blk + *ofs;
	while (de < end) {
		const char *de_name;
		int de_namelen;
		unsigned int de_namelen;
		unsigned char d_type;
#ifdef CONFIG_EROFS_FS_DEBUG
		unsigned dbg_namelen;
		unsigned char dbg_namebuf[EROFS_NAME_LEN];
#endif

		if (unlikely(de->file_type < EROFS_FT_MAX))
		if (de->file_type < EROFS_FT_MAX)
			d_type = erofs_filetype_table[de->file_type];
		else
			d_type = DT_UNKNOWN;
@@ -48,26 +59,20 @@ static int erofs_fill_dentries(struct dir_context *ctx,
		nameoff = le16_to_cpu(de->nameoff);
		de_name = (char *)dentry_blk + nameoff;

		de_namelen = unlikely(de + 1 >= end) ?
			/* last directory entry */
			strnlen(de_name, maxsize - nameoff) :
			le16_to_cpu(de[1].nameoff) - nameoff;
		/* the last dirent in the block? */
		if (de + 1 >= end)
			de_namelen = strnlen(de_name, maxsize - nameoff);
		else
			de_namelen = le16_to_cpu(de[1].nameoff) - nameoff;

		/* a corrupted entry is found */
		if (unlikely(de_namelen < 0)) {
		if (unlikely(nameoff + de_namelen > maxsize ||
			     de_namelen > EROFS_NAME_LEN)) {
			DBG_BUGON(1);
			return -EIO;
		}

#ifdef CONFIG_EROFS_FS_DEBUG
		dbg_namelen = min(EROFS_NAME_LEN - 1, de_namelen);
		memcpy(dbg_namebuf, de_name, dbg_namelen);
		dbg_namebuf[dbg_namelen] = '\0';

		debugln("%s, found de_name %s de_len %d d_type %d", __func__,
			dbg_namebuf, de_namelen, d_type);
#endif

		debug_one_dentry(d_type, de_name, de_namelen);
		if (!dir_emit(ctx, de_name, de_namelen,
					le64_to_cpu(de->nid), d_type))
			/* stoped by some reason */