427 lines · c
1// SPDX-License-Identifier: GPL-2.02/* 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 10char _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 17SEC("?raw_tp")18__success19int force_clang_to_emit_btf_for_externs(void *ctx)20{21 /* we need this as a workaround to enforce compiler emitting BTF22 * information for bpf_iter_num_{new,next,destroy}() kfuncs,23 * as, apparently, it doesn't emit it for symbols only referenced from24 * assembly (or cleanup attribute, for that matter, as well)25 */26 bpf_repeat(0);27 28 return 0;29}30 31SEC("?raw_tp")32__success __log_level(2)33__msg("fp-8_w=iter_num(ref_id=1,state=active,depth=0)")34int 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_HELPERS49 : __clobber_common50 );51 52 return 0;53}54 55SEC("?raw_tp")56__failure __msg("Unreleased reference id=1")57int 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_HELPERS69 : __clobber_common70 );71 72 return 0;73}74 75SEC("?raw_tp")76__failure __msg("expected an initialized iter_num as arg #1")77int 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_HELPERS87 : __clobber_common88 );89 90 return 0;91}92 93SEC("?raw_tp")94__failure __msg("expected an initialized iter_num as arg #1")95int 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_HELPERS114 : __clobber_common115 );116 117 return 0;118}119 120SEC("?raw_tp")121__failure __msg("Unreleased reference id=1")122int 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_HELPERS139 : __clobber_common140 );141 142 return 0;143}144 145SEC("?raw_tp")146__failure __msg("expected an initialized iter_num as arg #1")147int 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_common171 );172 173 return 0;174}175 176static __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 185SEC("?raw_tp")186__failure187/* ensure there was a call to subprog, which might happen without __noinline */188__msg("returning from callee:")189__msg("Unreleased reference id=1")190int leak_iter_from_subprog_fail(void *ctx)191{192 subprog_with_iter();193 194 return 0;195}196 197SEC("?raw_tp")198__success __log_level(2)199__msg("fp-8_w=iter_num(ref_id=1,state=active,depth=0)")200int 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_HELPERS226 : __clobber_common227 );228 229 return 0;230}231 232SEC("?raw_tp")233__failure __msg("expected uninitialized iter_num as arg #1")234int 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_HELPERS254 : __clobber_common255 );256 257 return 0;258}259 260SEC("?raw_tp")261__failure __msg("expected an initialized iter_num as arg #1")262int 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_HELPERS280 : __clobber_common281 );282 283 return 0;284}285 286SEC("?raw_tp")287__failure __msg("expected an initialized iter_num as arg #1")288int 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_HELPERS301 : __clobber_common302 );303 304 return 0;305}306 307SEC("?raw_tp")308__failure __msg("expected an initialized iter_num as arg #1")309int 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_HELPERS327 : __clobber_common328 );329 330 return 0;331}332 333SEC("?raw_tp")334__failure __msg("invalid read from stack")335int __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_HELPERS363 : __clobber_common, "r6", "r7", "r8"364 );365}366 367int zero;368 369SEC("?raw_tp")370__failure371__flag(BPF_F_TEST_STATE_FREQ)372__msg("Unreleased reference")373int 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 verifier384 * into believing that branch state is a valid subset of385 * a fallthrough state. Verifier should reject overall386 * validation, unless there is a bug somewhere in verifier387 * 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_HELPERS422 : __clobber_common, "r6", "r7"423 );424 425 return 0;426}427