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

After commit 419d6efc50, 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: 3aa8ec716e ("staging: erofs: add directory operations")
Cc: <stable@vger.kernel.org> # 4.19+
Signed-off-by: Gao Xiang <gaoxiang25@huawei.com>
Reviewed-by: Chao Yu <yuchao0@huawei.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Gao Xiang 2019-03-29 04:14:58 +08:00 committed by Greg Kroah-Hartman
parent 9498da46d1
commit 33bac91284

View File

@ -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 int *ofs,
unsigned int nameoff, unsigned int 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 int 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))
/* stopped by some reason */