1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * This program test's basic kernel shadow stack support. It enables shadow |
4 | * stack manual via the arch_prctl(), instead of relying on glibc. It's |
5 | * Makefile doesn't compile with shadow stack support, so it doesn't rely on |
6 | * any particular glibc. As a result it can't do any operations that require |
7 | * special glibc shadow stack support (longjmp(), swapcontext(), etc). Just |
8 | * stick to the basics and hope the compiler doesn't do anything strange. |
9 | */ |
10 | |
11 | #define _GNU_SOURCE |
12 | |
13 | #include <sys/syscall.h> |
14 | #include <asm/mman.h> |
15 | #include <sys/mman.h> |
16 | #include <sys/stat.h> |
17 | #include <sys/wait.h> |
18 | #include <stdio.h> |
19 | #include <stdlib.h> |
20 | #include <fcntl.h> |
21 | #include <unistd.h> |
22 | #include <string.h> |
23 | #include <errno.h> |
24 | #include <stdbool.h> |
25 | #include <x86intrin.h> |
26 | #include <asm/prctl.h> |
27 | #include <sys/prctl.h> |
28 | #include <stdint.h> |
29 | #include <signal.h> |
30 | #include <pthread.h> |
31 | #include <sys/ioctl.h> |
32 | #include <linux/userfaultfd.h> |
33 | #include <setjmp.h> |
34 | #include <sys/ptrace.h> |
35 | #include <sys/signal.h> |
36 | #include <linux/elf.h> |
37 | |
38 | /* |
39 | * Define the ABI defines if needed, so people can run the tests |
40 | * without building the headers. |
41 | */ |
42 | #ifndef __NR_map_shadow_stack |
43 | #define __NR_map_shadow_stack 453 |
44 | |
45 | #define SHADOW_STACK_SET_TOKEN (1ULL << 0) |
46 | |
47 | #define ARCH_SHSTK_ENABLE 0x5001 |
48 | #define ARCH_SHSTK_DISABLE 0x5002 |
49 | #define ARCH_SHSTK_LOCK 0x5003 |
50 | #define ARCH_SHSTK_UNLOCK 0x5004 |
51 | #define ARCH_SHSTK_STATUS 0x5005 |
52 | |
53 | #define ARCH_SHSTK_SHSTK (1ULL << 0) |
54 | #define ARCH_SHSTK_WRSS (1ULL << 1) |
55 | |
56 | #define NT_X86_SHSTK 0x204 |
57 | #endif |
58 | |
59 | #define SS_SIZE 0x200000 |
60 | #define PAGE_SIZE 0x1000 |
61 | |
62 | #if (__GNUC__ < 8) || (__GNUC__ == 8 && __GNUC_MINOR__ < 5) |
63 | int main(int argc, char *argv[]) |
64 | { |
65 | printf("[SKIP]\tCompiler does not support CET.\n" ); |
66 | return 0; |
67 | } |
68 | #else |
69 | void write_shstk(unsigned long *addr, unsigned long val) |
70 | { |
71 | asm volatile("wrssq %[val], (%[addr])\n" |
72 | : "=m" (addr) |
73 | : [addr] "r" (addr), [val] "r" (val)); |
74 | } |
75 | |
76 | static inline unsigned long __attribute__((always_inline)) get_ssp(void) |
77 | { |
78 | unsigned long ret = 0; |
79 | |
80 | asm volatile("xor %0, %0; rdsspq %0" : "=r" (ret)); |
81 | return ret; |
82 | } |
83 | |
84 | /* |
85 | * For use in inline enablement of shadow stack. |
86 | * |
87 | * The program can't return from the point where shadow stack gets enabled |
88 | * because there will be no address on the shadow stack. So it can't use |
89 | * syscall() for enablement, since it is a function. |
90 | * |
91 | * Based on code from nolibc.h. Keep a copy here because this can't pull in all |
92 | * of nolibc.h. |
93 | */ |
94 | #define ARCH_PRCTL(arg1, arg2) \ |
95 | ({ \ |
96 | long _ret; \ |
97 | register long _num asm("eax") = __NR_arch_prctl; \ |
98 | register long _arg1 asm("rdi") = (long)(arg1); \ |
99 | register long _arg2 asm("rsi") = (long)(arg2); \ |
100 | \ |
101 | asm volatile ( \ |
102 | "syscall\n" \ |
103 | : "=a"(_ret) \ |
104 | : "r"(_arg1), "r"(_arg2), \ |
105 | "0"(_num) \ |
106 | : "rcx", "r11", "memory", "cc" \ |
107 | ); \ |
108 | _ret; \ |
109 | }) |
110 | |
111 | void *create_shstk(void *addr) |
112 | { |
113 | return (void *)syscall(__NR_map_shadow_stack, addr, SS_SIZE, SHADOW_STACK_SET_TOKEN); |
114 | } |
115 | |
116 | void *create_normal_mem(void *addr) |
117 | { |
118 | return mmap(addr, SS_SIZE, PROT_READ | PROT_WRITE, |
119 | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); |
120 | } |
121 | |
122 | void free_shstk(void *shstk) |
123 | { |
124 | munmap(shstk, SS_SIZE); |
125 | } |
126 | |
127 | int reset_shstk(void *shstk) |
128 | { |
129 | return madvise(shstk, SS_SIZE, MADV_DONTNEED); |
130 | } |
131 | |
132 | void try_shstk(unsigned long new_ssp) |
133 | { |
134 | unsigned long ssp; |
135 | |
136 | printf("[INFO]\tnew_ssp = %lx, *new_ssp = %lx\n" , |
137 | new_ssp, *((unsigned long *)new_ssp)); |
138 | |
139 | ssp = get_ssp(); |
140 | printf("[INFO]\tchanging ssp from %lx to %lx\n" , ssp, new_ssp); |
141 | |
142 | asm volatile("rstorssp (%0)\n" :: "r" (new_ssp)); |
143 | asm volatile("saveprevssp" ); |
144 | printf("[INFO]\tssp is now %lx\n" , get_ssp()); |
145 | |
146 | /* Switch back to original shadow stack */ |
147 | ssp -= 8; |
148 | asm volatile("rstorssp (%0)\n" :: "r" (ssp)); |
149 | asm volatile("saveprevssp" ); |
150 | } |
151 | |
152 | int test_shstk_pivot(void) |
153 | { |
154 | void *shstk = create_shstk(0); |
155 | |
156 | if (shstk == MAP_FAILED) { |
157 | printf("[FAIL]\tError creating shadow stack: %d\n" , errno); |
158 | return 1; |
159 | } |
160 | try_shstk((unsigned long)shstk + SS_SIZE - 8); |
161 | free_shstk(shstk); |
162 | |
163 | printf("[OK]\tShadow stack pivot\n" ); |
164 | return 0; |
165 | } |
166 | |
167 | int test_shstk_faults(void) |
168 | { |
169 | unsigned long *shstk = create_shstk(0); |
170 | |
171 | /* Read shadow stack, test if it's zero to not get read optimized out */ |
172 | if (*shstk != 0) |
173 | goto err; |
174 | |
175 | /* Wrss memory that was already read. */ |
176 | write_shstk(shstk, 1); |
177 | if (*shstk != 1) |
178 | goto err; |
179 | |
180 | /* Page out memory, so we can wrss it again. */ |
181 | if (reset_shstk((void *)shstk)) |
182 | goto err; |
183 | |
184 | write_shstk(shstk, 1); |
185 | if (*shstk != 1) |
186 | goto err; |
187 | |
188 | printf("[OK]\tShadow stack faults\n" ); |
189 | return 0; |
190 | |
191 | err: |
192 | return 1; |
193 | } |
194 | |
195 | unsigned long saved_ssp; |
196 | unsigned long saved_ssp_val; |
197 | volatile bool segv_triggered; |
198 | |
199 | void __attribute__((noinline)) violate_ss(void) |
200 | { |
201 | saved_ssp = get_ssp(); |
202 | saved_ssp_val = *(unsigned long *)saved_ssp; |
203 | |
204 | /* Corrupt shadow stack */ |
205 | printf("[INFO]\tCorrupting shadow stack\n" ); |
206 | write_shstk((void *)saved_ssp, 0); |
207 | } |
208 | |
209 | void segv_handler(int signum, siginfo_t *si, void *uc) |
210 | { |
211 | printf("[INFO]\tGenerated shadow stack violation successfully\n" ); |
212 | |
213 | segv_triggered = true; |
214 | |
215 | /* Fix shadow stack */ |
216 | write_shstk((void *)saved_ssp, saved_ssp_val); |
217 | } |
218 | |
219 | int test_shstk_violation(void) |
220 | { |
221 | struct sigaction sa = {}; |
222 | |
223 | sa.sa_sigaction = segv_handler; |
224 | sa.sa_flags = SA_SIGINFO; |
225 | if (sigaction(SIGSEGV, &sa, NULL)) |
226 | return 1; |
227 | |
228 | segv_triggered = false; |
229 | |
230 | /* Make sure segv_triggered is set before violate_ss() */ |
231 | asm volatile("" : : : "memory" ); |
232 | |
233 | violate_ss(); |
234 | |
235 | signal(SIGSEGV, SIG_DFL); |
236 | |
237 | printf("[OK]\tShadow stack violation test\n" ); |
238 | |
239 | return !segv_triggered; |
240 | } |
241 | |
242 | /* Gup test state */ |
243 | #define MAGIC_VAL 0x12345678 |
244 | bool is_shstk_access; |
245 | void *shstk_ptr; |
246 | int fd; |
247 | |
248 | void reset_test_shstk(void *addr) |
249 | { |
250 | if (shstk_ptr) |
251 | free_shstk(shstk_ptr); |
252 | shstk_ptr = create_shstk(addr); |
253 | } |
254 | |
255 | void test_access_fix_handler(int signum, siginfo_t *si, void *uc) |
256 | { |
257 | printf("[INFO]\tViolation from %s\n" , is_shstk_access ? "shstk access" : "normal write" ); |
258 | |
259 | segv_triggered = true; |
260 | |
261 | /* Fix shadow stack */ |
262 | if (is_shstk_access) { |
263 | reset_test_shstk(shstk_ptr); |
264 | return; |
265 | } |
266 | |
267 | free_shstk(shstk_ptr); |
268 | create_normal_mem(shstk_ptr); |
269 | } |
270 | |
271 | bool test_shstk_access(void *ptr) |
272 | { |
273 | is_shstk_access = true; |
274 | segv_triggered = false; |
275 | write_shstk(ptr, MAGIC_VAL); |
276 | |
277 | asm volatile("" : : : "memory" ); |
278 | |
279 | return segv_triggered; |
280 | } |
281 | |
282 | bool test_write_access(void *ptr) |
283 | { |
284 | is_shstk_access = false; |
285 | segv_triggered = false; |
286 | *(unsigned long *)ptr = MAGIC_VAL; |
287 | |
288 | asm volatile("" : : : "memory" ); |
289 | |
290 | return segv_triggered; |
291 | } |
292 | |
293 | bool gup_write(void *ptr) |
294 | { |
295 | unsigned long val; |
296 | |
297 | lseek(fd, (unsigned long)ptr, SEEK_SET); |
298 | if (write(fd, &val, sizeof(val)) < 0) |
299 | return 1; |
300 | |
301 | return 0; |
302 | } |
303 | |
304 | bool gup_read(void *ptr) |
305 | { |
306 | unsigned long val; |
307 | |
308 | lseek(fd, (unsigned long)ptr, SEEK_SET); |
309 | if (read(fd, &val, sizeof(val)) < 0) |
310 | return 1; |
311 | |
312 | return 0; |
313 | } |
314 | |
315 | int test_gup(void) |
316 | { |
317 | struct sigaction sa = {}; |
318 | int status; |
319 | pid_t pid; |
320 | |
321 | sa.sa_sigaction = test_access_fix_handler; |
322 | sa.sa_flags = SA_SIGINFO; |
323 | if (sigaction(SIGSEGV, &sa, NULL)) |
324 | return 1; |
325 | |
326 | segv_triggered = false; |
327 | |
328 | fd = open("/proc/self/mem" , O_RDWR); |
329 | if (fd == -1) |
330 | return 1; |
331 | |
332 | reset_test_shstk(0); |
333 | if (gup_read(shstk_ptr)) |
334 | return 1; |
335 | if (test_shstk_access(shstk_ptr)) |
336 | return 1; |
337 | printf("[INFO]\tGup read -> shstk access success\n" ); |
338 | |
339 | reset_test_shstk(0); |
340 | if (gup_write(shstk_ptr)) |
341 | return 1; |
342 | if (test_shstk_access(shstk_ptr)) |
343 | return 1; |
344 | printf("[INFO]\tGup write -> shstk access success\n" ); |
345 | |
346 | reset_test_shstk(0); |
347 | if (gup_read(shstk_ptr)) |
348 | return 1; |
349 | if (!test_write_access(shstk_ptr)) |
350 | return 1; |
351 | printf("[INFO]\tGup read -> write access success\n" ); |
352 | |
353 | reset_test_shstk(0); |
354 | if (gup_write(shstk_ptr)) |
355 | return 1; |
356 | if (!test_write_access(shstk_ptr)) |
357 | return 1; |
358 | printf("[INFO]\tGup write -> write access success\n" ); |
359 | |
360 | close(fd); |
361 | |
362 | /* COW/gup test */ |
363 | reset_test_shstk(0); |
364 | pid = fork(); |
365 | if (!pid) { |
366 | fd = open("/proc/self/mem" , O_RDWR); |
367 | if (fd == -1) |
368 | exit(1); |
369 | |
370 | if (gup_write(shstk_ptr)) { |
371 | close(fd); |
372 | exit(1); |
373 | } |
374 | close(fd); |
375 | exit(0); |
376 | } |
377 | waitpid(pid, &status, 0); |
378 | if (WEXITSTATUS(status)) { |
379 | printf("[FAIL]\tWrite in child failed\n" ); |
380 | return 1; |
381 | } |
382 | if (*(unsigned long *)shstk_ptr == MAGIC_VAL) { |
383 | printf("[FAIL]\tWrite in child wrote through to shared memory\n" ); |
384 | return 1; |
385 | } |
386 | |
387 | printf("[INFO]\tCow gup write -> write access success\n" ); |
388 | |
389 | free_shstk(shstk_ptr); |
390 | |
391 | signal(SIGSEGV, SIG_DFL); |
392 | |
393 | printf("[OK]\tShadow gup test\n" ); |
394 | |
395 | return 0; |
396 | } |
397 | |
398 | int test_mprotect(void) |
399 | { |
400 | struct sigaction sa = {}; |
401 | |
402 | sa.sa_sigaction = test_access_fix_handler; |
403 | sa.sa_flags = SA_SIGINFO; |
404 | if (sigaction(SIGSEGV, &sa, NULL)) |
405 | return 1; |
406 | |
407 | segv_triggered = false; |
408 | |
409 | /* mprotect a shadow stack as read only */ |
410 | reset_test_shstk(0); |
411 | if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) { |
412 | printf("[FAIL]\tmprotect(PROT_READ) failed\n" ); |
413 | return 1; |
414 | } |
415 | |
416 | /* try to wrss it and fail */ |
417 | if (!test_shstk_access(shstk_ptr)) { |
418 | printf("[FAIL]\tShadow stack access to read-only memory succeeded\n" ); |
419 | return 1; |
420 | } |
421 | |
422 | /* |
423 | * The shadow stack was reset above to resolve the fault, make the new one |
424 | * read-only. |
425 | */ |
426 | if (mprotect(shstk_ptr, SS_SIZE, PROT_READ) < 0) { |
427 | printf("[FAIL]\tmprotect(PROT_READ) failed\n" ); |
428 | return 1; |
429 | } |
430 | |
431 | /* then back to writable */ |
432 | if (mprotect(shstk_ptr, SS_SIZE, PROT_WRITE | PROT_READ) < 0) { |
433 | printf("[FAIL]\tmprotect(PROT_WRITE) failed\n" ); |
434 | return 1; |
435 | } |
436 | |
437 | /* then wrss to it and succeed */ |
438 | if (test_shstk_access(shstk_ptr)) { |
439 | printf("[FAIL]\tShadow stack access to mprotect() writable memory failed\n" ); |
440 | return 1; |
441 | } |
442 | |
443 | free_shstk(shstk_ptr); |
444 | |
445 | signal(SIGSEGV, SIG_DFL); |
446 | |
447 | printf("[OK]\tmprotect() test\n" ); |
448 | |
449 | return 0; |
450 | } |
451 | |
452 | char zero[4096]; |
453 | |
454 | static void *uffd_thread(void *arg) |
455 | { |
456 | struct uffdio_copy req; |
457 | int uffd = *(int *)arg; |
458 | struct uffd_msg msg; |
459 | int ret; |
460 | |
461 | while (1) { |
462 | ret = read(uffd, &msg, sizeof(msg)); |
463 | if (ret > 0) |
464 | break; |
465 | else if (errno == EAGAIN) |
466 | continue; |
467 | return (void *)1; |
468 | } |
469 | |
470 | req.dst = msg.arg.pagefault.address; |
471 | req.src = (__u64)zero; |
472 | req.len = 4096; |
473 | req.mode = 0; |
474 | |
475 | if (ioctl(uffd, UFFDIO_COPY, &req)) |
476 | return (void *)1; |
477 | |
478 | return (void *)0; |
479 | } |
480 | |
481 | int test_userfaultfd(void) |
482 | { |
483 | struct uffdio_register uffdio_register; |
484 | struct uffdio_api uffdio_api; |
485 | struct sigaction sa = {}; |
486 | pthread_t thread; |
487 | void *res; |
488 | int uffd; |
489 | |
490 | sa.sa_sigaction = test_access_fix_handler; |
491 | sa.sa_flags = SA_SIGINFO; |
492 | if (sigaction(SIGSEGV, &sa, NULL)) |
493 | return 1; |
494 | |
495 | uffd = syscall(__NR_userfaultfd, O_CLOEXEC | O_NONBLOCK); |
496 | if (uffd < 0) { |
497 | printf("[SKIP]\tUserfaultfd unavailable.\n" ); |
498 | return 0; |
499 | } |
500 | |
501 | reset_test_shstk(0); |
502 | |
503 | uffdio_api.api = UFFD_API; |
504 | uffdio_api.features = 0; |
505 | if (ioctl(uffd, UFFDIO_API, &uffdio_api)) |
506 | goto err; |
507 | |
508 | uffdio_register.range.start = (__u64)shstk_ptr; |
509 | uffdio_register.range.len = 4096; |
510 | uffdio_register.mode = UFFDIO_REGISTER_MODE_MISSING; |
511 | if (ioctl(uffd, UFFDIO_REGISTER, &uffdio_register)) |
512 | goto err; |
513 | |
514 | if (pthread_create(&thread, NULL, &uffd_thread, &uffd)) |
515 | goto err; |
516 | |
517 | reset_shstk(shstk_ptr); |
518 | test_shstk_access(shstk_ptr); |
519 | |
520 | if (pthread_join(thread, &res)) |
521 | goto err; |
522 | |
523 | if (test_shstk_access(shstk_ptr)) |
524 | goto err; |
525 | |
526 | free_shstk(shstk_ptr); |
527 | |
528 | signal(SIGSEGV, SIG_DFL); |
529 | |
530 | if (!res) |
531 | printf("[OK]\tUserfaultfd test\n" ); |
532 | return !!res; |
533 | err: |
534 | free_shstk(shstk_ptr); |
535 | close(uffd); |
536 | signal(SIGSEGV, SIG_DFL); |
537 | return 1; |
538 | } |
539 | |
540 | /* Simple linked list for keeping track of mappings in test_guard_gap() */ |
541 | struct node { |
542 | struct node *next; |
543 | void *mapping; |
544 | }; |
545 | |
546 | /* |
547 | * This tests whether mmap will place other mappings in a shadow stack's guard |
548 | * gap. The steps are: |
549 | * 1. Finds an empty place by mapping and unmapping something. |
550 | * 2. Map a shadow stack in the middle of the known empty area. |
551 | * 3. Map a bunch of PAGE_SIZE mappings. These will use the search down |
552 | * direction, filling any gaps until it encounters the shadow stack's |
553 | * guard gap. |
554 | * 4. When a mapping lands below the shadow stack from step 2, then all |
555 | * of the above gaps are filled. The search down algorithm will have |
556 | * looked at the shadow stack gaps. |
557 | * 5. See if it landed in the gap. |
558 | */ |
559 | int test_guard_gap(void) |
560 | { |
561 | void *free_area, *shstk, *test_map = (void *)0xFFFFFFFFFFFFFFFF; |
562 | struct node *head = NULL, *cur; |
563 | |
564 | free_area = mmap(0, SS_SIZE * 3, PROT_READ | PROT_WRITE, |
565 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
566 | munmap(free_area, SS_SIZE * 3); |
567 | |
568 | shstk = create_shstk(free_area + SS_SIZE); |
569 | if (shstk == MAP_FAILED) |
570 | return 1; |
571 | |
572 | while (test_map > shstk) { |
573 | test_map = mmap(0, PAGE_SIZE, PROT_READ | PROT_WRITE, |
574 | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); |
575 | if (test_map == MAP_FAILED) |
576 | return 1; |
577 | cur = malloc(sizeof(*cur)); |
578 | cur->mapping = test_map; |
579 | |
580 | cur->next = head; |
581 | head = cur; |
582 | } |
583 | |
584 | while (head) { |
585 | cur = head; |
586 | head = cur->next; |
587 | munmap(cur->mapping, PAGE_SIZE); |
588 | free(cur); |
589 | } |
590 | |
591 | free_shstk(shstk); |
592 | |
593 | if (shstk - test_map - PAGE_SIZE != PAGE_SIZE) |
594 | return 1; |
595 | |
596 | printf("[OK]\tGuard gap test\n" ); |
597 | |
598 | return 0; |
599 | } |
600 | |
601 | /* |
602 | * Too complicated to pull it out of the 32 bit header, but also get the |
603 | * 64 bit one needed above. Just define a copy here. |
604 | */ |
605 | #define __NR_compat_sigaction 67 |
606 | |
607 | /* |
608 | * Call 32 bit signal handler to get 32 bit signals ABI. Make sure |
609 | * to push the registers that will get clobbered. |
610 | */ |
611 | int sigaction32(int signum, const struct sigaction *restrict act, |
612 | struct sigaction *restrict oldact) |
613 | { |
614 | register long syscall_reg asm("eax" ) = __NR_compat_sigaction; |
615 | register long signum_reg asm("ebx" ) = signum; |
616 | register long act_reg asm("ecx" ) = (long)act; |
617 | register long oldact_reg asm("edx" ) = (long)oldact; |
618 | int ret = 0; |
619 | |
620 | asm volatile ("int $0x80;" |
621 | : "=a" (ret), "=m" (oldact) |
622 | : "r" (syscall_reg), "r" (signum_reg), "r" (act_reg), |
623 | "r" (oldact_reg) |
624 | : "r8" , "r9" , "r10" , "r11" |
625 | ); |
626 | |
627 | return ret; |
628 | } |
629 | |
630 | sigjmp_buf jmp_buffer; |
631 | |
632 | void segv_gp_handler(int signum, siginfo_t *si, void *uc) |
633 | { |
634 | segv_triggered = true; |
635 | |
636 | /* |
637 | * To work with old glibc, this can't rely on siglongjmp working with |
638 | * shadow stack enabled, so disable shadow stack before siglongjmp(). |
639 | */ |
640 | ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK); |
641 | siglongjmp(jmp_buffer, -1); |
642 | } |
643 | |
644 | /* |
645 | * Transition to 32 bit mode and check that a #GP triggers a segfault. |
646 | */ |
647 | int test_32bit(void) |
648 | { |
649 | struct sigaction sa = {}; |
650 | struct sigaction *sa32; |
651 | |
652 | /* Create sigaction in 32 bit address range */ |
653 | sa32 = mmap(0, 4096, PROT_READ | PROT_WRITE, |
654 | MAP_32BIT | MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); |
655 | sa32->sa_flags = SA_SIGINFO; |
656 | |
657 | sa.sa_sigaction = segv_gp_handler; |
658 | sa.sa_flags = SA_SIGINFO; |
659 | if (sigaction(SIGSEGV, &sa, NULL)) |
660 | return 1; |
661 | |
662 | |
663 | segv_triggered = false; |
664 | |
665 | /* Make sure segv_triggered is set before triggering the #GP */ |
666 | asm volatile("" : : : "memory" ); |
667 | |
668 | /* |
669 | * Set handler to somewhere in 32 bit address space |
670 | */ |
671 | sa32->sa_handler = (void *)sa32; |
672 | if (sigaction32(SIGUSR1, sa32, NULL)) |
673 | return 1; |
674 | |
675 | if (!sigsetjmp(jmp_buffer, 1)) |
676 | raise(SIGUSR1); |
677 | |
678 | if (segv_triggered) |
679 | printf("[OK]\t32 bit test\n" ); |
680 | |
681 | return !segv_triggered; |
682 | } |
683 | |
684 | void segv_handler_ptrace(int signum, siginfo_t *si, void *uc) |
685 | { |
686 | /* The SSP adjustment caused a segfault. */ |
687 | exit(0); |
688 | } |
689 | |
690 | int test_ptrace(void) |
691 | { |
692 | unsigned long saved_ssp, ssp = 0; |
693 | struct sigaction sa= {}; |
694 | struct iovec iov; |
695 | int status; |
696 | int pid; |
697 | |
698 | iov.iov_base = &ssp; |
699 | iov.iov_len = sizeof(ssp); |
700 | |
701 | pid = fork(); |
702 | if (!pid) { |
703 | ssp = get_ssp(); |
704 | |
705 | sa.sa_sigaction = segv_handler_ptrace; |
706 | sa.sa_flags = SA_SIGINFO; |
707 | if (sigaction(SIGSEGV, &sa, NULL)) |
708 | return 1; |
709 | |
710 | ptrace(PTRACE_TRACEME, NULL, NULL, NULL); |
711 | /* |
712 | * The parent will tweak the SSP and return from this function |
713 | * will #CP. |
714 | */ |
715 | raise(SIGTRAP); |
716 | |
717 | exit(1); |
718 | } |
719 | |
720 | while (waitpid(pid, &status, 0) != -1 && WSTOPSIG(status) != SIGTRAP); |
721 | |
722 | if (ptrace(PTRACE_GETREGSET, pid, NT_X86_SHSTK, &iov)) { |
723 | printf("[INFO]\tFailed to PTRACE_GETREGS\n" ); |
724 | goto out_kill; |
725 | } |
726 | |
727 | if (!ssp) { |
728 | printf("[INFO]\tPtrace child SSP was 0\n" ); |
729 | goto out_kill; |
730 | } |
731 | |
732 | saved_ssp = ssp; |
733 | |
734 | iov.iov_len = 0; |
735 | if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { |
736 | printf("[INFO]\tToo small size accepted via PTRACE_SETREGS\n" ); |
737 | goto out_kill; |
738 | } |
739 | |
740 | iov.iov_len = sizeof(ssp) + 1; |
741 | if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { |
742 | printf("[INFO]\tToo large size accepted via PTRACE_SETREGS\n" ); |
743 | goto out_kill; |
744 | } |
745 | |
746 | ssp += 1; |
747 | if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { |
748 | printf("[INFO]\tUnaligned SSP written via PTRACE_SETREGS\n" ); |
749 | goto out_kill; |
750 | } |
751 | |
752 | ssp = 0xFFFFFFFFFFFF0000; |
753 | if (!ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { |
754 | printf("[INFO]\tKernel range SSP written via PTRACE_SETREGS\n" ); |
755 | goto out_kill; |
756 | } |
757 | |
758 | /* |
759 | * Tweak the SSP so the child with #CP when it resumes and returns |
760 | * from raise() |
761 | */ |
762 | ssp = saved_ssp + 8; |
763 | iov.iov_len = sizeof(ssp); |
764 | if (ptrace(PTRACE_SETREGSET, pid, NT_X86_SHSTK, &iov)) { |
765 | printf("[INFO]\tFailed to PTRACE_SETREGS\n" ); |
766 | goto out_kill; |
767 | } |
768 | |
769 | if (ptrace(PTRACE_DETACH, pid, NULL, NULL)) { |
770 | printf("[INFO]\tFailed to PTRACE_DETACH\n" ); |
771 | goto out_kill; |
772 | } |
773 | |
774 | waitpid(pid, &status, 0); |
775 | if (WEXITSTATUS(status)) |
776 | return 1; |
777 | |
778 | printf("[OK]\tPtrace test\n" ); |
779 | return 0; |
780 | |
781 | out_kill: |
782 | kill(pid, SIGKILL); |
783 | return 1; |
784 | } |
785 | |
786 | int main(int argc, char *argv[]) |
787 | { |
788 | int ret = 0; |
789 | |
790 | if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) { |
791 | printf("[SKIP]\tCould not enable Shadow stack\n" ); |
792 | return 1; |
793 | } |
794 | |
795 | if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) { |
796 | ret = 1; |
797 | printf("[FAIL]\tDisabling shadow stack failed\n" ); |
798 | } |
799 | |
800 | if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_SHSTK)) { |
801 | printf("[SKIP]\tCould not re-enable Shadow stack\n" ); |
802 | return 1; |
803 | } |
804 | |
805 | if (ARCH_PRCTL(ARCH_SHSTK_ENABLE, ARCH_SHSTK_WRSS)) { |
806 | printf("[SKIP]\tCould not enable WRSS\n" ); |
807 | ret = 1; |
808 | goto out; |
809 | } |
810 | |
811 | /* Should have succeeded if here, but this is a test, so double check. */ |
812 | if (!get_ssp()) { |
813 | printf("[FAIL]\tShadow stack disabled\n" ); |
814 | return 1; |
815 | } |
816 | |
817 | if (test_shstk_pivot()) { |
818 | ret = 1; |
819 | printf("[FAIL]\tShadow stack pivot\n" ); |
820 | goto out; |
821 | } |
822 | |
823 | if (test_shstk_faults()) { |
824 | ret = 1; |
825 | printf("[FAIL]\tShadow stack fault test\n" ); |
826 | goto out; |
827 | } |
828 | |
829 | if (test_shstk_violation()) { |
830 | ret = 1; |
831 | printf("[FAIL]\tShadow stack violation test\n" ); |
832 | goto out; |
833 | } |
834 | |
835 | if (test_gup()) { |
836 | ret = 1; |
837 | printf("[FAIL]\tShadow shadow stack gup\n" ); |
838 | goto out; |
839 | } |
840 | |
841 | if (test_mprotect()) { |
842 | ret = 1; |
843 | printf("[FAIL]\tShadow shadow mprotect test\n" ); |
844 | goto out; |
845 | } |
846 | |
847 | if (test_userfaultfd()) { |
848 | ret = 1; |
849 | printf("[FAIL]\tUserfaultfd test\n" ); |
850 | goto out; |
851 | } |
852 | |
853 | if (test_guard_gap()) { |
854 | ret = 1; |
855 | printf("[FAIL]\tGuard gap test\n" ); |
856 | goto out; |
857 | } |
858 | |
859 | if (test_ptrace()) { |
860 | ret = 1; |
861 | printf("[FAIL]\tptrace test\n" ); |
862 | } |
863 | |
864 | if (test_32bit()) { |
865 | ret = 1; |
866 | printf("[FAIL]\t32 bit test\n" ); |
867 | goto out; |
868 | } |
869 | |
870 | return ret; |
871 | |
872 | out: |
873 | /* |
874 | * Disable shadow stack before the function returns, or there will be a |
875 | * shadow stack violation. |
876 | */ |
877 | if (ARCH_PRCTL(ARCH_SHSTK_DISABLE, ARCH_SHSTK_SHSTK)) { |
878 | ret = 1; |
879 | printf("[FAIL]\tDisabling shadow stack failed\n" ); |
880 | } |
881 | |
882 | return ret; |
883 | } |
884 | #endif |
885 | |