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 24 TEST(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 48 FIXTURE(consecutive_prctl_flags) {}; 49 FIXTURE_SETUP(consecutive_prctl_flags) {} 50 FIXTURE_TEARDOWN(consecutive_prctl_flags) {} 51 52 FIXTURE_VARIANT(consecutive_prctl_flags) 53 { 54 unsigned long first_flags; 55 unsigned long second_flags; 56 bool should_work; 57 }; 58 59 FIXTURE_VARIANT_ADD(consecutive_prctl_flags, can_keep_no_flags) 60 { 61 .first_flags = 0, 62 .second_flags = 0, 63 .should_work = true, 64 }; 65 66 FIXTURE_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 73 FIXTURE_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 80 FIXTURE_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 87 FIXTURE_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 94 FIXTURE_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 101 FIXTURE_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 108 TEST_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 126 FIXTURE(mdwe) 127 { 128 void *p; 129 int flags; 130 size_t size; 131 pid_t pid; 132 }; 133 134 FIXTURE_VARIANT(mdwe) 135 { 136 bool enabled; 137 bool forked; 138 bool inherit; 139 }; 140 141 FIXTURE_VARIANT_ADD(mdwe, stock) 142 { 143 .enabled = false, 144 .forked = false, 145 .inherit = false, 146 }; 147 148 FIXTURE_VARIANT_ADD(mdwe, enabled) 149 { 150 .enabled = true, 151 .forked = false, 152 .inherit = true, 153 }; 154 155 FIXTURE_VARIANT_ADD(mdwe, inherited) 156 { 157 .enabled = true, 158 .forked = true, 159 .inherit = true, 160 }; 161 162 FIXTURE_VARIANT_ADD(mdwe, not_inherited) 163 { 164 .enabled = true, 165 .forked = true, 166 .inherit = false, 167 }; 168 169 static bool executable_map_should_fail(const FIXTURE_VARIANT(mdwe) *variant) 170 { 171 return variant->enabled && (!variant->forked || variant->inherit); 172 } 173 174 FIXTURE_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 212 FIXTURE_TEARDOWN(mdwe) 213 { 214 if (self->p && self->p != MAP_FAILED) 215 munmap(self->p, self->size); 216 } 217 218 TEST_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 224 TEST_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 234 TEST_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 245 TEST_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 260 TEST_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 275 TEST_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 288 TEST_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 304 TEST_HARNESS_MAIN 305