Commit eea1c227 authored by Alexei Starovoitov's avatar Alexei Starovoitov Committed by Daniel Borkmann

bpf: fix callees pruning callers

The commit 7640ead9 partially resolved the issue of callees
incorrectly pruning the callers.
With introduction of bounded loops and jmps_processed heuristic
single verifier state may contain multiple branches and calls.
It's possible that new verifier state (for future pruning) will be
allocated inside callee. Then callee will exit (still within the same
verifier state). It will go back to the caller and there R6-R9 registers
will be read and will trigger mark_reg_read. But the reg->live for all frames
but the top frame is not set to LIVE_NONE. Hence mark_reg_read will fail
to propagate liveness into parent and future walking will incorrectly
conclude that the states are equivalent because LIVE_READ is not set.
In other words the rule for parent/live should be:
whenever register parentage chain is set the reg->live should be set to LIVE_NONE.
is_state_visited logic already follows this rule for spilled registers.

Fixes: 7640ead9 ("bpf: verifier: make sure callees don't prune with caller differences")
Fixes: f4d7e40a ("bpf: introduce function calls (verification)")
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
Signed-off-by: default avatarDaniel Borkmann <daniel@iogearbox.net>
parent 2589726d
...@@ -6834,17 +6834,18 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx) ...@@ -6834,17 +6834,18 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
* the state of the call instruction (with WRITTEN set), and r0 comes * the state of the call instruction (with WRITTEN set), and r0 comes
* from callee with its full parentage chain, anyway. * from callee with its full parentage chain, anyway.
*/ */
for (j = 0; j <= cur->curframe; j++)
for (i = j < cur->curframe ? BPF_REG_6 : 0; i < BPF_REG_FP; i++)
cur->frame[j]->regs[i].parent = &new->frame[j]->regs[i];
/* clear write marks in current state: the writes we did are not writes /* clear write marks in current state: the writes we did are not writes
* our child did, so they don't screen off its reads from us. * our child did, so they don't screen off its reads from us.
* (There are no read marks in current state, because reads always mark * (There are no read marks in current state, because reads always mark
* their parent and current state never has children yet. Only * their parent and current state never has children yet. Only
* explored_states can get read marks.) * explored_states can get read marks.)
*/ */
for (j = 0; j <= cur->curframe; j++) {
for (i = j < cur->curframe ? BPF_REG_6 : 0; i < BPF_REG_FP; i++)
cur->frame[j]->regs[i].parent = &new->frame[j]->regs[i];
for (i = 0; i < BPF_REG_FP; i++) for (i = 0; i < BPF_REG_FP; i++)
cur->frame[cur->curframe]->regs[i].live = REG_LIVE_NONE; cur->frame[j]->regs[i].live = REG_LIVE_NONE;
}
/* all stack frames are accessible from callee, clear them all */ /* all stack frames are accessible from callee, clear them all */
for (j = 0; j <= cur->curframe; j++) { for (j = 0; j <= cur->curframe; j++) {
......
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