| From aa71ecd8d86500da6081a72da6b0b524007e0627 Mon Sep 17 00:00:00 2001 |
| From: Chen Jun <chenjun102@huawei.com> |
| Date: Sat, 30 Nov 2019 17:58:11 -0800 |
| Subject: mm/shmem.c: cast the type of unmap_start to u64 |
| |
| From: Chen Jun <chenjun102@huawei.com> |
| |
| commit aa71ecd8d86500da6081a72da6b0b524007e0627 upstream. |
| |
| In 64bit system. sb->s_maxbytes of shmem filesystem is MAX_LFS_FILESIZE, |
| which equal LLONG_MAX. |
| |
| If offset > LLONG_MAX - PAGE_SIZE, offset + len < LLONG_MAX in |
| shmem_fallocate, which will pass the checking in vfs_fallocate. |
| |
| /* Check for wrap through zero too */ |
| if (((offset + len) > inode->i_sb->s_maxbytes) || ((offset + len) < 0)) |
| return -EFBIG; |
| |
| loff_t unmap_start = round_up(offset, PAGE_SIZE) in shmem_fallocate |
| causes a overflow. |
| |
| Syzkaller reports a overflow problem in mm/shmem: |
| |
| UBSAN: Undefined behaviour in mm/shmem.c:2014:10 |
| signed integer overflow: '9223372036854775807 + 1' cannot be represented in type 'long long int' |
| CPU: 0 PID:17076 Comm: syz-executor0 Not tainted 4.1.46+ #1 |
| Hardware name: linux, dummy-virt (DT) |
| Call trace: |
| dump_backtrace+0x0/0x2c8 arch/arm64/kernel/traps.c:100 |
| show_stack+0x20/0x30 arch/arm64/kernel/traps.c:238 |
| __dump_stack lib/dump_stack.c:15 [inline] |
| ubsan_epilogue+0x18/0x70 lib/ubsan.c:164 |
| handle_overflow+0x158/0x1b0 lib/ubsan.c:195 |
| shmem_fallocate+0x6d0/0x820 mm/shmem.c:2104 |
| vfs_fallocate+0x238/0x428 fs/open.c:312 |
| SYSC_fallocate fs/open.c:335 [inline] |
| SyS_fallocate+0x54/0xc8 fs/open.c:239 |
| |
| The highest bit of unmap_start will be appended with sign bit 1 |
| (overflow) when calculate shmem_falloc.start: |
| |
| shmem_falloc.start = unmap_start >> PAGE_SHIFT. |
| |
| Fix it by casting the type of unmap_start to u64, when right shifted. |
| |
| This bug is found in LTS Linux 4.1. It also seems to exist in mainline. |
| |
| Link: http://lkml.kernel.org/r/1573867464-5107-1-git-send-email-chenjun102@huawei.com |
| Signed-off-by: Chen Jun <chenjun102@huawei.com> |
| Reviewed-by: Andrew Morton <akpm@linux-foundation.org> |
| Cc: Hugh Dickins <hughd@google.com> |
| Cc: Qian Cai <cai@lca.pw> |
| Cc: Kefeng Wang <wangkefeng.wang@huawei.com> |
| Signed-off-by: Andrew Morton <akpm@linux-foundation.org> |
| Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| mm/shmem.c | 2 +- |
| 1 file changed, 1 insertion(+), 1 deletion(-) |
| |
| --- a/mm/shmem.c |
| +++ b/mm/shmem.c |
| @@ -2077,7 +2077,7 @@ static long shmem_fallocate(struct file |
| } |
| |
| shmem_falloc.waitq = &shmem_falloc_waitq; |
| - shmem_falloc.start = unmap_start >> PAGE_SHIFT; |
| + shmem_falloc.start = (u64)unmap_start >> PAGE_SHIFT; |
| shmem_falloc.next = (unmap_end + 1) >> PAGE_SHIFT; |
| spin_lock(&inode->i_lock); |
| inode->i_private = &shmem_falloc; |