staging: erofs: keep corrupted fs from crashing kernel in erofs_readdir()
authorGao Xiang <gaoxiang25@huawei.com>
Thu, 28 Mar 2019 20:14:58 +0000 (04:14 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 29 Mar 2019 16:25:45 +0000 (17:25 +0100)
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>
drivers/staging/erofs/dir.c

index 829f7b12e0dcf4aa3ee34a5315f6d5f52b7087ec..9bbc68729c11052018c26335d6b88498e3491b32 100644 (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 */