Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

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: 3aa8ec716e52 ("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>

authored by

Gao Xiang and committed by
Greg Kroah-Hartman
33bac912 9498da46

+25 -20
+25 -20
drivers/staging/erofs/dir.c
··· 23 23 [EROFS_FT_SYMLINK] = DT_LNK, 24 24 }; 25 25 26 + static void debug_one_dentry(unsigned char d_type, const char *de_name, 27 + unsigned int de_namelen) 28 + { 29 + #ifdef CONFIG_EROFS_FS_DEBUG 30 + /* since the on-disk name could not have the trailing '\0' */ 31 + unsigned char dbg_namebuf[EROFS_NAME_LEN + 1]; 32 + 33 + memcpy(dbg_namebuf, de_name, de_namelen); 34 + dbg_namebuf[de_namelen] = '\0'; 35 + 36 + debugln("found dirent %s de_len %u d_type %d", dbg_namebuf, 37 + de_namelen, d_type); 38 + #endif 39 + } 40 + 26 41 static int erofs_fill_dentries(struct dir_context *ctx, 27 42 void *dentry_blk, unsigned int *ofs, 28 43 unsigned int nameoff, unsigned int maxsize) ··· 48 33 de = dentry_blk + *ofs; 49 34 while (de < end) { 50 35 const char *de_name; 51 - int de_namelen; 36 + unsigned int de_namelen; 52 37 unsigned char d_type; 53 - #ifdef CONFIG_EROFS_FS_DEBUG 54 - unsigned int dbg_namelen; 55 - unsigned char dbg_namebuf[EROFS_NAME_LEN]; 56 - #endif 57 38 58 - if (unlikely(de->file_type < EROFS_FT_MAX)) 39 + if (de->file_type < EROFS_FT_MAX) 59 40 d_type = erofs_filetype_table[de->file_type]; 60 41 else 61 42 d_type = DT_UNKNOWN; ··· 59 48 nameoff = le16_to_cpu(de->nameoff); 60 49 de_name = (char *)dentry_blk + nameoff; 61 50 62 - de_namelen = unlikely(de + 1 >= end) ? 63 - /* last directory entry */ 64 - strnlen(de_name, maxsize - nameoff) : 65 - le16_to_cpu(de[1].nameoff) - nameoff; 51 + /* the last dirent in the block? */ 52 + if (de + 1 >= end) 53 + de_namelen = strnlen(de_name, maxsize - nameoff); 54 + else 55 + de_namelen = le16_to_cpu(de[1].nameoff) - nameoff; 66 56 67 57 /* a corrupted entry is found */ 68 - if (unlikely(de_namelen < 0)) { 58 + if (unlikely(nameoff + de_namelen > maxsize || 59 + de_namelen > EROFS_NAME_LEN)) { 69 60 DBG_BUGON(1); 70 61 return -EIO; 71 62 } 72 63 73 - #ifdef CONFIG_EROFS_FS_DEBUG 74 - dbg_namelen = min(EROFS_NAME_LEN - 1, de_namelen); 75 - memcpy(dbg_namebuf, de_name, dbg_namelen); 76 - dbg_namebuf[dbg_namelen] = '\0'; 77 - 78 - debugln("%s, found de_name %s de_len %d d_type %d", __func__, 79 - dbg_namebuf, de_namelen, d_type); 80 - #endif 81 - 64 + debug_one_dentry(d_type, de_name, de_namelen); 82 65 if (!dir_emit(ctx, de_name, de_namelen, 83 66 le64_to_cpu(de->nid), d_type)) 84 67 /* stopped by some reason */