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, main_context; 46 47 static int count, first_time; 48 49 void usr_signal_handler(int signo, siginfo_t *si, void *uc) 50 { 51 ucontext_t *ucp = uc; 52 int ret; 53 54 /* 55 * Allocating memory in a signal handler, and never freeing it on 56 * purpose, forcing the heap increase, so, the memory leak is what 57 * we want here. 58 */ 59 ucp->uc_link = mmap(NULL, sizeof(ucontext_t), 60 PROT_READ | PROT_WRITE, 61 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 62 if (ucp->uc_link == (void *)-1) { 63 perror("Mmap failed"); 64 exit(-1); 65 } 66 67 /* Forcing the page to be allocated in a page fault */ 68 ret = madvise(ucp->uc_link, sizeof(ucontext_t), MADV_DONTNEED); 69 if (ret) { 70 perror("madvise failed"); 71 exit(-1); 72 } 73 74 memcpy(&ucp->uc_link->uc_mcontext, &ucp->uc_mcontext, 75 sizeof(ucp->uc_mcontext)); 76 77 /* Forcing to enable MSR[TM] */ 78 UCONTEXT_MSR(ucp) |= MSR_TS_S; 79 80 /* 81 * A fork inside a signal handler seems to be more efficient than a 82 * fork() prior to the signal being raised. 83 */ 84 if (fork() == 0) { 85 /* 86 * Both child and parent will return, but, child returns 87 * with count set so it will exit in the next segfault. 88 * Parent will continue to loop. 89 */ 90 count = COUNT_MAX; 91 } 92 93 /* 94 * If the change above does not hit the bug, it will cause a 95 * segmentation fault, since the ck structures are NULL. 96 */ 97 } 98 99 void seg_signal_handler(int signo, siginfo_t *si, void *uc) 100 { 101 if (count == COUNT_MAX) { 102 /* Return to tm_signal_force_msr() and exit */ 103 setcontext(&main_context); 104 } 105 106 count++; 107 108 /* Reexecute the test */ 109 setcontext(&init_context); 110 } 111 112 void tm_trap_test(void) 113 { 114 struct sigaction usr_sa, seg_sa; 115 stack_t ss; 116 117 usr_sa.sa_flags = SA_SIGINFO | SA_ONSTACK; 118 usr_sa.sa_sigaction = usr_signal_handler; 119 120 seg_sa.sa_flags = SA_SIGINFO; 121 seg_sa.sa_sigaction = seg_signal_handler; 122 123 /* 124 * Set initial context. Will get back here from 125 * seg_signal_handler() 126 */ 127 getcontext(&init_context); 128 129 /* Allocated an alternative signal stack area */ 130 ss.ss_sp = mmap(NULL, SIGSTKSZ, PROT_READ | PROT_WRITE, 131 MAP_PRIVATE | MAP_ANONYMOUS, 0, 0); 132 ss.ss_size = SIGSTKSZ; 133 ss.ss_flags = 0; 134 135 if (ss.ss_sp == (void *)-1) { 136 perror("mmap error\n"); 137 exit(-1); 138 } 139 140 /* Force the allocation through a page fault */ 141 if (madvise(ss.ss_sp, SIGSTKSZ, MADV_DONTNEED)) { 142 perror("madvise\n"); 143 exit(-1); 144 } 145 146 /* Setting an alternative stack to generate a page fault when 147 * the signal is raised. 148 */ 149 if (sigaltstack(&ss, NULL)) { 150 perror("sigaltstack\n"); 151 exit(-1); 152 } 153 154 /* The signal handler will enable MSR_TS */ 155 sigaction(SIGUSR1, &usr_sa, NULL); 156 /* If it does not crash, it will segfault, avoid it to retest */ 157 sigaction(SIGSEGV, &seg_sa, NULL); 158 159 raise(SIGUSR1); 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 /* Will get back here after COUNT_MAX interactions */ 173 getcontext(&main_context); 174 175 if (!first_time++) 176 tm_trap_test(); 177 178 return EXIT_SUCCESS; 179 } 180 181 int main(int argc, char **argv) 182 { 183 test_harness(tm_signal_context_force_tm, "tm_signal_context_force_tm"); 184 } 185