Re: [PATCH bpf-next 1/4] bpf: Keep fault protection when merging pointer types
Eduard Zingerman <[email protected]>
| Newsgroups | org.kernel.vger.bpf |
|---|---|
| Message-ID | <[email protected]> |
On Thu, 2026-08-13 at 22:40 +0200, Daniel Borkmann wrote:
...
> +static bool may_fault_on_deref(enum bpf_reg_type type)
> +{
> + /*
> + * The pointer types which must not be dereferenced without fault
> + * protection, that is, the ones bpf_convert_ctx_accesses() has to
> + * turn a BPF_LDX into a BPF_PROBE_MEM one for. Slightly wider than
> + * the list matched there, which relies on an untrusted PTR_TO_MEM
> + * always carrying MEM_RDONLY as well.
> + */
> + return type == PTR_TO_BTF_ID || (type_flag(type) & PTR_UNTRUSTED);
> +}
> +
> static bool is_load_acq_unsafe(struct bpf_verifier_env *env, int regno,
> struct bpf_insn *insn)
> {
...
> @@ -17021,11 +17025,24 @@ static bool is_ptr_to_mem(enum bpf_reg_type type)
> return base_type(type) == PTR_TO_MEM;
> }
>
> +static enum bpf_reg_type merge_ptr_types(enum bpf_reg_type type_a,
> + enum bpf_reg_type type_b)
> +{
> + bool to_mem = is_ptr_to_mem(type_a) || is_ptr_to_mem(type_b);
> + enum bpf_reg_type type_merged = to_mem ? PTR_TO_MEM : PTR_TO_BTF_ID;
> +
> + if (may_fault_on_deref(type_a) || may_fault_on_deref(type_b))
> + type_merged |= to_mem ? MEM_RDONLY | PTR_UNTRUSTED :
> + PTR_UNTRUSTED;
> + else
> + type_merged |= ((type_a | type_b) & MEM_RDONLY);
> + return type_merged;
> +}
> +
> static int save_aux_ptr_type(struct bpf_verifier_env *env, enum bpf_reg_type type,
> bool allow_trust_mismatch)
> {
> enum bpf_reg_type *prev_type = &env->insn_aux_data[env->insn_idx].ptr_type;
> - enum bpf_reg_type merged_type;
>
> if (*prev_type == NOT_INIT) {
> /* Saw a valid insn
> @@ -17046,20 +17063,12 @@ static int save_aux_ptr_type(struct bpf_verifier_env *env, enum bpf_reg_type typ
> is_ptr_to_mem_or_btf_id(*prev_type)) {
> /*
> * Have to support a use case when one path through
> - * the program yields TRUSTED pointer while another
> - * is UNTRUSTED. Fallback to UNTRUSTED to generate
> - * BPF_PROBE_MEM/BPF_PROBE_MEMSX.
> - * Same behavior of MEM_RDONLY flag.
> + * the program yields a TRUSTED pointer while another
> + * is UNTRUSTED. Merge them into a type which keeps
> + * the BPF_PROBE_MEM/BPF_PROBE_MEMSX rewrite when
> + * either side needs it.
> */
> - if (is_ptr_to_mem(type) || is_ptr_to_mem(*prev_type))
> - merged_type = PTR_TO_MEM;
> - else
> - merged_type = PTR_TO_BTF_ID;
> - if ((type & PTR_UNTRUSTED) || (*prev_type & PTR_UNTRUSTED))
> - merged_type |= PTR_UNTRUSTED;
> - if ((type & MEM_RDONLY) || (*prev_type & MEM_RDONLY))
> - merged_type |= MEM_RDONLY;
> - *prev_type = merged_type;
> + *prev_type = merge_ptr_types(type, *prev_type);
> } else {
> verbose(env, "same insn cannot be used with different pointers\n");
> return -EINVAL;
Given that we don't document the list of all valid flag and type
combinations, I think that extending bpf_convert_ctx_accesses() to
match base type and checking UNTRUSTED flag on it is more future proof
solution.