1*31e18b07Skettenis /* $OpenBSD: siginfo-fault.c,v 1.2 2021/09/28 08:56:15 kettenis Exp $ */
26bcfccdaSmpi /*
36bcfccdaSmpi * Copyright (c) 2014 Google Inc.
46bcfccdaSmpi *
56bcfccdaSmpi * Permission to use, copy, modify, and distribute this software for any
66bcfccdaSmpi * purpose with or without fee is hereby granted, provided that the above
76bcfccdaSmpi * copyright notice and this permission notice appear in all copies.
86bcfccdaSmpi *
96bcfccdaSmpi * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
106bcfccdaSmpi * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
116bcfccdaSmpi * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
126bcfccdaSmpi * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
136bcfccdaSmpi * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
146bcfccdaSmpi * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
156bcfccdaSmpi * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
166bcfccdaSmpi */
176bcfccdaSmpi
186bcfccdaSmpi #include <sys/mman.h>
196bcfccdaSmpi
206bcfccdaSmpi #include <assert.h>
216bcfccdaSmpi #include <errno.h>
226bcfccdaSmpi #include <fcntl.h>
236bcfccdaSmpi #include <limits.h>
246bcfccdaSmpi #include <setjmp.h>
256bcfccdaSmpi #include <signal.h>
266bcfccdaSmpi #include <stdio.h>
276bcfccdaSmpi #include <stdint.h>
286bcfccdaSmpi #include <stdlib.h>
296bcfccdaSmpi #include <string.h>
306bcfccdaSmpi #include <unistd.h>
316bcfccdaSmpi
326bcfccdaSmpi /*
33*31e18b07Skettenis * Some architectures may deliver an imprecise fault address.
346bcfccdaSmpi */
356bcfccdaSmpi #ifdef __sparc64__
366bcfccdaSmpi #define EXPADDR_MASK ~(3UL)
376bcfccdaSmpi #else
386bcfccdaSmpi #define EXPADDR_MASK ~(0UL)
396bcfccdaSmpi #endif
406bcfccdaSmpi
416bcfccdaSmpi #define CHECK_EQ(a, b) assert((a) == (b))
426bcfccdaSmpi #define CHECK_NE(a, b) assert((a) != (b))
436bcfccdaSmpi #define CHECK_LE(a, b) assert((a) <= (b))
446bcfccdaSmpi #define FAIL() assert(0)
456bcfccdaSmpi
466bcfccdaSmpi static jmp_buf env;
476bcfccdaSmpi static volatile int gotsigno;
486bcfccdaSmpi static volatile siginfo_t gotsi;
496bcfccdaSmpi
506bcfccdaSmpi static void
sigsegv(int signo,siginfo_t * si,void * ctx)516bcfccdaSmpi sigsegv(int signo, siginfo_t *si, void *ctx)
526bcfccdaSmpi {
536bcfccdaSmpi gotsigno = signo;
546bcfccdaSmpi gotsi = *si;
556bcfccdaSmpi siglongjmp(env, 1);
566bcfccdaSmpi }
576bcfccdaSmpi
586bcfccdaSmpi static const char *
strsigcode(int signum,int sigcode)596bcfccdaSmpi strsigcode(int signum, int sigcode)
606bcfccdaSmpi {
616bcfccdaSmpi switch (signum) {
626bcfccdaSmpi case SIGSEGV:
636bcfccdaSmpi switch (sigcode) {
646bcfccdaSmpi case SEGV_MAPERR:
656bcfccdaSmpi return "address not mapped to object";
666bcfccdaSmpi case SEGV_ACCERR:
676bcfccdaSmpi return "invalid permissions";
686bcfccdaSmpi }
696bcfccdaSmpi break;
706bcfccdaSmpi case SIGBUS:
716bcfccdaSmpi switch (sigcode) {
726bcfccdaSmpi case BUS_ADRALN:
736bcfccdaSmpi return "invalid address alignment";
746bcfccdaSmpi case BUS_ADRERR:
756bcfccdaSmpi return "non-existent physical address";
766bcfccdaSmpi case BUS_OBJERR:
776bcfccdaSmpi return "object specific hardware error";
786bcfccdaSmpi }
796bcfccdaSmpi break;
806bcfccdaSmpi }
816bcfccdaSmpi return "unknown";
826bcfccdaSmpi }
836bcfccdaSmpi
846bcfccdaSmpi static int
checksig(const char * name,int expsigno,int expcode,volatile char * expaddr)856bcfccdaSmpi checksig(const char *name, int expsigno, int expcode, volatile char *expaddr)
866bcfccdaSmpi {
876bcfccdaSmpi int fail = 0;
886bcfccdaSmpi char str1[NL_TEXTMAX], str2[NL_TEXTMAX];
896bcfccdaSmpi
906bcfccdaSmpi expaddr = (char *)((uintptr_t)expaddr & EXPADDR_MASK);
916bcfccdaSmpi
926bcfccdaSmpi if (expsigno != gotsigno) {
936bcfccdaSmpi strlcpy(str1, strsignal(expsigno), sizeof(str1));
946bcfccdaSmpi strlcpy(str2, strsignal(gotsigno), sizeof(str2));
956bcfccdaSmpi fprintf(stderr, "%s signo: expect %d (%s), actual %d (%s)\n",
966bcfccdaSmpi name, expsigno, str1, gotsigno, str2);
976bcfccdaSmpi ++fail;
986bcfccdaSmpi }
996bcfccdaSmpi if (expsigno != gotsi.si_signo) {
1006bcfccdaSmpi strlcpy(str1, strsignal(expsigno), sizeof(str1));
1016bcfccdaSmpi strlcpy(str2, strsignal(gotsi.si_signo), sizeof(str2));
1026bcfccdaSmpi fprintf(stderr, "%s si_signo: expect %d (%s), actual %d (%s)\n",
1036bcfccdaSmpi name, expsigno, str1, gotsi.si_signo, str2);
1046bcfccdaSmpi ++fail;
1056bcfccdaSmpi }
1066bcfccdaSmpi if (expcode != gotsi.si_code) {
1076bcfccdaSmpi fprintf(stderr, "%s si_code: expect %d (%s), actual %d (%s)\n",
1086bcfccdaSmpi name, expcode, strsigcode(expsigno, expcode),
1096bcfccdaSmpi gotsi.si_code, strsigcode(gotsigno, gotsi.si_code));
1106bcfccdaSmpi ++fail;
1116bcfccdaSmpi }
112*31e18b07Skettenis if (expaddr != (char *)((uintptr_t)gotsi.si_addr & EXPADDR_MASK)) {
1136bcfccdaSmpi fprintf(stderr, "%s si_addr: expect %p, actual %p\n",
1146bcfccdaSmpi name, expaddr, gotsi.si_addr);
1156bcfccdaSmpi ++fail;
1166bcfccdaSmpi }
1176bcfccdaSmpi return (fail);
1186bcfccdaSmpi }
1196bcfccdaSmpi
1206bcfccdaSmpi int
main()1216bcfccdaSmpi main()
1226bcfccdaSmpi {
1236bcfccdaSmpi int fail = 0;
1246bcfccdaSmpi long pagesize = sysconf(_SC_PAGESIZE);
1256bcfccdaSmpi CHECK_NE(-1, pagesize);
1266bcfccdaSmpi
1276bcfccdaSmpi const struct sigaction sa = {
1286bcfccdaSmpi .sa_sigaction = sigsegv,
1296bcfccdaSmpi .sa_flags = SA_SIGINFO,
1306bcfccdaSmpi };
1316bcfccdaSmpi CHECK_EQ(0, sigaction(SIGSEGV, &sa, NULL));
1326bcfccdaSmpi CHECK_EQ(0, sigaction(SIGBUS, &sa, NULL));
1336bcfccdaSmpi
1346bcfccdaSmpi volatile char *p;
1356bcfccdaSmpi CHECK_NE(MAP_FAILED, (p = mmap(NULL, pagesize, PROT_NONE,
1366bcfccdaSmpi MAP_PRIVATE|MAP_ANON, -1, 0)));
1376bcfccdaSmpi
1386bcfccdaSmpi CHECK_EQ(0, mprotect((void *)p, pagesize, PROT_READ));
1396bcfccdaSmpi if (sigsetjmp(env, 1) == 0) {
1406bcfccdaSmpi p[0] = 1;
1416bcfccdaSmpi FAIL();
1426bcfccdaSmpi }
1436bcfccdaSmpi fail += checksig("mprotect read", SIGSEGV, SEGV_ACCERR, p);
1446bcfccdaSmpi
1456bcfccdaSmpi CHECK_EQ(0, mprotect((void *)p, pagesize, PROT_NONE));
1466bcfccdaSmpi if (sigsetjmp(env, 1) == 0) {
1476bcfccdaSmpi (void)p[1];
1486bcfccdaSmpi FAIL();
1496bcfccdaSmpi }
1506bcfccdaSmpi fail += checksig("mprotect none", SIGSEGV, SEGV_ACCERR, p + 1);
1516bcfccdaSmpi
1526bcfccdaSmpi CHECK_EQ(0, munmap((void *)p, pagesize));
1536bcfccdaSmpi if (sigsetjmp(env, 1) == 0) {
1546bcfccdaSmpi (void)p[2];
1556bcfccdaSmpi FAIL();
1566bcfccdaSmpi }
1576bcfccdaSmpi fail += checksig("munmap", SIGSEGV, SEGV_MAPERR, p + 2);
1586bcfccdaSmpi
1596bcfccdaSmpi char filename[] = "/tmp/siginfo-fault.XXXXXXXX";
1606bcfccdaSmpi int fd;
1616bcfccdaSmpi CHECK_LE(0, (fd = mkstemp(filename)));
1626bcfccdaSmpi CHECK_EQ(0, unlink(filename));
1636bcfccdaSmpi CHECK_EQ(0, ftruncate(fd, 0)); /* just in case */
1646bcfccdaSmpi CHECK_NE(MAP_FAILED, (p = mmap(NULL, pagesize, PROT_READ|PROT_WRITE,
1656bcfccdaSmpi MAP_SHARED, fd, 0)));
1666bcfccdaSmpi CHECK_EQ(0, close(fd));
1676bcfccdaSmpi
1686bcfccdaSmpi if (sigsetjmp(env, 1) == 0) {
1696bcfccdaSmpi p[3] = 1;
1706bcfccdaSmpi FAIL();
1716bcfccdaSmpi }
1726bcfccdaSmpi fail += checksig("mmap file", SIGBUS, BUS_OBJERR, p + 3);
1736bcfccdaSmpi
1746bcfccdaSmpi return (fail);
1756bcfccdaSmpi }
176