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 when needed. So the whitelist needed to be expanded
to include 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.mas...@gmail.com>
Fixes: 8d2704d382a9 ("jfs: Define usercopy region in jfs_ip slab cache")
Cc: sta...@vger.kernel.org
Signed-off-by: Kees Cook <keesc...@chromium.org>
---
I intend this to land via the usercopy tree...
---
 fs/jfs/jfs_dinode.h | 7 +++++++
 fs/jfs/jfs_incore.h | 1 +
 fs/jfs/super.c      | 3 +--
 3 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/fs/jfs/jfs_dinode.h b/fs/jfs/jfs_dinode.h
index 395c4c0d0f06..1682a87c00b2 100644
--- a/fs/jfs/jfs_dinode.h
+++ b/fs/jfs/jfs_dinode.h
@@ -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];
diff --git a/fs/jfs/jfs_incore.h b/fs/jfs/jfs_incore.h
index 1f26d1910409..9940a1e04cbf 100644
--- a/fs/jfs/jfs_incore.h
+++ b/fs/jfs/jfs_incore.h
@@ -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
diff --git a/fs/jfs/super.c b/fs/jfs/super.c
index 1b9264fd54b6..f08571433aba 100644
--- a/fs/jfs/super.c
+++ b/fs/jfs/super.c
@@ -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;
-- 
2.17.1


-- 
Kees Cook
Pixel Security

Reply via email to