Commit a8237cc8 authored by Alexei Starovoitov's avatar Alexei Starovoitov

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/20230713003118.1327943-1-memxor@gmail.com

 * Fix commit message for patch 2 (Alexei)
====================

Link: https://lore.kernel.org/r/20230717161530.1238-1-memxor@gmail.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents 8fcd7c7b 824adae4
...@@ -5573,16 +5573,17 @@ static int update_stack_depth(struct bpf_verifier_env *env, ...@@ -5573,16 +5573,17 @@ static int update_stack_depth(struct bpf_verifier_env *env,
* Since recursion is prevented by check_cfg() this algorithm * Since recursion is prevented by check_cfg() this algorithm
* only needs a local stack of MAX_CALL_FRAMES to remember callsites * only needs a local stack of MAX_CALL_FRAMES to remember callsites
*/ */
static int check_max_stack_depth(struct bpf_verifier_env *env) static int check_max_stack_depth_subprog(struct bpf_verifier_env *env, int idx)
{ {
int depth = 0, frame = 0, idx = 0, i = 0, subprog_end;
struct bpf_subprog_info *subprog = env->subprog_info; struct bpf_subprog_info *subprog = env->subprog_info;
struct bpf_insn *insn = env->prog->insnsi; struct bpf_insn *insn = env->prog->insnsi;
int depth = 0, frame = 0, i, subprog_end;
bool tail_call_reachable = false; bool tail_call_reachable = false;
int ret_insn[MAX_CALL_FRAMES]; int ret_insn[MAX_CALL_FRAMES];
int ret_prog[MAX_CALL_FRAMES]; int ret_prog[MAX_CALL_FRAMES];
int j; int j;
i = subprog[idx].start;
process_func: process_func:
/* protect against potential stack overflow that might happen when /* protect against potential stack overflow that might happen when
* bpf2bpf calls get combined with tailcalls. Limit the caller's stack * 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) ...@@ -5621,7 +5622,7 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
continue_func: continue_func:
subprog_end = subprog[idx + 1].start; subprog_end = subprog[idx + 1].start;
for (; i < subprog_end; i++) { for (; i < subprog_end; i++) {
int next_insn; int next_insn, sidx;
if (!bpf_pseudo_call(insn + i) && !bpf_pseudo_func(insn + i)) if (!bpf_pseudo_call(insn + i) && !bpf_pseudo_func(insn + i))
continue; continue;
...@@ -5631,14 +5632,14 @@ static int check_max_stack_depth(struct bpf_verifier_env *env) ...@@ -5631,14 +5632,14 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
/* find the callee */ /* find the callee */
next_insn = i + insn[i].imm + 1; next_insn = i + insn[i].imm + 1;
idx = find_subprog(env, next_insn); sidx = find_subprog(env, next_insn);
if (idx < 0) { if (sidx < 0) {
WARN_ONCE(1, "verifier bug. No program starts at insn %d\n", WARN_ONCE(1, "verifier bug. No program starts at insn %d\n",
next_insn); next_insn);
return -EFAULT; return -EFAULT;
} }
if (subprog[idx].is_async_cb) { if (subprog[sidx].is_async_cb) {
if (subprog[idx].has_tail_call) { if (subprog[sidx].has_tail_call) {
verbose(env, "verifier bug. subprog has tail_call and async cb\n"); verbose(env, "verifier bug. subprog has tail_call and async cb\n");
return -EFAULT; return -EFAULT;
} }
...@@ -5647,6 +5648,7 @@ static int check_max_stack_depth(struct bpf_verifier_env *env) ...@@ -5647,6 +5648,7 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
continue; continue;
} }
i = next_insn; i = next_insn;
idx = sidx;
if (subprog[idx].has_tail_call) if (subprog[idx].has_tail_call)
tail_call_reachable = true; tail_call_reachable = true;
...@@ -5682,6 +5684,22 @@ static int check_max_stack_depth(struct bpf_verifier_env *env) ...@@ -5682,6 +5684,22 @@ static int check_max_stack_depth(struct bpf_verifier_env *env)
goto continue_func; goto continue_func;
} }
static int check_max_stack_depth(struct bpf_verifier_env *env)
{
struct bpf_subprog_info *si = env->subprog_info;
int ret;
for (int i = 0; i < env->subprog_cnt; i++) {
if (!i || si[i].is_async_cb) {
ret = check_max_stack_depth_subprog(env, i);
if (ret < 0)
return ret;
}
continue;
}
return 0;
}
#ifndef CONFIG_BPF_JIT_ALWAYS_ON #ifndef CONFIG_BPF_JIT_ALWAYS_ON
static int get_callee_stack_depth(struct bpf_verifier_env *env, static int get_callee_stack_depth(struct bpf_verifier_env *env,
const struct bpf_insn *insn, int idx) const struct bpf_insn *insn, int idx)
......
...@@ -22,9 +22,16 @@ static int timer_cb(void *map, int *key, struct bpf_timer *timer) ...@@ -22,9 +22,16 @@ static int timer_cb(void *map, int *key, struct bpf_timer *timer)
return buf[69]; return buf[69];
} }
__attribute__((noinline))
static int bad_timer_cb(void *map, int *key, struct bpf_timer *timer)
{
volatile char buf[300] = {};
return buf[255] + timer_cb(NULL, NULL, NULL);
}
SEC("tc") SEC("tc")
__failure __msg("combined stack size of 2 calls") __failure __msg("combined stack size of 2 calls is 576. Too large")
int prog(struct __sk_buff *ctx) int pseudo_call_check(struct __sk_buff *ctx)
{ {
struct hmap_elem *elem; struct hmap_elem *elem;
volatile char buf[256] = {}; volatile char buf[256] = {};
...@@ -37,4 +44,18 @@ int prog(struct __sk_buff *ctx) ...@@ -37,4 +44,18 @@ int prog(struct __sk_buff *ctx)
return bpf_timer_set_callback(&elem->timer, timer_cb) + buf[0]; return bpf_timer_set_callback(&elem->timer, timer_cb) + buf[0];
} }
SEC("tc")
__failure __msg("combined stack size of 2 calls is 608. Too large")
int async_call_root_check(struct __sk_buff *ctx)
{
struct hmap_elem *elem;
volatile char buf[256] = {};
elem = bpf_map_lookup_elem(&hmap, &(int){0});
if (!elem)
return 0;
return bpf_timer_set_callback(&elem->timer, bad_timer_cb) + buf[0];
}
char _license[] SEC("license") = "GPL"; char _license[] SEC("license") = "GPL";
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment