1// SPDX-License-Identifier: GPL-2.0
2
3#ifdef __aarch64__
4#include <asm/hwcap.h>
5#endif
6
7#include <linux/mman.h>
8#include <linux/prctl.h>
9
10#include <stdio.h>
11#include <stdlib.h>
12#include <sys/auxv.h>
13#include <sys/prctl.h>
14#include <sys/wait.h>
15#include <unistd.h>
16
17#include "../kselftest_harness.h"
18
19#ifndef __aarch64__
20# define PROT_BTI	0
21#endif
22
23TEST(prctl_flags)
24{
25	EXPECT_LT(prctl(PR_SET_MDWE, 7L, 0L, 0L, 0L), 0);
26	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 7L, 0L, 0L), 0);
27	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 7L, 0L), 0);
28	EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 0L, 7L), 0);
29
30	EXPECT_LT(prctl(PR_GET_MDWE, 7L, 0L, 0L, 0L), 0);
31	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 7L, 0L, 0L), 0);
32	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 7L, 0L), 0);
33	EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 0L, 7L), 0);
34}
35
36FIXTURE(mdwe)
37{
38	void *p;
39	int flags;
40	size_t size;
41	pid_t pid;
42};
43
44FIXTURE_VARIANT(mdwe)
45{
46	bool enabled;
47	bool forked;
48};
49
50FIXTURE_VARIANT_ADD(mdwe, stock)
51{
52        .enabled = false,
53	.forked = false,
54};
55
56FIXTURE_VARIANT_ADD(mdwe, enabled)
57{
58        .enabled = true,
59	.forked = false,
60};
61
62FIXTURE_VARIANT_ADD(mdwe, forked)
63{
64        .enabled = true,
65	.forked = true,
66};
67
68FIXTURE_SETUP(mdwe)
69{
70	int ret, status;
71
72	self->p = NULL;
73	self->flags = MAP_SHARED | MAP_ANONYMOUS;
74	self->size = getpagesize();
75
76	if (!variant->enabled)
77		return;
78
79	ret = prctl(PR_SET_MDWE, PR_MDWE_REFUSE_EXEC_GAIN, 0L, 0L, 0L);
80	ASSERT_EQ(ret, 0) {
81		TH_LOG("PR_SET_MDWE failed or unsupported");
82	}
83
84	ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L);
85	ASSERT_EQ(ret, 1);
86
87	if (variant->forked) {
88		self->pid = fork();
89		ASSERT_GE(self->pid, 0) {
90			TH_LOG("fork failed\n");
91		}
92
93		if (self->pid > 0) {
94			ret = waitpid(self->pid, &status, 0);
95			ASSERT_TRUE(WIFEXITED(status));
96			exit(WEXITSTATUS(status));
97		}
98	}
99}
100
101FIXTURE_TEARDOWN(mdwe)
102{
103	if (self->p && self->p != MAP_FAILED)
104		munmap(self->p, self->size);
105}
106
107TEST_F(mdwe, mmap_READ_EXEC)
108{
109	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
110	EXPECT_NE(self->p, MAP_FAILED);
111}
112
113TEST_F(mdwe, mmap_WRITE_EXEC)
114{
115	self->p = mmap(NULL, self->size, PROT_WRITE | PROT_EXEC, self->flags, 0, 0);
116	if (variant->enabled) {
117		EXPECT_EQ(self->p, MAP_FAILED);
118	} else {
119		EXPECT_NE(self->p, MAP_FAILED);
120	}
121}
122
123TEST_F(mdwe, mprotect_stay_EXEC)
124{
125	int ret;
126
127	self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0);
128	ASSERT_NE(self->p, MAP_FAILED);
129
130	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
131	EXPECT_EQ(ret, 0);
132}
133
134TEST_F(mdwe, mprotect_add_EXEC)
135{
136	int ret;
137
138	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
139	ASSERT_NE(self->p, MAP_FAILED);
140
141	ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC);
142	if (variant->enabled) {
143		EXPECT_LT(ret, 0);
144	} else {
145		EXPECT_EQ(ret, 0);
146	}
147}
148
149TEST_F(mdwe, mprotect_WRITE_EXEC)
150{
151	int ret;
152
153	self->p = mmap(NULL, self->size, PROT_WRITE, self->flags, 0, 0);
154	ASSERT_NE(self->p, MAP_FAILED);
155
156	ret = mprotect(self->p, self->size, PROT_WRITE | PROT_EXEC);
157	if (variant->enabled) {
158		EXPECT_LT(ret, 0);
159	} else {
160		EXPECT_EQ(ret, 0);
161	}
162}
163
164TEST_F(mdwe, mmap_FIXED)
165{
166	void *p;
167
168	self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0);
169	ASSERT_NE(self->p, MAP_FAILED);
170
171	/* MAP_FIXED unmaps the existing page before mapping which is allowed */
172	p = mmap(self->p, self->size, PROT_READ | PROT_EXEC,
173		 self->flags | MAP_FIXED, 0, 0);
174	EXPECT_EQ(p, self->p);
175}
176
177TEST_F(mdwe, arm64_BTI)
178{
179	int ret;
180
181#ifdef __aarch64__
182	if (!(getauxval(AT_HWCAP2) & HWCAP2_BTI))
183#endif
184		SKIP(return, "HWCAP2_BTI not supported");
185
186	self->p = mmap(NULL, self->size, PROT_EXEC, self->flags, 0, 0);
187	ASSERT_NE(self->p, MAP_FAILED);
188
189	ret = mprotect(self->p, self->size, PROT_EXEC | PROT_BTI);
190	EXPECT_EQ(ret, 0);
191}
192
193TEST_HARNESS_MAIN
194