OSDN Git Service

f2fs: sanity check of xattr entry size
authorJaegeuk Kim <jaegeuk@kernel.org>
Thu, 27 Dec 2018 03:54:07 +0000 (19:54 -0800)
committerJaegeuk Kim <jaegeuk@kernel.org>
Thu, 3 Jan 2019 00:17:57 +0000 (16:17 -0800)
There is a security report where f2fs_getxattr() has a hole to expose wrong
memory region when the image is malformed like this.

f2fs_getxattr: entry->e_name_len: 4, size: 12288, buffer_size: 16384, len: 4

Cc: <stable@vger.kernel.org>
Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
fs/f2fs/xattr.c

index 22b3cfd..71b27c5 100644 (file)
@@ -334,7 +334,7 @@ static int read_xattr_block(struct inode *inode, void *txattr_addr)
 static int lookup_all_xattrs(struct inode *inode, struct page *ipage,
                                unsigned int index, unsigned int len,
                                const char *name, struct f2fs_xattr_entry **xe,
-                               void **base_addr)
+                               void **base_addr, int *base_size)
 {
        void *cur_addr, *txattr_addr, *last_addr = NULL;
        nid_t xnid = F2FS_I(inode)->i_xattr_nid;
@@ -345,8 +345,8 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage,
        if (!size && !inline_size)
                return -ENODATA;
 
-       txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode),
-                       inline_size + size + XATTR_PADDING_SIZE, GFP_NOFS);
+       *base_size = inline_size + size + XATTR_PADDING_SIZE;
+       txattr_addr = f2fs_kzalloc(F2FS_I_SB(inode), *base_size, GFP_NOFS);
        if (!txattr_addr)
                return -ENOMEM;
 
@@ -358,8 +358,10 @@ static int lookup_all_xattrs(struct inode *inode, struct page *ipage,
 
                *xe = __find_inline_xattr(inode, txattr_addr, &last_addr,
                                                index, len, name);
-               if (*xe)
+               if (*xe) {
+                       *base_size = inline_size;
                        goto check;
+               }
        }
 
        /* read from xattr node block */
@@ -520,6 +522,7 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
        int error = 0;
        unsigned int size, len;
        void *base_addr = NULL;
+       int base_size;
 
        if (name == NULL)
                return -EINVAL;
@@ -530,7 +533,7 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
 
        down_read(&F2FS_I(inode)->i_xattr_sem);
        error = lookup_all_xattrs(inode, ipage, index, len, name,
-                               &entry, &base_addr);
+                               &entry, &base_addr, &base_size);
        up_read(&F2FS_I(inode)->i_xattr_sem);
        if (error)
                return error;
@@ -544,6 +547,11 @@ int f2fs_getxattr(struct inode *inode, int index, const char *name,
 
        if (buffer) {
                char *pval = entry->e_name + entry->e_name_len;
+
+               if (base_size - (pval - (char *)base_addr) < size) {
+                       error = -ERANGE;
+                       goto out;
+               }
                memcpy(buffer, pval, size);
        }
        error = size;