1 /*
2 Check that a fault signal handler gets the expected info
3 */
4 #include <signal.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <fcntl.h>
8 #include <setjmp.h>
9 #include "tests/sys_mman.h"
10 #include <unistd.h>
11
12 /* Division by zero triggers a SIGFPE on x86 and x86_64,
13 but not on the PowerPC architecture.
14
15 On ARM-Linux, we do get a SIGFPE, but not from the faulting of a
16 division instruction (there isn't any such thing) but rather
17 because the process exits via tgkill, sending itself a SIGFPE.
18 Hence we get a SIGFPE but the SI_CODE is different from that on
19 x86/amd64-linux.
20 */
21 #if defined(__powerpc__) || defined(__aarch64__)
22 # define DIVISION_BY_ZERO_TRIGGERS_FPE 0
23 # define DIVISION_BY_ZERO_SI_CODE SI_TKILL
24 #elif defined(__arm__)
25 # define DIVISION_BY_ZERO_TRIGGERS_FPE 1
26 # define DIVISION_BY_ZERO_SI_CODE SI_TKILL
27 #else
28 # define DIVISION_BY_ZERO_TRIGGERS_FPE 1
29 # define DIVISION_BY_ZERO_SI_CODE FPE_INTDIV
30 #endif
31
32 /* Accessing non-mapped virtual address results in SIGBUS
33 * with si_code equal to BUS_ADRERR on Linux, whereas in SIGBUS
34 * with si_code equal to BUS_OBJERR on Solaris. On Solaris,
35 * BUS_ADRERR is used for bus time out while BUS_OBJERR is translated
36 * from underlying codes FC_OBJERR (x86) or ASYNC_BERR (sparc).
37 */
38 #if defined(VGO_solaris)
39 # define BUS_ERROR_SI_CODE BUS_OBJERR
40 #else
41 # define BUS_ERROR_SI_CODE BUS_ADRERR
42 #endif
43
44 struct test {
45 void (*test)(void);
46 int sig;
47 int code;
48 volatile void *addr;
49 };
50
51 static const struct test *cur_test;
52
53 static int zero();
54
55 static sigjmp_buf escape;
56
57 #define BADADDR ((int *)0x1234)
58
59 #define FILESIZE (4*__pagesize)
60 #define MAPSIZE (2*FILESIZE)
61 static unsigned int __pagesize;
62 static char volatile *volatile mapping;
63
testsig(int sig,int want)64 static int testsig(int sig, int want)
65 {
66 if (sig != want) {
67 fprintf(stderr, " FAIL: expected signal %d, not %d\n", want, sig);
68 return 0;
69 }
70 return 1;
71 }
72
testcode(int code,int want)73 static int testcode(int code, int want)
74 {
75 if (code != want) {
76 fprintf(stderr, " FAIL: expected si_code==%d, not %d\n", want, code);
77 return 0;
78 }
79 return 1;
80 }
81
testaddr(void * addr,volatile void * want)82 static int testaddr(void *addr, volatile void *want)
83 {
84 /* Some architectures (e.g. s390) just provide enough information to
85 resolve the page fault, but do not provide the offset within a page */
86 #if defined(__s390__)
87 if (addr != (void *) ((unsigned long) want & ~0xffful)) {
88 #else
89 if (addr != want) {
90 #endif
91 fprintf(stderr, " FAIL: expected si_addr==%p, not %p\n", want, addr);
92 return 0;
93 }
94 return 1;
95
96 }
97
98 static void handler(int sig, siginfo_t *si, void *uc)
99 {
100 int ok = 1;
101
102 ok = ok && testsig(sig, cur_test->sig);
103 ok = ok && testcode(si->si_code, cur_test->code);
104 if (cur_test->addr)
105 ok = ok && testaddr(si->si_addr, cur_test->addr);
106
107 if (ok)
108 fprintf(stderr, " PASS\n");
109
110 siglongjmp(escape, ok + 1);
111 }
112
113
114 static void test1(void)
115 {
116 *BADADDR = 'x';
117 }
118
119 static void test2()
120 {
121 mapping[0] = 'x';
122 }
123
124 static void test3()
125 {
126 mapping[FILESIZE+10];
127 }
128
129 static void test4()
130 {
131 volatile int v = 44/zero();
132
133 (void)v;
134 #if DIVISION_BY_ZERO_TRIGGERS_FPE == 0
135 raise(SIGFPE);
136 #endif
137 }
138
139 int main()
140 {
141 int fd, i;
142 static const int sigs[] = { SIGSEGV, SIGILL, SIGBUS, SIGFPE, SIGTRAP };
143 struct sigaction sa;
144 __pagesize = (unsigned int)sysconf(_SC_PAGE_SIZE);
145 sa.sa_sigaction = handler;
146 sa.sa_flags = SA_SIGINFO;
147 sigfillset(&sa.sa_mask);
148
149 for(i = 0; i < sizeof(sigs)/sizeof(*sigs); i++)
150 sigaction(sigs[i], &sa, NULL);
151
152 /* we need O_RDWR for the truncate below */
153 fd = open("faultstatus.tmp", O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600);
154 if (fd == -1) {
155 perror("tmpfile");
156 exit(1);
157 }
158 unlink("faultstatus.tmp");
159 ftruncate(fd, FILESIZE);
160
161 mapping = mmap(0, MAPSIZE, PROT_READ, MAP_PRIVATE, fd, 0);
162 close(fd);
163
164 {
165 const struct test tests[] = {
166 #define T(n, sig, code, addr) { test##n, sig, code, addr }
167 T(1, SIGSEGV, SEGV_MAPERR, BADADDR),
168 T(2, SIGSEGV, SEGV_ACCERR, mapping),
169 T(3, SIGBUS, BUS_ERROR_SI_CODE, &mapping[FILESIZE+10]),
170 T(4, SIGFPE, DIVISION_BY_ZERO_SI_CODE, 0),
171 #undef T
172 };
173
174 for(i = 0; i < sizeof(tests)/sizeof(*tests); i++) {
175 cur_test = &tests[i];
176
177 if (sigsetjmp(escape, 1) == 0) {
178 fprintf(stderr, "Test %d: ", i+1);
179 tests[i].test();
180 fprintf(stderr, " FAIL: no fault, or handler returned\n");
181 }
182 }
183 }
184
185 return 0;
186 }
187
188 static int zero()
189 {
190 return 0;
191 }
192