11.15Sriastrad/*	$NetBSD: h_segv.c,v 1.15 2024/05/14 15:54:16 riastradh Exp $	*/
21.1Schristos
31.1Schristos/*-
41.1Schristos * Copyright (c) 2017 The NetBSD Foundation, Inc.
51.1Schristos * All rights reserved.
61.1Schristos *
71.1Schristos * This code is derived from software contributed to The NetBSD Foundation
81.1Schristos * by Christos Zoulas.
91.1Schristos *
101.1Schristos * Redistribution and use in source and binary forms, with or without
111.1Schristos * modification, are permitted provided that the following conditions
121.1Schristos * are met:
131.1Schristos * 1. Redistributions of source code must retain the above copyright
141.1Schristos *    notice, this list of conditions and the following disclaimer.
151.1Schristos * 2. Redistributions in binary form must reproduce the above copyright
161.1Schristos *    notice, this list of conditions and the following disclaimer in the
171.1Schristos *    documentation and/or other materials provided with the distribution.
181.1Schristos *
191.1Schristos * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
201.1Schristos * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
211.1Schristos * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
221.1Schristos * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
231.1Schristos * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
241.1Schristos * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
251.1Schristos * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
261.1Schristos * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
271.1Schristos * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
281.1Schristos * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
291.1Schristos * POSSIBILITY OF SUCH DAMAGE.
301.1Schristos */
311.1Schristos#include <sys/cdefs.h>
321.15Sriastrad__RCSID("$NetBSD: h_segv.c,v 1.15 2024/05/14 15:54:16 riastradh Exp $");
331.10Sriastrad
341.10Sriastrad#define	__TEST_FENV
351.1Schristos
361.4Skamil#include <sys/types.h>
371.5Skamil#include <sys/mman.h>
381.4Skamil#include <sys/ptrace.h>
391.8Sriastrad
401.8Sriastrad#include <err.h>
411.9Sriastrad#include <fenv.h>
421.12Schristos#if (__arm__ && !__SOFTFP__) || __aarch64__
431.11Smartin#include <ieeefp.h> /* only need for ARM Cortex/Neon hack */
441.12Schristos#endif
451.8Sriastrad#include <signal.h>
461.1Schristos#include <stdio.h>
471.8Sriastrad#include <stdlib.h>
481.1Schristos#include <string.h>
491.1Schristos#include <unistd.h>
501.1Schristos
511.1Schristosstatic int flags;
521.1Schristos#define F_RECURSE 	1
531.1Schristos#define F_HANDLE	2
541.1Schristos#define F_MASK		4
551.3Skamil#define F_IGNORE	8
561.11Smartin#define	F_CHECK		16
571.1Schristos
581.1Schristosstatic struct {
591.1Schristos	const char *n;
601.1Schristos	int v;
611.1Schristos} nv[] = {
621.1Schristos	{ "recurse",	F_RECURSE },
631.1Schristos	{ "handle",	F_HANDLE },
641.1Schristos	{ "mask",	F_MASK },
651.11Smartin	{ "ignore",	F_IGNORE },
661.11Smartin	{ "check",	F_CHECK }
671.1Schristos};
681.1Schristos
691.4Skamilstatic int sig;
701.4Skamilstatic struct {
711.4Skamil	const char *n;
721.4Skamil	int v;
731.4Skamil} sn[] = {
741.4Skamil	{ "segv",	SIGSEGV },
751.5Skamil	{ "trap",	SIGTRAP },
761.5Skamil	{ "ill",	SIGILL },
771.5Skamil	{ "fpe",	SIGFPE },
781.5Skamil	{ "bus",	SIGBUS }
791.4Skamil};
801.4Skamil
811.1Schristosstatic void
821.5Skamiltrigger_segv(void)
831.5Skamil{
841.5Skamil	volatile int *p = (int *)(intptr_t)atoi("0");
851.5Skamil
861.5Skamil	*p = 1;
871.5Skamil}
881.5Skamil
891.5Skamilstatic void
901.5Skamiltrigger_trap(void)
911.1Schristos{
921.5Skamil
931.4Skamil#ifdef PTRACE_BREAKPOINT_ASM
941.5Skamil	PTRACE_BREAKPOINT_ASM;
951.5Skamil#else
961.5Skamil	/* port me */
971.5Skamil#endif
981.5Skamil}
991.5Skamil
1001.5Skamilstatic void
1011.5Skamiltrigger_ill(void)
1021.5Skamil{
1031.5Skamil
1041.5Skamil#ifdef PTRACE_ILLEGAL_ASM
1051.5Skamil	PTRACE_ILLEGAL_ASM;
1061.4Skamil#else
1071.5Skamil	/* port me */
1081.5Skamil#endif
1091.5Skamil}
1101.5Skamil
1111.5Skamilstatic void
1121.11Smartincheck_fpe(void)
1131.11Smartin{
1141.11Smartin#if (__arm__ && !__SOFTFP__) || __aarch64__
1151.11Smartin	/*
1161.14Skamil	 * Some NEON fpus do not trap on IEEE 754 FP exceptions.
1171.13Smartin	 * Skip these tests if running on them and compiled for
1181.11Smartin	 * hard float.
1191.11Smartin	 */
1201.11Smartin	if (0 == fpsetmask(fpsetmask(FP_X_INV))) {
1211.13Smartin		printf("FPU does not implement traps on FP exceptions\n");
1221.11Smartin		exit(EXIT_FAILURE);
1231.11Smartin	}
1241.15Sriastrad#elif defined __riscv__
1251.15Sriastrad	printf("RISC-V does not support floating-point exception traps\n");
1261.15Sriastrad	exit(EXIT_FAILURE);
1271.11Smartin#endif
1281.11Smartin	exit(EXIT_SUCCESS);
1291.11Smartin}
1301.11Smartin
1311.15Sriastradvolatile int ignore_result;
1321.15Sriastrad
1331.11Smartinstatic void
1341.5Skamiltrigger_fpe(void)
1351.5Skamil{
1361.9Sriastrad	volatile double a = getpid();
1371.9Sriastrad	volatile double b = strtol("0", NULL, 0);
1381.5Skamil
1391.10Sriastrad#ifdef __HAVE_FENV
1401.9Sriastrad	feenableexcept(FE_ALL_EXCEPT);
1411.10Sriastrad#endif
1421.9Sriastrad
1431.15Sriastrad	/*
1441.15Sriastrad	 * Try to trigger SIGFPE either by dividing by zero (which is
1451.15Sriastrad	 * defined to raise FE_DIVBYZERO, but may just return infinity
1461.15Sriastrad	 * without trapping the exception) or by converting infinity to
1471.15Sriastrad	 * integer.
1481.15Sriastrad	 */
1491.15Sriastrad	ignore_result = (int)(a/b);
1501.5Skamil}
1511.5Skamil
1521.5Skamilstatic void
1531.5Skamiltrigger_bus(void)
1541.5Skamil{
1551.5Skamil	FILE *fp;
1561.5Skamil	char *p;
1571.5Skamil
1581.5Skamil	/* Open an empty file for writing. */
1591.5Skamil	fp = tmpfile();
1601.5Skamil	if (fp == NULL)
1611.5Skamil		err(EXIT_FAILURE, "tmpfile");
1621.5Skamil
1631.7Skamil	/*
1641.7Skamil	 * Map an empty file with mmap(2) to a pointer.
1651.7Skamil	 *
1661.7Skamil	 * PROT_READ handles read-modify-write sequences emitted for
1671.7Skamil	 * certain combinations of CPUs and compilers (e.g. Alpha AXP).
1681.7Skamil	 */
1691.6Skamil	p = mmap(0, 1, PROT_READ|PROT_WRITE, MAP_PRIVATE, fileno(fp), 0);
1701.5Skamil	if (p == MAP_FAILED)
1711.5Skamil		err(EXIT_FAILURE, "mmap");
1721.5Skamil
1731.5Skamil	/* Invalid memory access causes CPU trap, translated to SIGBUS */
1741.5Skamil	*p = 'a';
1751.5Skamil}
1761.5Skamil
1771.5Skamilstatic void
1781.5Skamiltrigger(void)
1791.5Skamil{
1801.5Skamil
1811.5Skamil	switch (sig) {
1821.5Skamil	case SIGSEGV:
1831.5Skamil		trigger_segv();
1841.5Skamil		break;
1851.5Skamil	case SIGTRAP:
1861.5Skamil		trigger_trap();
1871.5Skamil		break;
1881.5Skamil	case SIGILL:
1891.5Skamil		trigger_ill();
1901.5Skamil		break;
1911.5Skamil	case SIGFPE:
1921.5Skamil		trigger_fpe();
1931.5Skamil		break;
1941.5Skamil	case SIGBUS:
1951.5Skamil		trigger_bus();
1961.5Skamil		break;
1971.5Skamil	default:
1981.5Skamil		break;
1991.4Skamil	}
2001.5Skamil}
2011.5Skamil
2021.5Skamilstatic void
2031.5Skamilfoo(int s)
2041.5Skamil{
2051.5Skamil	char buf[64];
2061.5Skamil	int i = snprintf(buf, sizeof(buf), "got %d\n", s);
2071.5Skamil	write(2, buf, i);
2081.5Skamil
2091.5Skamil	if (flags & F_RECURSE)
2101.5Skamil		trigger();
2111.5Skamil
2121.5Skamil	exit(EXIT_SUCCESS);
2131.1Schristos}
2141.1Schristos
2151.1Schristosstatic __dead void
2161.1Schristosusage(void)
2171.1Schristos{
2181.4Skamil	const char *pname = getprogname();
2191.4Skamil
2201.5Skamil	fprintf(stderr, "Usage: %s segv|trap|ill|fpe|bus "
2211.11Smartin	                "[recurse|mask|handle|ignore|check] ...\n", pname);
2221.5Skamil
2231.1Schristos	exit(EXIT_FAILURE);
2241.1Schristos}
2251.1Schristos
2261.1Schristosint
2271.1Schristosmain(int argc, char *argv[])
2281.1Schristos{
2291.5Skamil
2301.1Schristos	if (argc == 1)
2311.1Schristos	    usage();
2321.1Schristos
2331.1Schristos	for (int i = 1; i < argc; i++) {
2341.1Schristos		size_t j;
2351.4Skamil		for (j = 0; j < __arraycount(nv); j++) {
2361.1Schristos			if (strcmp(nv[j].n, argv[i]) == 0) {
2371.1Schristos				flags |= nv[j].v;
2381.5Skamil				goto consumed;
2391.1Schristos			}
2401.5Skamil		}
2411.5Skamil		for (j = 0; j < __arraycount(sn); j++) {
2421.4Skamil			if (strcmp(sn[j].n, argv[i]) == 0) {
2431.4Skamil				sig = sn[j].v;
2441.5Skamil				goto consumed;
2451.4Skamil			}
2461.4Skamil		}
2471.5Skamil
2481.5Skamil		usage();
2491.5Skamil
2501.5Skamil	consumed:
2511.5Skamil		continue;
2521.1Schristos	}
2531.1Schristos
2541.4Skamil	if (flags == 0 || sig == 0)
2551.1Schristos		usage();
2561.1Schristos
2571.11Smartin	if (flags & F_CHECK && sig != SIGFPE) {
2581.11Smartin		fprintf(stderr, "can only check for fpe support\n");
2591.11Smartin		return 1;
2601.11Smartin	}
2611.11Smartin	if (flags & F_CHECK)
2621.11Smartin		check_fpe();
2631.11Smartin
2641.1Schristos	if (flags & F_HANDLE) {
2651.1Schristos		struct sigaction sa;
2661.1Schristos
2671.1Schristos		sa.sa_flags = SA_RESTART;
2681.1Schristos		sa.sa_handler = foo;
2691.1Schristos		sigemptyset(&sa.sa_mask);
2701.4Skamil		if (sigaction(sig, &sa, NULL) == -1)
2711.1Schristos			err(EXIT_FAILURE, "sigaction");
2721.1Schristos	}
2731.1Schristos
2741.1Schristos	if (flags & F_MASK) {
2751.1Schristos		sigset_t set;
2761.1Schristos
2771.1Schristos		sigemptyset(&set);
2781.4Skamil		sigaddset(&set, sig);
2791.1Schristos		if (sigprocmask(SIG_BLOCK, &set, NULL) == -1)
2801.1Schristos			err(EXIT_FAILURE, "sigprocmask");
2811.1Schristos	}
2821.1Schristos
2831.3Skamil	if (flags & F_IGNORE) {
2841.3Skamil		struct sigaction sa;
2851.3Skamil
2861.3Skamil		memset(&sa, 0, sizeof(sa));
2871.3Skamil		sa.sa_handler = SIG_IGN;
2881.3Skamil		sigemptyset(&sa.sa_mask);
2891.4Skamil		if (sigaction(sig, &sa, NULL) == -1)
2901.3Skamil			err(EXIT_FAILURE, "sigaction");
2911.3Skamil	}
2921.3Skamil
2931.5Skamil	trigger();
2941.5Skamil
2951.1Schristos	return EXIT_SUCCESS;
2961.1Schristos}
297