iter maybe-not-hack 6
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
diff --git a/include/linux/bpf_verifier.h b/include/linux/bpf_verifier.h
index 94ec766..76ccf85 100644
--- a/include/linux/bpf_verifier.h
+++ b/include/linux/bpf_verifier.h
@@ -369,6 +369,7 @@ struct bpf_verifier_state {
u32 branches;
u32 insn_idx;
u32 curframe;
+ int delayed;
struct bpf_active_lock active_lock;
bool speculative;
@@ -571,6 +572,11 @@ struct bpf_idset {
u32 ids[BPF_ID_MAP_SIZE];
};
+struct bpf_verifier_state_stack {
+ struct bpf_verifier_stack_elem *head; /* stack of verifier states to be processed */
+ int size; /* number of states to be processed */
+};
+
/* single container for all structs
* one verifier_env per bpf_check() call
*/
@@ -579,8 +585,7 @@ struct bpf_verifier_env {
u32 prev_insn_idx;
struct bpf_prog *prog; /* eBPF program being verified */
const struct bpf_verifier_ops *ops;
- struct bpf_verifier_stack_elem *head; /* stack of verifier states to be processed */
- int stack_size; /* number of states to be processed */
+ struct bpf_verifier_state_stack stack;
bool strict_alignment; /* perform strict pointer alignment checks */
bool test_state_freq; /* test verifier with different pruning frequency */
struct bpf_verifier_state *cur_state; /* current verifier state */
diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c
index eed7350..62a263c 100644
--- a/kernel/bpf/verifier.c
+++ b/kernel/bpf/verifier.c
@@ -178,6 +178,7 @@ struct bpf_verifier_stack_elem {
struct bpf_verifier_stack_elem *next;
/* length of verifier log at the time this state was pushed on stack */
u32 log_pos;
+ u32 loop_start;
};
#define BPF_COMPLEXITY_LIMIT_JMP_SEQ 8192
@@ -1765,6 +1766,7 @@ static int copy_verifier_state(struct bpf_verifier_state *dst_state,
dst_state->parent = src->parent;
dst_state->first_insn_idx = src->first_insn_idx;
dst_state->last_insn_idx = src->last_insn_idx;
+ dst_state->delayed = src->delayed;
for (i = 0; i <= src->curframe; i++) {
dst = dst_state->frame[i];
if (!dst) {
@@ -1785,6 +1787,9 @@ static void update_branch_counts(struct bpf_verifier_env *env, struct bpf_verifi
while (st) {
u32 br = --st->branches;
+ verbose(env, "update_br %lx branches=%d\n",
+ ((long)st) >> 3 & 0xFFFF, st->branches);
+
/* WARN_ON(br > 1) technically makes sense here,
* but see comment in push_stack(), hence:
*/
@@ -1797,20 +1802,23 @@ static void update_branch_counts(struct bpf_verifier_env *env, struct bpf_verifi
}
}
-static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
- int *insn_idx, bool pop_log)
+static int pop_stack(struct bpf_verifier_env *env,
+ int *prev_insn_idx, int *insn_idx, bool pop_log)
{
+ struct bpf_verifier_state_stack *stack = &env->stack;
struct bpf_verifier_state *cur = env->cur_state;
- struct bpf_verifier_stack_elem *elem, *head = env->head;
+ struct bpf_verifier_stack_elem *elem, *head = stack->head;
int err;
- if (env->head == NULL)
+ if (stack->head == NULL)
return -ENOENT;
if (cur) {
err = copy_verifier_state(cur, &head->st);
if (err)
return err;
+ verbose(env, "%d: pop_stack %lx branches=%d\n", head->insn_idx,
+ ((long)&head->st) >> 3 & 0xFFFF, cur->branches);
}
if (pop_log)
bpf_vlog_reset(&env->log, head->log_pos);
@@ -1821,17 +1829,18 @@ static int pop_stack(struct bpf_verifier_env *env, int *prev_insn_idx,
elem = head->next;
free_verifier_state(&head->st, false);
kfree(head);
- env->head = elem;
- env->stack_size--;
+ stack->head = elem;
+ stack->size--;
return 0;
}
-static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
- int insn_idx, int prev_insn_idx,
- bool speculative)
+static struct bpf_verifier_state *__push_stack(struct bpf_verifier_env *env,
+ int insn_idx, int prev_insn_idx,
+ bool speculative, u32 iter_ref_id)
{
+ struct bpf_verifier_state_stack *stack = &env->stack;
struct bpf_verifier_state *cur = env->cur_state;
- struct bpf_verifier_stack_elem *elem;
+ struct bpf_verifier_stack_elem *elem, *e, **pprev;
int err;
elem = kzalloc(sizeof(struct bpf_verifier_stack_elem), GFP_KERNEL);
@@ -1840,17 +1849,30 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
elem->insn_idx = insn_idx;
elem->prev_insn_idx = prev_insn_idx;
- elem->next = env->head;
+
+ pprev = &stack->head;
+ e = *pprev;
+ if (iter_ref_id) {
+ /* Find where iter_ref_id started the loop */
+ while (e && e->loop_start != iter_ref_id) {
+ pprev = &e->next;
+ e = e->next;
+ }
+ }
+ elem->next = e;
elem->log_pos = env->log.end_pos;
- env->head = elem;
- env->stack_size++;
+ *pprev = elem;
+ stack->size++;
err = copy_verifier_state(&elem->st, cur);
if (err)
goto err;
+ verbose(env, "%d: push_stack %lx branches=%d parent %lx\n", insn_idx,
+ ((long)&elem->st) >> 3 & 0xFFFF, cur->branches,
+ ((long)elem->st.parent) >> 3 & 0xFFFF);
elem->st.speculative |= speculative;
- if (env->stack_size > BPF_COMPLEXITY_LIMIT_JMP_SEQ) {
+ if (stack->size > BPF_COMPLEXITY_LIMIT_JMP_SEQ) {
verbose(env, "The sequence of %d jumps is too complex.\n",
- env->stack_size);
+ stack->size);
goto err;
}
if (elem->st.parent) {
@@ -1874,6 +1896,20 @@ static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
return NULL;
}
+static struct bpf_verifier_state *push_stack(struct bpf_verifier_env *env,
+ int insn_idx, int prev_insn_idx,
+ bool speculative)
+{
+ return __push_stack(env, insn_idx, prev_insn_idx, speculative, 0);
+}
+
+static struct bpf_verifier_state *push_stack_at(struct bpf_verifier_env *env,
+ int insn_idx, int prev_insn_idx,
+ u32 iter_ref_id)
+{
+ return __push_stack(env, insn_idx, prev_insn_idx, false, iter_ref_id);
+}
+
#define CALLER_SAVED_REGS 6
static const int caller_saved[CALLER_SAVED_REGS] = {
BPF_REG_0, BPF_REG_1, BPF_REG_2, BPF_REG_3, BPF_REG_4, BPF_REG_5
@@ -2376,14 +2412,14 @@ static struct bpf_verifier_state *push_async_cb(struct bpf_verifier_env *env,
elem->insn_idx = insn_idx;
elem->prev_insn_idx = prev_insn_idx;
- elem->next = env->head;
+ elem->next = env->stack.head;
elem->log_pos = env->log.end_pos;
- env->head = elem;
- env->stack_size++;
- if (env->stack_size > BPF_COMPLEXITY_LIMIT_JMP_SEQ) {
+ env->stack.head = elem;
+ env->stack.size++;
+ if (env->stack.size > BPF_COMPLEXITY_LIMIT_JMP_SEQ) {
verbose(env,
"The sequence of %d jumps is too complex for async cb.\n",
- env->stack_size);
+ env->stack.size);
goto err;
}
/* Unlike push_stack() do not copy_verifier_state().
@@ -7737,6 +7773,15 @@ static int process_iter_next_call(struct bpf_verifier_env *env, int insn_idx,
queued_iter = &queued_st->frame[iter_frameno]->stack[iter_spi].spilled_ptr;
queued_iter->iter.state = BPF_ITER_STATE_ACTIVE;
queued_iter->iter.depth++;
+ if (env->stack.head->next && queued_iter->iter.depth == 1)
+ /* The first time iter_next() is called for this iterator
+ * mark stack position with iter's ref_obj_id which is unique.
+ *
+ * If ->next is NULL the stack was empty before the verifier
+ * started exploring iter_next(), so don't leave any mark.
+ * Later push_stack_at() will place the state at the top of the stack.
+ */
+ env->stack.head->next->loop_start = queued_iter->ref_obj_id;
queued_fr = queued_st->frame[queued_st->curframe];
mark_ptr_not_null_reg(&queued_fr->regs[BPF_REG_0]);
@@ -16315,8 +16360,8 @@ static int propagate_precision(struct bpf_verifier_env *env,
return 0;
}
-static bool states_maybe_looping(struct bpf_verifier_state *old,
- struct bpf_verifier_state *cur)
+static bool all_regs_equal(struct bpf_verifier_state *old,
+ struct bpf_verifier_state *cur)
{
struct bpf_func_state *fold, *fcur;
int i, fr = cur->curframe;
@@ -16396,14 +16441,14 @@ static bool is_iter_next_insn(struct bpf_verifier_env *env, int insn_idx)
* }
*
*/
-static bool iter_active_depths_differ(struct bpf_verifier_state *old, struct bpf_verifier_state *cur)
+static bool has_active_iters(struct bpf_verifier_state *cur)
{
- struct bpf_reg_state *slot, *cur_slot;
struct bpf_func_state *state;
+ struct bpf_reg_state *slot;
int i, fr;
- for (fr = old->curframe; fr >= 0; fr--) {
- state = old->frame[fr];
+ for (fr = cur->curframe; fr >= 0; fr--) {
+ state = cur->frame[fr];
for (i = 0; i < state->allocated_stack / BPF_REG_SIZE; i++) {
if (state->stack[i].slot_type[0] != STACK_ITER)
continue;
@@ -16412,9 +16457,7 @@ static bool iter_active_depths_differ(struct bpf_verifier_state *old, struct bpf
if (slot->iter.state != BPF_ITER_STATE_ACTIVE)
continue;
- cur_slot = &cur->frame[fr]->stack[i].spilled_ptr;
- if (cur_slot->iter.depth != slot->iter.depth)
- return true;
+ return true;
}
}
return false;
@@ -16428,6 +16471,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
int i, j, err, states_cnt = 0;
bool force_new_state = env->test_state_freq || is_force_checkpoint(env, insn_idx);
bool add_new_state = force_new_state;
+ bool delay_incremented = false;
/* bpf progs typically have pruning point every 4 instructions
* http://vger.kernel.org/bpfconf2019.html#session-1
@@ -16451,6 +16495,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
if (sl->state.insn_idx != insn_idx)
goto next;
+ verbose(env, "search %lx branches=%d ", ((long)&sl->state) >> 3 & 0xFFFF,
+ sl->state.branches);
if (sl->state.branches) {
struct bpf_func_state *frame = sl->state.frame[sl->state.curframe];
@@ -16470,7 +16516,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
goto skip_inf_loop_check;
}
/* BPF open-coded iterators loop detection is special.
- * states_maybe_looping() logic is too simplistic in detecting
+ * all_regs_equal() logic is too simplistic in detecting
* states that *might* be equivalent, because it doesn't know
* about ID remapping, so don't even perform it.
* See process_iter_next_call() and iter_active_depths_differ()
@@ -16486,6 +16532,7 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
struct bpf_func_state *cur_frame;
struct bpf_reg_state *iter_state, *iter_reg;
int spi;
+ bool exact;
cur_frame = cur->frame[cur->curframe];
/* btf_check_iter_kfuncs() enforces that
@@ -16498,17 +16545,73 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
*/
spi = __get_spi(iter_reg->off + iter_reg->var_off.value);
iter_state = &func(env, iter_reg)->stack[spi].spilled_ptr;
- if (iter_state->iter.state == BPF_ITER_STATE_ACTIVE)
+ if (iter_state->iter.state != BPF_ITER_STATE_ACTIVE)
+ goto skip_inf_loop_check;
+
+ exact = all_regs_equal(&sl->state, cur);
+
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ verbose(env, "%d: exact %d delay %d ", insn_idx, exact, cur->delayed);
+ print_verifier_state(env, cur->frame[cur->curframe], true);
+ }
+ if (exact)
+ /* Nothing to explore further. */
goto hit;
+
+ /* The current state looks equal to the visited state,
+ * but the visited state might not have all liveness and
+ * precision marks, so look further.
+ */
+ if (sl->state.branches > 1 && cur->delayed < 2) {
+ /* branches > 1 means that one is looping and
+ * at least one normal branch wasn't explored.
+ * Keep exploring and make sure not to add
+ * the state to the list otherwise at the next
+ * iteration of the loop it may match exactly.
+ */
+ if (!delay_incremented) {
+ /* Several states in the link list can match.
+ * Increment once.
+ */
+ delay_incremented = true;
+ cur->delayed++;
+ }
+ add_new_state = false;
+ goto miss;
+ }
+ /* The state has likely only one branch which is our looping backedge.
+ * Reschedule it to be explored again at the start of the loop
+ * identified by iter_state->ref_obj_id.
+ */
+ if (cur->delayed >= 20)
+ /* It was explored */
+ goto hit;
+ new = push_stack_at(env, insn_idx, env->prev_insn_idx,
+ iter_state->ref_obj_id);
+ new->delayed += 10;
+ env->insn_processed += 1;
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ verbose(env, "delaying %d: d%d\n", insn_idx, cur->delayed);
+ }
+ /* Though liveness and precision may be incomplete
+ * propagate it and update branch counts.
+ */
+ goto hit;
}
goto skip_inf_loop_check;
}
/* attempt to detect infinite loop to avoid unnecessary doomed work */
- if (states_maybe_looping(&sl->state, cur) &&
+ if (all_regs_equal(&sl->state, cur) &&
states_equal(env, &sl->state, cur) &&
- !iter_active_depths_differ(&sl->state, cur)) {
+ !has_active_iters(cur)) {
verbose_linfo(env, insn_idx, "; ");
verbose(env, "infinite loop detected at insn %d\n", insn_idx);
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ verbose(env, "old: ");
+ print_verifier_state(env, sl->state.frame[sl->state.curframe], true);
+ verbose(env, "cur: ");
+ print_verifier_state(env, cur->frame[cur->curframe], true);
+ }
return -EINVAL;
}
/* if the verifier is processing a loop, avoid adding new state
@@ -16532,6 +16635,12 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
}
if (states_equal(env, &sl->state, cur)) {
hit:
+ if (env->log.level & BPF_LOG_LEVEL2) {
+ verbose(env, "hit: ");
+ print_verifier_state(env, sl->state.frame[sl->state.curframe], true);
+ verbose(env, "cur: ");
+ print_verifier_state(env, cur->frame[cur->curframe], true);
+ }
sl->hit_cnt++;
/* reached equivalent register/stack state,
* prune the search.
@@ -16570,7 +16679,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
* Higher numbers increase max_states_per_insn and verification time,
* but do not meaningfully decrease insn_processed.
*/
- if (sl->miss_cnt > sl->hit_cnt * 3 + 3) {
+ if (sl->miss_cnt > sl->hit_cnt * 3 + 3 &&
+ !(is_force_checkpoint(env, insn_idx) && sl->state.branches > 0)) {
/* the state is unlikely to be useful. Remove it to
* speed up verification
*/
@@ -16638,6 +16748,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
kfree(new_sl);
return err;
}
+ verbose(env, "%d: add_state %lx branches=%d\n", insn_idx, ((long)new) >> 3 & 0xFFFF, new->branches);
+
new->insn_idx = insn_idx;
WARN_ONCE(new->branches != 1,
"BUG is_state_visited:branches_to_explore=%d insn %d\n", new->branches, insn_idx);
@@ -16779,7 +16891,7 @@ static int do_check(struct bpf_verifier_env *env)
insn = &insns[env->insn_idx];
class = BPF_CLASS(insn->code);
- if (++env->insn_processed > BPF_COMPLEXITY_LIMIT_INSNS) {
+ if (++env->insn_processed > 3000) {//BPF_COMPLEXITY_LIMIT_INSNS) {
verbose(env,
"BPF program is too large. Processed %d insn\n",
env->insn_processed);