• Kirill A. Shutemov's avatar
    mm: fix vma_is_anonymous() false-positives · bfd40eaf
    Kirill A. Shutemov authored
    vma_is_anonymous() relies on ->vm_ops being NULL to detect anonymous
    VMA.  This is unreliable as ->mmap may not set ->vm_ops.
    
    False-positive vma_is_anonymous() may lead to crashes:
    
    	next ffff8801ce5e7040 prev ffff8801d20eca50 mm ffff88019c1e13c0
    	prot 27 anon_vma ffff88019680cdd8 vm_ops 0000000000000000
    	pgoff 0 file ffff8801b2ec2d00 private_data 0000000000000000
    	flags: 0xff(read|write|exec|shared|mayread|maywrite|mayexec|mayshare)
    	------------[ cut here ]------------
    	kernel BUG at mm/memory.c:1422!
    	invalid opcode: 0000 [#1] SMP KASAN
    	CPU: 0 PID: 18486 Comm: syz-executor3 Not tainted 4.18.0-rc3+ #136
    	Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google
    	01/01/2011
    	RIP: 0010:zap_pmd_range mm/memory.c:1421 [inline]
    	RIP: 0010:zap_pud_range mm/memory.c:1466 [inline]
    	RIP: 0010:zap_p4d_range mm/memory.c:1487 [inline]
    	RIP: 0010:unmap_page_range+0x1c18/0x2220 mm/memory.c:1508
    	Call Trace:
    	 unmap_single_vma+0x1a0/0x310 mm/memory.c:1553
    	 zap_page_range_single+0x3cc/0x580 mm/memory.c:1644
    	 unmap_mapping_range_vma mm/memory.c:2792 [inline]
    	 unmap_mapping_range_tree mm/memory.c:2813 [inline]
    	 unmap_mapping_pages+0x3a7/0x5b0 mm/memory.c:2845
    	 unmap_mapping_range+0x48/0x60 mm/memory.c:2880
    	 truncate_pagecache+0x54/0x90 mm/truncate.c:800
    	 truncate_setsize+0x70/0xb0 mm/truncate.c:826
    	 simple_setattr+0xe9/0x110 fs/libfs.c:409
    	 notify_change+0xf13/0x10f0 fs/attr.c:335
    	 do_truncate+0x1ac/0x2b0 fs/open.c:63
    	 do_sys_ftruncate+0x492/0x560 fs/open.c:205
    	 __do_sys_ftruncate fs/open.c:215 [inline]
    	 __se_sys_ftruncate fs/open.c:213 [inline]
    	 __x64_sys_ftruncate+0x59/0x80 fs/open.c:213
    	 do_syscall_64+0x1b9/0x820 arch/x86/entry/common.c:290
    	 entry_SYSCALL_64_after_hwframe+0x49/0xbe
    
    Reproducer:
    
    	#include <stdio.h>
    	#include <stddef.h>
    	#include <stdint.h>
    	#include <stdlib.h>
    	#include <string.h>
    	#include <sys/types.h>
    	#include <sys/stat.h>
    	#include <sys/ioctl.h>
    	#include <sys/mman.h>
    	#include <unistd.h>
    	#include <fcntl.h>
    
    	#define KCOV_INIT_TRACE			_IOR('c', 1, unsigned long)
    	#define KCOV_ENABLE			_IO('c', 100)
    	#define KCOV_DISABLE			_IO('c', 101)
    	#define COVER_SIZE			(1024<<10)
    
    	#define KCOV_TRACE_PC  0
    	#define KCOV_TRACE_CMP 1
    
    	int main(int argc, char **argv)
    	{
    		int fd;
    		unsigned long *cover;
    
    		system("mount -t debugfs none /sys/kernel/debug");
    		fd = open("/sys/kernel/debug/kcov", O_RDWR);
    		ioctl(fd, KCOV_INIT_TRACE, COVER_SIZE);
    		cover = mmap(NULL, COVER_SIZE * sizeof(unsigned long),
    				PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    		munmap(cover, COVER_SIZE * sizeof(unsigned long));
    		cover = mmap(NULL, COVER_SIZE * sizeof(unsigned long),
    				PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
    		memset(cover, 0, COVER_SIZE * sizeof(unsigned long));
    		ftruncate(fd, 3UL << 20);
    		return 0;
    	}
    
    This can be fixed by assigning anonymous VMAs own vm_ops and not relying
    on it being NULL.
    
    If ->mmap() failed to set ->vm_ops, mmap_region() will set it to
    dummy_vm_ops.  This way we will have non-NULL ->vm_ops for all VMAs.
    
    Link: http://lkml.kernel.org/r/20180724121139.62570-4-kirill.shutemov@linux.intel.comSigned-off-by: 's avatarKirill A. Shutemov <kirill.shutemov@linux.intel.com>
    Reported-by: syzbot+3f84280d52be9b7083cc@syzkaller.appspotmail.com
    Acked-by: 's avatarLinus Torvalds <torvalds@linux-foundation.org>
    Reviewed-by: 's avatarAndrew Morton <akpm@linux-foundation.org>
    Cc: Dmitry Vyukov <dvyukov@google.com>
    Cc: Oleg Nesterov <oleg@redhat.com>
    Cc: Andrea Arcangeli <aarcange@redhat.com>
    Cc: <stable@vger.kernel.org>
    Signed-off-by: 's avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: 's avatarLinus Torvalds <torvalds@linux-foundation.org>
    bfd40eaf