]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
5.15-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 11 Jul 2022 14:43:07 +0000 (16:43 +0200)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 11 Jul 2022 14:43:07 +0000 (16:43 +0200)
added patches:
bpf-stop-caching-subprog-index-in-the-bpf_pseudo_func-insn.patch

queue-5.15/bpf-stop-caching-subprog-index-in-the-bpf_pseudo_func-insn.patch [new file with mode: 0644]
queue-5.15/series

diff --git a/queue-5.15/bpf-stop-caching-subprog-index-in-the-bpf_pseudo_func-insn.patch b/queue-5.15/bpf-stop-caching-subprog-index-in-the-bpf_pseudo_func-insn.patch
new file mode 100644 (file)
index 0000000..32963c2
--- /dev/null
@@ -0,0 +1,184 @@
+From 3990ed4c426652fcd469f8c9dc08156294b36c28 Mon Sep 17 00:00:00 2001
+From: Martin KaFai Lau <kafai@fb.com>
+Date: Fri, 5 Nov 2021 18:40:14 -0700
+Subject: bpf: Stop caching subprog index in the bpf_pseudo_func insn
+
+From: Martin KaFai Lau <kafai@fb.com>
+
+commit 3990ed4c426652fcd469f8c9dc08156294b36c28 upstream.
+
+This patch is to fix an out-of-bound access issue when jit-ing the
+bpf_pseudo_func insn (i.e. ld_imm64 with src_reg == BPF_PSEUDO_FUNC)
+
+In jit_subprog(), it currently reuses the subprog index cached in
+insn[1].imm.  This subprog index is an index into a few array related
+to subprogs.  For example, in jit_subprog(), it is an index to the newly
+allocated 'struct bpf_prog **func' array.
+
+The subprog index was cached in insn[1].imm after add_subprog().  However,
+this could become outdated (and too big in this case) if some subprogs
+are completely removed during dead code elimination (in
+adjust_subprog_starts_after_remove).  The cached index in insn[1].imm
+is not updated accordingly and causing out-of-bound issue in the later
+jit_subprog().
+
+Unlike bpf_pseudo_'func' insn, the current bpf_pseudo_'call' insn
+is handling the DCE properly by calling find_subprog(insn->imm) to
+figure out the index instead of caching the subprog index.
+The existing bpf_adj_branches() will adjust the insn->imm
+whenever insn is added or removed.
+
+Instead of having two ways handling subprog index,
+this patch is to make bpf_pseudo_func works more like
+bpf_pseudo_call.
+
+First change is to stop caching the subprog index result
+in insn[1].imm after add_subprog().  The verification
+process will use find_subprog(insn->imm) to figure
+out the subprog index.
+
+Second change is in bpf_adj_branches() and have it to
+adjust the insn->imm for the bpf_pseudo_func insn also
+whenever insn is added or removed.
+
+Third change is in jit_subprog().  Like the bpf_pseudo_call handling,
+bpf_pseudo_func temporarily stores the find_subprog() result
+in insn->off.  It is fine because the prog's insn has been finalized
+at this point.  insn->off will be reset back to 0 later to avoid
+confusing the userspace prog dump tool.
+
+Fixes: 69c087ba6225 ("bpf: Add bpf_for_each_map_elem() helper")
+Signed-off-by: Martin KaFai Lau <kafai@fb.com>
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Link: https://lore.kernel.org/bpf/20211106014014.651018-1-kafai@fb.com
+Cc: Jon Hunter <jonathanh@nvidia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ include/linux/bpf.h   |    6 ++++++
+ kernel/bpf/core.c     |    7 +++++++
+ kernel/bpf/verifier.c |   37 ++++++++++++++-----------------------
+ 3 files changed, 27 insertions(+), 23 deletions(-)
+
+--- a/include/linux/bpf.h
++++ b/include/linux/bpf.h
+@@ -533,6 +533,12 @@ bpf_ctx_record_field_size(struct bpf_ins
+       aux->ctx_field_size = size;
+ }
++static inline bool bpf_pseudo_func(const struct bpf_insn *insn)
++{
++      return insn->code == (BPF_LD | BPF_IMM | BPF_DW) &&
++             insn->src_reg == BPF_PSEUDO_FUNC;
++}
++
+ struct bpf_prog_ops {
+       int (*test_run)(struct bpf_prog *prog, const union bpf_attr *kattr,
+                       union bpf_attr __user *uattr);
+--- a/kernel/bpf/core.c
++++ b/kernel/bpf/core.c
+@@ -389,6 +389,13 @@ static int bpf_adj_branches(struct bpf_p
+                       i = end_new;
+                       insn = prog->insnsi + end_old;
+               }
++              if (bpf_pseudo_func(insn)) {
++                      ret = bpf_adj_delta_to_imm(insn, pos, end_old,
++                                                 end_new, i, probe_pass);
++                      if (ret)
++                              return ret;
++                      continue;
++              }
+               code = insn->code;
+               if ((BPF_CLASS(code) != BPF_JMP &&
+                    BPF_CLASS(code) != BPF_JMP32) ||
+--- a/kernel/bpf/verifier.c
++++ b/kernel/bpf/verifier.c
+@@ -240,12 +240,6 @@ static bool bpf_pseudo_kfunc_call(const
+              insn->src_reg == BPF_PSEUDO_KFUNC_CALL;
+ }
+-static bool bpf_pseudo_func(const struct bpf_insn *insn)
+-{
+-      return insn->code == (BPF_LD | BPF_IMM | BPF_DW) &&
+-             insn->src_reg == BPF_PSEUDO_FUNC;
+-}
+-
+ struct bpf_call_arg_meta {
+       struct bpf_map *map_ptr;
+       bool raw_mode;
+@@ -1788,16 +1782,10 @@ static int add_subprog_and_kfunc(struct
+                       return -EPERM;
+               }
+-              if (bpf_pseudo_func(insn)) {
++              if (bpf_pseudo_func(insn) || bpf_pseudo_call(insn))
+                       ret = add_subprog(env, i + insn->imm + 1);
+-                      if (ret >= 0)
+-                              /* remember subprog */
+-                              insn[1].imm = ret;
+-              } else if (bpf_pseudo_call(insn)) {
+-                      ret = add_subprog(env, i + insn->imm + 1);
+-              } else {
++              else
+                       ret = add_kfunc_call(env, insn->imm);
+-              }
+               if (ret < 0)
+                       return ret;
+@@ -9222,7 +9210,8 @@ static int check_ld_imm(struct bpf_verif
+       if (insn->src_reg == BPF_PSEUDO_FUNC) {
+               struct bpf_prog_aux *aux = env->prog->aux;
+-              u32 subprogno = insn[1].imm;
++              u32 subprogno = find_subprog(env,
++                                           env->insn_idx + insn->imm + 1);
+               if (!aux->func_info) {
+                       verbose(env, "missing btf func_info\n");
+@@ -12392,14 +12381,9 @@ static int jit_subprogs(struct bpf_verif
+               return 0;
+       for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) {
+-              if (bpf_pseudo_func(insn)) {
+-                      env->insn_aux_data[i].call_imm = insn->imm;
+-                      /* subprog is encoded in insn[1].imm */
++              if (!bpf_pseudo_func(insn) && !bpf_pseudo_call(insn))
+                       continue;
+-              }
+-              if (!bpf_pseudo_call(insn))
+-                      continue;
+               /* Upon error here we cannot fall back to interpreter but
+                * need a hard reject of the program. Thus -EFAULT is
+                * propagated in any case.
+@@ -12420,6 +12404,12 @@ static int jit_subprogs(struct bpf_verif
+               env->insn_aux_data[i].call_imm = insn->imm;
+               /* point imm to __bpf_call_base+1 from JITs point of view */
+               insn->imm = 1;
++              if (bpf_pseudo_func(insn))
++                      /* jit (e.g. x86_64) may emit fewer instructions
++                       * if it learns a u32 imm is the same as a u64 imm.
++                       * Force a non zero here.
++                       */
++                      insn[1].imm = 1;
+       }
+       err = bpf_prog_alloc_jited_linfo(prog);
+@@ -12503,7 +12493,7 @@ static int jit_subprogs(struct bpf_verif
+               insn = func[i]->insnsi;
+               for (j = 0; j < func[i]->len; j++, insn++) {
+                       if (bpf_pseudo_func(insn)) {
+-                              subprog = insn[1].imm;
++                              subprog = insn->off;
+                               insn[0].imm = (u32)(long)func[subprog]->bpf_func;
+                               insn[1].imm = ((u64)(long)func[subprog]->bpf_func) >> 32;
+                               continue;
+@@ -12555,7 +12545,8 @@ static int jit_subprogs(struct bpf_verif
+       for (i = 0, insn = prog->insnsi; i < prog->len; i++, insn++) {
+               if (bpf_pseudo_func(insn)) {
+                       insn[0].imm = env->insn_aux_data[i].call_imm;
+-                      insn[1].imm = find_subprog(env, i + insn[0].imm + 1);
++                      insn[1].imm = insn->off;
++                      insn->off = 0;
+                       continue;
+               }
+               if (!bpf_pseudo_call(insn))
index d339775c5624c97db07d5abd51e189b7addc6a45..78f5e27c0b396d7595dbb00ed64369d0894dad56 100644 (file)
@@ -43,6 +43,7 @@ platform-x86-wmi-fix-driver-notify-vs-probe-race.patch
 mt76-mt7921-get-rid-of-mt7921_mac_set_beacon_filter.patch
 mt76-mt7921-introduce-mt7921_mcu_set_beacon_filter-u.patch
 mt76-mt7921-fix-a-possible-race-enabling-disabling-r.patch
+bpf-stop-caching-subprog-index-in-the-bpf_pseudo_func-insn.patch
 bpf-arm64-use-emit_addr_mov_i64-for-bpf_pseudo_func.patch
 riscv-defconfig-enable-drm_nouveau.patch
 risc-v-defconfigs-set-config_fb-y-for-fb-console.patch