]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.15-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 19 Apr 2024 11:08:52 +0000 (13:08 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 19 Apr 2024 11:08:52 +0000 (13:08 +0200)
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

queue-5.15/bpf-extend-kfunc-with-ptr_to_ctx-ptr_to_mem-argument-support.patch [new file with mode: 0644]
queue-5.15/bpf-fix-out-of-bounds-access-for-ringbuf-helpers.patch [new file with mode: 0644]
queue-5.15/bpf-fix-ringbuf-memory-type-confusion-when-passing-to-helpers.patch [new file with mode: 0644]
queue-5.15/bpf-generalize-check_ctx_reg-for-reuse-with-other-types.patch [new file with mode: 0644]
queue-5.15/bpf-generally-fix-helper-register-offset-check.patch [new file with mode: 0644]
queue-5.15/series

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 (file)
index 0000000..4f4f135
--- /dev/null
@@ -0,0 +1,190 @@
+From 3363bd0cfbb80dfcd25003cd3815b0ad8b68d0ff Mon Sep 17 00:00:00 2001
+From: Kumar Kartikeya Dwivedi <memxor@gmail.com>
+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 <memxor@gmail.com>
+
+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 <memxor@gmail.com>
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Link: https://lore.kernel.org/bpf/20211217015031.1278167-4-memxor@gmail.com
+Signed-off-by: Edward Liaw <edliaw@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..3bbf97e
--- /dev/null
@@ -0,0 +1,57 @@
+From 64620e0a1e712a778095bd35cbb277dc2259281f Mon Sep 17 00:00:00 2001
+From: Daniel Borkmann <daniel@iogearbox.net>
+Date: Tue, 11 Jan 2022 14:43:41 +0000
+Subject: bpf: Fix out of bounds access for ringbuf helpers
+
+From: Daniel Borkmann <daniel@iogearbox.net>
+
+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: <tr3e.wang@gmail.com> (SecCoder Security Lab)
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Acked-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Edward Liaw <edliaw@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..477e924
--- /dev/null
@@ -0,0 +1,103 @@
+From a672b2e36a648afb04ad3bda93b6bda947a479a5 Mon Sep 17 00:00:00 2001
+From: Daniel Borkmann <daniel@iogearbox.net>
+Date: Thu, 13 Jan 2022 11:11:30 +0000
+Subject: bpf: Fix ringbuf memory type confusion when passing to helpers
+
+From: Daniel Borkmann <daniel@iogearbox.net>
+
+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 <ast@kernel.org>
+Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Acked-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Edward Liaw <edliaw@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 (file)
index 0000000..c7f3dc4
--- /dev/null
@@ -0,0 +1,110 @@
+From be80a1d3f9dbe5aee79a325964f7037fe2d92f30 Mon Sep 17 00:00:00 2001
+From: Daniel Borkmann <daniel@iogearbox.net>
+Date: Mon, 10 Jan 2022 14:05:49 +0000
+Subject: bpf: Generalize check_ctx_reg for reuse with other types
+
+From: Daniel Borkmann <daniel@iogearbox.net>
+
+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 <daniel@iogearbox.net>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Acked-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Edward Liaw <edliaw@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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, &regs[ctx_reg], ctx_reg);
++      err = check_ptr_off_reg(env, &regs[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 (file)
index 0000000..aa82c5d
--- /dev/null
@@ -0,0 +1,110 @@
+From 6788ab23508bddb0a9d88e104284922cb2c22b77 Mon Sep 17 00:00:00 2001
+From: Daniel Borkmann <daniel@iogearbox.net>
+Date: Mon, 10 Jan 2022 14:40:40 +0000
+Subject: bpf: Generally fix helper register offset check
+
+From: Daniel Borkmann <daniel@iogearbox.net>
+
+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 <daniel@iogearbox.net>
+Acked-by: John Fastabend <john.fastabend@gmail.com>
+Acked-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Edward Liaw <edliaw@google.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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:
index 49327176cc13181224195411fbb530e8770115ef..4a02333228e60c8e28c4bfedac6975a63390a2a0 100644 (file)
@@ -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