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#define _GNU_SOURCE 11#include <stdio.h> 12#include <stdlib.h> 13#include <sys/auxv.h> 14#include <sys/prctl.h> 15#include <sys/wait.h> 16#include <unistd.h> 17 18#include "../kselftest_harness.h" 19 20#ifndef __aarch64__ 21# define PROT_BTI 0 22#endif 23 24TEST(prctl_flags) 25{ 26 EXPECT_LT(prctl(PR_SET_MDWE, PR_MDWE_NO_INHERIT, 0L, 0L, 7L), 0); 27 EXPECT_EQ(errno, EINVAL); 28 29 EXPECT_LT(prctl(PR_SET_MDWE, 7L, 0L, 0L, 0L), 0); 30 EXPECT_EQ(errno, EINVAL); 31 EXPECT_LT(prctl(PR_SET_MDWE, 0L, 7L, 0L, 0L), 0); 32 EXPECT_EQ(errno, EINVAL); 33 EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 7L, 0L), 0); 34 EXPECT_EQ(errno, EINVAL); 35 EXPECT_LT(prctl(PR_SET_MDWE, 0L, 0L, 0L, 7L), 0); 36 EXPECT_EQ(errno, EINVAL); 37 38 EXPECT_LT(prctl(PR_GET_MDWE, 7L, 0L, 0L, 0L), 0); 39 EXPECT_EQ(errno, EINVAL); 40 EXPECT_LT(prctl(PR_GET_MDWE, 0L, 7L, 0L, 0L), 0); 41 EXPECT_EQ(errno, EINVAL); 42 EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 7L, 0L), 0); 43 EXPECT_EQ(errno, EINVAL); 44 EXPECT_LT(prctl(PR_GET_MDWE, 0L, 0L, 0L, 7L), 0); 45 EXPECT_EQ(errno, EINVAL); 46} 47 48FIXTURE(consecutive_prctl_flags) {}; 49FIXTURE_SETUP(consecutive_prctl_flags) {} 50FIXTURE_TEARDOWN(consecutive_prctl_flags) {} 51 52FIXTURE_VARIANT(consecutive_prctl_flags) 53{ 54 unsigned long first_flags; 55 unsigned long second_flags; 56 bool should_work; 57}; 58 59FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_no_flags) 60{ 61 .first_flags = 0, 62 .second_flags = 0, 63 .should_work = true, 64}; 65 66FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_exec_gain) 67{ 68 .first_flags = PR_MDWE_REFUSE_EXEC_GAIN, 69 .second_flags = PR_MDWE_REFUSE_EXEC_GAIN, 70 .should_work = true, 71}; 72 73FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_both_flags) 74{ 75 .first_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT, 76 .second_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT, 77 .should_work = true, 78}; 79 80FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_disable_mdwe) 81{ 82 .first_flags = PR_MDWE_REFUSE_EXEC_GAIN, 83 .second_flags = 0, 84 .should_work = false, 85}; 86 87FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_disable_mdwe_no_inherit) 88{ 89 .first_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT, 90 .second_flags = 0, 91 .should_work = false, 92}; 93 94FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_disable_no_inherit) 95{ 96 .first_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT, 97 .second_flags = PR_MDWE_REFUSE_EXEC_GAIN, 98 .should_work = false, 99}; 100 101FIXTURE_VARIANT_ADD(consecutive_prctl_flags, cant_enable_no_inherit) 102{ 103 .first_flags = PR_MDWE_REFUSE_EXEC_GAIN, 104 .second_flags = PR_MDWE_REFUSE_EXEC_GAIN | PR_MDWE_NO_INHERIT, 105 .should_work = false, 106}; 107 108TEST_F(consecutive_prctl_flags, two_prctls) 109{ 110 int ret; 111 112 EXPECT_EQ(prctl(PR_SET_MDWE, variant->first_flags, 0L, 0L, 0L), 0); 113 114 ret = prctl(PR_SET_MDWE, variant->second_flags, 0L, 0L, 0L); 115 if (variant->should_work) { 116 EXPECT_EQ(ret, 0); 117 118 ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L); 119 ASSERT_EQ(ret, variant->second_flags); 120 } else { 121 EXPECT_NE(ret, 0); 122 ASSERT_EQ(errno, EPERM); 123 } 124} 125 126FIXTURE(mdwe) 127{ 128 void *p; 129 int flags; 130 size_t size; 131 pid_t pid; 132}; 133 134FIXTURE_VARIANT(mdwe) 135{ 136 bool enabled; 137 bool forked; 138 bool inherit; 139}; 140 141FIXTURE_VARIANT_ADD(mdwe, stock) 142{ 143 .enabled = false, 144 .forked = false, 145 .inherit = false, 146}; 147 148FIXTURE_VARIANT_ADD(mdwe, enabled) 149{ 150 .enabled = true, 151 .forked = false, 152 .inherit = true, 153}; 154 155FIXTURE_VARIANT_ADD(mdwe, inherited) 156{ 157 .enabled = true, 158 .forked = true, 159 .inherit = true, 160}; 161 162FIXTURE_VARIANT_ADD(mdwe, not_inherited) 163{ 164 .enabled = true, 165 .forked = true, 166 .inherit = false, 167}; 168 169static bool executable_map_should_fail(const FIXTURE_VARIANT(mdwe) *variant) 170{ 171 return variant->enabled && (!variant->forked || variant->inherit); 172} 173 174FIXTURE_SETUP(mdwe) 175{ 176 unsigned long mdwe_flags; 177 int ret, status; 178 179 self->p = NULL; 180 self->flags = MAP_SHARED | MAP_ANONYMOUS; 181 self->size = getpagesize(); 182 183 if (!variant->enabled) 184 return; 185 186 mdwe_flags = PR_MDWE_REFUSE_EXEC_GAIN; 187 if (!variant->inherit) 188 mdwe_flags |= PR_MDWE_NO_INHERIT; 189 190 ret = prctl(PR_SET_MDWE, mdwe_flags, 0L, 0L, 0L); 191 ASSERT_EQ(ret, 0) { 192 TH_LOG("PR_SET_MDWE failed or unsupported"); 193 } 194 195 ret = prctl(PR_GET_MDWE, 0L, 0L, 0L, 0L); 196 ASSERT_EQ(ret, mdwe_flags); 197 198 if (variant->forked) { 199 self->pid = fork(); 200 ASSERT_GE(self->pid, 0) { 201 TH_LOG("fork failed\n"); 202 } 203 204 if (self->pid > 0) { 205 ret = waitpid(self->pid, &status, 0); 206 ASSERT_TRUE(WIFEXITED(status)); 207 exit(WEXITSTATUS(status)); 208 } 209 } 210} 211 212FIXTURE_TEARDOWN(mdwe) 213{ 214 if (self->p && self->p != MAP_FAILED) 215 munmap(self->p, self->size); 216} 217 218TEST_F(mdwe, mmap_READ_EXEC) 219{ 220 self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0); 221 EXPECT_NE(self->p, MAP_FAILED); 222} 223 224TEST_F(mdwe, mmap_WRITE_EXEC) 225{ 226 self->p = mmap(NULL, self->size, PROT_WRITE | PROT_EXEC, self->flags, 0, 0); 227 if (executable_map_should_fail(variant)) { 228 EXPECT_EQ(self->p, MAP_FAILED); 229 } else { 230 EXPECT_NE(self->p, MAP_FAILED); 231 } 232} 233 234TEST_F(mdwe, mprotect_stay_EXEC) 235{ 236 int ret; 237 238 self->p = mmap(NULL, self->size, PROT_READ | PROT_EXEC, self->flags, 0, 0); 239 ASSERT_NE(self->p, MAP_FAILED); 240 241 ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC); 242 EXPECT_EQ(ret, 0); 243} 244 245TEST_F(mdwe, mprotect_add_EXEC) 246{ 247 int ret; 248 249 self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0); 250 ASSERT_NE(self->p, MAP_FAILED); 251 252 ret = mprotect(self->p, self->size, PROT_READ | PROT_EXEC); 253 if (executable_map_should_fail(variant)) { 254 EXPECT_LT(ret, 0); 255 } else { 256 EXPECT_EQ(ret, 0); 257 } 258} 259 260TEST_F(mdwe, mprotect_WRITE_EXEC) 261{ 262 int ret; 263 264 self->p = mmap(NULL, self->size, PROT_WRITE, self->flags, 0, 0); 265 ASSERT_NE(self->p, MAP_FAILED); 266 267 ret = mprotect(self->p, self->size, PROT_WRITE | PROT_EXEC); 268 if (executable_map_should_fail(variant)) { 269 EXPECT_LT(ret, 0); 270 } else { 271 EXPECT_EQ(ret, 0); 272 } 273} 274 275TEST_F(mdwe, mmap_FIXED) 276{ 277 void *p; 278 279 self->p = mmap(NULL, self->size, PROT_READ, self->flags, 0, 0); 280 ASSERT_NE(self->p, MAP_FAILED); 281 282 /* MAP_FIXED unmaps the existing page before mapping which is allowed */ 283 p = mmap(self->p, self->size, PROT_READ | PROT_EXEC, 284 self->flags | MAP_FIXED, 0, 0); 285 EXPECT_EQ(p, self->p); 286} 287 288TEST_F(mdwe, arm64_BTI) 289{ 290 int ret; 291 292#ifdef __aarch64__ 293 if (!(getauxval(AT_HWCAP2) & HWCAP2_BTI)) 294#endif 295 SKIP(return, "HWCAP2_BTI not supported"); 296 297 self->p = mmap(NULL, self->size, PROT_EXEC, self->flags, 0, 0); 298 ASSERT_NE(self->p, MAP_FAILED); 299 300 ret = mprotect(self->p, self->size, PROT_EXEC | PROT_BTI); 301 EXPECT_EQ(ret, 0); 302} 303 304TEST_HARNESS_MAIN 305