RE: [PATCH bpf-next 1/3] bpf: Propagate scalar ranges through register assignments.
flat view
From: John Fastabend <john.fastabend@gmail.com>
Date: 2020-10-07 23:56:03
Also in:
bpf
John Fastabend wrote:
Alexei Starovoitov wrote:quoted
From: Alexei Starovoitov <ast@kernel.org> The llvm register allocator may use two different registers representing the same virtual register. In such case the following pattern can be observed: 1047: (bf) r9 = r6 1048: (a5) if r6 < 0x1000 goto pc+1 1050: ... 1051: (a5) if r9 < 0x2 goto pc+66 1052: ... 1053: (bf) r2 = r9 /* r2 needs to have upper and lower bounds */ In order to track this information without backtracking allocate ID for scalars in a similar way as it's done for find_good_pkt_pointers(). When the verifier encounters r9 = r6 assignment it will assign the same ID to both registers. Later if either register range is narrowed via conditional jump propagate the register state into the other register. Clear register ID in adjust_reg_min_max_vals() for any alu instruction.Do we also need to clear the register ID on reg0 for CALL ops into a helper? Looks like check_helper_call might mark reg0 as a scalar, but I don't see where it would clear the reg->id? Did I miss it. Either way maybe a comment here would help make it obvious how CALLs are handled? Thanks, John
OK sorry for the noise found it right after hitting send. Any call to
mark_reg_unknown will zero the id.
/* Mark a register as having a completely unknown (scalar) value. */
static void __mark_reg_unknown(const struct bpf_verifier_env *env,
struct bpf_reg_state *reg)
{
/*
* Clear type, id, off, and union(map_ptr, range) and
* padding between 'type' and union
*/
memset(reg, 0, offsetof(struct bpf_reg_state, var_off));
And check_helper_call() does,
/* update return register (already marked as written above) */
if (fn->ret_type == RET_INTEGER) {
/* sets type to SCALAR_VALUE */
mark_reg_unknown(env, regs, BPF_REG_0);
so looks good to me. In the check_func_call() case the if is_global
branch will mark_reg_unknown(). The other case only seems to do a
clear_caller_saved_regs though. Is that enough?
.John
quoted
Newly allocated register ID is ignored for scalars in regsafe() and doesn't affect state pruning. mark_reg_unknown() also clears the ID. Signed-off-by: Alexei Starovoitov <ast@kernel.org> --- kernel/bpf/verifier.c | 38 +++++++++++++++++++ .../testing/selftests/bpf/prog_tests/align.c | 16 ++++---- .../bpf/verifier/direct_packet_access.c | 2 +- 3 files changed, 47 insertions(+), 9 deletions(-)diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 01120acab09a..09e17b483b0b 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c@@ -6432,6 +6432,8 @@ static int adjust_reg_min_max_vals(struct bpf_verifier_env *env, src_reg = NULL; if (dst_reg->type != SCALAR_VALUE) ptr_reg = dst_reg; + else + dst_reg->id = 0; if (BPF_SRC(insn->code) == BPF_X) { src_reg = ®s[insn->src_reg]; if (src_reg->type != SCALAR_VALUE) {@@ -6565,6 +6567,8 @@ static int check_alu_op(struct bpf_verifier_env *env, struct bpf_insn *insn) /* case: R1 = R2 * copy register state to dest reg */ + if (src_reg->type == SCALAR_VALUE) + src_reg->id = ++env->id_gen; *dst_reg = *src_reg; dst_reg->live |= REG_LIVE_WRITTEN; dst_reg->subreg_def = DEF_NOT_SUBREG;@@ -7365,6 +7369,30 @@ static bool try_match_pkt_pointers(const struct bpf_insn *insn, return true; }