jfs: Fix usercopy whitelist for inline inode data
authorKees Cook <keescook@chromium.org>
Fri, 3 Aug 2018 19:52:58 +0000 (12:52 -0700)
committerKees Cook <keescook@chromium.org>
Sat, 4 Aug 2018 14:53:46 +0000 (07:53 -0700)
Bart Massey reported what turned out to be a usercopy whitelist false
positive in JFS when symlink contents exceeded 128 bytes. The inline
inode data (i_inline) is actually designed to overflow into the "extended
area" following it (i_inline_ea) when needed. So the whitelist needed to
be expanded to include both i_inline and i_inline_ea (the whole size
of which is calculated internally using IDATASIZE, 256, instead of
sizeof(i_inline), 128).

$ cd /mnt/jfs
$ touch $(perl -e 'print "B" x 250')
$ ln -s B* b
$ ls -l >/dev/null

[  249.436410] Bad or missing usercopy whitelist? Kernel memory exposure attempt detected from SLUB object 'jfs_ip' (offset 616, size 250)!

Reported-by: Bart Massey <bart.massey@gmail.com>
Fixes: 8d2704d382a9 ("jfs: Define usercopy region in jfs_ip slab cache")
Cc: Dave Kleikamp <shaggy@kernel.org>
Cc: jfs-discussion@lists.sourceforge.net
Cc: stable@vger.kernel.org
Signed-off-by: Kees Cook <keescook@chromium.org>
fs/jfs/jfs_dinode.h
fs/jfs/jfs_incore.h
fs/jfs/super.c

index 395c4c0d0f0667bf0f8e604d2e9b4b3eb1600698..1682a87c00b25992975bffacf2df5d7609931b1f 100644 (file)
@@ -115,6 +115,13 @@ struct dinode {
                                        dxd_t _dxd;     /* 16: */
                                        union {
                                                __le32 _rdev;   /* 4: */
+                                               /*
+                                                * The fast symlink area
+                                                * is expected to overflow
+                                                * into _inlineea when
+                                                * needed (which will clear
+                                                * INLINEEA).
+                                                */
                                                u8 _fastsymlink[128];
                                        } _u;
                                        u8 _inlineea[128];
index 1f26d1910409afb8d3de7ba4455b7b195ed5c4bd..9940a1e04cbfb766bb8609cb85890b0823fa1af5 100644 (file)
@@ -87,6 +87,7 @@ struct jfs_inode_info {
                struct {
                        unchar _unused[16];     /* 16: */
                        dxd_t _dxd;             /* 16: */
+                       /* _inline may overflow into _inline_ea when needed */
                        unchar _inline[128];    /* 128: inline symlink */
                        /* _inline_ea may overlay the last part of
                         * file._xtroot if maxentry = XTROOTINITSLOT
index 1b9264fd54b68606ac73b749ce1f3af3ea860381..f08571433aba2b3b78588eca8759894a39c86755 100644 (file)
@@ -967,8 +967,7 @@ static int __init init_jfs_fs(void)
        jfs_inode_cachep =
            kmem_cache_create_usercopy("jfs_ip", sizeof(struct jfs_inode_info),
                        0, SLAB_RECLAIM_ACCOUNT|SLAB_MEM_SPREAD|SLAB_ACCOUNT,
-                       offsetof(struct jfs_inode_info, i_inline),
-                       sizeof_field(struct jfs_inode_info, i_inline),
+                       offsetof(struct jfs_inode_info, i_inline), IDATASIZE,
                        init_once);
        if (jfs_inode_cachep == NULL)
                return -ENOMEM;