1 #include <assert.h> 2 #include <execinfo.h> 3 #include <signal.h> 4 #include <string.h> 5 #include <sys/mman.h> 6 #include <ucontext.h> 7 #include <unistd.h> 8 9 /* 10 * Various instructions that generate SIGILL and SIGSEGV. They could have been 11 * defined in a separate .s file, but this would complicate the build, so the 12 * inline asm is used instead. 13 */ 14 15 #define DEFINE_ASM_FUNCTION(name, body) \ 16 asm(".globl " #name "\n" \ 17 #name ":\n" \ 18 ".cfi_startproc\n" \ 19 body "\n" \ 20 "br %r14\n" \ 21 ".cfi_endproc"); 22 23 void illegal_op(void); 24 extern const char after_illegal_op; 25 DEFINE_ASM_FUNCTION(illegal_op, 26 ".byte 0x00,0x00\n" 27 ".globl after_illegal_op\n" 28 "after_illegal_op:") 29 30 void stg(void *dst, unsigned long src); 31 DEFINE_ASM_FUNCTION(stg, "stg %r3,0(%r2)") 32 33 void mvc_8(void *dst, void *src); 34 DEFINE_ASM_FUNCTION(mvc_8, "mvc 0(8,%r2),0(%r3)") 35 36 extern const char return_from_main_1; 37 38 static void safe_puts(const char *s) 39 { 40 write(0, s, strlen(s)); 41 write(0, "\n", 1); 42 } 43 44 enum exception { 45 exception_operation, 46 exception_translation, 47 exception_protection, 48 }; 49 50 static struct { 51 int sig; 52 void *addr; 53 unsigned long psw_addr; 54 enum exception exception; 55 } expected; 56 57 static void handle_signal(int sig, siginfo_t *info, void *ucontext) 58 { 59 int err, i, n_frames; 60 void *frames[16]; 61 void *page; 62 63 if (sig != expected.sig) { 64 safe_puts("[ FAILED ] wrong signal"); 65 _exit(1); 66 } 67 68 if (info->si_addr != expected.addr) { 69 safe_puts("[ FAILED ] wrong si_addr"); 70 _exit(1); 71 } 72 73 if (((ucontext_t *)ucontext)->uc_mcontext.psw.addr != expected.psw_addr) { 74 safe_puts("[ FAILED ] wrong psw.addr"); 75 _exit(1); 76 } 77 78 switch (expected.exception) { 79 case exception_translation: 80 page = mmap(expected.addr, 4096, PROT_READ | PROT_WRITE, 81 MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0); 82 if (page != expected.addr) { 83 safe_puts("[ FAILED ] mmap() failed"); 84 _exit(1); 85 } 86 break; 87 case exception_protection: 88 err = mprotect(expected.addr, 4096, PROT_READ | PROT_WRITE); 89 if (err != 0) { 90 safe_puts("[ FAILED ] mprotect() failed"); 91 _exit(1); 92 } 93 break; 94 default: 95 break; 96 } 97 98 n_frames = backtrace(frames, sizeof(frames) / sizeof(frames[0])); 99 for (i = 0; i < n_frames; i++) { 100 if (frames[i] == &return_from_main_1) { 101 break; 102 } 103 } 104 if (i == n_frames) { 105 safe_puts("[ FAILED ] backtrace() is broken"); 106 _exit(1); 107 } 108 } 109 110 static void check_sigsegv(void *func, enum exception exception, 111 unsigned long val) 112 { 113 int prot; 114 unsigned long *page; 115 unsigned long *addr; 116 int err; 117 118 prot = exception == exception_translation ? PROT_NONE : PROT_READ; 119 page = mmap(NULL, 4096, prot, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 120 assert(page != MAP_FAILED); 121 if (exception == exception_translation) { 122 /* Hopefully nothing will be mapped at this address. */ 123 err = munmap(page, 4096); 124 assert(err == 0); 125 } 126 addr = page + (val & 0x1ff); 127 128 expected.sig = SIGSEGV; 129 expected.addr = page; 130 expected.psw_addr = (unsigned long)func; 131 expected.exception = exception; 132 if (func == stg) { 133 stg(addr, val); 134 } else { 135 assert(func == mvc_8); 136 mvc_8(addr, &val); 137 } 138 assert(*addr == val); 139 140 err = munmap(page, 4096); 141 assert(err == 0); 142 } 143 144 int main_1(void) 145 { 146 struct sigaction act; 147 int err; 148 149 memset(&act, 0, sizeof(act)); 150 act.sa_sigaction = handle_signal; 151 act.sa_flags = SA_SIGINFO; 152 err = sigaction(SIGILL, &act, NULL); 153 assert(err == 0); 154 err = sigaction(SIGSEGV, &act, NULL); 155 assert(err == 0); 156 157 safe_puts("[ RUN ] Operation exception"); 158 expected.sig = SIGILL; 159 expected.addr = illegal_op; 160 expected.psw_addr = (unsigned long)&after_illegal_op; 161 expected.exception = exception_operation; 162 illegal_op(); 163 safe_puts("[ OK ]"); 164 165 safe_puts("[ RUN ] Translation exception from stg"); 166 check_sigsegv(stg, exception_translation, 42); 167 safe_puts("[ OK ]"); 168 169 safe_puts("[ RUN ] Translation exception from mvc"); 170 check_sigsegv(mvc_8, exception_translation, 4242); 171 safe_puts("[ OK ]"); 172 173 safe_puts("[ RUN ] Protection exception from stg"); 174 check_sigsegv(stg, exception_protection, 424242); 175 safe_puts("[ OK ]"); 176 177 safe_puts("[ RUN ] Protection exception from mvc"); 178 check_sigsegv(mvc_8, exception_protection, 42424242); 179 safe_puts("[ OK ]"); 180 181 safe_puts("[ PASSED ]"); 182 183 _exit(0); 184 } 185 186 /* 187 * Define main() in assembly in order to test that unwinding from signal 188 * handlers until main() works. This way we can define a specific point that 189 * the unwinder should reach. This is also better than defining main() in C 190 * and using inline assembly to call main_1(), since it's not easy to get all 191 * the clobbers right. 192 */ 193 194 DEFINE_ASM_FUNCTION(main, 195 "stmg %r14,%r15,112(%r15)\n" 196 ".cfi_offset 14,-48\n" 197 ".cfi_offset 15,-40\n" 198 "lay %r15,-160(%r15)\n" 199 ".cfi_def_cfa_offset 320\n" 200 "brasl %r14,main_1\n" 201 ".globl return_from_main_1\n" 202 "return_from_main_1:\n" 203 "lmg %r14,%r15,272(%r15)\n" 204 ".cfi_restore 15\n" 205 ".cfi_restore 14\n" 206 ".cfi_def_cfa_offset 160"); 207