1 /* $OpenBSD: siginfo-fault.c,v 1.2 2021/09/28 08:56:15 kettenis 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 may 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
sigsegv(int signo,siginfo_t * si,void * ctx)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 *
strsigcode(int signum,int sigcode)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
checksig(const char * name,int expsigno,int expcode,volatile char * expaddr)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 != (char *)((uintptr_t)gotsi.si_addr & EXPADDR_MASK)) {
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
main()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