1 // SPDX-License-Identifier: GPL-2.0 2 /* Copyright (c) 2022 Facebook */ 3 4 #include <errno.h> 5 #include <string.h> 6 #include <linux/bpf.h> 7 #include <bpf/bpf_helpers.h> 8 #include "bpf_misc.h" 9 10 char _license[] SEC("license") = "GPL"; 11 12 #define ITER_HELPERS \ 13 __imm(bpf_iter_num_new), \ 14 __imm(bpf_iter_num_next), \ 15 __imm(bpf_iter_num_destroy) 16 17 SEC("?raw_tp") 18 __success 19 int force_clang_to_emit_btf_for_externs(void *ctx) 20 { 21 /* we need this as a workaround to enforce compiler emitting BTF 22 * information for bpf_iter_num_{new,next,destroy}() kfuncs, 23 * as, apparently, it doesn't emit it for symbols only referenced from 24 * assembly (or cleanup attribute, for that matter, as well) 25 */ 26 bpf_repeat(0); 27 28 return 0; 29 } 30 31 SEC("?raw_tp") 32 __success __log_level(2) 33 __msg("fp-8_w=iter_num(ref_id=1,state=active,depth=0)") 34 int create_and_destroy(void *ctx) 35 { 36 struct bpf_iter_num iter; 37 38 asm volatile ( 39 /* create iterator */ 40 "r1 = %[iter];" 41 "r2 = 0;" 42 "r3 = 1000;" 43 "call %[bpf_iter_num_new];" 44 /* destroy iterator */ 45 "r1 = %[iter];" 46 "call %[bpf_iter_num_destroy];" 47 : 48 : __imm_ptr(iter), ITER_HELPERS 49 : __clobber_common 50 ); 51 52 return 0; 53 } 54 55 SEC("?raw_tp") 56 __failure __msg("Unreleased reference id=1") 57 int create_and_forget_to_destroy_fail(void *ctx) 58 { 59 struct bpf_iter_num iter; 60 61 asm volatile ( 62 /* create iterator */ 63 "r1 = %[iter];" 64 "r2 = 0;" 65 "r3 = 1000;" 66 "call %[bpf_iter_num_new];" 67 : 68 : __imm_ptr(iter), ITER_HELPERS 69 : __clobber_common 70 ); 71 72 return 0; 73 } 74 75 SEC("?raw_tp") 76 __failure __msg("expected an initialized iter_num as arg #1") 77 int destroy_without_creating_fail(void *ctx) 78 { 79 /* init with zeros to stop verifier complaining about uninit stack */ 80 struct bpf_iter_num iter; 81 82 asm volatile ( 83 "r1 = %[iter];" 84 "call %[bpf_iter_num_destroy];" 85 : 86 : __imm_ptr(iter), ITER_HELPERS 87 : __clobber_common 88 ); 89 90 return 0; 91 } 92 93 SEC("?raw_tp") 94 __failure __msg("expected an initialized iter_num as arg #1") 95 int compromise_iter_w_direct_write_fail(void *ctx) 96 { 97 struct bpf_iter_num iter; 98 99 asm volatile ( 100 /* create iterator */ 101 "r1 = %[iter];" 102 "r2 = 0;" 103 "r3 = 1000;" 104 "call %[bpf_iter_num_new];" 105 106 /* directly write over first half of iter state */ 107 "*(u64 *)(%[iter] + 0) = r0;" 108 109 /* (attempt to) destroy iterator */ 110 "r1 = %[iter];" 111 "call %[bpf_iter_num_destroy];" 112 : 113 : __imm_ptr(iter), ITER_HELPERS 114 : __clobber_common 115 ); 116 117 return 0; 118 } 119 120 SEC("?raw_tp") 121 __failure __msg("Unreleased reference id=1") 122 int compromise_iter_w_direct_write_and_skip_destroy_fail(void *ctx) 123 { 124 struct bpf_iter_num iter; 125 126 asm volatile ( 127 /* create iterator */ 128 "r1 = %[iter];" 129 "r2 = 0;" 130 "r3 = 1000;" 131 "call %[bpf_iter_num_new];" 132 133 /* directly write over first half of iter state */ 134 "*(u64 *)(%[iter] + 0) = r0;" 135 136 /* don't destroy iter, leaking ref, which should fail */ 137 : 138 : __imm_ptr(iter), ITER_HELPERS 139 : __clobber_common 140 ); 141 142 return 0; 143 } 144 145 SEC("?raw_tp") 146 __failure __msg("expected an initialized iter_num as arg #1") 147 int compromise_iter_w_helper_write_fail(void *ctx) 148 { 149 struct bpf_iter_num iter; 150 151 asm volatile ( 152 /* create iterator */ 153 "r1 = %[iter];" 154 "r2 = 0;" 155 "r3 = 1000;" 156 "call %[bpf_iter_num_new];" 157 158 /* overwrite 8th byte with bpf_probe_read_kernel() */ 159 "r1 = %[iter];" 160 "r1 += 7;" 161 "r2 = 1;" 162 "r3 = 0;" /* NULL */ 163 "call %[bpf_probe_read_kernel];" 164 165 /* (attempt to) destroy iterator */ 166 "r1 = %[iter];" 167 "call %[bpf_iter_num_destroy];" 168 : 169 : __imm_ptr(iter), ITER_HELPERS, __imm(bpf_probe_read_kernel) 170 : __clobber_common 171 ); 172 173 return 0; 174 } 175 176 static __noinline void subprog_with_iter(void) 177 { 178 struct bpf_iter_num iter; 179 180 bpf_iter_num_new(&iter, 0, 1); 181 182 return; 183 } 184 185 SEC("?raw_tp") 186 __failure 187 /* ensure there was a call to subprog, which might happen without __noinline */ 188 __msg("returning from callee:") 189 __msg("Unreleased reference id=1") 190 int leak_iter_from_subprog_fail(void *ctx) 191 { 192 subprog_with_iter(); 193 194 return 0; 195 } 196 197 SEC("?raw_tp") 198 __success __log_level(2) 199 __msg("fp-8_w=iter_num(ref_id=1,state=active,depth=0)") 200 int valid_stack_reuse(void *ctx) 201 { 202 struct bpf_iter_num iter; 203 204 asm volatile ( 205 /* create iterator */ 206 "r1 = %[iter];" 207 "r2 = 0;" 208 "r3 = 1000;" 209 "call %[bpf_iter_num_new];" 210 /* destroy iterator */ 211 "r1 = %[iter];" 212 "call %[bpf_iter_num_destroy];" 213 214 /* now reuse same stack slots */ 215 216 /* create iterator */ 217 "r1 = %[iter];" 218 "r2 = 0;" 219 "r3 = 1000;" 220 "call %[bpf_iter_num_new];" 221 /* destroy iterator */ 222 "r1 = %[iter];" 223 "call %[bpf_iter_num_destroy];" 224 : 225 : __imm_ptr(iter), ITER_HELPERS 226 : __clobber_common 227 ); 228 229 return 0; 230 } 231 232 SEC("?raw_tp") 233 __failure __msg("expected uninitialized iter_num as arg #1") 234 int double_create_fail(void *ctx) 235 { 236 struct bpf_iter_num iter; 237 238 asm volatile ( 239 /* create iterator */ 240 "r1 = %[iter];" 241 "r2 = 0;" 242 "r3 = 1000;" 243 "call %[bpf_iter_num_new];" 244 /* (attempt to) create iterator again */ 245 "r1 = %[iter];" 246 "r2 = 0;" 247 "r3 = 1000;" 248 "call %[bpf_iter_num_new];" 249 /* destroy iterator */ 250 "r1 = %[iter];" 251 "call %[bpf_iter_num_destroy];" 252 : 253 : __imm_ptr(iter), ITER_HELPERS 254 : __clobber_common 255 ); 256 257 return 0; 258 } 259 260 SEC("?raw_tp") 261 __failure __msg("expected an initialized iter_num as arg #1") 262 int double_destroy_fail(void *ctx) 263 { 264 struct bpf_iter_num iter; 265 266 asm volatile ( 267 /* create iterator */ 268 "r1 = %[iter];" 269 "r2 = 0;" 270 "r3 = 1000;" 271 "call %[bpf_iter_num_new];" 272 /* destroy iterator */ 273 "r1 = %[iter];" 274 "call %[bpf_iter_num_destroy];" 275 /* (attempt to) destroy iterator again */ 276 "r1 = %[iter];" 277 "call %[bpf_iter_num_destroy];" 278 : 279 : __imm_ptr(iter), ITER_HELPERS 280 : __clobber_common 281 ); 282 283 return 0; 284 } 285 286 SEC("?raw_tp") 287 __failure __msg("expected an initialized iter_num as arg #1") 288 int next_without_new_fail(void *ctx) 289 { 290 struct bpf_iter_num iter; 291 292 asm volatile ( 293 /* don't create iterator and try to iterate*/ 294 "r1 = %[iter];" 295 "call %[bpf_iter_num_next];" 296 /* destroy iterator */ 297 "r1 = %[iter];" 298 "call %[bpf_iter_num_destroy];" 299 : 300 : __imm_ptr(iter), ITER_HELPERS 301 : __clobber_common 302 ); 303 304 return 0; 305 } 306 307 SEC("?raw_tp") 308 __failure __msg("expected an initialized iter_num as arg #1") 309 int next_after_destroy_fail(void *ctx) 310 { 311 struct bpf_iter_num iter; 312 313 asm volatile ( 314 /* create iterator */ 315 "r1 = %[iter];" 316 "r2 = 0;" 317 "r3 = 1000;" 318 "call %[bpf_iter_num_new];" 319 /* destroy iterator */ 320 "r1 = %[iter];" 321 "call %[bpf_iter_num_destroy];" 322 /* don't create iterator and try to iterate*/ 323 "r1 = %[iter];" 324 "call %[bpf_iter_num_next];" 325 : 326 : __imm_ptr(iter), ITER_HELPERS 327 : __clobber_common 328 ); 329 330 return 0; 331 } 332 333 SEC("?raw_tp") 334 __failure __msg("invalid read from stack") 335 int __naked read_from_iter_slot_fail(void) 336 { 337 asm volatile ( 338 /* r6 points to struct bpf_iter_num on the stack */ 339 "r6 = r10;" 340 "r6 += -24;" 341 342 /* create iterator */ 343 "r1 = r6;" 344 "r2 = 0;" 345 "r3 = 1000;" 346 "call %[bpf_iter_num_new];" 347 348 /* attemp to leak bpf_iter_num state */ 349 "r7 = *(u64 *)(r6 + 0);" 350 "r8 = *(u64 *)(r6 + 8);" 351 352 /* destroy iterator */ 353 "r1 = r6;" 354 "call %[bpf_iter_num_destroy];" 355 356 /* leak bpf_iter_num state */ 357 "r0 = r7;" 358 "if r7 > r8 goto +1;" 359 "r0 = r8;" 360 "exit;" 361 : 362 : ITER_HELPERS 363 : __clobber_common, "r6", "r7", "r8" 364 ); 365 } 366 367 int zero; 368 369 SEC("?raw_tp") 370 __failure 371 __flag(BPF_F_TEST_STATE_FREQ) 372 __msg("Unreleased reference") 373 int stacksafe_should_not_conflate_stack_spill_and_iter(void *ctx) 374 { 375 struct bpf_iter_num iter; 376 377 asm volatile ( 378 /* Create a fork in logic, with general setup as follows: 379 * - fallthrough (first) path is valid; 380 * - branch (second) path is invalid. 381 * Then depending on what we do in fallthrough vs branch path, 382 * we try to detect bugs in func_states_equal(), regsafe(), 383 * refsafe(), stack_safe(), and similar by tricking verifier 384 * into believing that branch state is a valid subset of 385 * a fallthrough state. Verifier should reject overall 386 * validation, unless there is a bug somewhere in verifier 387 * logic. 388 */ 389 "call %[bpf_get_prandom_u32];" 390 "r6 = r0;" 391 "call %[bpf_get_prandom_u32];" 392 "r7 = r0;" 393 394 "if r6 > r7 goto bad;" /* fork */ 395 396 /* spill r6 into stack slot of bpf_iter_num var */ 397 "*(u64 *)(%[iter] + 0) = r6;" 398 399 "goto skip_bad;" 400 401 "bad:" 402 /* create iterator in the same stack slot */ 403 "r1 = %[iter];" 404 "r2 = 0;" 405 "r3 = 1000;" 406 "call %[bpf_iter_num_new];" 407 408 /* but then forget about it and overwrite it back to r6 spill */ 409 "*(u64 *)(%[iter] + 0) = r6;" 410 411 "skip_bad:" 412 "goto +0;" /* force checkpoint */ 413 414 /* corrupt stack slots, if they are really dynptr */ 415 "*(u64 *)(%[iter] + 0) = r6;" 416 : 417 : __imm_ptr(iter), 418 __imm_addr(zero), 419 __imm(bpf_get_prandom_u32), 420 __imm(bpf_dynptr_from_mem), 421 ITER_HELPERS 422 : __clobber_common, "r6", "r7" 423 ); 424 425 return 0; 426 } 427
Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.