162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#ifdef __aarch64__
462306a36Sopenharmony_ci#include <asm/hwcap.h>
562306a36Sopenharmony_ci#endif
662306a36Sopenharmony_ci
762306a36Sopenharmony_ci#include <linux/mman.h>
862306a36Sopenharmony_ci#include <linux/prctl.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <stdio.h>
1162306a36Sopenharmony_ci#include <stdlib.h>
1262306a36Sopenharmony_ci#include <sys/auxv.h>
1362306a36Sopenharmony_ci#include <sys/prctl.h>
1462306a36Sopenharmony_ci#include <sys/wait.h>
1562306a36Sopenharmony_ci#include <unistd.h>
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci#include "../kselftest_harness.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci#ifndef __aarch64__
2062306a36Sopenharmony_ci# define PROT_BTI	0
2162306a36Sopenharmony_ci#endif
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciTEST(prctl_flags)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	EXPECT_LT(prctl(PR_SET_MDWE, 7L, 0L, 0L, 0L), 0);
2662306a36Sopenharmony_ci	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 7L, 0L, 0L), 0);
2762306a36Sopenharmony_ci	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 7L, 0L), 0);
2862306a36Sopenharmony_ci	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 0L, 7L), 0);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	EXPECT_LT(prctl(PR_GET_MDWE, 7L, 0L, 0L, 0L), 0);
3162306a36Sopenharmony_ci	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 7L, 0L, 0L), 0);
3262306a36Sopenharmony_ci	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 7L, 0L), 0);
3362306a36Sopenharmony_ci	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 0L, 7L), 0);
3462306a36Sopenharmony_ci}
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ciFIXTURE(mdwe)
3762306a36Sopenharmony_ci{
3862306a36Sopenharmony_ci	void *p;
3962306a36Sopenharmony_ci	int flags;
4062306a36Sopenharmony_ci	size_t size;
4162306a36Sopenharmony_ci	pid_t pid;
4262306a36Sopenharmony_ci};
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ciFIXTURE_VARIANT(mdwe)
4562306a36Sopenharmony_ci{
4662306a36Sopenharmony_ci	bool enabled;
4762306a36Sopenharmony_ci	bool forked;
4862306a36Sopenharmony_ci};
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(mdwe, stock)
5162306a36Sopenharmony_ci{
5262306a36Sopenharmony_ci        .enabled = false,
5362306a36Sopenharmony_ci	.forked = false,
5462306a36Sopenharmony_ci};
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(mdwe, enabled)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci        .enabled = true,
5962306a36Sopenharmony_ci	.forked = false,
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ciFIXTURE_VARIANT_ADD(mdwe, forked)
6362306a36Sopenharmony_ci{
6462306a36Sopenharmony_ci        .enabled = true,
6562306a36Sopenharmony_ci	.forked = true,
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciFIXTURE_SETUP(mdwe)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	int ret, status;
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	self->p = NULL;
7362306a36Sopenharmony_ci	self->flags = MAP_SHARED | MAP_ANONYMOUS;
7462306a36Sopenharmony_ci	self->size = getpagesize();
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	if (!variant->enabled)
7762306a36Sopenharmony_ci		return;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	ret = prctl(PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN, 0L, 0L, 0L);
8062306a36Sopenharmony_ci	ASSERT_EQ(ret, 0) {
8162306a36Sopenharmony_ci		TH_LOG("PR_SET_MDWE failed or unsupported");
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L);
8562306a36Sopenharmony_ci	ASSERT_EQ(ret, 1);
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (variant->forked) {
8862306a36Sopenharmony_ci		self->pid = fork();
8962306a36Sopenharmony_ci		ASSERT_GE(self->pid, 0) {
9062306a36Sopenharmony_ci			TH_LOG("fork failed\n");
9162306a36Sopenharmony_ci		}
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci		if (self->pid > 0) {
9462306a36Sopenharmony_ci			ret = waitpid(self->pid, &status, 0);
9562306a36Sopenharmony_ci			ASSERT_TRUE(WIFEXITED(status));
9662306a36Sopenharmony_ci			exit(WEXITSTATUS(status));
9762306a36Sopenharmony_ci		}
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ciFIXTURE_TEARDOWN(mdwe)
10262306a36Sopenharmony_ci{
10362306a36Sopenharmony_ci	if (self->p && self->p != MAP_FAILED)
10462306a36Sopenharmony_ci		munmap(self->p, self->size);
10562306a36Sopenharmony_ci}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ciTEST_F(mdwe, mmap_READ_EXEC)
10862306a36Sopenharmony_ci{
10962306a36Sopenharmony_ci	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
11062306a36Sopenharmony_ci	EXPECT_NE(self->p, MAP_FAILED);
11162306a36Sopenharmony_ci}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ciTEST_F(mdwe, mmap_WRITE_EXEC)
11462306a36Sopenharmony_ci{
11562306a36Sopenharmony_ci	self->p = mmap(NULL, self->size, PROT_WRITE | PROT_EXEC, self->flags, 0, 0);
11662306a36Sopenharmony_ci	if (variant->enabled) {
11762306a36Sopenharmony_ci		EXPECT_EQ(self->p, MAP_FAILED);
11862306a36Sopenharmony_ci	} else {
11962306a36Sopenharmony_ci		EXPECT_NE(self->p, MAP_FAILED);
12062306a36Sopenharmony_ci	}
12162306a36Sopenharmony_ci}
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ciTEST_F(mdwe, mprotect_stay_EXEC)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	int ret;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
12862306a36Sopenharmony_ci	ASSERT_NE(self->p, MAP_FAILED);
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
13162306a36Sopenharmony_ci	EXPECT_EQ(ret, 0);
13262306a36Sopenharmony_ci}
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ciTEST_F(mdwe, mprotect_add_EXEC)
13562306a36Sopenharmony_ci{
13662306a36Sopenharmony_ci	int ret;
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
13962306a36Sopenharmony_ci	ASSERT_NE(self->p, MAP_FAILED);
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
14262306a36Sopenharmony_ci	if (variant->enabled) {
14362306a36Sopenharmony_ci		EXPECT_LT(ret, 0);
14462306a36Sopenharmony_ci	} else {
14562306a36Sopenharmony_ci		EXPECT_EQ(ret, 0);
14662306a36Sopenharmony_ci	}
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciTEST_F(mdwe, mprotect_WRITE_EXEC)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int ret;
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	self->p = mmap(NULL, self->size, PROT_WRITE, self->flags, 0, 0);
15462306a36Sopenharmony_ci	ASSERT_NE(self->p, MAP_FAILED);
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	ret = mprotect(self->p, self->size, PROT_WRITE | PROT_EXEC);
15762306a36Sopenharmony_ci	if (variant->enabled) {
15862306a36Sopenharmony_ci		EXPECT_LT(ret, 0);
15962306a36Sopenharmony_ci	} else {
16062306a36Sopenharmony_ci		EXPECT_EQ(ret, 0);
16162306a36Sopenharmony_ci	}
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ciTEST_F(mdwe, mmap_FIXED)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	void *p;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
16962306a36Sopenharmony_ci	ASSERT_NE(self->p, MAP_FAILED);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	/* MAP_FIXED unmaps the existing page before mapping which is allowed */
17262306a36Sopenharmony_ci	p = mmap(self->p, self->size, PROT_READ | PROT_EXEC,
17362306a36Sopenharmony_ci		 self->flags | MAP_FIXED, 0, 0);
17462306a36Sopenharmony_ci	EXPECT_EQ(p, self->p);
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ciTEST_F(mdwe, arm64_BTI)
17862306a36Sopenharmony_ci{
17962306a36Sopenharmony_ci	int ret;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci#ifdef __aarch64__
18262306a36Sopenharmony_ci	if (!(getauxval(AT_HWCAP2) & HWCAP2_BTI))
18362306a36Sopenharmony_ci#endif
18462306a36Sopenharmony_ci		SKIP(return, "HWCAP2_BTI not supported");
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	self->p = mmap(NULL, self->size, PROT_EXEC, self->flags, 0, 0);
18762306a36Sopenharmony_ci	ASSERT_NE(self->p, MAP_FAILED);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	ret = mprotect(self->p, self->size, PROT_EXEC | PROT_BTI);
19062306a36Sopenharmony_ci	EXPECT_EQ(ret, 0);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ciTEST_HARNESS_MAIN
194