1 /* $OpenBSD: siginfo-fault.c,v 1.1 2020/09/16 14:02:23 mpi Exp $ */ 2 /* 3 * Copyright (c) 2014 Google Inc. 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/mman.h> 19 20 #include <assert.h> 21 #include <errno.h> 22 #include <fcntl.h> 23 #include <limits.h> 24 #include <setjmp.h> 25 #include <signal.h> 26 #include <stdio.h> 27 #include <stdint.h> 28 #include <stdlib.h> 29 #include <string.h> 30 #include <unistd.h> 31 32 /* 33 * Some architectures deliver an imprecise fault address. 34 */ 35 #ifdef __sparc64__ 36 #define EXPADDR_MASK ~(3UL) 37 #else 38 #define EXPADDR_MASK ~(0UL) 39 #endif 40 41 #define CHECK_EQ(a, b) assert((a) == (b)) 42 #define CHECK_NE(a, b) assert((a) != (b)) 43 #define CHECK_LE(a, b) assert((a) <= (b)) 44 #define FAIL() assert(0) 45 46 static jmp_buf env; 47 static volatile int gotsigno; 48 static volatile siginfo_t gotsi; 49 50 static void 51 sigsegv(int signo, siginfo_t *si, void *ctx) 52 { 53 gotsigno = signo; 54 gotsi = *si; 55 siglongjmp(env, 1); 56 } 57 58 static const char * 59 strsigcode(int signum, int sigcode) 60 { 61 switch (signum) { 62 case SIGSEGV: 63 switch (sigcode) { 64 case SEGV_MAPERR: 65 return "address not mapped to object"; 66 case SEGV_ACCERR: 67 return "invalid permissions"; 68 } 69 break; 70 case SIGBUS: 71 switch (sigcode) { 72 case BUS_ADRALN: 73 return "invalid address alignment"; 74 case BUS_ADRERR: 75 return "non-existent physical address"; 76 case BUS_OBJERR: 77 return "object specific hardware error"; 78 } 79 break; 80 } 81 return "unknown"; 82 } 83 84 static int 85 checksig(const char *name, int expsigno, int expcode, volatile char *expaddr) 86 { 87 int fail = 0; 88 char str1[NL_TEXTMAX], str2[NL_TEXTMAX]; 89 90 expaddr = (char *)((uintptr_t)expaddr & EXPADDR_MASK); 91 92 if (expsigno != gotsigno) { 93 strlcpy(str1, strsignal(expsigno), sizeof(str1)); 94 strlcpy(str2, strsignal(gotsigno), sizeof(str2)); 95 fprintf(stderr, "%s signo: expect %d (%s), actual %d (%s)\n", 96 name, expsigno, str1, gotsigno, str2); 97 ++fail; 98 } 99 if (expsigno != gotsi.si_signo) { 100 strlcpy(str1, strsignal(expsigno), sizeof(str1)); 101 strlcpy(str2, strsignal(gotsi.si_signo), sizeof(str2)); 102 fprintf(stderr, "%s si_signo: expect %d (%s), actual %d (%s)\n", 103 name, expsigno, str1, gotsi.si_signo, str2); 104 ++fail; 105 } 106 if (expcode != gotsi.si_code) { 107 fprintf(stderr, "%s si_code: expect %d (%s), actual %d (%s)\n", 108 name, expcode, strsigcode(expsigno, expcode), 109 gotsi.si_code, strsigcode(gotsigno, gotsi.si_code)); 110 ++fail; 111 } 112 if (expaddr != gotsi.si_addr) { 113 fprintf(stderr, "%s si_addr: expect %p, actual %p\n", 114 name, expaddr, gotsi.si_addr); 115 ++fail; 116 } 117 return (fail); 118 } 119 120 int 121 main() 122 { 123 int fail = 0; 124 long pagesize = sysconf(_SC_PAGESIZE); 125 CHECK_NE(-1, pagesize); 126 127 const struct sigaction sa = { 128 .sa_sigaction = sigsegv, 129 .sa_flags = SA_SIGINFO, 130 }; 131 CHECK_EQ(0, sigaction(SIGSEGV, &sa, NULL)); 132 CHECK_EQ(0, sigaction(SIGBUS, &sa, NULL)); 133 134 volatile char *p; 135 CHECK_NE(MAP_FAILED, (p = mmap(NULL, pagesize, PROT_NONE, 136 MAP_PRIVATE|MAP_ANON, -1, 0))); 137 138 CHECK_EQ(0, mprotect((void *)p, pagesize, PROT_READ)); 139 if (sigsetjmp(env, 1) == 0) { 140 p[0] = 1; 141 FAIL(); 142 } 143 fail += checksig("mprotect read", SIGSEGV, SEGV_ACCERR, p); 144 145 CHECK_EQ(0, mprotect((void *)p, pagesize, PROT_NONE)); 146 if (sigsetjmp(env, 1) == 0) { 147 (void)p[1]; 148 FAIL(); 149 } 150 fail += checksig("mprotect none", SIGSEGV, SEGV_ACCERR, p + 1); 151 152 CHECK_EQ(0, munmap((void *)p, pagesize)); 153 if (sigsetjmp(env, 1) == 0) { 154 (void)p[2]; 155 FAIL(); 156 } 157 fail += checksig("munmap", SIGSEGV, SEGV_MAPERR, p + 2); 158 159 char filename[] = "/tmp/siginfo-fault.XXXXXXXX"; 160 int fd; 161 CHECK_LE(0, (fd = mkstemp(filename))); 162 CHECK_EQ(0, unlink(filename)); 163 CHECK_EQ(0, ftruncate(fd, 0)); /* just in case */ 164 CHECK_NE(MAP_FAILED, (p = mmap(NULL, pagesize, PROT_READ|PROT_WRITE, 165 MAP_SHARED, fd, 0))); 166 CHECK_EQ(0, close(fd)); 167 168 if (sigsetjmp(env, 1) == 0) { 169 p[3] = 1; 170 FAIL(); 171 } 172 fail += checksig("mmap file", SIGBUS, BUS_OBJERR, p + 3); 173 174 return (fail); 175 } 176