Skip to content
  • Eric Biggers's avatar
    ipc/shm: fix use-after-free of shm file via remap_file_pages() · 3f05317d
    Eric Biggers authored
    syzbot reported a use-after-free of shm_file_data(file)->file->f_op in
    shm_get_unmapped_area(), called via sys_remap_file_pages().
    
    Unfortunately it couldn't generate a reproducer, but I found a bug which
    I think caused it.  When remap_file_pages() is passed a full System V
    shared memory segment, the memory is first unmapped, then a new map is
    created using the ->vm_file.  Between these steps, the shm ID can be
    removed and reused for a new shm segment.  But, shm_mmap() only checks
    whether the ID is currently valid before calling the underlying file's
    ->mmap(); it doesn't check whether it was reused.  Thus it can use the
    wrong underlying file, one that was already freed.
    
    Fix this by making the "outer" shm file (the one that gets put in
    ->vm_file) hold a reference to the real shm file, and by making
    __shm_open() require that the file associated with the shm ID matches
    the one associated with the "outer" file.
    
    Taking the reference to the real shm file is needed to fully solve the
    problem, since otherwise sfd->file could point to a freed file, which
    then could be reallocated for the reused shm ID, causing the wrong shm
    segment to be mapped (and without the required permission checks).
    
    Commit 1ac0b6de ("ipc/shm: handle removed segments gracefully in
    shm_mmap()") almost fixed this bug, but it didn't go far enough because
    it didn't consider the case where the shm ID is reused.
    
    The following program usually reproduces this bug:
    
    	#include <stdlib.h>
    	#include <sys/shm.h>
    	#include <sys/syscall.h>
    	#include <unistd.h>
    
    	int main()
    	{
    		int is_parent = (fork() != 0);
    		srand(getpid());
    		for (;;) {
    			int id = shmget(0xF00F, 4096, IPC_CREAT|0700);
    			if (is_parent) {
    				void *addr = shmat(id, NULL, 0);
    				usleep(rand() % 50);
    				while (!syscall(__NR_remap_file_pages, addr, 4096, 0, 0, 0));
    			} else {
    				usleep(rand() % 50);
    				shmctl(id, IPC_RMID, NULL);
    			}
    		}
    	}
    
    It causes the following NULL pointer dereference due to a 'struct file'
    being used while it's being freed.  (I couldn't actually get a KASAN
    use-after-free splat like in the syzbot report.  But I think it's
    possible with this bug; it would just take a more extraordinary race...)
    
    	BUG: unable to handle kernel NULL pointer dereference at 0000000000000058
    	PGD 0 P4D 0
    	Oops: 0000 [#1] SMP NOPTI
    	CPU: 9 PID: 258 Comm: syz_ipc Not tainted 4.16.0-05140-gf8cf2f16 #189
    	Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.11.0-20171110_100015-anatol 04/01/2014
    	RIP: 0010:d_inode include/linux/dcache.h:519 [inline]
    	RIP: 0010:touch_atime+0x25/0xd0 fs/inode.c:1724
    	[...]
    	Call Trace:
    	 file_accessed include/linux/fs.h:2063 [inline]
    	 shmem_mmap+0x25/0x40 mm/shmem.c:2149
    	 call_mmap include/linux/fs.h:1789 [inline]
    	 shm_mmap+0x34/0x80 ipc/shm.c:465
    	 call_mmap include/linux/fs.h:1789 [inline]
    	 mmap_region+0x309/0x5b0 mm/mmap.c:1712
    	 do_mmap+0x294/0x4a0 mm/mmap.c:1483
    	 do_mmap_pgoff include/linux/mm.h:2235 [inline]
    	 SYSC_remap_file_pages mm/mmap.c:2853 [inline]
    	 SyS_remap_file_pages+0x232/0x310 mm/mmap.c:2769
    	 do_syscall_64+0x64/0x1a0 arch/x86/entry/common.c:287
    	 entry_SYSCALL_64_after_hwframe+0x42/0xb7
    
    [ebiggers@google.com: add comment]
      Link: http://lkml.kernel.org/r/20180410192850.235835-1-ebiggers3@gmail.com
    Link: http://lkml.kernel.org/r/20180409043039.28915-1-ebiggers3@gmail.com
    
    
    Reported-by: default avatar <syzbot+d11f321e7f1923157eac80aa990b446596f46439@syzkaller.appspotmail.com>
    Fixes: c8d78c18
    
     ("mm: replace remap_file_pages() syscall with emulation")
    Signed-off-by: default avatarEric Biggers <ebiggers@google.com>
    Acked-by: default avatarKirill A. Shutemov <kirill.shutemov@linux.intel.com>
    Acked-by: default avatarDavidlohr Bueso <dbueso@suse.de>
    Cc: Manfred Spraul <manfred@colorfullife.com>
    Cc: "Eric W . Biederman" <ebiederm@xmission.com>
    Cc: <stable@vger.kernel.org>
    Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
    Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
    3f05317d