1 | // SPDX-License-Identifier: GPL-2.0+ |
2 | // |
3 | // Copyright 2022, Michael Ellerman, IBM Corp. |
4 | // |
5 | // Test that the 4PB address space SLB handling doesn't corrupt userspace registers |
6 | // (r9-r13) due to a SLB fault while saving the PPR. |
7 | // |
8 | // The bug was introduced in f384796c4 ("powerpc/mm: Add support for handling > 512TB |
9 | // address in SLB miss") and fixed in 4c2de74cc869 ("powerpc/64: Interrupts save PPR on |
10 | // stack rather than thread_struct"). |
11 | // |
12 | // To hit the bug requires the task struct and kernel stack to be in different segments. |
13 | // Usually that requires more than 1TB of RAM, or if that's not practical, boot the kernel |
14 | // with "disable_1tb_segments". |
15 | // |
16 | // The test works by creating mappings above 512TB, to trigger the large address space |
17 | // support. It creates 64 mappings, double the size of the SLB, to cause SLB faults on |
18 | // each access (assuming naive replacement). It then loops over those mappings touching |
19 | // each, and checks that r9-r13 aren't corrupted. |
20 | // |
21 | // It then forks another child and tries again, because a new child process will get a new |
22 | // kernel stack and thread struct allocated, which may be more optimally placed to trigger |
23 | // the bug. It would probably be better to leave the previous child processes hanging |
24 | // around, so that kernel stack & thread struct allocations are not reused, but that would |
25 | // amount to a 30 second fork bomb. The current design reliably triggers the bug on |
26 | // unpatched kernels. |
27 | |
28 | #include <signal.h> |
29 | #include <stdio.h> |
30 | #include <stdlib.h> |
31 | #include <sys/mman.h> |
32 | #include <sys/types.h> |
33 | #include <sys/wait.h> |
34 | #include <unistd.h> |
35 | |
36 | #include "utils.h" |
37 | |
38 | #ifndef MAP_FIXED_NOREPLACE |
39 | #define MAP_FIXED_NOREPLACE MAP_FIXED // "Should be safe" above 512TB |
40 | #endif |
41 | |
42 | #define BASE_ADDRESS (1ul << 50) // 1PB |
43 | #define STRIDE (2ul << 40) // 2TB |
44 | #define SLB_SIZE 32 |
45 | #define NR_MAPPINGS (SLB_SIZE * 2) |
46 | |
47 | static volatile sig_atomic_t signaled; |
48 | |
49 | static void signal_handler(int sig) |
50 | { |
51 | signaled = 1; |
52 | } |
53 | |
54 | #define CHECK_REG(_reg) \ |
55 | if (_reg != _reg##_orig) { \ |
56 | printf(str(_reg) " corrupted! Expected 0x%lx != 0x%lx\n", _reg##_orig, \ |
57 | _reg); \ |
58 | _exit(1); \ |
59 | } |
60 | |
61 | static int touch_mappings(void) |
62 | { |
63 | unsigned long r9_orig, r10_orig, r11_orig, r12_orig, r13_orig; |
64 | unsigned long r9, r10, r11, r12, r13; |
65 | unsigned long addr, *p; |
66 | int i; |
67 | |
68 | for (i = 0; i < NR_MAPPINGS; i++) { |
69 | addr = BASE_ADDRESS + (i * STRIDE); |
70 | p = (unsigned long *)addr; |
71 | |
72 | asm volatile("mr %0, %%r9 ;" // Read original GPR values |
73 | "mr %1, %%r10 ;" |
74 | "mr %2, %%r11 ;" |
75 | "mr %3, %%r12 ;" |
76 | "mr %4, %%r13 ;" |
77 | "std %10, 0(%11) ;" // Trigger SLB fault |
78 | "mr %5, %%r9 ;" // Save possibly corrupted values |
79 | "mr %6, %%r10 ;" |
80 | "mr %7, %%r11 ;" |
81 | "mr %8, %%r12 ;" |
82 | "mr %9, %%r13 ;" |
83 | "mr %%r9, %0 ;" // Restore original values |
84 | "mr %%r10, %1 ;" |
85 | "mr %%r11, %2 ;" |
86 | "mr %%r12, %3 ;" |
87 | "mr %%r13, %4 ;" |
88 | : "=&b" (r9_orig), "=&b" (r10_orig), "=&b" (r11_orig), |
89 | "=&b" (r12_orig), "=&b" (r13_orig), "=&b" (r9), "=&b" (r10), |
90 | "=&b" (r11), "=&b" (r12), "=&b" (r13) |
91 | : "b" (i), "b" (p) |
92 | : "r9" , "r10" , "r11" , "r12" , "r13" ); |
93 | |
94 | CHECK_REG(r9); |
95 | CHECK_REG(r10); |
96 | CHECK_REG(r11); |
97 | CHECK_REG(r12); |
98 | CHECK_REG(r13); |
99 | } |
100 | |
101 | return 0; |
102 | } |
103 | |
104 | static int test(void) |
105 | { |
106 | unsigned long page_size, addr, *p; |
107 | struct sigaction action; |
108 | bool hash_mmu; |
109 | int i, status; |
110 | pid_t pid; |
111 | |
112 | // This tests a hash MMU specific bug. |
113 | FAIL_IF(using_hash_mmu(&hash_mmu)); |
114 | SKIP_IF(!hash_mmu); |
115 | // 4K kernels don't support 4PB address space |
116 | SKIP_IF(sysconf(_SC_PAGESIZE) < 65536); |
117 | |
118 | page_size = sysconf(_SC_PAGESIZE); |
119 | |
120 | for (i = 0; i < NR_MAPPINGS; i++) { |
121 | addr = BASE_ADDRESS + (i * STRIDE); |
122 | |
123 | p = mmap((void *)addr, page_size, PROT_READ | PROT_WRITE, |
124 | MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0); |
125 | if (p == MAP_FAILED) { |
126 | perror("mmap" ); |
127 | printf("Error: couldn't mmap(), confirm kernel has 4PB support?\n" ); |
128 | return 1; |
129 | } |
130 | } |
131 | |
132 | action.sa_handler = signal_handler; |
133 | action.sa_flags = SA_RESTART; |
134 | FAIL_IF(sigaction(SIGALRM, &action, NULL) < 0); |
135 | |
136 | // Seen to always crash in under ~10s on affected kernels. |
137 | alarm(30); |
138 | |
139 | while (!signaled) { |
140 | // Fork new processes, to increase the chance that we hit the case where |
141 | // the kernel stack and task struct are in different segments. |
142 | pid = fork(); |
143 | if (pid == 0) |
144 | exit(touch_mappings()); |
145 | |
146 | FAIL_IF(waitpid(-1, &status, 0) == -1); |
147 | FAIL_IF(WIFSIGNALED(status)); |
148 | FAIL_IF(!WIFEXITED(status)); |
149 | FAIL_IF(WEXITSTATUS(status)); |
150 | } |
151 | |
152 | return 0; |
153 | } |
154 | |
155 | int main(void) |
156 | { |
157 | return test_harness(test, "large_vm_gpr_corruption" ); |
158 | } |
159 | |