The limitation on fixed offsets stems from the fact that certain program types rewrite the accesses to the context structure and translate them to accesses to the real underlying type. Technically, in the past, we could have stashed the register offset in insn_aux and made rewrites work, but we've never needed it in the past since the offset for such context structures easily fit in the s16 signed instruction offset. Regardless, the consequence is that for program types where the program type's verifier ops doesn't supply a convert_ctx_access callback, we unnecessarily reject accesses with a modified ctx pointer (i.e., one whose offset has been shifted) in check_ptr_off_reg. Make an exception for such program types (like syscall, tracepoint, raw_tp, etc.). Pass in fixed_off_ok as true to __check_ptr_off_reg for such cases, and accumulate the register offset into insn->off passed to check_ctx_access. In particular, the accumulation is critical since we need to correctly track the max_ctx_offset which is used for bounds checking the buffer for syscall programs at runtime. Reported-by: Tejun Heo Reported-by: Dan Schatzberg Signed-off-by: Kumar Kartikeya Dwivedi --- kernel/bpf/verifier.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1153a828ce8d..63872b981dcb 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -7722,6 +7722,11 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn if (!err && value_regno >= 0 && (t == BPF_READ || rdonly_mem)) mark_reg_unknown(env, regs, value_regno); } else if (reg->type == PTR_TO_CTX) { + /* + * Program types that don't rewrite ctx accesses can safely + * dereference ctx pointers with fixed offsets. + */ + bool fixed_off_ok = !env->ops->convert_ctx_access; struct bpf_retval_range range; struct bpf_insn_access_aux info = { .reg_type = SCALAR_VALUE, @@ -7735,10 +7740,16 @@ static int check_mem_access(struct bpf_verifier_env *env, int insn_idx, u32 regn return -EACCES; } - err = check_ptr_off_reg(env, reg, regno); + err = __check_ptr_off_reg(env, reg, regno, fixed_off_ok); if (err < 0) return err; + /* + * Fold the register's constant offset into the insn offset so + * that is_valid_access() sees the true effective offset. + */ + if (fixed_off_ok) + off += reg->var_off.value; err = check_ctx_access(env, insn_idx, off, size, t, &info); if (err) verbose_linfo(env, insn_idx, "; "); -- 2.47.3