Home | History | Annotate | Download | only in tests
      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 
     33 struct test {
     34 	void (*test)(void);
     35 	int sig;
     36 	int code;
     37 	volatile void *addr;
     38 };
     39 
     40 static const struct test *cur_test;
     41 
     42 static int zero();
     43 
     44 static jmp_buf escape;
     45 
     46 #define BADADDR	((int *)0x1234)
     47 
     48 #define FILESIZE	(4*__pagesize)
     49 #define MAPSIZE		(2*FILESIZE)
     50 static unsigned int __pagesize;
     51 static char volatile *volatile mapping;
     52 
     53 static 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 
     62 static 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 
     71 static 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 
     87 static 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 
    103 static void test1(void)
    104 {
    105 	*BADADDR = 'x';
    106 }
    107 
    108 static void test2()
    109 {
    110 	mapping[0] = 'x';
    111 }
    112 
    113 static void test3()
    114 {
    115 	mapping[FILESIZE+10];
    116 }
    117 
    118 static 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 
    128 int 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 
    177 static int zero()
    178 {
    179 	return 0;
    180 }
    181