]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/blob - releases/4.4.129/ipc-shm-fix-use-after-free-of-shm-file-via-remap_file_pages.patch
3.18-stable patches
[thirdparty/kernel/stable-queue.git] / releases / 4.4.129 / ipc-shm-fix-use-after-free-of-shm-file-via-remap_file_pages.patch
1 From 3f05317d9889ab75c7190dcd39491d2a97921984 Mon Sep 17 00:00:00 2001
2 From: Eric Biggers <ebiggers@google.com>
3 Date: Fri, 13 Apr 2018 15:35:30 -0700
4 Subject: ipc/shm: fix use-after-free of shm file via remap_file_pages()
5
6 From: Eric Biggers <ebiggers@google.com>
7
8 commit 3f05317d9889ab75c7190dcd39491d2a97921984 upstream.
9
10 syzbot reported a use-after-free of shm_file_data(file)->file->f_op in
11 shm_get_unmapped_area(), called via sys_remap_file_pages().
12
13 Unfortunately it couldn't generate a reproducer, but I found a bug which
14 I think caused it. When remap_file_pages() is passed a full System V
15 shared memory segment, the memory is first unmapped, then a new map is
16 created using the ->vm_file. Between these steps, the shm ID can be
17 removed and reused for a new shm segment. But, shm_mmap() only checks
18 whether the ID is currently valid before calling the underlying file's
19 ->mmap(); it doesn't check whether it was reused. Thus it can use the
20 wrong underlying file, one that was already freed.
21
22 Fix this by making the "outer" shm file (the one that gets put in
23 ->vm_file) hold a reference to the real shm file, and by making
24 __shm_open() require that the file associated with the shm ID matches
25 the one associated with the "outer" file.
26
27 Taking the reference to the real shm file is needed to fully solve the
28 problem, since otherwise sfd->file could point to a freed file, which
29 then could be reallocated for the reused shm ID, causing the wrong shm
30 segment to be mapped (and without the required permission checks).
31
32 Commit 1ac0b6dec656 ("ipc/shm: handle removed segments gracefully in
33 shm_mmap()") almost fixed this bug, but it didn't go far enough because
34 it didn't consider the case where the shm ID is reused.
35
36 The following program usually reproduces this bug:
37
38 #include <stdlib.h>
39 #include <sys/shm.h>
40 #include <sys/syscall.h>
41 #include <unistd.h>
42
43 int main()
44 {
45 int is_parent = (fork() != 0);
46 srand(getpid());
47 for (;;) {
48 int id = shmget(0xF00F, 4096, IPC_CREAT|0700);
49 if (is_parent) {
50 void *addr = shmat(id, NULL, 0);
51 usleep(rand() % 50);
52 while (!syscall(__NR_remap_file_pages, addr, 4096, 0, 0, 0));
53 } else {
54 usleep(rand() % 50);
55 shmctl(id, IPC_RMID, NULL);
56 }
57 }
58 }
59
60 It causes the following NULL pointer dereference due to a 'struct file'
61 being used while it's being freed. (I couldn't actually get a KASAN
62 use-after-free splat like in the syzbot report. But I think it's
63 possible with this bug; it would just take a more extraordinary race...)
64
65 BUG: unable to handle kernel NULL pointer dereference at 0000000000000058
66 PGD 0 P4D 0
67 Oops: 0000 [#1] SMP NOPTI
68 CPU: 9 PID: 258 Comm: syz_ipc Not tainted 4.16.0-05140-gf8cf2f16a7c95 #189
69 Hardware name: QEMU Standard PC (i440FX + PIIX, 1996), BIOS 1.11.0-20171110_100015-anatol 04/01/2014
70 RIP: 0010:d_inode include/linux/dcache.h:519 [inline]
71 RIP: 0010:touch_atime+0x25/0xd0 fs/inode.c:1724
72 [...]
73 Call Trace:
74 file_accessed include/linux/fs.h:2063 [inline]
75 shmem_mmap+0x25/0x40 mm/shmem.c:2149
76 call_mmap include/linux/fs.h:1789 [inline]
77 shm_mmap+0x34/0x80 ipc/shm.c:465
78 call_mmap include/linux/fs.h:1789 [inline]
79 mmap_region+0x309/0x5b0 mm/mmap.c:1712
80 do_mmap+0x294/0x4a0 mm/mmap.c:1483
81 do_mmap_pgoff include/linux/mm.h:2235 [inline]
82 SYSC_remap_file_pages mm/mmap.c:2853 [inline]
83 SyS_remap_file_pages+0x232/0x310 mm/mmap.c:2769
84 do_syscall_64+0x64/0x1a0 arch/x86/entry/common.c:287
85 entry_SYSCALL_64_after_hwframe+0x42/0xb7
86
87 [ebiggers@google.com: add comment]
88 Link: http://lkml.kernel.org/r/20180410192850.235835-1-ebiggers3@gmail.com
89 Link: http://lkml.kernel.org/r/20180409043039.28915-1-ebiggers3@gmail.com
90 Reported-by: syzbot+d11f321e7f1923157eac80aa990b446596f46439@syzkaller.appspotmail.com
91 Fixes: c8d78c1823f4 ("mm: replace remap_file_pages() syscall with emulation")
92 Signed-off-by: Eric Biggers <ebiggers@google.com>
93 Acked-by: Kirill A. Shutemov <kirill.shutemov@linux.intel.com>
94 Acked-by: Davidlohr Bueso <dbueso@suse.de>
95 Cc: Manfred Spraul <manfred@colorfullife.com>
96 Cc: "Eric W . Biederman" <ebiederm@xmission.com>
97 Cc: <stable@vger.kernel.org>
98 Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
99 Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
100 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
101
102 ---
103 ipc/shm.c | 23 ++++++++++++++++++++---
104 1 file changed, 20 insertions(+), 3 deletions(-)
105
106 --- a/ipc/shm.c
107 +++ b/ipc/shm.c
108 @@ -198,6 +198,12 @@ static int __shm_open(struct vm_area_str
109 if (IS_ERR(shp))
110 return PTR_ERR(shp);
111
112 + if (shp->shm_file != sfd->file) {
113 + /* ID was reused */
114 + shm_unlock(shp);
115 + return -EINVAL;
116 + }
117 +
118 shp->shm_atim = get_seconds();
119 shp->shm_lprid = task_tgid_vnr(current);
120 shp->shm_nattch++;
121 @@ -414,8 +420,9 @@ static int shm_mmap(struct file *file, s
122 int ret;
123
124 /*
125 - * In case of remap_file_pages() emulation, the file can represent
126 - * removed IPC ID: propogate shm_lock() error to caller.
127 + * In case of remap_file_pages() emulation, the file can represent an
128 + * IPC ID that was removed, and possibly even reused by another shm
129 + * segment already. Propagate this case as an error to caller.
130 */
131 ret =__shm_open(vma);
132 if (ret)
133 @@ -439,6 +446,7 @@ static int shm_release(struct inode *ino
134 struct shm_file_data *sfd = shm_file_data(file);
135
136 put_ipc_ns(sfd->ns);
137 + fput(sfd->file);
138 shm_file_data(file) = NULL;
139 kfree(sfd);
140 return 0;
141 @@ -1198,7 +1206,16 @@ long do_shmat(int shmid, char __user *sh
142 file->f_mapping = shp->shm_file->f_mapping;
143 sfd->id = shp->shm_perm.id;
144 sfd->ns = get_ipc_ns(ns);
145 - sfd->file = shp->shm_file;
146 + /*
147 + * We need to take a reference to the real shm file to prevent the
148 + * pointer from becoming stale in cases where the lifetime of the outer
149 + * file extends beyond that of the shm segment. It's not usually
150 + * possible, but it can happen during remap_file_pages() emulation as
151 + * that unmaps the memory, then does ->mmap() via file reference only.
152 + * We'll deny the ->mmap() if the shm segment was since removed, but to
153 + * detect shm ID reuse we need to compare the file pointers.
154 + */
155 + sfd->file = get_file(shp->shm_file);
156 sfd->vm_ops = NULL;
157
158 err = security_mmap_file(file, prot, flags);