Skip to content

Commit c1a56c7

Browse files
Alexei Starovoitovshiloong
authored andcommitted
bpf: add self-check logic to liveness analysis
commit 9242b5f upstream ANBZ: torvalds#200 Introduce REG_LIVE_DONE to check the liveness propagation and prepare the states for merging. See algorithm description in clean_live_states(). Signed-off-by: Alexei Starovoitov <[email protected]> Acked-by: Jakub Kicinski <[email protected]> Signed-off-by: Daniel Borkmann <[email protected]> Signed-off-by: Qiao Ma <[email protected]> Acked-by: Tony Lu <[email protected]>
1 parent d01fc8c commit c1a56c7

File tree

2 files changed

+108
-1
lines changed

2 files changed

+108
-1
lines changed

include/linux/bpf_verifier.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ enum bpf_reg_liveness {
3838
REG_LIVE_NONE = 0, /* reg hasn't been read or written this branch */
3939
REG_LIVE_READ, /* reg was read, so we're sensitive to initial value */
4040
REG_LIVE_WRITTEN, /* reg was written first, screening off later reads */
41+
REG_LIVE_DONE = 4, /* liveness won't be updating this register anymore */
4142
};
4243

4344
struct bpf_reg_state {

kernel/bpf/verifier.c

Lines changed: 107 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -355,12 +355,14 @@ static char slot_type_char[] = {
355355
static void print_liveness(struct bpf_verifier_env *env,
356356
enum bpf_reg_liveness live)
357357
{
358-
if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN))
358+
if (live & (REG_LIVE_READ | REG_LIVE_WRITTEN | REG_LIVE_DONE))
359359
verbose(env, "_");
360360
if (live & REG_LIVE_READ)
361361
verbose(env, "r");
362362
if (live & REG_LIVE_WRITTEN)
363363
verbose(env, "w");
364+
if (live & REG_LIVE_DONE)
365+
verbose(env, "D");
364366
}
365367

