Skip to content

Commit a8237cc

Browse files
author
Alexei Starovoitov
committed
Merge branch 'two-more-fixes-for-check_max_stack_depth'
Kumar Kartikeya Dwivedi says: ==================== Two more fixes for check_max_stack_depth I noticed two more bugs while reviewing the code, description and examples available in the patches. One leads to incorrect subprog index to be stored in the frame stack maintained by the function (leading to incorrect tail_call_reachable marks, among other things). The other problem is missing exploration pass of other async callbacks when they are not called from the main prog. Call chains rooted at them can thus bypass the stack limits (32 call frames * max permitted stack depth per function). Changelog: ---------- v1 -> v2 v1: https://lore.kernel.org/bpf/[email protected] * Fix commit message for patch 2 (Alexei) ==================== Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Alexei Starovoitov <[email protected]>
2 parents 8fcd7c7 + 824adae commit a8237cc

File tree

2 files changed

+48
-9
lines changed

2 files changed

+48
-9
lines changed

kernel/bpf/verifier.c

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5573,16 +5573,17 @@ static int update_stack_depth(struct bpf_verifier_env *env,
55735573
* Since recursion is prevented by check_cfg() this algorithm
55745574
* only needs a local stack of MAX_CALL_FRAMES to remember callsites
55755575
*/
5576-
static int check_max_stack_depth(struct bpf_verifier_env *env)
5576+
static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx)
55775577
{
5578-
int depth = 0, frame = 0, idx = 0, i = 0, subprog_end;
55795578
struct bpf_subprog_info *subprog = env->subprog_info;
55805579
struct bpf_insn *insn = env->prog->insnsi;
5580+
int depth = 0, frame = 0, i, subprog_end;
55815581
bool tail_call_reachable = false;
55825582
int ret_insn[MAX_CALL_FRAMES];
55835583
int ret_prog[MAX_CALL_FRAMES];
55845584
int j;
55855585

5586+
i = subprog[idx].start;
55865587
process_func:
55875588
/* protect against potential stack overflow that might happen when
55885589
* bpf2bpf calls get combined with tailcalls. Limit the caller's stack
@@ -5621,7 +5622,7 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
56215622
continue_func:
56225623
subprog_end = subprog[idx + 1].start;
56235624
for (; i < subprog_end; i++) {
5624-
int next_insn;
5625+
int next_insn, sidx;
56255626

56265627
if (!bpf_pseudo_call(insn + i) && !bpf_pseudo_func(insn + i))
56275628
continue;
@@ -5631,14 +5632,14 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
56315632

56325633
/* find the callee */
56335634
next_insn = i + insn[i].imm + 1;
5634-
idx = find_subprog(env, next_insn);
5635-
if (idx < 0) {
5635+
sidx = find_subprog(env, next_insn);
5636+
if (sidx < 0) {
56365637
WARN_ONCE(1, "verifier bug. No program starts at insn %d\n",
56375638
next_insn);
56385639
return -EFAULT;
56395640
}
5640-
if (subprog[idx].is_async_cb) {
5641-
if (subprog[idx].has_tail_call) {
5641+
if (subprog[sidx].is_async_cb) {
5642+
if (subprog[sidx].has_tail_call) {
56425643
verbose(env, "verifier bug. subprog has tail_call and async cb\n");
56435644
return -EFAULT;
56445645
}
@@ -5647,6 +5648,7 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
56475648
continue;
56485649
}
56495650
i = next_insn;
5651+
idx = sidx;
56505652

56515653
if (subprog[idx].has_tail_call)
56525654
tail_call_reachable = true;
@@ -5682,6 +5684,22 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
56825684
goto continue_func;
56835685
}
56845686

5687+
static int check_max_stack_depth(struct bpf_verifier_env *env)
5688+
{
5689+
struct bpf_subprog_info *si = env->subprog_info;
5690+
int ret;
5691+
5692+
for (int i = 0; i < env->subprog_cnt; i++) {
5693+
if (!i || si[i].is_async_cb) {
5694+
ret = check_max_stack_depth_subprog(env, i);
5695+
if (ret < 0)
5696+
return ret;
5697+
}
5698+
continue;
5699+
}
5700+
return 0;
5701+
}
5702+
56855703
#ifndef CONFIG_BPF_JIT_ALWAYS_ON
56865704
static int get_callee_stack_depth(struct bpf_verifier_env *env,
56875705
const struct bpf_insn *insn, int idx)

tools/testing/selftests/bpf/progs/async_stack_depth.c

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,16 @@ static int timer_cb(void *map, int *key, struct bpf_timer *timer)
2222
return buf[69];
2323
}
2424

25+
__attribute__((noinline))
26+
static int bad_timer_cb(void *map, int *key, struct bpf_timer *timer)
27+
{
28+
volatile char buf[300] = {};
29+
return buf[255] + timer_cb(NULL, NULL, NULL);
30+
}
31+
2532
SEC("tc")
26-
__failure __msg("combined stack size of 2 calls")
27-
int prog(struct __sk_buff *ctx)
33+
__failure __msg("combined stack size of 2 calls is 576. Too large")
34+
int pseudo_call_check(struct __sk_buff *ctx)
2835
{
2936
struct hmap_elem *elem;
3037
volatile char buf[256] = {};
@@ -37,4 +44,18 @@ int prog(struct __sk_buff *ctx)
3744
return bpf_timer_set_callback(&elem->timer, timer_cb) + buf[0];
3845
}
3946

47+
SEC("tc")
48+
__failure __msg("combined stack size of 2 calls is 608. Too large")
49+
int async_call_root_check(struct __sk_buff *ctx)
50+
{
51+
struct hmap_elem *elem;
52+
volatile char buf[256] = {};
53+
54+
elem = bpf_map_lookup_elem(&hmap, &(int){0});
55+
if (!elem)
56+
return 0;
57+
58+
return bpf_timer_set_callback(&elem->timer, bad_timer_cb) + buf[0];
59+
}
60+
4061
char _license[] SEC("license") = "GPL";

0 commit comments

Comments
 (0)