From: Eduard Zingerman MEM_WRITE without MEM_UNINIT denotes memory that is read as well as written. Helper argument checking requests only BPF_WRITE for it, which checks stack initialization but omits read permission checks on map values. For example, bpf_check_mtu() reads its mtu_len argument before overwriting it, but the verifier permits that argument to point into a BPF_F_WRONLY_PROG map. bpf_fib_lookup() and bpf_load_hdr_opt() are affected the same way. Derive generic memory access from argument flags: read-only for inputs, write-only for MEM_WRITE | MEM_UNINIT, and read/write for MEM_WRITE alone. Partial-output helpers such as bpf_snprintf() now carry MEM_UNINIT, so their write-only map destinations remain valid. Generated kfunc prototypes already encode the same distinction, which retires the kfunc-specific access override. Signed-off-by: Eduard Zingerman Signed-off-by: Kumar Kartikeya Dwivedi --- kernel/bpf/verifier.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 7f1cc115456f..d62c0f74cff5 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8907,6 +8907,19 @@ static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_stat return 0; } +/* + * MEM_WRITE alone denotes an input/output buffer. MEM_UNINIT marks an output + * whose incoming contents are not read. + */ +static enum bpf_access_type func_arg_access_type(enum bpf_arg_type arg_type) +{ + if (!(arg_type & MEM_WRITE)) + return BPF_READ; + if (arg_type & MEM_UNINIT) + return BPF_WRITE; + return BPF_READ | BPF_WRITE; +} + static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 slot, u32 prev_slot, struct bpf_call_arg_meta *meta, int insn_idx) @@ -9199,10 +9212,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 slot, u32 p if (!(arg_type & MEM_FIXED_SIZE)) break; - access_type = arg_type & MEM_WRITE ? BPF_WRITE : BPF_READ; - /* Ordinary kfunc buffers are input/output; __uninit buffers are outputs. */ - if (meta->btf && !(arg_type & MEM_UNINIT)) - access_type = BPF_READ | BPF_WRITE; + access_type = func_arg_access_type(arg_type); err = check_mem_reg(env, reg, argno, arg_size, access_type, meta, &known_memory); if (err < 0) { @@ -9248,9 +9258,7 @@ static int check_func_arg(struct bpf_verifier_env *env, u32 arg, u32 slot, u32 p if (meta->btf && bpf_register_is_null(buff_reg)) break; - access_type = fn->arg_type[arg - 1] & MEM_WRITE ? BPF_WRITE : BPF_READ; - if (meta->btf && !(fn->arg_type[arg - 1] & MEM_UNINIT)) - access_type = BPF_READ | BPF_WRITE; + access_type = func_arg_access_type(fn->arg_type[arg - 1]); zero_size_allowed = meta->btf || base_type(arg_type) == ARG_MEM_SIZE_OR_ZERO; -- 2.53.0