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