162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Stas Sergeev <stsp@users.sourceforge.net>
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * test sigaltstack(SS_ONSTACK | SS_AUTODISARM)
662306a36Sopenharmony_ci * If that succeeds, then swapcontext() can be used inside sighandler safely.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#define _GNU_SOURCE
1162306a36Sopenharmony_ci#include <signal.h>
1262306a36Sopenharmony_ci#include <stdio.h>
1362306a36Sopenharmony_ci#include <stdlib.h>
1462306a36Sopenharmony_ci#include <sys/mman.h>
1562306a36Sopenharmony_ci#include <ucontext.h>
1662306a36Sopenharmony_ci#include <alloca.h>
1762306a36Sopenharmony_ci#include <string.h>
1862306a36Sopenharmony_ci#include <assert.h>
1962306a36Sopenharmony_ci#include <errno.h>
2062306a36Sopenharmony_ci#include <sys/auxv.h>
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#include "../kselftest.h"
2362306a36Sopenharmony_ci#include "current_stack_pointer.h"
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#ifndef SS_AUTODISARM
2662306a36Sopenharmony_ci#define SS_AUTODISARM  (1U << 31)
2762306a36Sopenharmony_ci#endif
2862306a36Sopenharmony_ci
2962306a36Sopenharmony_ci#ifndef AT_MINSIGSTKSZ
3062306a36Sopenharmony_ci#define AT_MINSIGSTKSZ	51
3162306a36Sopenharmony_ci#endif
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cistatic unsigned int stack_size;
3462306a36Sopenharmony_cistatic void *sstack, *ustack;
3562306a36Sopenharmony_cistatic ucontext_t uc, sc;
3662306a36Sopenharmony_cistatic const char *msg = "[OK]\tStack preserved";
3762306a36Sopenharmony_cistatic const char *msg2 = "[FAIL]\tStack corrupted";
3862306a36Sopenharmony_cistruct stk_data {
3962306a36Sopenharmony_ci	char msg[128];
4062306a36Sopenharmony_ci	int flag;
4162306a36Sopenharmony_ci};
4262306a36Sopenharmony_ci
4362306a36Sopenharmony_civoid my_usr1(int sig, siginfo_t *si, void *u)
4462306a36Sopenharmony_ci{
4562306a36Sopenharmony_ci	char *aa;
4662306a36Sopenharmony_ci	int err;
4762306a36Sopenharmony_ci	stack_t stk;
4862306a36Sopenharmony_ci	struct stk_data *p;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	if (sp < (unsigned long)sstack ||
5162306a36Sopenharmony_ci			sp >= (unsigned long)sstack + stack_size) {
5262306a36Sopenharmony_ci		ksft_exit_fail_msg("SP is not on sigaltstack\n");
5362306a36Sopenharmony_ci	}
5462306a36Sopenharmony_ci	/* put some data on stack. other sighandler will try to overwrite it */
5562306a36Sopenharmony_ci	aa = alloca(1024);
5662306a36Sopenharmony_ci	assert(aa);
5762306a36Sopenharmony_ci	p = (struct stk_data *)(aa + 512);
5862306a36Sopenharmony_ci	strcpy(p->msg, msg);
5962306a36Sopenharmony_ci	p->flag = 1;
6062306a36Sopenharmony_ci	ksft_print_msg("[RUN]\tsignal USR1\n");
6162306a36Sopenharmony_ci	err = sigaltstack(NULL, &stk);
6262306a36Sopenharmony_ci	if (err) {
6362306a36Sopenharmony_ci		ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
6462306a36Sopenharmony_ci		exit(EXIT_FAILURE);
6562306a36Sopenharmony_ci	}
6662306a36Sopenharmony_ci	if (stk.ss_flags != SS_DISABLE)
6762306a36Sopenharmony_ci		ksft_test_result_fail("tss_flags=%x, should be SS_DISABLE\n",
6862306a36Sopenharmony_ci				stk.ss_flags);
6962306a36Sopenharmony_ci	else
7062306a36Sopenharmony_ci		ksft_test_result_pass(
7162306a36Sopenharmony_ci				"sigaltstack is disabled in sighandler\n");
7262306a36Sopenharmony_ci	swapcontext(&sc, &uc);
7362306a36Sopenharmony_ci	ksft_print_msg("%s\n", p->msg);
7462306a36Sopenharmony_ci	if (!p->flag) {
7562306a36Sopenharmony_ci		ksft_exit_fail_msg("[RUN]\tAborting\n");
7662306a36Sopenharmony_ci		exit(EXIT_FAILURE);
7762306a36Sopenharmony_ci	}
7862306a36Sopenharmony_ci}
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_civoid my_usr2(int sig, siginfo_t *si, void *u)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	char *aa;
8362306a36Sopenharmony_ci	struct stk_data *p;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	ksft_print_msg("[RUN]\tsignal USR2\n");
8662306a36Sopenharmony_ci	aa = alloca(1024);
8762306a36Sopenharmony_ci	/* dont run valgrind on this */
8862306a36Sopenharmony_ci	/* try to find the data stored by previous sighandler */
8962306a36Sopenharmony_ci	p = memmem(aa, 1024, msg, strlen(msg));
9062306a36Sopenharmony_ci	if (p) {
9162306a36Sopenharmony_ci		ksft_test_result_fail("sigaltstack re-used\n");
9262306a36Sopenharmony_ci		/* corrupt the data */
9362306a36Sopenharmony_ci		strcpy(p->msg, msg2);
9462306a36Sopenharmony_ci		/* tell other sighandler that his data is corrupted */
9562306a36Sopenharmony_ci		p->flag = 0;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci}
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_cistatic void switch_fn(void)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	ksft_print_msg("[RUN]\tswitched to user ctx\n");
10262306a36Sopenharmony_ci	raise(SIGUSR2);
10362306a36Sopenharmony_ci	setcontext(&sc);
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ciint main(void)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct sigaction act;
10962306a36Sopenharmony_ci	stack_t stk;
11062306a36Sopenharmony_ci	int err;
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	/* Make sure more than the required minimum. */
11362306a36Sopenharmony_ci	stack_size = getauxval(AT_MINSIGSTKSZ) + SIGSTKSZ;
11462306a36Sopenharmony_ci	ksft_print_msg("[NOTE]\tthe stack size is %lu\n", stack_size);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	ksft_print_header();
11762306a36Sopenharmony_ci	ksft_set_plan(3);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	sigemptyset(&act.sa_mask);
12062306a36Sopenharmony_ci	act.sa_flags = SA_ONSTACK | SA_SIGINFO;
12162306a36Sopenharmony_ci	act.sa_sigaction = my_usr1;
12262306a36Sopenharmony_ci	sigaction(SIGUSR1, &act, NULL);
12362306a36Sopenharmony_ci	act.sa_sigaction = my_usr2;
12462306a36Sopenharmony_ci	sigaction(SIGUSR2, &act, NULL);
12562306a36Sopenharmony_ci	sstack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
12662306a36Sopenharmony_ci		      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
12762306a36Sopenharmony_ci	if (sstack == MAP_FAILED) {
12862306a36Sopenharmony_ci		ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));
12962306a36Sopenharmony_ci		return EXIT_FAILURE;
13062306a36Sopenharmony_ci	}
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	err = sigaltstack(NULL, &stk);
13362306a36Sopenharmony_ci	if (err) {
13462306a36Sopenharmony_ci		ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
13562306a36Sopenharmony_ci		exit(EXIT_FAILURE);
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci	if (stk.ss_flags == SS_DISABLE) {
13862306a36Sopenharmony_ci		ksft_test_result_pass(
13962306a36Sopenharmony_ci				"Initial sigaltstack state was SS_DISABLE\n");
14062306a36Sopenharmony_ci	} else {
14162306a36Sopenharmony_ci		ksft_exit_fail_msg("Initial sigaltstack state was %x; "
14262306a36Sopenharmony_ci		       "should have been SS_DISABLE\n", stk.ss_flags);
14362306a36Sopenharmony_ci		return EXIT_FAILURE;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	stk.ss_sp = sstack;
14762306a36Sopenharmony_ci	stk.ss_size = stack_size;
14862306a36Sopenharmony_ci	stk.ss_flags = SS_ONSTACK | SS_AUTODISARM;
14962306a36Sopenharmony_ci	err = sigaltstack(&stk, NULL);
15062306a36Sopenharmony_ci	if (err) {
15162306a36Sopenharmony_ci		if (errno == EINVAL) {
15262306a36Sopenharmony_ci			ksft_test_result_skip(
15362306a36Sopenharmony_ci				"[NOTE]\tThe running kernel doesn't support SS_AUTODISARM\n");
15462306a36Sopenharmony_ci			/*
15562306a36Sopenharmony_ci			 * If test cases for the !SS_AUTODISARM variant were
15662306a36Sopenharmony_ci			 * added, we could still run them.  We don't have any
15762306a36Sopenharmony_ci			 * test cases like that yet, so just exit and report
15862306a36Sopenharmony_ci			 * success.
15962306a36Sopenharmony_ci			 */
16062306a36Sopenharmony_ci			return 0;
16162306a36Sopenharmony_ci		} else {
16262306a36Sopenharmony_ci			ksft_exit_fail_msg(
16362306a36Sopenharmony_ci				"sigaltstack(SS_ONSTACK | SS_AUTODISARM)  %s\n",
16462306a36Sopenharmony_ci					strerror(errno));
16562306a36Sopenharmony_ci			return EXIT_FAILURE;
16662306a36Sopenharmony_ci		}
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	ustack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE,
17062306a36Sopenharmony_ci		      MAP_PRIVATE | MAP_ANONYMOUS | MAP_STACK, -1, 0);
17162306a36Sopenharmony_ci	if (ustack == MAP_FAILED) {
17262306a36Sopenharmony_ci		ksft_exit_fail_msg("mmap() - %s\n", strerror(errno));
17362306a36Sopenharmony_ci		return EXIT_FAILURE;
17462306a36Sopenharmony_ci	}
17562306a36Sopenharmony_ci	getcontext(&uc);
17662306a36Sopenharmony_ci	uc.uc_link = NULL;
17762306a36Sopenharmony_ci	uc.uc_stack.ss_sp = ustack;
17862306a36Sopenharmony_ci	uc.uc_stack.ss_size = stack_size;
17962306a36Sopenharmony_ci	makecontext(&uc, switch_fn, 0);
18062306a36Sopenharmony_ci	raise(SIGUSR1);
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	err = sigaltstack(NULL, &stk);
18362306a36Sopenharmony_ci	if (err) {
18462306a36Sopenharmony_ci		ksft_exit_fail_msg("sigaltstack() - %s\n", strerror(errno));
18562306a36Sopenharmony_ci		exit(EXIT_FAILURE);
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	if (stk.ss_flags != SS_AUTODISARM) {
18862306a36Sopenharmony_ci		ksft_exit_fail_msg("ss_flags=%x, should be SS_AUTODISARM\n",
18962306a36Sopenharmony_ci				stk.ss_flags);
19062306a36Sopenharmony_ci		exit(EXIT_FAILURE);
19162306a36Sopenharmony_ci	}
19262306a36Sopenharmony_ci	ksft_test_result_pass(
19362306a36Sopenharmony_ci			"sigaltstack is still SS_AUTODISARM after signal\n");
19462306a36Sopenharmony_ci
19562306a36Sopenharmony_ci	ksft_exit_pass();
19662306a36Sopenharmony_ci	return 0;
19762306a36Sopenharmony_ci}
198