| /* |
| 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> |
| |
| /* 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__) |
| # define DIVISION_BY_ZERO_TRIGGERS_FPE 0 |
| # define DIVISION_BY_ZERO_SI_CODE SI_TKILL |
| #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 |
| |
| |
| struct test { |
| void (*test)(void); |
| int sig; |
| int code; |
| volatile void *addr; |
| }; |
| |
| static const struct test *cur_test; |
| |
| static int zero(); |
| |
| static jmp_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_ADRERR, &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 int zero() |
| { |
| return 0; |
| } |