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