366368
static struct bpf_func_state *func(struct bpf_verifier_env *env,
@@ -1126,6 +1128,12 @@ static int mark_reg_read(struct bpf_verifier_env *env,
11261128
/* if read wasn't screened by an earlier write ... */
11271129
if (writes && state->live & REG_LIVE_WRITTEN)
11281130
break;
1131+
if (parent->live & REG_LIVE_DONE) {
1132+
verbose(env, "verifier BUG type %s var_off %lld off %d\n",
1133+
reg_type_str[parent->type],
1134+
parent->var_off.value, parent->off);
1135+
return -EFAULT;
1136+
}
11291137
/* ... then we depend on parent's value */
11301138
parent->live |= REG_LIVE_READ;
11311139
state = parent;
@@ -5695,6 +5703,102 @@ static bool check_ids(u32 old_id, u32 cur_id, struct bpf_id_pair *idmap)
56955703
return false;
56965704
}
56975705

5706+
static void clean_func_state(struct bpf_verifier_env *env,
5707+
struct bpf_func_state *st)
5708+
{
5709+
enum bpf_reg_liveness live;
5710+
int i, j;
5711+
5712+
for (i = 0; i < BPF_REG_FP; i++) {
5713+
live = st->regs[i].live;
5714+
/* liveness must not touch this register anymore */
5715+
st->regs[i].live |= REG_LIVE_DONE;
5716+
if (!(live & REG_LIVE_READ))
5717+
/* since the register is unused, clear its state
5718+
* to make further comparison simpler
5719+
*/
5720+
__mark_reg_not_init(&st->regs[i]);
5721+
}
5722+
5723+
for (i = 0; i < st->allocated_stack / BPF_REG_SIZE; i++) {
5724+
live = st->stack[i].spilled_ptr.live;
5725+
/* liveness must not touch this stack slot anymore */
5726+
st->stack[i].spilled_ptr.live |= REG_LIVE_DONE;
5727+
if (!(live & REG_LIVE_READ)) {
5728+
__mark_reg_not_init(&st->stack[i].spilled_ptr);
5729+
for (j = 0; j < BPF_REG_SIZE; j++)
5730+
st->stack[i].slot_type[j] = STACK_INVALID;
5731+
}
5732+
}
5733+
}
5734+
5735+
static void clean_verifier_state(struct bpf_verifier_env *env,
5736+
struct bpf_verifier_state *st)
5737+
{
5738+
int i;
5739+
5740+
if (st->frame[0]->regs[0].live & REG_LIVE_DONE)
5741+
/* all regs in this state in all frames were already marked */
5742+
return;
5743+
5744+
for (i = 0; i <= st->curframe; i++)
5745+
clean_func_state(env, st->frame[i]);
5746+
}
5747+
5748+
/* the parentage chains form a tree.
5749+
* the verifier states are added to state lists at given insn and
5750+
* pushed into state stack for future exploration.
5751+
* when the verifier reaches bpf_exit insn some of the verifer states
5752+
* stored in the state lists have their final liveness state already,
5753+
* but a lot of states will get revised from liveness point of view when
5754+
* the verifier explores other branches.
5755+
* Example:
5756+
* 1: r0 = 1
5757+
* 2: if r1 == 100 goto pc+1
5758+
* 3: r0 = 2
5759+
* 4: exit
5760+
* when the verifier reaches exit insn the register r0 in the state list of
5761+
* insn 2 will be seen as !REG_LIVE_READ. Then the verifier pops the other_branch
5762+
* of insn 2 and goes exploring further. At the insn 4 it will walk the
5763+
* parentage chain from insn 4 into insn 2 and will mark r0 as REG_LIVE_READ.
5764+
*
5765+
* Since the verifier pushes the branch states as it sees them while exploring
5766+
* the program the condition of walking the branch instruction for the second
5767+
* time means that all states below this branch were already explored and
5768+
* their final liveness markes are already propagated.
5769+
* Hence when the verifier completes the search of state list in is_state_visited()
5770+
* we can call this clean_live_states() function to mark all liveness states
5771+
* as REG_LIVE_DONE to indicate that 'parent' pointers of 'struct bpf_reg_state'
5772+
* will not be used.
5773+
* This function also clears the registers and stack for states that !READ
5774+
* to simplify state merging.
5775+
*
5776+
* Important note here that walking the same branch instruction in the callee
5777+
* doesn't meant that the states are DONE. The verifier has to compare
5778+
* the callsites
5779+
*/
5780+
static void clean_live_states(struct bpf_verifier_env *env, int insn,
5781+
struct bpf_verifier_state *cur)
5782+
{
5783+
struct bpf_verifier_state_list *sl;
5784+
int i;
5785+
5786+
sl = env->explored_states[insn];
5787+
if (!sl)
5788+
return;
5789+
5790+
while (sl != STATE_LIST_MARK) {
5791+
if (sl->state.curframe != cur->curframe)
5792+
goto next;
5793+
for (i = 0; i <= cur->curframe; i++)
5794+
if (sl->state.frame[i]->callsite != cur->frame[i]->callsite)
5795+
goto next;
5796+
clean_verifier_state(env, &sl->state);
5797+
next:
5798+
sl = sl->next;
5799+
}
5800+
}
5801+
56985802
/* Returns true if (rold safe implies rcur safe) */
56995803
static bool regsafe(struct bpf_verifier_env *env, struct bpf_reg_state *rold,
57005804
struct bpf_reg_state *rcur, struct bpf_id_pair *idmap)
@@ -6010,6 +6114,8 @@ static int is_state_visited(struct bpf_verifier_env *env, int insn_idx)
60106114
*/
60116115
return 0;
60126116

6117+
clean_live_states(env, insn_idx, cur);
6118+
60136119
while (sl != STATE_LIST_MARK) {
60146120
if (states_equal(env, &sl->state, cur)) {
60156121
/* reached equivalent register/stack state,

0 commit comments

Comments
 (0)