On Wed, 2025-01-29 at 22:04 +0000, Peilin Ye wrote: [...] > > > +static int check_atomic_load(struct bpf_verifier_env *env, int insn_idx, > > > + struct bpf_insn *insn) > > > +{ > > > + struct bpf_reg_state *regs = cur_regs(env); > > > + int err; > > > + > > > + err = check_reg_arg(env, insn->src_reg, SRC_OP); > > > + if (err) > > > + return err; > > > + > > > + err = check_reg_arg(env, insn->dst_reg, DST_OP_NO_MARK); > > > + if (err) > > > + return err; > > > + > > > + if (!atomic_ptr_type_ok(env, insn->src_reg, insn)) { > > > + verbose(env, "BPF_ATOMIC loads from R%d %s is not allowed\n", > > > + insn->src_reg, > > > + reg_type_str(env, reg_state(env, insn->src_reg)->type)); > > > + return -EACCES; > > > + } > > > + > > > + if (is_arena_reg(env, insn->src_reg)) { > > > + err = save_aux_ptr_type(env, PTR_TO_ARENA, false); > > > + if (err) > > > + return err; > > > > Nit: this and the next function look very similar to processing of > > generic load and store in do_check(). Maybe extract that code > > as an auxiliary function and call it in both places? > > Sure, I agree that they look a bit repetitive. > > > The only major difference is is_arena_reg() check guarding > > save_aux_ptr_type(), but I think it is ok to do save_aux_ptr_type > > unconditionally. Fwiw, the code would be a bit simpler, > > just spent half an hour convincing myself that such conditional handling > > is not an error. Wdyt? > > :-O > > Thanks a lot for that; would you mind sharing a bit more on how you > reasoned about it (i.e., why is it OK to save_aux_ptr_type() > unconditionally) ? Well, save_aux_ptr_type() does two things: - if there is no env->insn_aux_data[env->insn_idx].ptr_type associated with the instruction it saves one; - if there is .ptr_type, it checks if a new one is compatible and errors out if it's not. The .ptr_type is used in convert_ctx_accesses() to rewrite access instruction (STX/LDX, atomic or not) in a way specific to pointer type. So, doing save_aux_ptr_type() conditionally is already sketchy, as there is a risk to miss if some instruction is used in a context where pointer type requires different rewrites. convert_ctx_accesses() rewrites instruction for pointer following types: - PTR_TO_CTX - PTR_TO_SOCKET - PTR_TO_SOCK_COMMON - PTR_TO_TCP_SOCK - PTR_TO_XDP_SOCK - PTR_TO_BTF_ID - PTR_TO_ARENA atomic_ptr_type_ok() allows the following pointer types: - CONST_PTR_TO_MAP - PTR_TO_MAP_VALUE - PTR_TO_MAP_KEY - PTR_TO_STACK - PTR_TO_BTF_ID - PTR_TO_MEM - PTR_TO_ARENA - PTR_TO_BUF - PTR_TO_FUNC - CONST_PTR_TO_DYNPTR One has to check rewrites applied by convert_ctx_accesses() to atomic instructions to reason about correctness of the conditional save_aux_ptr_type() call. If is_arena_reg() guard is removed from save_aux_ptr_type() we risk to reject programs that do atomic load/store where same instruction is used to modify a pointer that can be either of the above types. I speculate that this is not the problem, as do_check() processing for BPF_STX/BPF_LDX already calls save_aux_ptr_type() unconditionally. [...]