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__) || 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 
     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 
     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 
     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