From: Eduard Zingerman MEM_WRITE without MEM_UNINIT denotes memory that may be read as well as written. Helper argument checking currently requests only BPF_WRITE, 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. 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. Encode MEM_WRITE when classifying kfunc memory arguments and resolving scalar-only struct pointers, replacing the kfunc-specific access override. This rejects write-only map values passed to helper arguments that require initialized writable memory. Generic kfunc __uninit output bookkeeping is completed in a following patch. Fixes: 8ea607330a39 ("bpf: Fix overloading of MEM_UNINIT's meaning") Signed-off-by: Eduard Zingerman Signed-off-by: Kumar Kartikeya Dwivedi --- kernel/bpf/verifier.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/kernel/bpf/verifier.c b/kernel/bpf/verifier.c index 1d5e4ee64fc0..21eb806b1351 100644 --- a/kernel/bpf/verifier.c +++ b/kernel/bpf/verifier.c @@ -8886,6 +8886,15 @@ static int process_map_ptr_arg(struct bpf_verifier_env *env, struct bpf_reg_stat return 0; } +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) @@ -9180,9 +9189,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; - if (meta->btf) - 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) { @@ -9228,9 +9235,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) - 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; @@ -12436,7 +12441,8 @@ static int resolve_func_arg_type(struct bpf_verifier_env *env, PTR_ERR(resolve_ret)); return -EINVAL; } - *arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | (*arg_type & PTR_MAYBE_NULL); + *arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | MEM_WRITE | + (*arg_type & PTR_MAYBE_NULL); return 0; } @@ -12933,7 +12939,7 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, reg_arg_name(env, argno), btf_type_str(ref_t), ref_tname); return -EINVAL; } - arg_type = ARG_PTR_TO_MEM; + arg_type = ARG_PTR_TO_MEM | MEM_WRITE; } else if (btf_type_is_struct(ref_t)) /* A pointer to a struct without a size argument is classified as ARG_PTR_TO_BTF_ID */ arg_type = ARG_PTR_TO_BTF_ID; @@ -12958,7 +12964,7 @@ get_kfunc_arg_type(struct bpf_verifier_env *env, struct bpf_call_arg_meta *meta, return -EINVAL; } proto->arg_size[arg] = type_size; - arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE; + arg_type = ARG_PTR_TO_MEM | MEM_FIXED_SIZE | MEM_WRITE; } if (is_kfunc_arg_uninit(meta->btf, &args[arg])) -- 2.53.0