Initial repository content import
[opteya:ib-hw-nes-create-qp-null.git] / README
1
2 Introduction
3 ------------
4
5 This a tool that should make Linux kernel's NetEffect RNIC driver (nes) returns
6 NULL to uverbs layer, while a NULL pointer is not an expected return value,
7 leading to NULL pointer dereferenced in uverbs_create_qp() (and under specific
8 circumstances, later, in ib_destroy_qp()).
9
10 While the tool can be used with any InfiniBand, iWARP, RoCE, RDMA adapter
11 (HCA), only NetEffect driver should be able to trigger the NULL pointer
12 dereference in upper layer.
13
14 To test, a NetEffect HCA (aka. NetEffect NE020 10Gb Accelerated Ethernet Adapter
15 (iWARP RNIC), aka. Intel NetEffect Ethernet Server Cluster Adapter) is needed
16 along with the matching kernel driver (iw_nes, enabled with
17 CONFIG_INFINIBAND_NES=[my]) and userspace driver (libnes library).
18
19 Build
20 -----
21
22 The tool can be built with:
23
24  $ autoreconf
25  $ ./configure
26  $ make
27
28 Run
29 ---
30
31 The tool can be executed directly:
32
33  $ ./ib-hw-nes-create-qp-null
34
35 Output
36 ------
37
38 Here's the output of test ran on a system with a QLogic^WIntel InfiniBand
39 HCA.
40
41 $ ./ib-hw-nes-create-qp-null
42 Opening qib0
43 Memory mapped @ 0x7f4ed4b04000 [page 0]
44               @ 0x7f4ed4b05000 [page 1]
45               @ 0x7f4ed4b06000 [page 2]
46               @ 0x7f4ed4b07000 [page 3]
47               @ 0x7f4ed4b08000 [page 4]
48     Unmapping @ 0x7f4ed4b05000 [page 1]
49     Unmapping @ 0x7f4ed4b07000 [page 3]
50 Using unmmaped page @ 0x7f4ed4b07000 [page 3] for response
51            Response @ 0x7f4ed4b06fe0 [page 2]
52 Using unmmaped page @ 0x7f4ed4b05000 [page 1]  for command
53             Command @ 0x7f4ed4b04fc0 [page 0]
54 CREATE_QP :  sret = 64 errno = 0 :  SUCCESS
55 DESTROY_QP :  sret = 24, errno = 0 :  SUCCESS
56
57 Explanation
58 -----------
59
60 In drivers/infiniband/hw/nes/nes_verbs.c, function nes_create_qp() calls
61 ib_copy_from_udata() [1] which in turn calls copy_from_user() [2].
62 If the pointer udata->inbuf [3] is not pointing to valid userspace page,
63 ib_copy_from_udata() will fail and returns -EFAULT.
64
65 [1] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/hw/nes/nes_verbs.c?id=v3.14-rc5#n1185
66 [2] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/rdma/ib_verbs.h?id=v3.14-rc5#n1506
67 [3] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/include/rdma/ib_verbs.h?id=v3.14-rc5#n1000
68
69 Then nes_create_qp() returns NULL [4].
70
71 [4] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/hw/nes/nes_verbs.c?id=v3.14-rc5#n1189
72
73 In drivers/infiniband/core/uverbs_cmd.c, function uverbs_create_qp(), which was
74 calling nes_create_qp() through device->create_qp function pointer [5], tests
75 its return code using IS_ERR() [6]. But does not check for NULL.
76
77 [5] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/uverbs_cmd.c?id=v3.14-rc5#n1589
78 [6] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/uverbs_cmd.c?id=v3.14-rc5#n1591
79
80 In the following lines of uverbs_create_qp(), NULL pointer is dereferenced
81 to access various fields of the struct ib_qp.
82
83 In most cases, memory page at address 0 is not mapped, so kernel will report
84 an Oops and will terminate the test program.
85
86 Unfortunately, if a memory page is mapped by userspace at 0x0,
87 uverbs_create_qp() would continue and record the NULL pointer in
88 struct idr ib_uverbs_qp_idr with idr_add_uobj() [7].
89
90 Then uverbs_create_qp() will try to returns the QP handler as allocated by
91 idr_add_uobj() to userspace [8]. (It will only be possible if page holding the
92 response buffer are valid and mapped, otherwise ib_destroy_qp() is called early
93 on NULL).
94
95 [7] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/uverbs_cmd.c?id=v3.14-rc5#n1617
96 [8] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/uverbs_cmd.c?id=v3.14-rc5#n1623
97
98 uverbs_create_qp() will return success to userspace, with a valid
99 QP handler in the response buffer.
100
101 But most uverbs won't be able to use the QP handler returned to userspace,
102 since they will try to retrieve the QP by its handler using idr_read_qp() [9]
103 and check for NULL pointer returned by idr_read_obj() [10] in case of invalid or
104 non-matching handler.
105
106 [9] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/uverbs_cmd.c?id=v3.14-rc5#n238
107 [10] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/uverbs_cmd.c?id=v3.14-rc5#n205
108
109 The only uverb that can be called against the handle is uverbs_destroy_qp()
110 since it use idr_write_uobj() [11] directly, allowing it to retrieve the NULL
111 pointer [12]. Then the function will call ib_destroy_qp() [13] with the NULL
112 pointer as struct qp * argument.
113
114 [11] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/uverbs_cmd.c?id=v3.14-rc5#n2003
115 [12] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/uverbs_cmd.c?id=v3.14-rc5#n2006
116 [13] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/uverbs_cmd.c?id=v3.14-rc5#n2014
117
118 ib_destroy_qp() [14] dereferences the NULL pointer to access to the struct
119 ib_device holding the function pointer destroy_qp() in order to call it [15].
120
121 [14] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/verbs.c?id=v3.14-rc5#n975
122 [15] http://git.kernel.org/cgit/linux/kernel/git/torvalds/linux.git/tree/drivers/infiniband/core/verbs.c?id=v3.14-rc5#n993
123
124 And you should guess the end of the story (see bibliography for related
125 documentation if not).
126
127 Why it is not a big deal ?
128 --------------------------
129
130 To turn this into an exploit page 0 must be mapped and accessible by userspace
131 and kernel: it's not possible by default, thanks to features added to Linux
132 kernel:
133
134 - vm.mmap_min_addr is the minimal address the kernel will allow you to map
135
136   - get:
137
138   $ sysctl vm.mmap_min_addr
139     or
140   $ cat /proc/sys/vm/mmap_min_addr
141
142   - set:
143
144   # sysctl -w vm.mmap_min_addr=0
145     or
146   # echo 0 > /proc/sys/vm/mmap_min_addr
147
148 - SELinux enforce another limit on context:
149
150   - get:
151
152   $ getsebool mmap_low_allowed
153
154   - set:
155
156   # setsebool mmap_low_allowed=on
157
158 - PaX UDREF (and KERNEXEC) (within grsecurity kernel) will disallow direct access
159   to userspace from kernel mode, so the kernel won't be able to access page 0
160   with a NULL pointer dereference.
161
162 - IA32 (eg. x86) SMAP[1] (and SMEP[2]) can also defeat NULL pointer dereference
163   from kernel mode [3].
164
165   [1] Supervisor Mode Access Prevention
166   [2] Supervisor Mode Execution Prevention
167   [3] http://forums.grsecurity.net/viewtopic.php?f=7&t=3046
168
169 What's needed to turn the test program in an exploit:
170 -----------------------------------------------------
171
172 After making nes driver returning NULL and create qp uverbs returns the
173 QP handler to userspace, exploit must have to overwrite struct ib_device
174 pointer and make it point to a struct ib_device of its own where
175 destroy_qp function pointer will be an exploit controlled function.
176 Then, test tool has to call destroy qp uverbs to make the kernel
177 call its exploit function.
178
179 Aside
180 -----
181
182 This also demonstrate that nes driver does not check udata->inlen before
183 trying to access to userspace command buffer. Conversely, it does not check
184 udata->outlen before trying to write to userspace response buffer.
185 Unfortunately, those behaviors are quite common in InfiniBand drivers.
186
187 Bibliography
188 ------------
189
190 "Bypassing Linux' NULL pointer dereference exploit prevention (mmap_min_addr)",
191 by Julien Tinnes, June 26, 2009
192 http://blog.cr0.org/2009/06/bypassing-linux-null-pointer.html
193
194 "Fun with NULL pointers, part 1", by Jonathan Corbet, July 20, 2009
195 http://lwn.net/Articles/342330/
196
197 "Fun with NULL pointers, part 2", by Jonathan Corbet, July 21, 2009
198 http://lwn.net/Articles/342420/
199
200 "mmap_min_addr on SELinux and non-SELinux systems", by Eric Paris,
201 July 21, 2009
202 http://eparis.livejournal.com/606.html
203
204 "Confining the unconfined. Oxymoron?", by Dan Walsh, July 21st, 2009
205 http://danwalsh.livejournal.com/30084.html
206
207 "Bug 511143 - selinux policy allows addr 0 mappings by default", July 13, 2009
208 https://bugzilla.redhat.com/show_bug.cgi?id=511143
209
210 "Security-Enhanced Linux (SELinux) policy and the mmap_min_addr protection (CVE-2009-2695)"
211 https://access.redhat.com/site/articles/17995
212
213 "How do I mitigate against NULL pointer dereference vulnerabilities?"
214 https://access.redhat.com/site/articles/20484
215
216 "SELinux hardening for mmap_min_addr protections", by Eric Paris, August 26th,
217 2009
218 http://eparis.livejournal.com/891.html
219
220 "Much ado about NULL: Exploiting a kernel NULL dereference", By Nelson Elhage,
221 Apr 12, 2010
222 https://blogs.oracle.com/ksplice/entry/much_ado_about_null_exploiting1
223
224
225 Author
226 ------
227
228 Yann Droneaud <ydroneaud@opteya.com>
229
230 License
231 -------
232
233 Copyright (C) 2014 OPTEYA SAS
234
235 This software is available to you under a choice of one of two licenses.
236 You may choose to be licensed under the terms of the the OpenIB.org BSD license
237 or the GNU General Public License (GPL) Version 2, both included in file
238 COPYING.
239