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