--- /dev/null
+From stable+bounces-135227-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:07 2025
+From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+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 <ast@kernel.org>, Dan Carpenter <dan.carpenter@linaro.org>, Eduard Zingerman <eddyz87@gmail.com>, Nick Zavaritsky <mejedi@gmail.com>, Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Message-ID: <20250423055334.52791-2-shung-hsi.yu@suse.com>
+
+From: Eduard Zingerman <eddyz87@gmail.com>
+
+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 <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20241210041100.1898468-2-eddyz87@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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)
--- /dev/null
+From stable+bounces-135230-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:25 2025
+From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+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 <ast@kernel.org>, Dan Carpenter <dan.carpenter@linaro.org>, Eduard Zingerman <eddyz87@gmail.com>, Nick Zavaritsky <mejedi@gmail.com>, Shung-Hsi Yu <shung-hsi.yu@suse.com>, Alexei Starovoitov <alexei.starovoitov@gmail.com>
+Message-ID: <20250423055334.52791-5-shung-hsi.yu@suse.com>
+
+From: Eduard Zingerman <eddyz87@gmail.com>
+
+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 <alexei.starovoitov@gmail.com>
+Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20241210041100.1898468-6-eddyz87@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+[ 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 <shung-hsi.yu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
--- /dev/null
+From stable+bounces-135233-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:38 2025
+From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+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 <ast@kernel.org>, Dan Carpenter <dan.carpenter@linaro.org>, Eduard Zingerman <eddyz87@gmail.com>, Nick Zavaritsky <mejedi@gmail.com>, Shung-Hsi Yu <shung-hsi.yu@suse.com>, kernel test robot <lkp@intel.com>
+Message-ID: <20250423055334.52791-8-shung-hsi.yu@suse.com>
+
+From: Eduard Zingerman <eddyz87@gmail.com>
+
+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 <lkp@intel.com>
+Reported-by: Dan Carpenter <dan.carpenter@linaro.org>
+Closes: https://lore.kernel.org/r/202412111822.qGw6tOyB-lkp@intel.com/
+Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20241212070711.427443-1-eddyz87@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
--- /dev/null
+From stable+bounces-135228-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:11 2025
+From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+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 <ast@kernel.org>, Dan Carpenter <dan.carpenter@linaro.org>, Eduard Zingerman <eddyz87@gmail.com>, Nick Zavaritsky <mejedi@gmail.com>, Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Message-ID: <20250423055334.52791-3-shung-hsi.yu@suse.com>
+
+From: Eduard Zingerman <eddyz87@gmail.com>
+
+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 <mejedi@gmail.com>
+Closes: https://lore.kernel.org/bpf/0498CA22-5779-4767-9C0C-A9515CEA711F@gmail.com/
+Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20241210041100.1898468-4-eddyz87@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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;
+
--- /dev/null
+From stable+bounces-135234-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:44 2025
+From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+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 <ast@kernel.org>, Dan Carpenter <dan.carpenter@linaro.org>, Eduard Zingerman <eddyz87@gmail.com>, Nick Zavaritsky <mejedi@gmail.com>, Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Message-ID: <20250423055334.52791-9-shung-hsi.yu@suse.com>
+
+From: Eduard Zingerman <eddyz87@gmail.com>
+
+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 <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20241212070711.427443-2-eddyz87@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 <bpf/bpf_helpers.h>
+
+ __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 <bpf/bpf_helpers.h>
+
+ 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;
+ }
--- /dev/null
+From stable+bounces-135231-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:27 2025
+From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+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 <ast@kernel.org>, Dan Carpenter <dan.carpenter@linaro.org>, Eduard Zingerman <eddyz87@gmail.com>, Nick Zavaritsky <mejedi@gmail.com>, Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Message-ID: <20250423055334.52791-6-shung-hsi.yu@suse.com>
+
+From: Eduard Zingerman <eddyz87@gmail.com>
+
+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 <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20241210041100.1898468-7-eddyz87@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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 <test_progs.h>
++
++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 <linux/bpf.h>
++#include <bpf/bpf_helpers.h>
++
++__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 <linux/bpf.h>
++#include <bpf/bpf_helpers.h>
++
++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";
--- /dev/null
+From stable+bounces-135229-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:15 2025
+From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+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 <ast@kernel.org>, Dan Carpenter <dan.carpenter@linaro.org>, Eduard Zingerman <eddyz87@gmail.com>, Nick Zavaritsky <mejedi@gmail.com>, Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Message-ID: <20250423055334.52791-4-shung-hsi.yu@suse.com>
+
+From: Eduard Zingerman <eddyz87@gmail.com>
+
+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 <mejedi@gmail.com>
+Signed-off-by: Eduard Zingerman <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20241210041100.1898468-5-eddyz87@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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";
--- /dev/null
+From stable+bounces-135232-greg=kroah.com@vger.kernel.org Wed Apr 23 07:54:34 2025
+From: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+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 <ast@kernel.org>, Dan Carpenter <dan.carpenter@linaro.org>, Eduard Zingerman <eddyz87@gmail.com>, Nick Zavaritsky <mejedi@gmail.com>, Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Message-ID: <20250423055334.52791-7-shung-hsi.yu@suse.com>
+
+From: Eduard Zingerman <eddyz87@gmail.com>
+
+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 <eddyz87@gmail.com>
+Link: https://lore.kernel.org/r/20241210041100.1898468-9-eddyz87@gmail.com
+Signed-off-by: Alexei Starovoitov <ast@kernel.org>
+Signed-off-by: Shung-Hsi Yu <shung-hsi.yu@suse.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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";
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
--- /dev/null
+From 63fdc4509bcf483e79548de6bc08bf3c8e504bb3 Mon Sep 17 00:00:00 2001
+From: P Praneesh <quic_ppranees@quicinc.com>
+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 <quic_ppranees@quicinc.com>
+
+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 <quic_ppranees@quicinc.com>
+Link: https://patch.msgid.link/20241223060132.3506372-7-quic_ppranees@quicinc.com
+Signed-off-by: Jeff Johnson <jeff.johnson@oss.qualcomm.com>
+Signed-off-by: Alexander Tsoy <alexander@tsoy.me>
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+---
+ 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++;
+ }
+