t_ptrace_amd64_wait.h revision 1.3 1 1.3 kamil /* $NetBSD: t_ptrace_amd64_wait.h,v 1.3 2018/05/13 23:14:47 kamil Exp $ */
2 1.1 kamil
3 1.1 kamil /*-
4 1.1 kamil * Copyright (c) 2016 The NetBSD Foundation, Inc.
5 1.1 kamil * All rights reserved.
6 1.1 kamil *
7 1.1 kamil * Redistribution and use in source and binary forms, with or without
8 1.1 kamil * modification, are permitted provided that the following conditions
9 1.1 kamil * are met:
10 1.1 kamil * 1. Redistributions of source code must retain the above copyright
11 1.1 kamil * notice, this list of conditions and the following disclaimer.
12 1.1 kamil * 2. Redistributions in binary form must reproduce the above copyright
13 1.1 kamil * notice, this list of conditions and the following disclaimer in the
14 1.1 kamil * documentation and/or other materials provided with the distribution.
15 1.1 kamil *
16 1.1 kamil * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 1.1 kamil * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 1.1 kamil * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 1.1 kamil * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 1.1 kamil * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 1.1 kamil * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 1.1 kamil * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 1.1 kamil * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 1.1 kamil * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 1.1 kamil * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 1.1 kamil * POSSIBILITY OF SUCH DAMAGE.
27 1.1 kamil */
28 1.1 kamil
29 1.1 kamil #if defined(__x86_64__)
30 1.3 kamil
31 1.3 kamil /// ----------------------------------------------------------------------------
32 1.3 kamil
33 1.1 kamil ATF_TC(x86_64_regs1);
34 1.1 kamil ATF_TC_HEAD(x86_64_regs1, tc)
35 1.1 kamil {
36 1.1 kamil atf_tc_set_md_var(tc, "descr",
37 1.1 kamil "Call PT_GETREGS and iterate over General Purpose registers");
38 1.1 kamil }
39 1.1 kamil
40 1.1 kamil ATF_TC_BODY(x86_64_regs1, tc)
41 1.1 kamil {
42 1.1 kamil const int exitval = 5;
43 1.1 kamil const int sigval = SIGSTOP;
44 1.1 kamil pid_t child, wpid;
45 1.1 kamil #if defined(TWAIT_HAVE_STATUS)
46 1.1 kamil int status;
47 1.1 kamil #endif
48 1.1 kamil struct reg r;
49 1.1 kamil
50 1.2 christos DPRINTF("Before forking process PID=%d\n", getpid());
51 1.2 christos SYSCALL_REQUIRE((child = fork()) != -1);
52 1.1 kamil if (child == 0) {
53 1.2 christos DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
54 1.1 kamil FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
55 1.1 kamil
56 1.2 christos DPRINTF("Before raising %s from child\n", strsignal(sigval));
57 1.1 kamil FORKEE_ASSERT(raise(sigval) == 0);
58 1.1 kamil
59 1.2 christos DPRINTF("Before exiting of the child process\n");
60 1.1 kamil _exit(exitval);
61 1.1 kamil }
62 1.2 christos DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);
63 1.1 kamil
64 1.2 christos DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
65 1.1 kamil TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
66 1.1 kamil
67 1.1 kamil validate_status_stopped(status, sigval);
68 1.1 kamil
69 1.2 christos DPRINTF("Call GETREGS for the child process\n");
70 1.2 christos SYSCALL_REQUIRE(ptrace(PT_GETREGS, child, &r, 0) != -1);
71 1.1 kamil
72 1.2 christos DPRINTF("RAX=%#" PRIxREGISTER "\n", r.regs[_REG_RAX]);
73 1.2 christos DPRINTF("RBX=%#" PRIxREGISTER "\n", r.regs[_REG_RBX]);
74 1.2 christos DPRINTF("RCX=%#" PRIxREGISTER "\n", r.regs[_REG_RCX]);
75 1.2 christos DPRINTF("RDX=%#" PRIxREGISTER "\n", r.regs[_REG_RDX]);
76 1.2 christos
77 1.2 christos DPRINTF("RDI=%#" PRIxREGISTER "\n", r.regs[_REG_RDI]);
78 1.2 christos DPRINTF("RSI=%#" PRIxREGISTER "\n", r.regs[_REG_RSI]);
79 1.2 christos
80 1.2 christos DPRINTF("GS=%#" PRIxREGISTER "\n", r.regs[_REG_GS]);
81 1.2 christos DPRINTF("FS=%#" PRIxREGISTER "\n", r.regs[_REG_FS]);
82 1.2 christos DPRINTF("ES=%#" PRIxREGISTER "\n", r.regs[_REG_ES]);
83 1.2 christos DPRINTF("DS=%#" PRIxREGISTER "\n", r.regs[_REG_DS]);
84 1.2 christos DPRINTF("CS=%#" PRIxREGISTER "\n", r.regs[_REG_CS]);
85 1.2 christos DPRINTF("SS=%#" PRIxREGISTER "\n", r.regs[_REG_SS]);
86 1.2 christos
87 1.2 christos DPRINTF("RSP=%#" PRIxREGISTER "\n", r.regs[_REG_RSP]);
88 1.2 christos DPRINTF("RIP=%#" PRIxREGISTER "\n", r.regs[_REG_RIP]);
89 1.2 christos
90 1.2 christos DPRINTF("RFLAGS=%#" PRIxREGISTER "\n", r.regs[_REG_RFLAGS]);
91 1.2 christos
92 1.2 christos DPRINTF("R8=%#" PRIxREGISTER "\n", r.regs[_REG_R8]);
93 1.2 christos DPRINTF("R9=%#" PRIxREGISTER "\n", r.regs[_REG_R9]);
94 1.2 christos DPRINTF("R10=%#" PRIxREGISTER "\n", r.regs[_REG_R10]);
95 1.2 christos DPRINTF("R11=%#" PRIxREGISTER "\n", r.regs[_REG_R11]);
96 1.2 christos DPRINTF("R12=%#" PRIxREGISTER "\n", r.regs[_REG_R12]);
97 1.2 christos DPRINTF("R13=%#" PRIxREGISTER "\n", r.regs[_REG_R13]);
98 1.2 christos DPRINTF("R14=%#" PRIxREGISTER "\n", r.regs[_REG_R14]);
99 1.2 christos DPRINTF("R15=%#" PRIxREGISTER "\n", r.regs[_REG_R15]);
100 1.1 kamil
101 1.2 christos DPRINTF("Before resuming the child process where it left off and "
102 1.1 kamil "without signal to be sent\n");
103 1.2 christos SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
104 1.1 kamil
105 1.2 christos DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
106 1.1 kamil TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
107 1.1 kamil
108 1.1 kamil validate_status_exited(status, exitval);
109 1.1 kamil
110 1.2 christos DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
111 1.1 kamil TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
112 1.1 kamil }
113 1.3 kamil
114 1.3 kamil /// ----------------------------------------------------------------------------
115 1.3 kamil
116 1.3 kamil ATF_TC(x86_64_cve_2018_8897);
117 1.3 kamil ATF_TC_HEAD(x86_64_cve_2018_8897, tc)
118 1.3 kamil {
119 1.3 kamil atf_tc_set_md_var(tc, "descr",
120 1.3 kamil "Verify mitigation for CVE-2018-8897 (POP SS debug exception)");
121 1.3 kamil }
122 1.3 kamil
123 1.3 kamil #define CVE_2018_8897_PAGE 0x5000 /* page addressable by 32-bit registers */
124 1.3 kamil
125 1.3 kamil static void
126 1.3 kamil trigger_cve_2018_8897(void)
127 1.3 kamil {
128 1.3 kamil /*
129 1.3 kamil * A function to trigger the POP SS (CVE-2018-8897) vulnerability
130 1.3 kamil *
131 1.3 kamil * We need to switch to 32-bit mode execution on 64-bit kernel.
132 1.3 kamil * This is achieved with far jump instruction and GDT descriptor
133 1.3 kamil * set to 32-bit CS selector. The 32-bit CS selector is kernel
134 1.3 kamil * specific, in the NetBSD case registered as GUCODE32_SEL
135 1.3 kamil * that is equal to (14 (decimal) << 3) with GDT and user
136 1.3 kamil * privilege level (this makes it 0x73).
137 1.3 kamil *
138 1.3 kamil * In UNIX as(1) assembly x86_64 far jump is coded as ljmp.
139 1.3 kamil * amd64 ljmp requires an indirect address with cs:RIP.
140 1.3 kamil *
141 1.3 kamil * When we are running in 32-bit mode, it's similar to the
142 1.3 kamil * mode as if the binary had been launched in netbsd32.
143 1.3 kamil *
144 1.3 kamil * There are two versions of this exploit, one with RIP
145 1.3 kamil * relative code and the other with static addresses.
146 1.3 kamil * The first one is PIE code aware, the other no-PIE one.
147 1.3 kamil *
148 1.3 kamil *
149 1.3 kamil * After switching to the 32-bit mode we can move on to the remaining
150 1.3 kamil * part of the exploit.
151 1.3 kamil *
152 1.3 kamil *
153 1.3 kamil * Set the stack pointer to the page we allocated earlier. Remember
154 1.3 kamil * that we put an SS selector exactly at this address, so we can pop.
155 1.3 kamil *
156 1.3 kamil * movl $0x5000,%esp
157 1.3 kamil *
158 1.3 kamil * Pop the SS selector off the stack. This reloads the SS selector,
159 1.3 kamil * which is fine. Remember that we set DR0 at address 0x5000, which
160 1.3 kamil * we are now reading. Therefore, on this instruction, the CPU will
161 1.3 kamil * raise a #DB exception.
162 1.3 kamil *
163 1.3 kamil * But the "pop %ss" instruction is special: it blocks exceptions
164 1.3 kamil * until the next instruction is executed. So the #DB that we just
165 1.3 kamil * raised is actually blocked.
166 1.3 kamil *
167 1.3 kamil * pop %ss
168 1.3 kamil *
169 1.3 kamil * We are still here, and didn't receive the #DB. After we execute
170 1.3 kamil * this instruction, the effect of "pop %ss" will disappear, and
171 1.3 kamil * we will receive the #DB for real.
172 1.3 kamil *
173 1.3 kamil * int $4
174 1.3 kamil *
175 1.3 kamil * Here the bug happens. We executed "int $4", so we entered the
176 1.3 kamil * kernel, with interrupts disabled. The #DB that was pending is
177 1.3 kamil * received. But, it is received immediately in kernel mode, and is
178 1.3 kamil * _NOT_ received when interrupts are enabled again.
179 1.3 kamil *
180 1.3 kamil * It means that, in the first instruction of the $4 handler, we
181 1.3 kamil * think we are safe with interrupts disabled. But we aren't, and
182 1.3 kamil * just got interrupted.
183 1.3 kamil *
184 1.3 kamil * The new interrupt handler doesn't handle this particular context:
185 1.3 kamil * we are entered in kernel mode, the previous context was kernel
186 1.3 kamil * mode too but it still had the user context loaded.
187 1.3 kamil *
188 1.3 kamil * We find ourselves not doing a 'swapgs'. At the end of the day, it
189 1.3 kamil * means that we call trap() with a curcpu() that is fully
190 1.3 kamil * controllable by userland. From then on, it is easy to escalate
191 1.3 kamil * privileges.
192 1.3 kamil *
193 1.3 kamil * With SVS it also means we don't switch CR3, so this results in a
194 1.3 kamil * triple fault, which this time cannot be turned to a privilege
195 1.3 kamil * escalation.
196 1.3 kamil */
197 1.3 kamil
198 1.3 kamil #if __PIE__
199 1.3 kamil void *csRIP;
200 1.3 kamil
201 1.3 kamil csRIP = malloc(sizeof(int) + sizeof(short));
202 1.3 kamil FORKEE_ASSERT(csRIP != NULL);
203 1.3 kamil
204 1.3 kamil __asm__ __volatile__(
205 1.3 kamil " leal 24(%%eip), %%eax\n\t"
206 1.3 kamil " movq %0, %%rdx\n\t"
207 1.3 kamil " movl %%eax, (%%rdx)\n\t"
208 1.3 kamil " movw $0x73, 4(%%rdx)\n\t"
209 1.3 kamil " movq %1, %%rax\n\t"
210 1.3 kamil " ljmp *(%%rax)\n\t"
211 1.3 kamil " .code32\n\t"
212 1.3 kamil " movl $0x5000, %%esp\n\t"
213 1.3 kamil " pop %%ss\n\t"
214 1.3 kamil " int $4\n\t"
215 1.3 kamil " .code64\n\t"
216 1.3 kamil : "=m"(csRIP)
217 1.3 kamil : "m"(csRIP)
218 1.3 kamil : "%rax", "%rdx", "%rsp"
219 1.3 kamil );
220 1.3 kamil #else /* !__PIE__ */
221 1.3 kamil __asm__ __volatile__(
222 1.3 kamil " movq $farjmp32, %%rax\n\t"
223 1.3 kamil " ljmp *(%%rax)\n\t"
224 1.3 kamil "farjmp32:\n\t"
225 1.3 kamil " .long trigger32\n\t"
226 1.3 kamil " .word 0x73\n\t"
227 1.3 kamil " .code32\n\t"
228 1.3 kamil "trigger32:\n\t"
229 1.3 kamil " movl $0x5000, %%esp\n\t"
230 1.3 kamil " pop %%ss\n\t"
231 1.3 kamil " int $4\n\t"
232 1.3 kamil " .code64\n\t"
233 1.3 kamil :
234 1.3 kamil :
235 1.3 kamil : "%rax", "%rsp"
236 1.3 kamil );
237 1.3 kamil #endif
238 1.3 kamil }
239 1.3 kamil
240 1.3 kamil ATF_TC_BODY(x86_64_cve_2018_8897, tc)
241 1.3 kamil {
242 1.3 kamil const int exitval = 5;
243 1.3 kamil const int sigval = SIGSTOP;
244 1.3 kamil pid_t child, wpid;
245 1.3 kamil #if defined(TWAIT_HAVE_STATUS)
246 1.3 kamil int status;
247 1.3 kamil #endif
248 1.3 kamil char *trap_page;
249 1.3 kamil struct dbreg db;
250 1.3 kamil
251 1.3 kamil if (!can_we_set_dbregs()) {
252 1.3 kamil atf_tc_skip("Either run this test as root or set sysctl(3) "
253 1.3 kamil "security.models.extensions.user_set_dbregs to 1");
254 1.3 kamil }
255 1.3 kamil
256 1.3 kamil DPRINTF("Before forking process PID=%d\n", getpid());
257 1.3 kamil SYSCALL_REQUIRE((child = fork()) != -1);
258 1.3 kamil if (child == 0) {
259 1.3 kamil DPRINTF("Before calling PT_TRACE_ME from child %d\n", getpid());
260 1.3 kamil FORKEE_ASSERT(ptrace(PT_TRACE_ME, 0, NULL, 0) != -1);
261 1.3 kamil
262 1.3 kamil trap_page = mmap((void *)CVE_2018_8897_PAGE,
263 1.3 kamil sysconf(_SC_PAGESIZE), PROT_READ|PROT_WRITE,
264 1.3 kamil MAP_FIXED|MAP_ANON|MAP_PRIVATE, -1, 0);
265 1.3 kamil
266 1.3 kamil /* trigger page fault */
267 1.3 kamil memset(trap_page, 0, sysconf(_SC_PAGESIZE));
268 1.3 kamil
269 1.3 kamil /* SS selector (descriptor 9 (0x4f >> 3)) */
270 1.3 kamil *trap_page = 0x4f;
271 1.3 kamil
272 1.3 kamil DPRINTF("Before raising %s from child\n", strsignal(sigval));
273 1.3 kamil FORKEE_ASSERT(raise(sigval) == 0);
274 1.3 kamil
275 1.3 kamil trigger_cve_2018_8897();
276 1.3 kamil
277 1.3 kamil DPRINTF("Before exiting of the child process\n");
278 1.3 kamil _exit(exitval);
279 1.3 kamil }
280 1.3 kamil DPRINTF("Parent process PID=%d, child's PID=%d\n", getpid(), child);
281 1.3 kamil
282 1.3 kamil DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
283 1.3 kamil TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
284 1.3 kamil
285 1.3 kamil validate_status_stopped(status, sigval);
286 1.3 kamil
287 1.3 kamil DPRINTF("Call GETDBREGS for the child process\n");
288 1.3 kamil SYSCALL_REQUIRE(ptrace(PT_GETDBREGS, child, &db, 0) != -1);
289 1.3 kamil
290 1.3 kamil /*
291 1.3 kamil * Set up the dbregs. We put the 0x5000 address in DR0.
292 1.3 kamil * It means that, the first time we touch this, the CPU will trigger a
293 1.3 kamil * #DB exception.
294 1.3 kamil */
295 1.3 kamil db.dr[0] = CVE_2018_8897_PAGE;
296 1.3 kamil db.dr[7] = 0x30003;
297 1.3 kamil
298 1.3 kamil DPRINTF("Call SETDBREGS for the child process\n");
299 1.3 kamil SYSCALL_REQUIRE(ptrace(PT_SETDBREGS, child, &db, 0) != -1);
300 1.3 kamil
301 1.3 kamil DPRINTF("Before resuming the child process where it left off and "
302 1.3 kamil "without signal to be sent\n");
303 1.3 kamil SYSCALL_REQUIRE(ptrace(PT_CONTINUE, child, (void *)1, 0) != -1);
304 1.3 kamil
305 1.3 kamil DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
306 1.3 kamil TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
307 1.3 kamil
308 1.3 kamil // In this test we receive SIGFPE, is this appropriate?
309 1.3 kamil // validate_status_stopped(status, SIGFPE);
310 1.3 kamil
311 1.3 kamil DPRINTF("Kill the child process\n");
312 1.3 kamil SYSCALL_REQUIRE(ptrace(PT_KILL, child, NULL, 0) != -1);
313 1.3 kamil
314 1.3 kamil DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
315 1.3 kamil TWAIT_REQUIRE_SUCCESS(wpid = TWAIT_GENERIC(child, &status, 0), child);
316 1.3 kamil
317 1.3 kamil validate_status_signaled(status, SIGKILL, 0);
318 1.3 kamil
319 1.3 kamil DPRINTF("Before calling %s() for the child\n", TWAIT_FNAME);
320 1.3 kamil TWAIT_REQUIRE_FAILURE(ECHILD, wpid = TWAIT_GENERIC(child, &status, 0));
321 1.3 kamil }
322 1.3 kamil
323 1.3 kamil #undef CVE_2018_8897_PAGE
324 1.3 kamil
325 1.3 kamil /// ----------------------------------------------------------------------------
326 1.3 kamil
327 1.1 kamil #define ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64() \
328 1.3 kamil ATF_TP_ADD_TC_HAVE_GPREGS(tp, x86_64_regs1); \
329 1.3 kamil ATF_TP_ADD_TC_HAVE_GPREGS(tp, x86_64_cve_2018_8897);
330 1.1 kamil #else
331 1.1 kamil #define ATF_TP_ADD_TCS_PTRACE_WAIT_AMD64()
332 1.1 kamil #endif
333