From f994353a8a7492dc21c41ee9b92cdca0a306b10f Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Fri, 19 Apr 2024 13:08:52 +0200 Subject: [PATCH] 5.15-stable patches added patches: bpf-extend-kfunc-with-ptr_to_ctx-ptr_to_mem-argument-support.patch bpf-fix-out-of-bounds-access-for-ringbuf-helpers.patch bpf-fix-ringbuf-memory-type-confusion-when-passing-to-helpers.patch bpf-generalize-check_ctx_reg-for-reuse-with-other-types.patch bpf-generally-fix-helper-register-offset-check.patch --- ...r_to_ctx-ptr_to_mem-argument-support.patch | 190 ++++++++++++++++++ ...of-bounds-access-for-ringbuf-helpers.patch | 57 ++++++ ...pe-confusion-when-passing-to-helpers.patch | 103 ++++++++++ ...k_ctx_reg-for-reuse-with-other-types.patch | 110 ++++++++++ ...lly-fix-helper-register-offset-check.patch | 110 ++++++++++ queue-5.15/series | 5 + 6 files changed, 575 insertions(+) create mode 100644 queue-5.15/bpf-extend-kfunc-with-ptr_to_ctx-ptr_to_mem-argument-support.patch create mode 100644 queue-5.15/bpf-fix-out-of-bounds-access-for-ringbuf-helpers.patch create mode 100644 queue-5.15/bpf-fix-ringbuf-memory-type-confusion-when-passing-to-helpers.patch create mode 100644 queue-5.15/bpf-generalize-check_ctx_reg-for-reuse-with-other-types.patch create mode 100644 queue-5.15/bpf-generally-fix-helper-register-offset-check.patch diff --git a/queue-5.15/bpf-extend-kfunc-with-ptr_to_ctx-ptr_to_mem-argument-support.patch b/queue-5.15/bpf-extend-kfunc-with-ptr_to_ctx-ptr_to_mem-argument-support.patch new file mode 100644 index 00000000000..4f4f135b71f --- /dev/null +++ b/queue-5.15/bpf-extend-kfunc-with-ptr_to_ctx-ptr_to_mem-argument-support.patch @@ -0,0 +1,190 @@ +From 3363bd0cfbb80dfcd25003cd3815b0ad8b68d0ff Mon Sep 17 00:00:00 2001 +From: Kumar Kartikeya Dwivedi +Date: Fri, 17 Dec 2021 07:20:24 +0530 +Subject: bpf: Extend kfunc with PTR_TO_CTX, PTR_TO_MEM argument support + +From: Kumar Kartikeya Dwivedi + +commit 3363bd0cfbb80dfcd25003cd3815b0ad8b68d0ff upstream. + +Allow passing PTR_TO_CTX, if the kfunc expects a matching struct type, +and punt to PTR_TO_MEM block if reg->type does not fall in one of +PTR_TO_BTF_ID or PTR_TO_SOCK* types. This will be used by future commits +to get access to XDP and TC PTR_TO_CTX, and pass various data (flags, +l4proto, netns_id, etc.) encoded in opts struct passed as pointer to +kfunc. + +For PTR_TO_MEM support, arguments are currently limited to pointer to +scalar, or pointer to struct composed of scalars. This is done so that +unsafe scenarios (like passing PTR_TO_MEM where PTR_TO_BTF_ID of +in-kernel valid structure is expected, which may have pointers) are +avoided. Since the argument checking happens basd on argument register +type, it is not easy to ascertain what the expected type is. In the +future, support for PTR_TO_MEM for kfunc can be extended to serve other +usecases. The struct type whose pointer is passed in may have maximum +nesting depth of 4, all recursively composed of scalars or struct with +scalars. + +Future commits will add negative tests that check whether these +restrictions imposed for kfunc arguments are duly rejected by BPF +verifier or not. + +Signed-off-by: Kumar Kartikeya Dwivedi +Signed-off-by: Alexei Starovoitov +Link: https://lore.kernel.org/bpf/20211217015031.1278167-4-memxor@gmail.com +Signed-off-by: Edward Liaw +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/btf.c | 93 +++++++++++++++++++++++++++++++++++++++++++------------ + 1 file changed, 73 insertions(+), 20 deletions(-) + +--- a/kernel/bpf/btf.c ++++ b/kernel/bpf/btf.c +@@ -5447,6 +5447,46 @@ static u32 *reg2btf_ids[__BPF_REG_TYPE_M + #endif + }; + ++/* Returns true if struct is composed of scalars, 4 levels of nesting allowed */ ++static bool __btf_type_is_scalar_struct(struct bpf_verifier_log *log, ++ const struct btf *btf, ++ const struct btf_type *t, int rec) ++{ ++ const struct btf_type *member_type; ++ const struct btf_member *member; ++ u32 i; ++ ++ if (!btf_type_is_struct(t)) ++ return false; ++ ++ for_each_member(i, t, member) { ++ const struct btf_array *array; ++ ++ member_type = btf_type_skip_modifiers(btf, member->type, NULL); ++ if (btf_type_is_struct(member_type)) { ++ if (rec >= 3) { ++ bpf_log(log, "max struct nesting depth exceeded\n"); ++ return false; ++ } ++ if (!__btf_type_is_scalar_struct(log, btf, member_type, rec + 1)) ++ return false; ++ continue; ++ } ++ if (btf_type_is_array(member_type)) { ++ array = btf_type_array(member_type); ++ if (!array->nelems) ++ return false; ++ member_type = btf_type_skip_modifiers(btf, array->type, NULL); ++ if (!btf_type_is_scalar(member_type)) ++ return false; ++ continue; ++ } ++ if (!btf_type_is_scalar(member_type)) ++ return false; ++ } ++ return true; ++} ++ + static int btf_check_func_arg_match(struct bpf_verifier_env *env, + const struct btf *btf, u32 func_id, + struct bpf_reg_state *regs, +@@ -5455,6 +5495,7 @@ static int btf_check_func_arg_match(stru + enum bpf_prog_type prog_type = env->prog->type == BPF_PROG_TYPE_EXT ? + env->prog->aux->dst_prog->type : env->prog->type; + struct bpf_verifier_log *log = &env->log; ++ bool is_kfunc = btf_is_kernel(btf); + const char *func_name, *ref_tname; + const struct btf_type *t, *ref_t; + const struct btf_param *args; +@@ -5507,7 +5548,20 @@ static int btf_check_func_arg_match(stru + + ref_t = btf_type_skip_modifiers(btf, t->type, &ref_id); + ref_tname = btf_name_by_offset(btf, ref_t->name_off); +- if (btf_is_kernel(btf)) { ++ if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) { ++ /* If function expects ctx type in BTF check that caller ++ * is passing PTR_TO_CTX. ++ */ ++ if (reg->type != PTR_TO_CTX) { ++ bpf_log(log, ++ "arg#%d expected pointer to ctx, but got %s\n", ++ i, btf_type_str(t)); ++ return -EINVAL; ++ } ++ if (check_ctx_reg(env, reg, regno)) ++ return -EINVAL; ++ } else if (is_kfunc && (reg->type == PTR_TO_BTF_ID || ++ (reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type)))) { + const struct btf_type *reg_ref_t; + const struct btf *reg_btf; + const char *reg_ref_tname; +@@ -5523,14 +5577,9 @@ static int btf_check_func_arg_match(stru + if (reg->type == PTR_TO_BTF_ID) { + reg_btf = reg->btf; + reg_ref_id = reg->btf_id; +- } else if (reg2btf_ids[base_type(reg->type)]) { ++ } else { + reg_btf = btf_vmlinux; + reg_ref_id = *reg2btf_ids[base_type(reg->type)]; +- } else { +- bpf_log(log, "kernel function %s args#%d expected pointer to %s %s but R%d is not a pointer to btf_id\n", +- func_name, i, +- btf_type_str(ref_t), ref_tname, regno); +- return -EINVAL; + } + + reg_ref_t = btf_type_skip_modifiers(reg_btf, reg_ref_id, +@@ -5546,22 +5595,24 @@ static int btf_check_func_arg_match(stru + reg_ref_tname); + return -EINVAL; + } +- } else if (btf_get_prog_ctx_type(log, btf, t, prog_type, i)) { +- /* If function expects ctx type in BTF check that caller +- * is passing PTR_TO_CTX. +- */ +- if (reg->type != PTR_TO_CTX) { +- bpf_log(log, +- "arg#%d expected pointer to ctx, but got %s\n", +- i, btf_type_str(t)); +- return -EINVAL; +- } +- if (check_ctx_reg(env, reg, regno)) +- return -EINVAL; + } else if (ptr_to_mem_ok) { + const struct btf_type *resolve_ret; + u32 type_size; + ++ if (is_kfunc) { ++ /* Permit pointer to mem, but only when argument ++ * type is pointer to scalar, or struct composed ++ * (recursively) of scalars. ++ */ ++ if (!btf_type_is_scalar(ref_t) && ++ !__btf_type_is_scalar_struct(log, btf, ref_t, 0)) { ++ bpf_log(log, ++ "arg#%d pointer type %s %s must point to scalar or struct with scalar\n", ++ i, btf_type_str(ref_t), ref_tname); ++ return -EINVAL; ++ } ++ } ++ + resolve_ret = btf_resolve_size(btf, ref_t, &type_size); + if (IS_ERR(resolve_ret)) { + bpf_log(log, +@@ -5574,6 +5625,8 @@ static int btf_check_func_arg_match(stru + if (check_mem_reg(env, reg, regno, type_size)) + return -EINVAL; + } else { ++ bpf_log(log, "reg type unsupported for arg#%d %sfunction %s#%d\n", i, ++ is_kfunc ? "kernel " : "", func_name, func_id); + return -EINVAL; + } + } +@@ -5623,7 +5676,7 @@ int btf_check_kfunc_arg_match(struct bpf + const struct btf *btf, u32 func_id, + struct bpf_reg_state *regs) + { +- return btf_check_func_arg_match(env, btf, func_id, regs, false); ++ return btf_check_func_arg_match(env, btf, func_id, regs, true); + } + + /* Convert BTF of a function into bpf_reg_state if possible diff --git a/queue-5.15/bpf-fix-out-of-bounds-access-for-ringbuf-helpers.patch b/queue-5.15/bpf-fix-out-of-bounds-access-for-ringbuf-helpers.patch new file mode 100644 index 00000000000..3bbf97e84f8 --- /dev/null +++ b/queue-5.15/bpf-fix-out-of-bounds-access-for-ringbuf-helpers.patch @@ -0,0 +1,57 @@ +From 64620e0a1e712a778095bd35cbb277dc2259281f Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Tue, 11 Jan 2022 14:43:41 +0000 +Subject: bpf: Fix out of bounds access for ringbuf helpers + +From: Daniel Borkmann + +commit 64620e0a1e712a778095bd35cbb277dc2259281f upstream. + +Both bpf_ringbuf_submit() and bpf_ringbuf_discard() have ARG_PTR_TO_ALLOC_MEM +in their bpf_func_proto definition as their first argument. They both expect +the result from a prior bpf_ringbuf_reserve() call which has a return type of +RET_PTR_TO_ALLOC_MEM_OR_NULL. + +Meaning, after a NULL check in the code, the verifier will promote the register +type in the non-NULL branch to a PTR_TO_MEM and in the NULL branch to a known +zero scalar. Generally, pointer arithmetic on PTR_TO_MEM is allowed, so the +latter could have an offset. + +The ARG_PTR_TO_ALLOC_MEM expects a PTR_TO_MEM register type. However, the non- +zero result from bpf_ringbuf_reserve() must be fed into either bpf_ringbuf_submit() +or bpf_ringbuf_discard() but with the original offset given it will then read +out the struct bpf_ringbuf_hdr mapping. + +The verifier missed to enforce a zero offset, so that out of bounds access +can be triggered which could be used to escalate privileges if unprivileged +BPF was enabled (disabled by default in kernel). + +Fixes: 457f44363a88 ("bpf: Implement BPF ring buffer and verifier support for it") +Reported-by: (SecCoder Security Lab) +Signed-off-by: Daniel Borkmann +Acked-by: John Fastabend +Acked-by: Alexei Starovoitov +Signed-off-by: Edward Liaw +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/verifier.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -5340,9 +5340,15 @@ static int check_func_arg(struct bpf_ver + case PTR_TO_BUF: + case PTR_TO_BUF | MEM_RDONLY: + case PTR_TO_STACK: ++ /* Some of the argument types nevertheless require a ++ * zero register offset. ++ */ ++ if (arg_type == ARG_PTR_TO_ALLOC_MEM) ++ goto force_off_check; + break; + /* All the rest must be rejected: */ + default: ++force_off_check: + err = __check_ptr_off_reg(env, reg, regno, + type == PTR_TO_BTF_ID); + if (err < 0) diff --git a/queue-5.15/bpf-fix-ringbuf-memory-type-confusion-when-passing-to-helpers.patch b/queue-5.15/bpf-fix-ringbuf-memory-type-confusion-when-passing-to-helpers.patch new file mode 100644 index 00000000000..477e9242731 --- /dev/null +++ b/queue-5.15/bpf-fix-ringbuf-memory-type-confusion-when-passing-to-helpers.patch @@ -0,0 +1,103 @@ +From a672b2e36a648afb04ad3bda93b6bda947a479a5 Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Thu, 13 Jan 2022 11:11:30 +0000 +Subject: bpf: Fix ringbuf memory type confusion when passing to helpers + +From: Daniel Borkmann + +commit a672b2e36a648afb04ad3bda93b6bda947a479a5 upstream. + +The bpf_ringbuf_submit() and bpf_ringbuf_discard() have ARG_PTR_TO_ALLOC_MEM +in their bpf_func_proto definition as their first argument, and thus both expect +the result from a prior bpf_ringbuf_reserve() call which has a return type of +RET_PTR_TO_ALLOC_MEM_OR_NULL. + +While the non-NULL memory from bpf_ringbuf_reserve() can be passed to other +helpers, the two sinks (bpf_ringbuf_submit(), bpf_ringbuf_discard()) right now +only enforce a register type of PTR_TO_MEM. + +This can lead to potential type confusion since it would allow other PTR_TO_MEM +memory to be passed into the two sinks which did not come from bpf_ringbuf_reserve(). + +Add a new MEM_ALLOC composable type attribute for PTR_TO_MEM, and enforce that: + + - bpf_ringbuf_reserve() returns NULL or PTR_TO_MEM | MEM_ALLOC + - bpf_ringbuf_submit() and bpf_ringbuf_discard() only take PTR_TO_MEM | MEM_ALLOC + but not plain PTR_TO_MEM arguments via ARG_PTR_TO_ALLOC_MEM + - however, other helpers might treat PTR_TO_MEM | MEM_ALLOC as plain PTR_TO_MEM + to populate the memory area when they use ARG_PTR_TO_{UNINIT_,}MEM in their + func proto description + +Fixes: 457f44363a88 ("bpf: Implement BPF ring buffer and verifier support for it") +Reported-by: Alexei Starovoitov +Signed-off-by: Daniel Borkmann +Acked-by: John Fastabend +Acked-by: Alexei Starovoitov +Signed-off-by: Edward Liaw +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 9 +++++++-- + kernel/bpf/verifier.c | 6 +++++- + 2 files changed, 12 insertions(+), 3 deletions(-) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -321,7 +321,12 @@ enum bpf_type_flag { + */ + MEM_RDONLY = BIT(1 + BPF_BASE_TYPE_BITS), + +- __BPF_TYPE_LAST_FLAG = MEM_RDONLY, ++ /* MEM was "allocated" from a different helper, and cannot be mixed ++ * with regular non-MEM_ALLOC'ed MEM types. ++ */ ++ MEM_ALLOC = BIT(2 + BPF_BASE_TYPE_BITS), ++ ++ __BPF_TYPE_LAST_FLAG = MEM_ALLOC, + }; + + /* Max number of base types. */ +@@ -405,7 +410,7 @@ enum bpf_return_type { + RET_PTR_TO_SOCKET_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_SOCKET, + RET_PTR_TO_TCP_SOCK_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_TCP_SOCK, + RET_PTR_TO_SOCK_COMMON_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_SOCK_COMMON, +- RET_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_ALLOC_MEM, ++ RET_PTR_TO_ALLOC_MEM_OR_NULL = PTR_MAYBE_NULL | MEM_ALLOC | RET_PTR_TO_ALLOC_MEM, + RET_PTR_TO_BTF_ID_OR_NULL = PTR_MAYBE_NULL | RET_PTR_TO_BTF_ID, + + /* This must be the last entry. Its purpose is to ensure the enum is +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -573,6 +573,8 @@ static const char *reg_type_str(struct b + + if (type & MEM_RDONLY) + strncpy(prefix, "rdonly_", 16); ++ if (type & MEM_ALLOC) ++ strncpy(prefix, "alloc_", 16); + + snprintf(env->type_str_buf, TYPE_STR_BUF_LEN, "%s%s%s", + prefix, str[base_type(type)], postfix); +@@ -5157,6 +5159,7 @@ static const struct bpf_reg_types mem_ty + PTR_TO_MAP_KEY, + PTR_TO_MAP_VALUE, + PTR_TO_MEM, ++ PTR_TO_MEM | MEM_ALLOC, + PTR_TO_BUF, + }, + }; +@@ -5174,7 +5177,7 @@ static const struct bpf_reg_types int_pt + static const struct bpf_reg_types fullsock_types = { .types = { PTR_TO_SOCKET } }; + static const struct bpf_reg_types scalar_types = { .types = { SCALAR_VALUE } }; + static const struct bpf_reg_types context_types = { .types = { PTR_TO_CTX } }; +-static const struct bpf_reg_types alloc_mem_types = { .types = { PTR_TO_MEM } }; ++static const struct bpf_reg_types alloc_mem_types = { .types = { PTR_TO_MEM | MEM_ALLOC } }; + static const struct bpf_reg_types const_map_ptr_types = { .types = { CONST_PTR_TO_MAP } }; + static const struct bpf_reg_types btf_ptr_types = { .types = { PTR_TO_BTF_ID } }; + static const struct bpf_reg_types spin_lock_types = { .types = { PTR_TO_MAP_VALUE } }; +@@ -5337,6 +5340,7 @@ static int check_func_arg(struct bpf_ver + case PTR_TO_MAP_VALUE: + case PTR_TO_MEM: + case PTR_TO_MEM | MEM_RDONLY: ++ case PTR_TO_MEM | MEM_ALLOC: + case PTR_TO_BUF: + case PTR_TO_BUF | MEM_RDONLY: + case PTR_TO_STACK: diff --git a/queue-5.15/bpf-generalize-check_ctx_reg-for-reuse-with-other-types.patch b/queue-5.15/bpf-generalize-check_ctx_reg-for-reuse-with-other-types.patch new file mode 100644 index 00000000000..c7f3dc416bd --- /dev/null +++ b/queue-5.15/bpf-generalize-check_ctx_reg-for-reuse-with-other-types.patch @@ -0,0 +1,110 @@ +From be80a1d3f9dbe5aee79a325964f7037fe2d92f30 Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Mon, 10 Jan 2022 14:05:49 +0000 +Subject: bpf: Generalize check_ctx_reg for reuse with other types + +From: Daniel Borkmann + +commit be80a1d3f9dbe5aee79a325964f7037fe2d92f30 upstream. + +Generalize the check_ctx_reg() helper function into a more generic named one +so that it can be reused for other register types as well to check whether +their offset is non-zero. No functional change. + +Signed-off-by: Daniel Borkmann +Acked-by: John Fastabend +Acked-by: Alexei Starovoitov +Signed-off-by: Edward Liaw +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf_verifier.h | 4 ++-- + kernel/bpf/btf.c | 2 +- + kernel/bpf/verifier.c | 21 +++++++++++---------- + 3 files changed, 14 insertions(+), 13 deletions(-) + +--- a/include/linux/bpf_verifier.h ++++ b/include/linux/bpf_verifier.h +@@ -541,8 +541,8 @@ bpf_prog_offload_replace_insn(struct bpf + void + bpf_prog_offload_remove_insns(struct bpf_verifier_env *env, u32 off, u32 cnt); + +-int check_ctx_reg(struct bpf_verifier_env *env, +- const struct bpf_reg_state *reg, int regno); ++int check_ptr_off_reg(struct bpf_verifier_env *env, ++ const struct bpf_reg_state *reg, int regno); + int check_mem_reg(struct bpf_verifier_env *env, struct bpf_reg_state *reg, + u32 regno, u32 mem_size); + +--- a/kernel/bpf/btf.c ++++ b/kernel/bpf/btf.c +@@ -5558,7 +5558,7 @@ static int btf_check_func_arg_match(stru + i, btf_type_str(t)); + return -EINVAL; + } +- if (check_ctx_reg(env, reg, regno)) ++ if (check_ptr_off_reg(env, reg, regno)) + return -EINVAL; + } else if (is_kfunc && (reg->type == PTR_TO_BTF_ID || + (reg2btf_ids[base_type(reg->type)] && !type_flag(reg->type)))) { +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -3980,16 +3980,16 @@ static int get_callee_stack_depth(struct + } + #endif + +-int check_ctx_reg(struct bpf_verifier_env *env, +- const struct bpf_reg_state *reg, int regno) ++int check_ptr_off_reg(struct bpf_verifier_env *env, ++ const struct bpf_reg_state *reg, int regno) + { +- /* Access to ctx or passing it to a helper is only allowed in +- * its original, unmodified form. ++ /* Access to this pointer-typed register or passing it to a helper ++ * is only allowed in its original, unmodified form. + */ + + if (reg->off) { +- verbose(env, "dereference of modified ctx ptr R%d off=%d disallowed\n", +- regno, reg->off); ++ verbose(env, "dereference of modified %s ptr R%d off=%d disallowed\n", ++ reg_type_str(env, reg->type), regno, reg->off); + return -EACCES; + } + +@@ -3997,7 +3997,8 @@ int check_ctx_reg(struct bpf_verifier_en + char tn_buf[48]; + + tnum_strn(tn_buf, sizeof(tn_buf), reg->var_off); +- verbose(env, "variable ctx access var_off=%s disallowed\n", tn_buf); ++ verbose(env, "variable %s access var_off=%s disallowed\n", ++ reg_type_str(env, reg->type), tn_buf); + return -EACCES; + } + +@@ -4447,7 +4448,7 @@ static int check_mem_access(struct bpf_v + return -EACCES; + } + +- err = check_ctx_reg(env, reg, regno); ++ err = check_ptr_off_reg(env, reg, regno); + if (err < 0) + return err; + +@@ -5327,7 +5328,7 @@ static int check_func_arg(struct bpf_ver + return err; + + if (type == PTR_TO_CTX) { +- err = check_ctx_reg(env, reg, regno); ++ err = check_ptr_off_reg(env, reg, regno); + if (err < 0) + return err; + } +@@ -9561,7 +9562,7 @@ static int check_ld_abs(struct bpf_verif + return err; + } + +- err = check_ctx_reg(env, ®s[ctx_reg], ctx_reg); ++ err = check_ptr_off_reg(env, ®s[ctx_reg], ctx_reg); + if (err < 0) + return err; + diff --git a/queue-5.15/bpf-generally-fix-helper-register-offset-check.patch b/queue-5.15/bpf-generally-fix-helper-register-offset-check.patch new file mode 100644 index 00000000000..aa82c5d94fb --- /dev/null +++ b/queue-5.15/bpf-generally-fix-helper-register-offset-check.patch @@ -0,0 +1,110 @@ +From 6788ab23508bddb0a9d88e104284922cb2c22b77 Mon Sep 17 00:00:00 2001 +From: Daniel Borkmann +Date: Mon, 10 Jan 2022 14:40:40 +0000 +Subject: bpf: Generally fix helper register offset check + +From: Daniel Borkmann + +commit 6788ab23508bddb0a9d88e104284922cb2c22b77 upstream. + +Right now the assertion on check_ptr_off_reg() is only enforced for register +types PTR_TO_CTX (and open coded also for PTR_TO_BTF_ID), however, this is +insufficient since many other PTR_TO_* register types such as PTR_TO_FUNC do +not handle/expect register offsets when passed to helper functions. + +Given this can slip-through easily when adding new types, make this an explicit +allow-list and reject all other current and future types by default if this is +encountered. + +Also, extend check_ptr_off_reg() to handle PTR_TO_BTF_ID as well instead of +duplicating it. For PTR_TO_BTF_ID, reg->off is used for BTF to match expected +BTF ids if struct offset is used. This part still needs to be allowed, but the +dynamic off from the tnum must be rejected. + +Fixes: 69c087ba6225 ("bpf: Add bpf_for_each_map_elem() helper") +Fixes: eaa6bcb71ef6 ("bpf: Introduce bpf_per_cpu_ptr()") +Signed-off-by: Daniel Borkmann +Acked-by: John Fastabend +Acked-by: Alexei Starovoitov +Signed-off-by: Edward Liaw +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/verifier.c | 39 ++++++++++++++++++++++++++++----------- + 1 file changed, 28 insertions(+), 11 deletions(-) + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -3980,14 +3980,15 @@ static int get_callee_stack_depth(struct + } + #endif + +-int check_ptr_off_reg(struct bpf_verifier_env *env, +- const struct bpf_reg_state *reg, int regno) ++static int __check_ptr_off_reg(struct bpf_verifier_env *env, ++ const struct bpf_reg_state *reg, int regno, ++ bool fixed_off_ok) + { + /* Access to this pointer-typed register or passing it to a helper + * is only allowed in its original, unmodified form. + */ + +- if (reg->off) { ++ if (!fixed_off_ok && reg->off) { + verbose(env, "dereference of modified %s ptr R%d off=%d disallowed\n", + reg_type_str(env, reg->type), regno, reg->off); + return -EACCES; +@@ -4005,6 +4006,12 @@ int check_ptr_off_reg(struct bpf_verifie + return 0; + } + ++int check_ptr_off_reg(struct bpf_verifier_env *env, ++ const struct bpf_reg_state *reg, int regno) ++{ ++ return __check_ptr_off_reg(env, reg, regno, false); ++} ++ + static int __check_buffer_access(struct bpf_verifier_env *env, + const char *buf_info, + const struct bpf_reg_state *reg, +@@ -5267,12 +5274,6 @@ found: + kernel_type_name(btf_vmlinux, *arg_btf_id)); + return -EACCES; + } +- +- if (!tnum_is_const(reg->var_off) || reg->var_off.value) { +- verbose(env, "R%d is a pointer to in-kernel struct with non-zero offset\n", +- regno); +- return -EACCES; +- } + } + + return 0; +@@ -5327,10 +5328,26 @@ static int check_func_arg(struct bpf_ver + if (err) + return err; + +- if (type == PTR_TO_CTX) { +- err = check_ptr_off_reg(env, reg, regno); ++ switch ((u32)type) { ++ case SCALAR_VALUE: ++ /* Pointer types where reg offset is explicitly allowed: */ ++ case PTR_TO_PACKET: ++ case PTR_TO_PACKET_META: ++ case PTR_TO_MAP_KEY: ++ case PTR_TO_MAP_VALUE: ++ case PTR_TO_MEM: ++ case PTR_TO_MEM | MEM_RDONLY: ++ case PTR_TO_BUF: ++ case PTR_TO_BUF | MEM_RDONLY: ++ case PTR_TO_STACK: ++ break; ++ /* All the rest must be rejected: */ ++ default: ++ err = __check_ptr_off_reg(env, reg, regno, ++ type == PTR_TO_BTF_ID); + if (err < 0) + return err; ++ break; + } + + skip_type_check: diff --git a/queue-5.15/series b/queue-5.15/series index 49327176cc1..4a02333228e 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -4,3 +4,8 @@ ksmbd-do-not-set-smb2_global_cap_encryption-for-smb-.patch btrfs-record-delayed-inode-root-in-transaction.patch sunrpc-fix-rpcgss_context-trace-event-acceptor-field.patch selftests-ftrace-limit-length-in-subsystem-enable-tests.patch +bpf-extend-kfunc-with-ptr_to_ctx-ptr_to_mem-argument-support.patch +bpf-generalize-check_ctx_reg-for-reuse-with-other-types.patch +bpf-generally-fix-helper-register-offset-check.patch +bpf-fix-out-of-bounds-access-for-ringbuf-helpers.patch +bpf-fix-ringbuf-memory-type-confusion-when-passing-to-helpers.patch -- 2.39.5