Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

selftests/powerpc: Add a test of 4PB SLB handling

Add a test for a bug we had in the 4PB address space SLB handling. It
was fixed in commit 4c2de74cc869 ("powerpc/64: Interrupts save PPR on
stack rather than thread_struct").

Signed-off-by: Michael Ellerman <mpe@ellerman.id.au>
Link: https://lore.kernel.org/r/20220317143925.1030447-1-mpe@ellerman.id.au

+160 -1
+1
tools/testing/selftests/powerpc/mm/.gitignore
··· 12 12 pkey_siginfo 13 13 stack_expansion_ldst 14 14 stack_expansion_signal 15 + large_vm_gpr_corruption
+3 -1
tools/testing/selftests/powerpc/mm/Makefile
··· 4 4 5 5 TEST_GEN_PROGS := hugetlb_vs_thp_test subpage_prot prot_sao segv_errors wild_bctr \ 6 6 large_vm_fork_separation bad_accesses pkey_exec_prot \ 7 - pkey_siginfo stack_expansion_signal stack_expansion_ldst 7 + pkey_siginfo stack_expansion_signal stack_expansion_ldst \ 8 + large_vm_gpr_corruption 8 9 TEST_PROGS := stress_code_patching.sh 9 10 10 11 TEST_GEN_PROGS_EXTENDED := tlbie_test ··· 20 19 21 20 $(OUTPUT)/wild_bctr: CFLAGS += -m64 22 21 $(OUTPUT)/large_vm_fork_separation: CFLAGS += -m64 22 + $(OUTPUT)/large_vm_gpr_corruption: CFLAGS += -m64 23 23 $(OUTPUT)/bad_accesses: CFLAGS += -m64 24 24 $(OUTPUT)/pkey_exec_prot: CFLAGS += -m64 25 25 $(OUTPUT)/pkey_siginfo: CFLAGS += -m64
+156
tools/testing/selftests/powerpc/mm/large_vm_gpr_corruption.c
··· 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 + 116 + page_size = sysconf(_SC_PAGESIZE); 117 + 118 + for (i = 0; i < NR_MAPPINGS; i++) { 119 + addr = BASE_ADDRESS + (i * STRIDE); 120 + 121 + p = mmap((void *)addr, page_size, PROT_READ | PROT_WRITE, 122 + MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED_NOREPLACE, -1, 0); 123 + if (p == MAP_FAILED) { 124 + perror("mmap"); 125 + printf("Error: couldn't mmap(), confirm kernel has 4PB support?\n"); 126 + return 1; 127 + } 128 + } 129 + 130 + action.sa_handler = signal_handler; 131 + action.sa_flags = SA_RESTART; 132 + FAIL_IF(sigaction(SIGALRM, &action, NULL) < 0); 133 + 134 + // Seen to always crash in under ~10s on affected kernels. 135 + alarm(30); 136 + 137 + while (!signaled) { 138 + // Fork new processes, to increase the chance that we hit the case where 139 + // the kernel stack and task struct are in different segments. 140 + pid = fork(); 141 + if (pid == 0) 142 + exit(touch_mappings()); 143 + 144 + FAIL_IF(waitpid(-1, &status, 0) == -1); 145 + FAIL_IF(WIFSIGNALED(status)); 146 + FAIL_IF(!WIFEXITED(status)); 147 + FAIL_IF(WEXITSTATUS(status)); 148 + } 149 + 150 + return 0; 151 + } 152 + 153 + int main(void) 154 + { 155 + return test_harness(test, "large_vm_gpr_corruption"); 156 + }