From d95cf2508d7f1202e238b54b1fd445faf02902ce Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 23 Apr 2025 13:22:53 +0200 Subject: [PATCH] 6.12-stable patches added patches: bpf-add-find_containing_subprog-utility-function.patch bpf-check-changes_pkt_data-property-for-extension-programs.patch bpf-fix-null-dereference-when-computing-changes_pkt_data-of-prog-w-o-subprogs.patch bpf-track-changes_pkt_data-property-for-global-functions.patch selftests-bpf-extend-changes_pkt_data-with-cases-w-o-subprograms.patch selftests-bpf-freplace-tests-for-tracking-of-changes_packet_data.patch selftests-bpf-test-for-changing-packet-data-from-global-functions.patch selftests-bpf-validate-that-tail-call-invalidates-packet-pointers.patch wifi-ath12k-fix-invalid-entry-fetch-in-ath12k_dp_mon_srng_process.patch --- ..._containing_subprog-utility-function.patch | 67 +++++++ ...data-property-for-extension-programs.patch | 128 ++++++++++++ ...hanges_pkt_data-of-prog-w-o-subprogs.patch | 73 +++++++ ...t_data-property-for-global-functions.patch | 161 +++++++++++++++ ..._pkt_data-with-cases-w-o-subprograms.patch | 185 ++++++++++++++++++ ...-for-tracking-of-changes_packet_data.patch | 161 +++++++++++++++ ...ng-packet-data-from-global-functions.patch | 62 ++++++ ...ail-call-invalidates-packet-pointers.patch | 67 +++++++ queue-6.12/series | 9 + ...-fetch-in-ath12k_dp_mon_srng_process.patch | 45 +++++ 10 files changed, 958 insertions(+) create mode 100644 queue-6.12/bpf-add-find_containing_subprog-utility-function.patch create mode 100644 queue-6.12/bpf-check-changes_pkt_data-property-for-extension-programs.patch create mode 100644 queue-6.12/bpf-fix-null-dereference-when-computing-changes_pkt_data-of-prog-w-o-subprogs.patch create mode 100644 queue-6.12/bpf-track-changes_pkt_data-property-for-global-functions.patch create mode 100644 queue-6.12/selftests-bpf-extend-changes_pkt_data-with-cases-w-o-subprograms.patch create mode 100644 queue-6.12/selftests-bpf-freplace-tests-for-tracking-of-changes_packet_data.patch create mode 100644 queue-6.12/selftests-bpf-test-for-changing-packet-data-from-global-functions.patch create mode 100644 queue-6.12/selftests-bpf-validate-that-tail-call-invalidates-packet-pointers.patch create mode 100644 queue-6.12/wifi-ath12k-fix-invalid-entry-fetch-in-ath12k_dp_mon_srng_process.patch diff --git a/queue-6.12/bpf-add-find_containing_subprog-utility-function.patch b/queue-6.12/bpf-add-find_containing_subprog-utility-function.patch new file mode 100644 index 0000000000..982f9df110 --- /dev/null +++ b/queue-6.12/bpf-add-find_containing_subprog-utility-function.patch @@ -0,0 +1,67 @@ +From stable+bounces-135227-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:07 2025 +From: Shung-Hsi Yu +Date: Wed, 23 Apr 2025 13:53:22 +0800 +Subject: bpf: add find_containing_subprog() utility function +To: stable@vger.kernel.org +Cc: Alexei Starovoitov , Dan Carpenter , Eduard Zingerman , Nick Zavaritsky , Shung-Hsi Yu +Message-ID: <20250423055334.52791-2-shung-hsi.yu@suse.com> + +From: Eduard Zingerman + +commit 27e88bc4df1d80888fe1aaca786a7cc6e69587e2 upstream. + +Add a utility function, looking for a subprogram containing a given +instruction index, rewrite find_subprog() to use this function. + +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20241210041100.1898468-2-eddyz87@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/verifier.c | 28 ++++++++++++++++++++++++---- + 1 file changed, 24 insertions(+), 4 deletions(-) + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -2528,16 +2528,36 @@ static int cmp_subprogs(const void *a, c + ((struct bpf_subprog_info *)b)->start; + } + ++/* Find subprogram that contains instruction at 'off' */ ++static struct bpf_subprog_info *find_containing_subprog(struct bpf_verifier_env *env, int off) ++{ ++ struct bpf_subprog_info *vals = env->subprog_info; ++ int l, r, m; ++ ++ if (off >= env->prog->len || off < 0 || env->subprog_cnt == 0) ++ return NULL; ++ ++ l = 0; ++ r = env->subprog_cnt - 1; ++ while (l < r) { ++ m = l + (r - l + 1) / 2; ++ if (vals[m].start <= off) ++ l = m; ++ else ++ r = m - 1; ++ } ++ return &vals[l]; ++} ++ ++/* Find subprogram that starts exactly at 'off' */ + static int find_subprog(struct bpf_verifier_env *env, int off) + { + struct bpf_subprog_info *p; + +- p = bsearch(&off, env->subprog_info, env->subprog_cnt, +- sizeof(env->subprog_info[0]), cmp_subprogs); +- if (!p) ++ p = find_containing_subprog(env, off); ++ if (!p || p->start != off) + return -ENOENT; + return p - env->subprog_info; +- + } + + static int add_subprog(struct bpf_verifier_env *env, int off) diff --git a/queue-6.12/bpf-check-changes_pkt_data-property-for-extension-programs.patch b/queue-6.12/bpf-check-changes_pkt_data-property-for-extension-programs.patch new file mode 100644 index 0000000000..bab2486563 --- /dev/null +++ b/queue-6.12/bpf-check-changes_pkt_data-property-for-extension-programs.patch @@ -0,0 +1,128 @@ +From stable+bounces-135230-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:25 2025 +From: Shung-Hsi Yu +Date: Wed, 23 Apr 2025 13:53:25 +0800 +Subject: bpf: check changes_pkt_data property for extension programs +To: stable@vger.kernel.org +Cc: Alexei Starovoitov , Dan Carpenter , Eduard Zingerman , Nick Zavaritsky , Shung-Hsi Yu , Alexei Starovoitov +Message-ID: <20250423055334.52791-5-shung-hsi.yu@suse.com> + +From: Eduard Zingerman + +commit 81f6d0530ba031b5f038a091619bf2ff29568852 upstream. + +When processing calls to global sub-programs, verifier decides whether +to invalidate all packet pointers in current state depending on the +changes_pkt_data property of the global sub-program. + +Because of this, an extension program replacing a global sub-program +must be compatible with changes_pkt_data property of the sub-program +being replaced. + +This commit: +- adds changes_pkt_data flag to struct bpf_prog_aux: + - this flag is set in check_cfg() for main sub-program; + - in jit_subprogs() for other sub-programs; +- modifies bpf_check_attach_btf_id() to check changes_pkt_data flag; +- moves call to check_attach_btf_id() after the call to check_cfg(), + because it needs changes_pkt_data flag to be set: + + bpf_check: + ... ... + - check_attach_btf_id resolve_pseudo_ldimm64 + resolve_pseudo_ldimm64 --> bpf_prog_is_offloaded + bpf_prog_is_offloaded check_cfg + check_cfg + check_attach_btf_id + ... ... + +The following fields are set by check_attach_btf_id(): +- env->ops +- prog->aux->attach_btf_trace +- prog->aux->attach_func_name +- prog->aux->attach_func_proto +- prog->aux->dst_trampoline +- prog->aux->mod +- prog->aux->saved_dst_attach_type +- prog->aux->saved_dst_prog_type +- prog->expected_attach_type + +Neither of these fields are used by resolve_pseudo_ldimm64() or +bpf_prog_offload_verifier_prep() (for netronome and netdevsim +drivers), so the reordering is safe. + +Suggested-by: Alexei Starovoitov +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20241210041100.1898468-6-eddyz87@gmail.com +Signed-off-by: Alexei Starovoitov +[ shung-hsi.yu: both jits_use_priv_stack and priv_stack_requested fields are +missing from context because "bpf: Support private stack for bpf progs" series +is not present.] +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf.h | 1 + + kernel/bpf/verifier.c | 16 ++++++++++++---- + 2 files changed, 13 insertions(+), 4 deletions(-) + +--- a/include/linux/bpf.h ++++ b/include/linux/bpf.h +@@ -1499,6 +1499,7 @@ struct bpf_prog_aux { + bool exception_cb; + bool exception_boundary; + bool is_extended; /* true if extended by freplace program */ ++ bool changes_pkt_data; + u64 prog_array_member_cnt; /* counts how many times as member of prog_array */ + struct mutex ext_mutex; /* mutex for is_extended and prog_array_member_cnt */ + struct bpf_arena *arena; +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -16650,6 +16650,7 @@ walk_cfg: + } + } + ret = 0; /* cfg looks good */ ++ env->prog->aux->changes_pkt_data = env->subprog_info[0].changes_pkt_data; + + err_free: + kvfree(insn_state); +@@ -20152,6 +20153,7 @@ static int jit_subprogs(struct bpf_verif + func[i]->aux->num_exentries = num_exentries; + func[i]->aux->tail_call_reachable = env->subprog_info[i].tail_call_reachable; + func[i]->aux->exception_cb = env->subprog_info[i].is_exception_cb; ++ func[i]->aux->changes_pkt_data = env->subprog_info[i].changes_pkt_data; + if (!i) + func[i]->aux->exception_boundary = env->seen_exception; + func[i] = bpf_int_jit_compile(func[i]); +@@ -22022,6 +22024,12 @@ int bpf_check_attach_target(struct bpf_v + "Extension programs should be JITed\n"); + return -EINVAL; + } ++ if (prog->aux->changes_pkt_data && ++ !aux->func[subprog]->aux->changes_pkt_data) { ++ bpf_log(log, ++ "Extension program changes packet data, while original does not\n"); ++ return -EINVAL; ++ } + } + if (!tgt_prog->jited) { + bpf_log(log, "Can attach to only JITed progs\n"); +@@ -22487,10 +22495,6 @@ int bpf_check(struct bpf_prog **prog, un + if (ret < 0) + goto skip_full_check; + +- ret = check_attach_btf_id(env); +- if (ret) +- goto skip_full_check; +- + ret = resolve_pseudo_ldimm64(env); + if (ret < 0) + goto skip_full_check; +@@ -22505,6 +22509,10 @@ int bpf_check(struct bpf_prog **prog, un + if (ret < 0) + goto skip_full_check; + ++ ret = check_attach_btf_id(env); ++ if (ret) ++ goto skip_full_check; ++ + ret = mark_fastcall_patterns(env); + if (ret < 0) + goto skip_full_check; diff --git a/queue-6.12/bpf-fix-null-dereference-when-computing-changes_pkt_data-of-prog-w-o-subprogs.patch b/queue-6.12/bpf-fix-null-dereference-when-computing-changes_pkt_data-of-prog-w-o-subprogs.patch new file mode 100644 index 0000000000..022318c4e1 --- /dev/null +++ b/queue-6.12/bpf-fix-null-dereference-when-computing-changes_pkt_data-of-prog-w-o-subprogs.patch @@ -0,0 +1,73 @@ +From stable+bounces-135233-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:38 2025 +From: Shung-Hsi Yu +Date: Wed, 23 Apr 2025 13:53:28 +0800 +Subject: bpf: fix null dereference when computing changes_pkt_data of prog w/o subprogs +To: stable@vger.kernel.org +Cc: Alexei Starovoitov , Dan Carpenter , Eduard Zingerman , Nick Zavaritsky , Shung-Hsi Yu , kernel test robot +Message-ID: <20250423055334.52791-8-shung-hsi.yu@suse.com> + +From: Eduard Zingerman + +commit ac6542ad92759cda383ad62b4e4cbfc28136abc1 upstream. + +bpf_prog_aux->func field might be NULL if program does not have +subprograms except for main sub-program. The fixed commit does +bpf_prog_aux->func access unconditionally, which might lead to null +pointer dereference. + +The bug could be triggered by replacing the following BPF program: + + SEC("tc") + int main_changes(struct __sk_buff *sk) + { + bpf_skb_pull_data(sk, 0); + return 0; + } + +With the following BPF program: + + SEC("freplace") + long changes_pkt_data(struct __sk_buff *sk) + { + return bpf_skb_pull_data(sk, 0); + } + +bpf_prog_aux instance itself represents the main sub-program, +use this property to fix the bug. + +Fixes: 81f6d0530ba0 ("bpf: check changes_pkt_data property for extension programs") +Reported-by: kernel test robot +Reported-by: Dan Carpenter +Closes: https://lore.kernel.org/r/202412111822.qGw6tOyB-lkp@intel.com/ +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20241212070711.427443-1-eddyz87@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Greg Kroah-Hartman +--- + kernel/bpf/verifier.c | 7 +++++-- + 1 file changed, 5 insertions(+), 2 deletions(-) + +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -21990,6 +21990,7 @@ int bpf_check_attach_target(struct bpf_v + } + if (tgt_prog) { + struct bpf_prog_aux *aux = tgt_prog->aux; ++ bool tgt_changes_pkt_data; + + if (bpf_prog_is_dev_bound(prog->aux) && + !bpf_prog_dev_bound_match(prog, tgt_prog)) { +@@ -22024,8 +22025,10 @@ int bpf_check_attach_target(struct bpf_v + "Extension programs should be JITed\n"); + return -EINVAL; + } +- if (prog->aux->changes_pkt_data && +- !aux->func[subprog]->aux->changes_pkt_data) { ++ tgt_changes_pkt_data = aux->func ++ ? aux->func[subprog]->aux->changes_pkt_data ++ : aux->changes_pkt_data; ++ if (prog->aux->changes_pkt_data && !tgt_changes_pkt_data) { + bpf_log(log, + "Extension program changes packet data, while original does not\n"); + return -EINVAL; diff --git a/queue-6.12/bpf-track-changes_pkt_data-property-for-global-functions.patch b/queue-6.12/bpf-track-changes_pkt_data-property-for-global-functions.patch new file mode 100644 index 0000000000..47a6e92108 --- /dev/null +++ b/queue-6.12/bpf-track-changes_pkt_data-property-for-global-functions.patch @@ -0,0 +1,161 @@ +From stable+bounces-135228-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:11 2025 +From: Shung-Hsi Yu +Date: Wed, 23 Apr 2025 13:53:23 +0800 +Subject: bpf: track changes_pkt_data property for global functions +To: stable@vger.kernel.org +Cc: Alexei Starovoitov , Dan Carpenter , Eduard Zingerman , Nick Zavaritsky , Shung-Hsi Yu +Message-ID: <20250423055334.52791-3-shung-hsi.yu@suse.com> + +From: Eduard Zingerman + +commit 51081a3f25c742da5a659d7fc6fd77ebfdd555be upstream. + +When processing calls to certain helpers, verifier invalidates all +packet pointers in a current state. For example, consider the +following program: + + __attribute__((__noinline__)) + long skb_pull_data(struct __sk_buff *sk, __u32 len) + { + return bpf_skb_pull_data(sk, len); + } + + SEC("tc") + int test_invalidate_checks(struct __sk_buff *sk) + { + int *p = (void *)(long)sk->data; + if ((void *)(p + 1) > (void *)(long)sk->data_end) return TCX_DROP; + skb_pull_data(sk, 0); + *p = 42; + return TCX_PASS; + } + +After a call to bpf_skb_pull_data() the pointer 'p' can't be used +safely. See function filter.c:bpf_helper_changes_pkt_data() for a list +of such helpers. + +At the moment verifier invalidates packet pointers when processing +helper function calls, and does not traverse global sub-programs when +processing calls to global sub-programs. This means that calls to +helpers done from global sub-programs do not invalidate pointers in +the caller state. E.g. the program above is unsafe, but is not +rejected by verifier. + +This commit fixes the omission by computing field +bpf_subprog_info->changes_pkt_data for each sub-program before main +verification pass. +changes_pkt_data should be set if: +- subprogram calls helper for which bpf_helper_changes_pkt_data + returns true; +- subprogram calls a global function, + for which bpf_subprog_info->changes_pkt_data should be set. + +The verifier.c:check_cfg() pass is modified to compute this +information. The commit relies on depth first instruction traversal +done by check_cfg() and absence of recursive function calls: +- check_cfg() would eventually visit every call to subprogram S in a + state when S is fully explored; +- when S is fully explored: + - every direct helper call within S is explored + (and thus changes_pkt_data is set if needed); + - every call to subprogram S1 called by S was visited with S1 fully + explored (and thus S inherits changes_pkt_data from S1). + +The downside of such approach is that dead code elimination is not +taken into account: if a helper call inside global function is dead +because of current configuration, verifier would conservatively assume +that the call occurs for the purpose of the changes_pkt_data +computation. + +Reported-by: Nick Zavaritsky +Closes: https://lore.kernel.org/bpf/0498CA22-5779-4767-9C0C-A9515CEA711F@gmail.com/ +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20241210041100.1898468-4-eddyz87@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Greg Kroah-Hartman +--- + include/linux/bpf_verifier.h | 1 + + kernel/bpf/verifier.c | 32 +++++++++++++++++++++++++++++++- + 2 files changed, 32 insertions(+), 1 deletion(-) + +--- a/include/linux/bpf_verifier.h ++++ b/include/linux/bpf_verifier.h +@@ -668,6 +668,7 @@ struct bpf_subprog_info { + bool args_cached: 1; + /* true if bpf_fastcall stack region is used by functions that can't be inlined */ + bool keep_fastcall_stack: 1; ++ bool changes_pkt_data: 1; + + u8 arg_cnt; + struct bpf_subprog_arg_info args[MAX_BPF_FUNC_REG_ARGS]; +--- a/kernel/bpf/verifier.c ++++ b/kernel/bpf/verifier.c +@@ -9831,6 +9831,8 @@ static int check_func_call(struct bpf_ve + + verbose(env, "Func#%d ('%s') is global and assumed valid.\n", + subprog, sub_name); ++ if (env->subprog_info[subprog].changes_pkt_data) ++ clear_all_pkt_pointers(env); + /* mark global subprog for verifying after main prog */ + subprog_aux(env, subprog)->called = true; + clear_caller_saved_regs(env, caller->regs); +@@ -16021,6 +16023,29 @@ enforce_retval: + return 0; + } + ++static void mark_subprog_changes_pkt_data(struct bpf_verifier_env *env, int off) ++{ ++ struct bpf_subprog_info *subprog; ++ ++ subprog = find_containing_subprog(env, off); ++ subprog->changes_pkt_data = true; ++} ++ ++/* 't' is an index of a call-site. ++ * 'w' is a callee entry point. ++ * Eventually this function would be called when env->cfg.insn_state[w] == EXPLORED. ++ * Rely on DFS traversal order and absence of recursive calls to guarantee that ++ * callee's change_pkt_data marks would be correct at that moment. ++ */ ++static void merge_callee_effects(struct bpf_verifier_env *env, int t, int w) ++{ ++ struct bpf_subprog_info *caller, *callee; ++ ++ caller = find_containing_subprog(env, t); ++ callee = find_containing_subprog(env, w); ++ caller->changes_pkt_data |= callee->changes_pkt_data; ++} ++ + /* non-recursive DFS pseudo code + * 1 procedure DFS-iterative(G,v): + * 2 label v as discovered +@@ -16154,6 +16179,7 @@ static int visit_func_call_insn(int t, s + bool visit_callee) + { + int ret, insn_sz; ++ int w; + + insn_sz = bpf_is_ldimm64(&insns[t]) ? 2 : 1; + ret = push_insn(t, t + insn_sz, FALLTHROUGH, env); +@@ -16165,8 +16191,10 @@ static int visit_func_call_insn(int t, s + mark_jmp_point(env, t + insn_sz); + + if (visit_callee) { ++ w = t + insns[t].imm + 1; + mark_prune_point(env, t); +- ret = push_insn(t, t + insns[t].imm + 1, BRANCH, env); ++ merge_callee_effects(env, t, w); ++ ret = push_insn(t, w, BRANCH, env); + } + return ret; + } +@@ -16486,6 +16514,8 @@ static int visit_insn(int t, struct bpf_ + mark_prune_point(env, t); + mark_jmp_point(env, t); + } ++ if (bpf_helper_call(insn) && bpf_helper_changes_pkt_data(insn->imm)) ++ mark_subprog_changes_pkt_data(env, t); + if (insn->src_reg == BPF_PSEUDO_KFUNC_CALL) { + struct bpf_kfunc_call_arg_meta meta; + diff --git a/queue-6.12/selftests-bpf-extend-changes_pkt_data-with-cases-w-o-subprograms.patch b/queue-6.12/selftests-bpf-extend-changes_pkt_data-with-cases-w-o-subprograms.patch new file mode 100644 index 0000000000..d4de519892 --- /dev/null +++ b/queue-6.12/selftests-bpf-extend-changes_pkt_data-with-cases-w-o-subprograms.patch @@ -0,0 +1,185 @@ +From stable+bounces-135234-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:44 2025 +From: Shung-Hsi Yu +Date: Wed, 23 Apr 2025 13:53:29 +0800 +Subject: selftests/bpf: extend changes_pkt_data with cases w/o subprograms +To: stable@vger.kernel.org +Cc: Alexei Starovoitov , Dan Carpenter , Eduard Zingerman , Nick Zavaritsky , Shung-Hsi Yu +Message-ID: <20250423055334.52791-9-shung-hsi.yu@suse.com> + +From: Eduard Zingerman + +commit 04789af756a4a43e72986185f66f148e65b32fed upstream. + +Extend changes_pkt_data tests with test cases freplacing the main +program that does not have subprograms. Try four combinations when +both main program and replacement do and do not change packet data. + +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20241212070711.427443-2-eddyz87@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/bpf/prog_tests/changes_pkt_data.c | 55 +++++++--- + tools/testing/selftests/bpf/progs/changes_pkt_data.c | 27 +++- + tools/testing/selftests/bpf/progs/changes_pkt_data_freplace.c | 6 - + 3 files changed, 66 insertions(+), 22 deletions(-) + +--- a/tools/testing/selftests/bpf/prog_tests/changes_pkt_data.c ++++ b/tools/testing/selftests/bpf/prog_tests/changes_pkt_data.c +@@ -10,10 +10,14 @@ static void print_verifier_log(const cha + fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log); + } + +-static void test_aux(const char *main_prog_name, const char *freplace_prog_name, bool expect_load) ++static void test_aux(const char *main_prog_name, ++ const char *to_be_replaced, ++ const char *replacement, ++ bool expect_load) + { + struct changes_pkt_data_freplace *freplace = NULL; + struct bpf_program *freplace_prog = NULL; ++ struct bpf_program *main_prog = NULL; + LIBBPF_OPTS(bpf_object_open_opts, opts); + struct changes_pkt_data *main = NULL; + char log[16*1024]; +@@ -26,6 +30,10 @@ static void test_aux(const char *main_pr + main = changes_pkt_data__open_opts(&opts); + if (!ASSERT_OK_PTR(main, "changes_pkt_data__open")) + goto out; ++ main_prog = bpf_object__find_program_by_name(main->obj, main_prog_name); ++ if (!ASSERT_OK_PTR(main_prog, "main_prog")) ++ goto out; ++ bpf_program__set_autoload(main_prog, true); + err = changes_pkt_data__load(main); + print_verifier_log(log); + if (!ASSERT_OK(err, "changes_pkt_data__load")) +@@ -33,14 +41,14 @@ static void test_aux(const char *main_pr + freplace = changes_pkt_data_freplace__open_opts(&opts); + if (!ASSERT_OK_PTR(freplace, "changes_pkt_data_freplace__open")) + goto out; +- freplace_prog = bpf_object__find_program_by_name(freplace->obj, freplace_prog_name); ++ freplace_prog = bpf_object__find_program_by_name(freplace->obj, replacement); + if (!ASSERT_OK_PTR(freplace_prog, "freplace_prog")) + goto out; + bpf_program__set_autoload(freplace_prog, true); + bpf_program__set_autoattach(freplace_prog, true); + bpf_program__set_attach_target(freplace_prog, +- bpf_program__fd(main->progs.dummy), +- main_prog_name); ++ bpf_program__fd(main_prog), ++ to_be_replaced); + err = changes_pkt_data_freplace__load(freplace); + print_verifier_log(log); + if (expect_load) { +@@ -62,15 +70,38 @@ out: + * that either do or do not. It is only ok to freplace subprograms + * that do not change packet data with those that do not as well. + * The below tests check outcomes for each combination of such freplace. ++ * Also test a case when main subprogram itself is replaced and is a single ++ * subprogram in a program. + */ + void test_changes_pkt_data_freplace(void) + { +- if (test__start_subtest("changes_with_changes")) +- test_aux("changes_pkt_data", "changes_pkt_data", true); +- if (test__start_subtest("changes_with_doesnt_change")) +- test_aux("changes_pkt_data", "does_not_change_pkt_data", true); +- if (test__start_subtest("doesnt_change_with_changes")) +- test_aux("does_not_change_pkt_data", "changes_pkt_data", false); +- if (test__start_subtest("doesnt_change_with_doesnt_change")) +- test_aux("does_not_change_pkt_data", "does_not_change_pkt_data", true); ++ struct { ++ const char *main; ++ const char *to_be_replaced; ++ bool changes; ++ } mains[] = { ++ { "main_with_subprogs", "changes_pkt_data", true }, ++ { "main_with_subprogs", "does_not_change_pkt_data", false }, ++ { "main_changes", "main_changes", true }, ++ { "main_does_not_change", "main_does_not_change", false }, ++ }; ++ struct { ++ const char *func; ++ bool changes; ++ } replacements[] = { ++ { "changes_pkt_data", true }, ++ { "does_not_change_pkt_data", false } ++ }; ++ char buf[64]; ++ ++ for (int i = 0; i < ARRAY_SIZE(mains); ++i) { ++ for (int j = 0; j < ARRAY_SIZE(replacements); ++j) { ++ snprintf(buf, sizeof(buf), "%s_with_%s", ++ mains[i].to_be_replaced, replacements[j].func); ++ if (!test__start_subtest(buf)) ++ continue; ++ test_aux(mains[i].main, mains[i].to_be_replaced, replacements[j].func, ++ mains[i].changes || !replacements[j].changes); ++ } ++ } + } +--- a/tools/testing/selftests/bpf/progs/changes_pkt_data.c ++++ b/tools/testing/selftests/bpf/progs/changes_pkt_data.c +@@ -4,22 +4,35 @@ + #include + + __noinline +-long changes_pkt_data(struct __sk_buff *sk, __u32 len) ++long changes_pkt_data(struct __sk_buff *sk) + { +- return bpf_skb_pull_data(sk, len); ++ return bpf_skb_pull_data(sk, 0); + } + + __noinline __weak +-long does_not_change_pkt_data(struct __sk_buff *sk, __u32 len) ++long does_not_change_pkt_data(struct __sk_buff *sk) + { + return 0; + } + +-SEC("tc") +-int dummy(struct __sk_buff *sk) ++SEC("?tc") ++int main_with_subprogs(struct __sk_buff *sk) ++{ ++ changes_pkt_data(sk); ++ does_not_change_pkt_data(sk); ++ return 0; ++} ++ ++SEC("?tc") ++int main_changes(struct __sk_buff *sk) ++{ ++ bpf_skb_pull_data(sk, 0); ++ return 0; ++} ++ ++SEC("?tc") ++int main_does_not_change(struct __sk_buff *sk) + { +- changes_pkt_data(sk, 0); +- does_not_change_pkt_data(sk, 0); + return 0; + } + +--- a/tools/testing/selftests/bpf/progs/changes_pkt_data_freplace.c ++++ b/tools/testing/selftests/bpf/progs/changes_pkt_data_freplace.c +@@ -4,13 +4,13 @@ + #include + + SEC("?freplace") +-long changes_pkt_data(struct __sk_buff *sk, __u32 len) ++long changes_pkt_data(struct __sk_buff *sk) + { +- return bpf_skb_pull_data(sk, len); ++ return bpf_skb_pull_data(sk, 0); + } + + SEC("?freplace") +-long does_not_change_pkt_data(struct __sk_buff *sk, __u32 len) ++long does_not_change_pkt_data(struct __sk_buff *sk) + { + return 0; + } diff --git a/queue-6.12/selftests-bpf-freplace-tests-for-tracking-of-changes_packet_data.patch b/queue-6.12/selftests-bpf-freplace-tests-for-tracking-of-changes_packet_data.patch new file mode 100644 index 0000000000..741cc15e4b --- /dev/null +++ b/queue-6.12/selftests-bpf-freplace-tests-for-tracking-of-changes_packet_data.patch @@ -0,0 +1,161 @@ +From stable+bounces-135231-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:27 2025 +From: Shung-Hsi Yu +Date: Wed, 23 Apr 2025 13:53:26 +0800 +Subject: selftests/bpf: freplace tests for tracking of changes_packet_data +To: stable@vger.kernel.org +Cc: Alexei Starovoitov , Dan Carpenter , Eduard Zingerman , Nick Zavaritsky , Shung-Hsi Yu +Message-ID: <20250423055334.52791-6-shung-hsi.yu@suse.com> + +From: Eduard Zingerman + +commit 89ff40890d8f12a7d7e93fb602cc27562f3834f0 upstream. + +Try different combinations of global functions replacement: +- replace function that changes packet data with one that doesn't; +- replace function that changes packet data with one that does; +- replace function that doesn't change packet data with one that does; +- replace function that doesn't change packet data with one that doesn't; + +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20241210041100.1898468-7-eddyz87@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/bpf/prog_tests/changes_pkt_data.c | 76 ++++++++++ + tools/testing/selftests/bpf/progs/changes_pkt_data.c | 26 +++ + tools/testing/selftests/bpf/progs/changes_pkt_data_freplace.c | 18 ++ + 3 files changed, 120 insertions(+) + create mode 100644 tools/testing/selftests/bpf/prog_tests/changes_pkt_data.c + create mode 100644 tools/testing/selftests/bpf/progs/changes_pkt_data.c + create mode 100644 tools/testing/selftests/bpf/progs/changes_pkt_data_freplace.c + +--- /dev/null ++++ b/tools/testing/selftests/bpf/prog_tests/changes_pkt_data.c +@@ -0,0 +1,76 @@ ++// SPDX-License-Identifier: GPL-2.0 ++#include "bpf/libbpf.h" ++#include "changes_pkt_data_freplace.skel.h" ++#include "changes_pkt_data.skel.h" ++#include ++ ++static void print_verifier_log(const char *log) ++{ ++ if (env.verbosity >= VERBOSE_VERY) ++ fprintf(stdout, "VERIFIER LOG:\n=============\n%s=============\n", log); ++} ++ ++static void test_aux(const char *main_prog_name, const char *freplace_prog_name, bool expect_load) ++{ ++ struct changes_pkt_data_freplace *freplace = NULL; ++ struct bpf_program *freplace_prog = NULL; ++ LIBBPF_OPTS(bpf_object_open_opts, opts); ++ struct changes_pkt_data *main = NULL; ++ char log[16*1024]; ++ int err; ++ ++ opts.kernel_log_buf = log; ++ opts.kernel_log_size = sizeof(log); ++ if (env.verbosity >= VERBOSE_SUPER) ++ opts.kernel_log_level = 1 | 2 | 4; ++ main = changes_pkt_data__open_opts(&opts); ++ if (!ASSERT_OK_PTR(main, "changes_pkt_data__open")) ++ goto out; ++ err = changes_pkt_data__load(main); ++ print_verifier_log(log); ++ if (!ASSERT_OK(err, "changes_pkt_data__load")) ++ goto out; ++ freplace = changes_pkt_data_freplace__open_opts(&opts); ++ if (!ASSERT_OK_PTR(freplace, "changes_pkt_data_freplace__open")) ++ goto out; ++ freplace_prog = bpf_object__find_program_by_name(freplace->obj, freplace_prog_name); ++ if (!ASSERT_OK_PTR(freplace_prog, "freplace_prog")) ++ goto out; ++ bpf_program__set_autoload(freplace_prog, true); ++ bpf_program__set_autoattach(freplace_prog, true); ++ bpf_program__set_attach_target(freplace_prog, ++ bpf_program__fd(main->progs.dummy), ++ main_prog_name); ++ err = changes_pkt_data_freplace__load(freplace); ++ print_verifier_log(log); ++ if (expect_load) { ++ ASSERT_OK(err, "changes_pkt_data_freplace__load"); ++ } else { ++ ASSERT_ERR(err, "changes_pkt_data_freplace__load"); ++ ASSERT_HAS_SUBSTR(log, "Extension program changes packet data", "error log"); ++ } ++ ++out: ++ changes_pkt_data_freplace__destroy(freplace); ++ changes_pkt_data__destroy(main); ++} ++ ++/* There are two global subprograms in both changes_pkt_data.skel.h: ++ * - one changes packet data; ++ * - another does not. ++ * It is ok to freplace subprograms that change packet data with those ++ * that either do or do not. It is only ok to freplace subprograms ++ * that do not change packet data with those that do not as well. ++ * The below tests check outcomes for each combination of such freplace. ++ */ ++void test_changes_pkt_data_freplace(void) ++{ ++ if (test__start_subtest("changes_with_changes")) ++ test_aux("changes_pkt_data", "changes_pkt_data", true); ++ if (test__start_subtest("changes_with_doesnt_change")) ++ test_aux("changes_pkt_data", "does_not_change_pkt_data", true); ++ if (test__start_subtest("doesnt_change_with_changes")) ++ test_aux("does_not_change_pkt_data", "changes_pkt_data", false); ++ if (test__start_subtest("doesnt_change_with_doesnt_change")) ++ test_aux("does_not_change_pkt_data", "does_not_change_pkt_data", true); ++} +--- /dev/null ++++ b/tools/testing/selftests/bpf/progs/changes_pkt_data.c +@@ -0,0 +1,26 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++ ++__noinline ++long changes_pkt_data(struct __sk_buff *sk, __u32 len) ++{ ++ return bpf_skb_pull_data(sk, len); ++} ++ ++__noinline __weak ++long does_not_change_pkt_data(struct __sk_buff *sk, __u32 len) ++{ ++ return 0; ++} ++ ++SEC("tc") ++int dummy(struct __sk_buff *sk) ++{ ++ changes_pkt_data(sk, 0); ++ does_not_change_pkt_data(sk, 0); ++ return 0; ++} ++ ++char _license[] SEC("license") = "GPL"; +--- /dev/null ++++ b/tools/testing/selftests/bpf/progs/changes_pkt_data_freplace.c +@@ -0,0 +1,18 @@ ++// SPDX-License-Identifier: GPL-2.0 ++ ++#include ++#include ++ ++SEC("?freplace") ++long changes_pkt_data(struct __sk_buff *sk, __u32 len) ++{ ++ return bpf_skb_pull_data(sk, len); ++} ++ ++SEC("?freplace") ++long does_not_change_pkt_data(struct __sk_buff *sk, __u32 len) ++{ ++ return 0; ++} ++ ++char _license[] SEC("license") = "GPL"; diff --git a/queue-6.12/selftests-bpf-test-for-changing-packet-data-from-global-functions.patch b/queue-6.12/selftests-bpf-test-for-changing-packet-data-from-global-functions.patch new file mode 100644 index 0000000000..e215ae6d34 --- /dev/null +++ b/queue-6.12/selftests-bpf-test-for-changing-packet-data-from-global-functions.patch @@ -0,0 +1,62 @@ +From stable+bounces-135229-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:15 2025 +From: Shung-Hsi Yu +Date: Wed, 23 Apr 2025 13:53:24 +0800 +Subject: selftests/bpf: test for changing packet data from global functions +To: stable@vger.kernel.org +Cc: Alexei Starovoitov , Dan Carpenter , Eduard Zingerman , Nick Zavaritsky , Shung-Hsi Yu +Message-ID: <20250423055334.52791-4-shung-hsi.yu@suse.com> + +From: Eduard Zingerman + +commit 3f23ee5590d9605dbde9a5e1d4b97637a4803329 upstream. + +Check if verifier is aware of packet pointers invalidation done in +global functions. Based on a test shared by Nick Zavaritsky in [0]. + +[0] https://lore.kernel.org/bpf/0498CA22-5779-4767-9C0C-A9515CEA711F@gmail.com/ + +Suggested-by: Nick Zavaritsky +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20241210041100.1898468-5-eddyz87@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/bpf/progs/verifier_sock.c | 28 ++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +--- a/tools/testing/selftests/bpf/progs/verifier_sock.c ++++ b/tools/testing/selftests/bpf/progs/verifier_sock.c +@@ -977,4 +977,32 @@ l1_%=: r0 = *(u8*)(r7 + 0); \ + : __clobber_all); + } + ++__noinline ++long skb_pull_data2(struct __sk_buff *sk, __u32 len) ++{ ++ return bpf_skb_pull_data(sk, len); ++} ++ ++__noinline ++long skb_pull_data1(struct __sk_buff *sk, __u32 len) ++{ ++ return skb_pull_data2(sk, len); ++} ++ ++/* global function calls bpf_skb_pull_data(), which invalidates packet ++ * pointers established before global function call. ++ */ ++SEC("tc") ++__failure __msg("invalid mem access") ++int invalidate_pkt_pointers_from_global_func(struct __sk_buff *sk) ++{ ++ int *p = (void *)(long)sk->data; ++ ++ if ((void *)(p + 1) > (void *)(long)sk->data_end) ++ return TCX_DROP; ++ skb_pull_data1(sk, 0); ++ *p = 42; /* this is unsafe */ ++ return TCX_PASS; ++} ++ + char _license[] SEC("license") = "GPL"; diff --git a/queue-6.12/selftests-bpf-validate-that-tail-call-invalidates-packet-pointers.patch b/queue-6.12/selftests-bpf-validate-that-tail-call-invalidates-packet-pointers.patch new file mode 100644 index 0000000000..26b18af888 --- /dev/null +++ b/queue-6.12/selftests-bpf-validate-that-tail-call-invalidates-packet-pointers.patch @@ -0,0 +1,67 @@ +From stable+bounces-135232-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:34 2025 +From: Shung-Hsi Yu +Date: Wed, 23 Apr 2025 13:53:27 +0800 +Subject: selftests/bpf: validate that tail call invalidates packet pointers +To: stable@vger.kernel.org +Cc: Alexei Starovoitov , Dan Carpenter , Eduard Zingerman , Nick Zavaritsky , Shung-Hsi Yu +Message-ID: <20250423055334.52791-7-shung-hsi.yu@suse.com> + +From: Eduard Zingerman + +commit d9706b56e13b7916461ca6b4b731e169ed44ed09 upstream. + +Add a test case with a tail call done from a global sub-program. Such +tails calls should be considered as invalidating packet pointers. + +Signed-off-by: Eduard Zingerman +Link: https://lore.kernel.org/r/20241210041100.1898468-9-eddyz87@gmail.com +Signed-off-by: Alexei Starovoitov +Signed-off-by: Shung-Hsi Yu +Signed-off-by: Greg Kroah-Hartman +--- + tools/testing/selftests/bpf/progs/verifier_sock.c | 28 ++++++++++++++++++++++ + 1 file changed, 28 insertions(+) + +--- a/tools/testing/selftests/bpf/progs/verifier_sock.c ++++ b/tools/testing/selftests/bpf/progs/verifier_sock.c +@@ -50,6 +50,13 @@ struct { + __uint(map_flags, BPF_F_NO_PREALLOC); + } sk_storage_map SEC(".maps"); + ++struct { ++ __uint(type, BPF_MAP_TYPE_PROG_ARRAY); ++ __uint(max_entries, 1); ++ __uint(key_size, sizeof(__u32)); ++ __uint(value_size, sizeof(__u32)); ++} jmp_table SEC(".maps"); ++ + SEC("cgroup/skb") + __description("skb->sk: no NULL check") + __failure __msg("invalid mem access 'sock_common_or_null'") +@@ -1004,5 +1011,26 @@ int invalidate_pkt_pointers_from_global_ + *p = 42; /* this is unsafe */ + return TCX_PASS; + } ++ ++__noinline ++int tail_call(struct __sk_buff *sk) ++{ ++ bpf_tail_call_static(sk, &jmp_table, 0); ++ return 0; ++} ++ ++/* Tail calls invalidate packet pointers. */ ++SEC("tc") ++__failure __msg("invalid mem access") ++int invalidate_pkt_pointers_by_tail_call(struct __sk_buff *sk) ++{ ++ int *p = (void *)(long)sk->data; ++ ++ if ((void *)(p + 1) > (void *)(long)sk->data_end) ++ return TCX_DROP; ++ tail_call(sk); ++ *p = 42; /* this is unsafe */ ++ return TCX_PASS; ++} + + char _license[] SEC("license") = "GPL"; diff --git a/queue-6.12/series b/queue-6.12/series index c820bd2279..65edf685cd 100644 --- a/queue-6.12/series +++ b/queue-6.12/series @@ -211,3 +211,12 @@ revert-wifi-ath12k-fix-invalid-entry-fetch-in-ath12k_dp_mon_srng_process.patch mips-dec-declare-which_prom-as-static.patch mips-cevt-ds1287-add-missing-ds1287.h-include.patch mips-ds1287-match-ds1287_set_base_clock-function-types.patch +wifi-ath12k-fix-invalid-entry-fetch-in-ath12k_dp_mon_srng_process.patch +bpf-add-find_containing_subprog-utility-function.patch +bpf-track-changes_pkt_data-property-for-global-functions.patch +selftests-bpf-test-for-changing-packet-data-from-global-functions.patch +bpf-check-changes_pkt_data-property-for-extension-programs.patch +selftests-bpf-freplace-tests-for-tracking-of-changes_packet_data.patch +selftests-bpf-validate-that-tail-call-invalidates-packet-pointers.patch +bpf-fix-null-dereference-when-computing-changes_pkt_data-of-prog-w-o-subprogs.patch +selftests-bpf-extend-changes_pkt_data-with-cases-w-o-subprograms.patch diff --git a/queue-6.12/wifi-ath12k-fix-invalid-entry-fetch-in-ath12k_dp_mon_srng_process.patch b/queue-6.12/wifi-ath12k-fix-invalid-entry-fetch-in-ath12k_dp_mon_srng_process.patch new file mode 100644 index 0000000000..1164cad9cb --- /dev/null +++ b/queue-6.12/wifi-ath12k-fix-invalid-entry-fetch-in-ath12k_dp_mon_srng_process.patch @@ -0,0 +1,45 @@ +From 63fdc4509bcf483e79548de6bc08bf3c8e504bb3 Mon Sep 17 00:00:00 2001 +From: P Praneesh +Date: Mon, 23 Dec 2024 11:31:24 +0530 +Subject: wifi: ath12k: Fix invalid entry fetch in ath12k_dp_mon_srng_process + +From: P Praneesh + +commit 63fdc4509bcf483e79548de6bc08bf3c8e504bb3 upstream. + +Currently, ath12k_dp_mon_srng_process uses ath12k_hal_srng_src_get_next_entry +to fetch the next entry from the destination ring. This is incorrect because +ath12k_hal_srng_src_get_next_entry is intended for source rings, not destination +rings. This leads to invalid entry fetches, causing potential data corruption or +crashes due to accessing incorrect memory locations. This happens because the +source ring and destination ring have different handling mechanisms and using +the wrong function results in incorrect pointer arithmetic and ring management. + +To fix this issue, replace the call to ath12k_hal_srng_src_get_next_entry with +ath12k_hal_srng_dst_get_next_entry in ath12k_dp_mon_srng_process. This ensures +that the correct function is used for fetching entries from the destination +ring, preventing invalid memory accesses. + +Tested-on: QCN9274 hw2.0 PCI WLAN.WBE.1.3.1-00173-QCAHKSWPL_SILICONZ-1 +Tested-on: WCN7850 hw2.0 WLAN.HMT.1.0.c5-00481-QCAHMTSWPL_V1.0_V2.0_SILICONZ-3 + +Signed-off-by: P Praneesh +Link: https://patch.msgid.link/20241223060132.3506372-7-quic_ppranees@quicinc.com +Signed-off-by: Jeff Johnson +Signed-off-by: Alexander Tsoy +Signed-off-by: Greg Kroah-Hartman +--- + drivers/net/wireless/ath/ath12k/dp_mon.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- a/drivers/net/wireless/ath/ath12k/dp_mon.c ++++ b/drivers/net/wireless/ath/ath12k/dp_mon.c +@@ -2118,7 +2118,7 @@ int ath12k_dp_mon_srng_process(struct at + dest_idx = 0; + move_next: + ath12k_dp_mon_buf_replenish(ab, buf_ring, 1); +- ath12k_hal_srng_src_get_next_entry(ab, srng); ++ ath12k_hal_srng_dst_get_next_entry(ab, srng); + num_buffs_reaped++; + } + -- 2.47.3