1 // SPDX-License-Identifier: GPL-2.0 2 /* 3 * Copyright 2018, Breno Leitao, Gustavo Romero, IBM Corp. 4 * 5 * This test raises a SIGUSR1 signal, and toggle the MSR[TS] 6 * fields at the signal handler. With MSR[TS] being set, the kernel will 7 * force a recheckpoint, which may cause a segfault when returning to 8 * user space. Since the test needs to re-run, the segfault needs to be 9 * caught and handled. 10 * 11 * In order to continue the test even after a segfault, the context is 12 * saved prior to the signal being raised, and it is restored when there is 13 * a segmentation fault. This happens for COUNT_MAX times. 14 * 15 * This test never fails (as returning EXIT_FAILURE). It either succeeds, 16 * or crash the kernel (on a buggy kernel). 17 */ 18 19 #define _GNU_SOURCE 20 #include <stdio.h> 21 #include <stdlib.h> 22 #include <signal.h> 23 #include <string.h> 24 #include <ucontext.h> 25 #include <unistd.h> 26 #include <sys/mman.h> 27 28 #include "tm.h" 29 #include "utils.h" 30 #include "reg.h" 31 32 #define COUNT_MAX 5000 /* Number of interactions */ 33 34 /* 35 * This test only runs on 64 bits system. Unsetting MSR_TS_S to avoid 36 * compilation issue on 32 bits system. There is no side effect, since the 37 * whole test will be skipped if it is not running on 64 bits system. 38 */ 39 #ifndef __powerpc64__ 40 #undef MSR_TS_S 41 #define MSR_TS_S 0 42 #endif 43 44 /* Setting contexts because the test will crash and we want to recover */ 45 ucontext_t init_context; 46 47 /* count is changed in the signal handler, so it must be volatile */ 48 static volatile int count; 49 50 void usr_signal_handler(int signo, siginfo_t *si, void *uc) 51 { 52 ucontext_t *ucp = uc; 53 int ret; 54 55 /* 56 * Allocating memory in a signal handler, and never freeing it on 57 * purpose, forcing the heap increase, so, the memory leak is what 58 * we want here. 59 */ 60 ucp->uc_link = mmap(NULL, sizeof(ucontext_t), 61 PROT_READ | PROT_WRITE, 62 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 63 if (ucp->uc_link == (void *)-1) { 64 perror("Mmap failed"); 65 exit(-1); 66 } 67 68 /* Forcing the page to be allocated in a page fault */ 69 ret = madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED); 70 if (ret) { 71 perror("madvise failed"); 72 exit(-1); 73 } 74 75 memcpy(&ucp->uc_link->uc_mcontext, &ucp->uc_mcontext, 76 sizeof(ucp->uc_mcontext)); 77 78 /* Forcing to enable MSR[TM] */ 79 UCONTEXT_MSR(ucp) |= MSR_TS_S; 80 81 /* 82 * A fork inside a signal handler seems to be more efficient than a 83 * fork() prior to the signal being raised. 84 */ 85 if (fork() == 0) { 86 /* 87 * Both child and parent will return, but, child returns 88 * with count set so it will exit in the next segfault. 89 * Parent will continue to loop. 90 */ 91 count = COUNT_MAX; 92 } 93 94 /* 95 * If the change above does not hit the bug, it will cause a 96 * segmentation fault, since the ck structures are NULL. 97 */ 98 } 99 100 void seg_signal_handler(int signo, siginfo_t *si, void *uc) 101 { 102 count++; 103 104 /* Reexecute the test */ 105 setcontext(&init_context); 106 } 107 108 void tm_trap_test(void) 109 { 110 struct sigaction usr_sa, seg_sa; 111 stack_t ss; 112 113 usr_sa.sa_flags = SA_SIGINFO | SA_ONSTACK; 114 usr_sa.sa_sigaction = usr_signal_handler; 115 116 seg_sa.sa_flags = SA_SIGINFO; 117 seg_sa.sa_sigaction = seg_signal_handler; 118 119 /* 120 * Set initial context. Will get back here from 121 * seg_signal_handler() 122 */ 123 getcontext(&init_context); 124 125 while (count < COUNT_MAX) { 126 /* Allocated an alternative signal stack area */ 127 ss.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, 128 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 129 ss.ss_size = SIGSTKSZ; 130 ss.ss_flags = 0; 131 132 if (ss.ss_sp == (void *)-1) { 133 perror("mmap error\n"); 134 exit(-1); 135 } 136 137 /* Force the allocation through a page fault */ 138 if (madvise(ss.ss_sp, SIGSTKSZ, MADV_DONTNEED)) { 139 perror("madvise\n"); 140 exit(-1); 141 } 142 143 /* 144 * Setting an alternative stack to generate a page fault when 145 * the signal is raised. 146 */ 147 if (sigaltstack(&ss, NULL)) { 148 perror("sigaltstack\n"); 149 exit(-1); 150 } 151 152 /* The signal handler will enable MSR_TS */ 153 sigaction(SIGUSR1, &usr_sa, NULL); 154 /* If it does not crash, it might segfault, avoid it to retest */ 155 sigaction(SIGSEGV, &seg_sa, NULL); 156 157 raise(SIGUSR1); 158 count++; 159 } 160 } 161 162 int tm_signal_context_force_tm(void) 163 { 164 SKIP_IF(!have_htm()); 165 /* 166 * Skipping if not running on 64 bits system, since I think it is 167 * not possible to set mcontext's [MSR] with TS, due to it being 32 168 * bits. 169 */ 170 SKIP_IF(!is_ppc64le()); 171 172 tm_trap_test(); 173 174 return EXIT_SUCCESS; 175 } 176 177 int main(int argc, char **argv) 178 { 179 test_harness(tm_signal_context_force_tm, "tm_signal_context_force_tm"); 180 } 181