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__)
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
33struct test {
34	void (*test)(void);
35	int sig;
36	int code;
37	volatile void *addr;
38};
39
40static const struct test *cur_test;
41
42static int zero();
43
44static jmp_buf escape;
45
46#define BADADDR	((int *)0x1234)
47
48#define FILESIZE	(4*__pagesize)
49#define MAPSIZE		(2*FILESIZE)
50static unsigned int __pagesize;
51static char volatile *volatile mapping;
52
53static int testsig(int sig, int want)
54{
55	if (sig != want) {
56		fprintf(stderr, "  FAIL: expected signal %d, not %d\n", want, sig);
57		return 0;
58	}
59	return 1;
60}
61
62static int testcode(int code, int want)
63{
64	if (code != want) {
65		fprintf(stderr, "  FAIL: expected si_code==%d, not %d\n", want, code);
66		return 0;
67	}
68	return 1;
69}
70
71static int testaddr(void *addr, volatile void *want)
72{
73	/* Some architectures (e.g. s390) just provide enough information to
74         resolve the page fault, but do not provide the offset within a page */
75#if defined(__s390__)
76	if (addr != (void *) ((unsigned long) want & ~0xffful)) {
77#else
78	if (addr != want) {
79#endif
80		fprintf(stderr, "  FAIL: expected si_addr==%p, not %p\n", want, addr);
81		return 0;
82	}
83	return 1;
84
85}
86
87static void handler(int sig, siginfo_t *si, void *uc)
88{
89	int ok = 1;
90
91	ok = ok && testsig(sig, cur_test->sig);
92	ok = ok && testcode(si->si_code, cur_test->code);
93	if (cur_test->addr)
94		ok = ok && testaddr(si->si_addr, cur_test->addr);
95
96	if (ok)
97		fprintf(stderr, "  PASS\n");
98
99	siglongjmp(escape, ok + 1);
100}
101
102
103static void test1(void)
104{
105	*BADADDR = 'x';
106}
107
108static void test2()
109{
110	mapping[0] = 'x';
111}
112
113static void test3()
114{
115	mapping[FILESIZE+10];
116}
117
118static void test4()
119{
120	volatile int v = 44/zero();
121
122	(void)v;
123#if DIVISION_BY_ZERO_TRIGGERS_FPE == 0
124	raise(SIGFPE);
125#endif
126}
127
128int main()
129{
130	int fd, i;
131	static const int sigs[] = { SIGSEGV, SIGILL, SIGBUS, SIGFPE, SIGTRAP };
132	struct sigaction sa;
133	__pagesize = (unsigned int)sysconf(_SC_PAGE_SIZE);
134	sa.sa_sigaction = handler;
135	sa.sa_flags = SA_SIGINFO;
136	sigfillset(&sa.sa_mask);
137
138	for(i = 0; i < sizeof(sigs)/sizeof(*sigs); i++)
139		sigaction(sigs[i], &sa, NULL);
140
141	/* we need O_RDWR for the truncate below */
142	fd = open("faultstatus.tmp", O_CREAT|O_TRUNC|O_EXCL|O_RDWR, 0600);
143	if (fd == -1) {
144		perror("tmpfile");
145		exit(1);
146	}
147	unlink("faultstatus.tmp");
148	ftruncate(fd, FILESIZE);
149
150	mapping = mmap(0, MAPSIZE, PROT_READ, MAP_PRIVATE, fd, 0);
151	close(fd);
152
153	{
154		const struct test tests[] = {
155#define T(n, sig, code, addr) { test##n, sig, code, addr }
156			T(1, SIGSEGV,	SEGV_MAPERR,	BADADDR),
157			T(2, SIGSEGV,	SEGV_ACCERR,	mapping),
158			T(3, SIGBUS,	BUS_ADRERR,	&mapping[FILESIZE+10]),
159			T(4, SIGFPE,    DIVISION_BY_ZERO_SI_CODE, 0),
160#undef T
161		};
162
163		for(i = 0; i < sizeof(tests)/sizeof(*tests); i++) {
164			cur_test = &tests[i];
165
166			if (sigsetjmp(escape, 1) == 0) {
167				fprintf(stderr, "Test %d: ", i+1);
168				tests[i].test();
169				fprintf(stderr, "  FAIL: no fault, or handler returned\n");
170			}
171		}
172	}
173
174	return 0;
175}
176
177static int zero()
178{
179	return 0;
180}
181