1// SPDX-License-Identifier: GPL-2.0-only
2
3#define _GNU_SOURCE
4#include <signal.h>
5#include <stdio.h>
6#include <stdbool.h>
7#include <string.h>
8#include <err.h>
9#include <errno.h>
10#include <limits.h>
11#include <sys/mman.h>
12#include <sys/auxv.h>
13#include <sys/prctl.h>
14#include <sys/resource.h>
15#include <setjmp.h>
16
17/* sigaltstack()-enforced minimum stack */
18#define ENFORCED_MINSIGSTKSZ	2048
19
20#ifndef AT_MINSIGSTKSZ
21#  define AT_MINSIGSTKSZ	51
22#endif
23
24static int nerrs;
25
26static bool sigalrm_expected;
27
28static unsigned long at_minstack_size;
29
30static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
31		       int flags)
32{
33	struct sigaction sa;
34
35	memset(&sa, 0, sizeof(sa));
36	sa.sa_sigaction = handler;
37	sa.sa_flags = SA_SIGINFO | flags;
38	sigemptyset(&sa.sa_mask);
39	if (sigaction(sig, &sa, 0))
40		err(1, "sigaction");
41}
42
43static void clearhandler(int sig)
44{
45	struct sigaction sa;
46
47	memset(&sa, 0, sizeof(sa));
48	sa.sa_handler = SIG_DFL;
49	sigemptyset(&sa.sa_mask);
50	if (sigaction(sig, &sa, 0))
51		err(1, "sigaction");
52}
53
54static int setup_altstack(void *start, unsigned long size)
55{
56	stack_t ss;
57
58	memset(&ss, 0, sizeof(ss));
59	ss.ss_size = size;
60	ss.ss_sp = start;
61
62	return sigaltstack(&ss, NULL);
63}
64
65static jmp_buf jmpbuf;
66
67static void sigsegv(int sig, siginfo_t *info, void *ctx_void)
68{
69	if (sigalrm_expected) {
70		printf("[FAIL]\tWrong signal delivered: SIGSEGV (expected SIGALRM).");
71		nerrs++;
72	} else {
73		printf("[OK]\tSIGSEGV signal delivered.\n");
74	}
75
76	siglongjmp(jmpbuf, 1);
77}
78
79static void sigalrm(int sig, siginfo_t *info, void *ctx_void)
80{
81	if (!sigalrm_expected) {
82		printf("[FAIL]\tWrong signal delivered: SIGALRM (expected SIGSEGV).");
83		nerrs++;
84	} else {
85		printf("[OK]\tSIGALRM signal delivered.\n");
86	}
87}
88
89static void test_sigaltstack(void *altstack, unsigned long size)
90{
91	if (setup_altstack(altstack, size))
92		err(1, "sigaltstack()");
93
94	sigalrm_expected = (size > at_minstack_size) ? true : false;
95
96	sethandler(SIGSEGV, sigsegv, 0);
97	sethandler(SIGALRM, sigalrm, SA_ONSTACK);
98
99	if (!sigsetjmp(jmpbuf, 1)) {
100		printf("[RUN]\tTest an alternate signal stack of %ssufficient size.\n",
101		       sigalrm_expected ? "" : "in");
102		printf("\tRaise SIGALRM. %s is expected to be delivered.\n",
103		       sigalrm_expected ? "It" : "SIGSEGV");
104		raise(SIGALRM);
105	}
106
107	clearhandler(SIGALRM);
108	clearhandler(SIGSEGV);
109}
110
111int main(void)
112{
113	void *altstack;
114
115	at_minstack_size = getauxval(AT_MINSIGSTKSZ);
116
117	altstack = mmap(NULL, at_minstack_size + SIGSTKSZ, PROT_READ | PROT_WRITE,
118			MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
119	if (altstack == MAP_FAILED)
120		err(1, "mmap()");
121
122	if ((ENFORCED_MINSIGSTKSZ + 1) < at_minstack_size)
123		test_sigaltstack(altstack, ENFORCED_MINSIGSTKSZ + 1);
124
125	test_sigaltstack(altstack, at_minstack_size + SIGSTKSZ);
126
127	return nerrs == 0 ? 0 : 1;
128}
129