Commit cafe2c21 authored by Eduard Zingerman's avatar Eduard Zingerman Committed by Alexei Starovoitov

bpf: widening for callback iterators

Callbacks are similar to open coded iterators, so add imprecise
widening logic for callback body processing. This makes callback based
loops behave identically to open coded iterators, e.g. allowing to
verify programs like below:

  struct ctx { u32 i; };
  int cb(u32 idx, struct ctx* ctx)
  {
          ++ctx->i;
          return 0;
  }
  ...
  struct ctx ctx = { .i = 0 };
  bpf_loop(100, cb, &ctx, 0);
  ...
Acked-by: default avatarAndrii Nakryiko <andrii@kernel.org>
Signed-off-by: default avatarEduard Zingerman <eddyz87@gmail.com>
Link: https://lore.kernel.org/r/20231121020701.26440-9-eddyz87@gmail.comSigned-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parent 958465e2
...@@ -9799,9 +9799,10 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env) ...@@ -9799,9 +9799,10 @@ static bool in_rbtree_lock_required_cb(struct bpf_verifier_env *env)
static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
{ {
struct bpf_verifier_state *state = env->cur_state; struct bpf_verifier_state *state = env->cur_state, *prev_st;
struct bpf_func_state *caller, *callee; struct bpf_func_state *caller, *callee;
struct bpf_reg_state *r0; struct bpf_reg_state *r0;
bool in_callback_fn;
int err; int err;
callee = state->frame[state->curframe]; callee = state->frame[state->curframe];
...@@ -9856,7 +9857,8 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) ...@@ -9856,7 +9857,8 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
* there function call logic would reschedule callback visit. If iteration * there function call logic would reschedule callback visit. If iteration
* converges is_state_visited() would prune that visit eventually. * converges is_state_visited() would prune that visit eventually.
*/ */
if (callee->in_callback_fn) in_callback_fn = callee->in_callback_fn;
if (in_callback_fn)
*insn_idx = callee->callsite; *insn_idx = callee->callsite;
else else
*insn_idx = callee->callsite + 1; *insn_idx = callee->callsite + 1;
...@@ -9871,6 +9873,24 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx) ...@@ -9871,6 +9873,24 @@ static int prepare_func_exit(struct bpf_verifier_env *env, int *insn_idx)
* bpf_throw, this will be done by copy_verifier_state for extra frames. */ * bpf_throw, this will be done by copy_verifier_state for extra frames. */
free_func_state(callee); free_func_state(callee);
state->frame[state->curframe--] = NULL; state->frame[state->curframe--] = NULL;
/* for callbacks widen imprecise scalars to make programs like below verify:
*
* struct ctx { int i; }
* void cb(int idx, struct ctx *ctx) { ctx->i++; ... }
* ...
* struct ctx = { .i = 0; }
* bpf_loop(100, cb, &ctx, 0);
*
* This is similar to what is done in process_iter_next_call() for open
* coded iterators.
*/
prev_st = in_callback_fn ? find_prev_entry(env, state, *insn_idx) : NULL;
if (prev_st) {
err = widen_imprecise_scalars(env, prev_st, state);
if (err)
return err;
}
return 0; return 0;
} }
......
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