AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/07/16 13:47 flow patch-triage
2m Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT",
    "BPF_EVENTS",
    "DYNAMIC_FTRACE_WITH_DIRECT_CALLS"
  ],
  "FocusSymbols": [
    "bpf_trampoline_multi_detach",
    "bpf_trampoline_multi_attach_free",
    "bpf_trampoline_multi_attach_rollback"
  ],
  "Reasoning": "The patch fixes a use-after-free bug in the BPF trampoline multi-detach logic by correctly handling ftrace update failures and rolling back the active image. This is reachable and functional core kernel logic for BPF tracing.",
  "WorthFuzzing": true
}

1/1 2026/07/16 13:47 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 3cfb068cdf01575f6dcb61b625c2226e02eab26e\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Jul 16 13:47:17 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c\nindex 6eadf64f7ec90f..44ef25beb4cc5c 100644\n--- a/kernel/bpf/trampoline.c\n+++ b/kernel/bpf/trampoline.c\n@@ -1585,7 +1585,17 @@ static void bpf_trampoline_multi_attach_init(struct bpf_trampoline *tr)\n \n static void bpf_trampoline_multi_attach_free(struct bpf_trampoline *tr)\n {\n-\tif (tr-\u003emulti_attach.old_image)\n+\t/* Only free old_image if it is no longer the active image.\n+\t * When bpf_trampoline_update() fails before modify_fentry_multi()/\n+\t * unregister_fentry_multi() is called, cur_image is unchanged\n+\t * (cur_image == old_image) and ftrace still points to it. Freeing\n+\t * it would cause a UAF when ftrace calls into the freed memory.\n+\t * On success, cur_image is either a new image or NULL, so\n+\t * old_image != cur_image correctly identifies a stale image that\n+\t * is safe to free.\n+\t */\n+\tif (tr-\u003emulti_attach.old_image \u0026\u0026\n+\t    tr-\u003emulti_attach.old_image != tr-\u003ecur_image)\n \t\tbpf_tramp_image_put(tr-\u003emulti_attach.old_image);\n \n \ttr-\u003emulti_attach.old_image = NULL;\n@@ -1713,7 +1723,7 @@ int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_\n {\n \tstruct bpf_tracing_multi_data *data = \u0026link-\u003edata;\n \tstruct bpf_tracing_multi_node *mnode;\n-\tint i;\n+\tint i, err_unreg = 0, err_mod = 0;\n \n \ttrampoline_lock_all();\n \n@@ -1724,13 +1734,41 @@ int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_\n \t\t\t\t\tNULL, \u0026trampoline_multi_ops, data));\n \t}\n \n-\tif (ftrace_hash_count(data-\u003eunreg))\n-\t\tWARN_ON_ONCE(update_ftrace_direct_del(\u0026direct_ops, data-\u003eunreg));\n-\tif (ftrace_hash_count(data-\u003emodify))\n-\t\tWARN_ON_ONCE(update_ftrace_direct_mod(\u0026direct_ops, data-\u003emodify, true));\n+\tif (ftrace_hash_count(data-\u003eunreg)) {\n+\t\terr_unreg = update_ftrace_direct_del(\u0026direct_ops, data-\u003eunreg);\n+\t\tWARN_ON_ONCE(err_unreg);\n+\t}\n+\tif (ftrace_hash_count(data-\u003emodify)) {\n+\t\terr_mod = update_ftrace_direct_mod(\u0026direct_ops, data-\u003emodify, true);\n+\t\tWARN_ON_ONCE(err_mod);\n+\t}\n \n-\tfor_each_mnode(mnode, link)\n-\t\tbpf_trampoline_multi_attach_free(mnode-\u003etrampoline);\n+\tfor_each_mnode(mnode, link) {\n+\t\tstruct bpf_trampoline *tr = mnode-\u003etrampoline;\n+\n+\t\t/* If the batch ftrace update failed for this mnode's path,\n+\t\t * ftrace still points to old_image. Use rollback to restore\n+\t\t * cur_image to old_image (putting the new cur_image if any)\n+\t\t * so the trampoline keeps the image ftrace is calling.\n+\t\t *\n+\t\t * This relies on update_ftrace_direct_del/mod being atomic:\n+\t\t * on failure, NO IPs in the hash are modified in ftrace (all\n+\t\t * validation/allocation happens before any ftrace record is\n+\t\t * touched). If this assumption is broken in the future (i.e.,\n+\t\t * partial success becomes possible), this rollback logic would\n+\t\t * need to be revisited.\n+\t\t *\n+\t\t * cur_image == NULL indicates the unreg path (total == 0);\n+\t\t * cur_image != NULL indicates the modify path (total \u003e 0).\n+\t\t */\n+\t\tif (tr-\u003emulti_attach.old_image \u0026\u0026\n+\t\t    tr-\u003emulti_attach.old_image != tr-\u003ecur_image \u0026\u0026\n+\t\t    ((err_unreg \u0026\u0026 !tr-\u003ecur_image) ||\n+\t\t     (err_mod \u0026\u0026 tr-\u003ecur_image)))\n+\t\t\tbpf_trampoline_multi_attach_rollback(tr);\n+\t\telse\n+\t\t\tbpf_trampoline_multi_attach_free(tr);\n+\t}\n \n \ttrampoline_unlock_all();\n \n"
}

2/1 2026/07/16 13:47 agent patch-evaluator
2m Model:
gemini-3.1-pro-preview

Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT",
    "BPF_EVENTS",
    "DYNAMIC_FTRACE_WITH_DIRECT_CALLS"
  ],
  "FocusSymbols": [
    "bpf_trampoline_multi_detach",
    "bpf_trampoline_multi_attach_free",
    "bpf_trampoline_multi_attach_rollback"
  ],
  "Reasoning": "The patch fixes a use-after-free bug in the BPF trampoline multi-detach logic by correctly handling ftrace update failures and rolling back the active image. This is reachable and functional core kernel logic for BPF tracing.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer.
Your job is to review a provided patch series and determine
if it makes functional changes to the kernel that should be fuzzed.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on your internal knowledge of the kernel. You must actively
use your code access tools to examine the actual source code and confirm any assumptions.

Return WorthFuzzing=false if the patch only contains:
- Modifications to Documentation/, Kconfig files, or code comments.
- Purely decorative changes, such as logging (e.g., pr_err, printk) or tracepoints.
- Changes to numeric constants or macros that do not functionally alter execution flow.
- Code paths that are impossible to reach in virtualized environments like GCE or QEMU,
even when utilizing software-emulated hardware (e.g., usb gadget, mac80211_hwsim).

If it modifies reachable core kernel logic, drivers, or architectures, use your code search
tools to verify the code can be executed, then return WorthFuzzing=true.

When returning WorthFuzzing=true, you MUST ALSO:
1. Extract any specific kernel functions that should be heavily fuzzed into FocusSymbols.
   Avoid listing generic hot-path functions to prevent skewed test distributions.
2. Identify any specific CONFIG_ options required to properly test this new/modified feature.
   Go and look into the Kconfig files and check for ifdefs around the code, do not make assumptions.
   Do not list too generic configs (we already have them enabled). Only list those that
   specifically cover the modified code. List them in the EnableConfigs output array,
   and DO NOT add a 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
For your convenience, here is the diff of the changes:
commit 3cfb068cdf01575f6dcb61b625c2226e02eab26e
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Jul 16 13:47:17 2026 +0000

    syz-cluster: applied patch under review

diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 6eadf64f7ec90f..44ef25beb4cc5c 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -1585,7 +1585,17 @@ static void bpf_trampoline_multi_attach_init(struct bpf_trampoline *tr)
 
 static void bpf_trampoline_multi_attach_free(struct bpf_trampoline *tr)
 {
-	if (tr->multi_attach.old_image)
+	/* Only free old_image if it is no longer the active image.
+	 * When bpf_trampoline_update() fails before modify_fentry_multi()/
+	 * unregister_fentry_multi() is called, cur_image is unchanged
+	 * (cur_image == old_image) and ftrace still points to it. Freeing
+	 * it would cause a UAF when ftrace calls into the freed memory.
+	 * On success, cur_image is either a new image or NULL, so
+	 * old_image != cur_image correctly identifies a stale image that
+	 * is safe to free.
+	 */
+	if (tr->multi_attach.old_image &&
+	    tr->multi_attach.old_image != tr->cur_image)
 		bpf_tramp_image_put(tr->multi_attach.old_image);
 
 	tr->multi_attach.old_image = NULL;
@@ -1713,7 +1723,7 @@ int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_
 {
 	struct bpf_tracing_multi_data *data = &link->data;
 	struct bpf_tracing_multi_node *mnode;
-	int i;
+	int i, err_unreg = 0, err_mod = 0;
 
 	trampoline_lock_all();
 
@@ -1724,13 +1734,41 @@ int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_
 					NULL, &trampoline_multi_ops, data));
 	}
 
-	if (ftrace_hash_count(data->unreg))
-		WARN_ON_ONCE(update_ftrace_direct_del(&direct_ops, data->unreg));
-	if (ftrace_hash_count(data->modify))
-		WARN_ON_ONCE(update_ftrace_direct_mod(&direct_ops, data->modify, true));
+	if (ftrace_hash_count(data->unreg)) {
+		err_unreg = update_ftrace_direct_del(&direct_ops, data->unreg);
+		WARN_ON_ONCE(err_unreg);
+	}
+	if (ftrace_hash_count(data->modify)) {
+		err_mod = update_ftrace_direct_mod(&direct_ops, data->modify, true);
+		WARN_ON_ONCE(err_mod);
+	}
 
-	for_each_mnode(mnode, link)
-		bpf_trampoline_multi_attach_free(mnode->trampoline);
+	for_each_mnode(mnode, link) {
+		struct bpf_trampoline *tr = mnode->trampoline;
+
+		/* If the batch ftrace update failed for this mnode's path,
+		 * ftrace still points to old_image. Use rollback to restore
+		 * cur_image to old_image (putting the new cur_image if any)
+		 * so the trampoline keeps the image ftrace is calling.
+		 *
+		 * This relies on update_ftrace_direct_del/mod being atomic:
+		 * on failure, NO IPs in the hash are modified in ftrace (all
+		 * validation/allocation happens before any ftrace record is
+		 * touched). If this assumption is broken in the future (i.e.,
+		 * partial success becomes possible), this rollback logic would
+		 * need to be revisited.
+		 *
+		 * cur_image == NULL indicates the unreg path (total == 0);
+		 * cur_image != NULL indicates the modify path (total > 0).
+		 */
+		if (tr->multi_attach.old_image &&
+		    tr->multi_attach.old_image != tr->cur_image &&
+		    ((err_unreg && !tr->cur_image) ||
+		     (err_mod && tr->cur_image)))
+			bpf_trampoline_multi_attach_rollback(tr);
+		else
+			bpf_trampoline_multi_attach_free(tr);
+	}
 
 	trampoline_unlock_all();
 

3/2 2026/07/16 13:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 2836
							output: 23
							thoughts: 238
						

Thoughts:
**Analyzing a BPF Trampoline Patch**

Okay, so I'm looking at a kernel patch specifically targeting `kernel/bpf/trampoline.c`. From what I see, it's modifying the `bpf_trampoline_multi_attach_free` and `bpf_trampoline_multi_detach` functions. It seems like a new check, `tr->multi_attach.old_image != tr->cur_image`, has been added before freeing `old_image`, which is interesting. It also appears the patch is now handling potential errors from `update_ftrace_direct_del` and `update_ftrace_direct_mod` in `bpf_trampoline_multi_detach`. In the event of an error, it's calling `bpf_trampoline_multi_attach_rollback(tr)`.

This makes sense, it looks like a targeted change within the BPF trampoline multi-attach/detach logic. To verify its behavior and robustness, I should put together some fuzzing routines for both `bpf_trampoline_multi_detach` and `bpf_trampoline_multi_attach_free`.

Now, I need to figure out the relevant configuration options that enable this BPF trampoline functionality. I'll use `grepper` to quickly locate the configuration knobs associated with `bpf_trampoline_multi_detach` and `bpf_trampoline_multi_attach_free`.



4/2 2026/07/16 13:47 tool grepper
0m Args:
{
  "Expression": "bpf_trampoline_multi_detach"
}

Results:
{
  "Output": "include/linux/bpf.h=1522=int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,\ninclude/linux/bpf.h-1523-\t\t\t\tstruct bpf_tracing_multi_link *link);\ninclude/linux/bpf.h:1524:int bpf_trampoline_multi_detach(struct bpf_prog *prog,\ninclude/linux/bpf.h-1525-\t\t\t\tstruct bpf_tracing_multi_link *link);\n--\ninclude/linux/bpf.h=1639=static inline int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,\n--\ninclude/linux/bpf.h-1643-}\ninclude/linux/bpf.h:1644:static inline int bpf_trampoline_multi_detach(struct bpf_prog *prog,\ninclude/linux/bpf.h-1645-\t\t\t\t\t      struct bpf_tracing_multi_link *link)\n--\nkernel/bpf/trampoline.c=1622=int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,\n--\nkernel/bpf/trampoline.c-1721-\nkernel/bpf/trampoline.c:1722:int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_link *link)\nkernel/bpf/trampoline.c-1723-{\n--\nkernel/trace/bpf_trace.c=3685=static void bpf_tracing_multi_link_release(struct bpf_link *link)\n--\nkernel/trace/bpf_trace.c-3689-\nkernel/trace/bpf_trace.c:3690:\tWARN_ON_ONCE(bpf_trampoline_multi_detach(link-\u003eprog, tr_link));\nkernel/trace/bpf_trace.c-3691-}\n"
}

5/2 2026/07/16 13:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 3915
							output: 30
							thoughts: 47
						

Thoughts:
Okay, here's my interpretation of that thought process, assuming I'm already deep in kernel development and familiar with BPF:

**Navigating the BPF Trampoline Landscape**

Right, so I'm thinking about `bpf_trampoline_multi_detach`. I know that function, it's a key part of the multi-detach functionality we've got in BPF. Now, where is that actually defined? Okay, memory jogged, it's sitting in `kernel/bpf/trampoline.c`. Good, good. But I need to refresh the details, especially as it relates to config options. Let's pull up the Kconfig for that file to see what features need to be enabled for it to be compiled in. Gotta make sure all the right configuration knobs are flipped, so that multi-detach is active and ready to use!



6/2 2026/07/16 13:47 tool grepper
0m Args:
{
  "Expression": "trampoline\\.o",
  "PathPrefix": "kernel/bpf/Makefile"
}

Results:
{
  "Output": "kernel/bpf/Makefile=16=obj-$(CONFIG_BPF_SYSCALL) += disasm.o mprog.o\nkernel/bpf/Makefile:17:obj-$(CONFIG_BPF_JIT) += trampoline.o\nkernel/bpf/Makefile-18-obj-$(CONFIG_BPF_SYSCALL) += btf.o memalloc.o rqspinlock.o stream.o\n"
}

7/2 2026/07/16 13:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 3999
							output: 24
							thoughts: 0
						

8/2 2026/07/16 13:47 tool grepper
0m Args:
{
  "Expression": "bpf_tracing_multi_link_release"
}

Results:
{
  "Output": "kernel/trace/bpf_trace.c=3680=__bpf_kfunc_end_defs();\n--\nkernel/trace/bpf_trace.c-3684-\nkernel/trace/bpf_trace.c:3685:static void bpf_tracing_multi_link_release(struct bpf_link *link)\nkernel/trace/bpf_trace.c-3686-{\n--\nkernel/trace/bpf_trace.c=3784=static const struct bpf_link_ops bpf_tracing_multi_link_lops = {\nkernel/trace/bpf_trace.c:3785:\t.release = bpf_tracing_multi_link_release,\nkernel/trace/bpf_trace.c-3786-\t.dealloc_deferred = bpf_tracing_multi_link_dealloc,\n"
}

9/2 2026/07/16 13:47 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 4246
							output: 25
							thoughts: 0
						

10/2 2026/07/16 13:48 tool grepper
0m Args:
{
  "Expression": "bpf_tracing_multi_link_lops"
}

Results:
{
  "Output": "kernel/trace/bpf_trace.c=3758=static void bpf_tracing_multi_show_fdinfo(const struct bpf_link *link,\n--\nkernel/trace/bpf_trace.c-3783-\nkernel/trace/bpf_trace.c:3784:static const struct bpf_link_ops bpf_tracing_multi_link_lops = {\nkernel/trace/bpf_trace.c-3785-\t.release = bpf_tracing_multi_link_release,\n--\nkernel/trace/bpf_trace.c=3840=int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)\n--\nkernel/trace/bpf_trace.c-3901-\tbpf_link_init(\u0026link-\u003elink, BPF_LINK_TYPE_TRACING_MULTI,\nkernel/trace/bpf_trace.c:3902:\t\t      \u0026bpf_tracing_multi_link_lops, prog, prog-\u003eexpected_attach_type);\nkernel/trace/bpf_trace.c-3903-\n"
}

11/2 2026/07/16 13:48 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 4561
							output: 22
							thoughts: 0
						

12/2 2026/07/16 13:48 tool grepper
0m Args:
{
  "Expression": "bpf_tracing_multi_attach"
}

Results:
{
  "Output": "include/linux/trace_events.h=789=int bpf_uprobe_multi_link_attach(const union bpf_attr *attr, struct bpf_prog *prog);\ninclude/linux/trace_events.h:790:int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr);\ninclude/linux/trace_events.h-791-#else\n--\ninclude/linux/trace_events.h=848=static inline int\ninclude/linux/trace_events.h:849:bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)\ninclude/linux/trace_events.h-850-{\n--\nkernel/bpf/syscall.c=5765=static int link_create(union bpf_attr *attr, bpfptr_t uattr)\n--\nkernel/bpf/syscall.c-5815-\t\telse if (is_tracing_multi(prog-\u003eexpected_attach_type))\nkernel/bpf/syscall.c:5816:\t\t\tret = bpf_tracing_multi_attach(prog, attr);\nkernel/bpf/syscall.c-5817-\t\telse\n--\nkernel/trace/bpf_trace.c=3819=static int check_dup_ids(u32 *ids, u64 *cookies, u32 cnt)\n--\nkernel/trace/bpf_trace.c-3839-\nkernel/trace/bpf_trace.c:3840:int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)\nkernel/trace/bpf_trace.c-3841-{\n--\nkernel/trace/bpf_trace.c-3929-\nkernel/trace/bpf_trace.c:3930:int bpf_tracing_multi_attach(struct bpf_prog *prog, const union bpf_attr *attr)\nkernel/trace/bpf_trace.c-3931-{\n"
}

13/2 2026/07/16 13:48 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5385
							output: 31
							thoughts: 114
						

Thoughts:
**My Analysis of BPF Tracing Multi-Attach**

Okay, let's break this down. It looks like we're dealing with `BPF_LINK_TYPE_TRACING_MULTI`. That's created through the `bpf_tracing_multi_attach` function, which is ultimately called from `link_create` in `kernel/bpf/syscall.c`. Good, that means it's a definite pathway, reachable and functional in the kernel.

Now, I need to make sure the right kernel configuration options are enabled. Definitely need `CONFIG_BPF_SYSCALL` and `CONFIG_BPF_JIT`. Those are essential. And, since this involves tracing, it's highly probable that `CONFIG_FTRACE` is also required. Let me quickly double-check that by examining `kernel/trace/Makefile` to see if `bpf_trace.c` is being compiled. Just to be completely certain that tracing support is available and properly linked.



14/2 2026/07/16 13:48 tool grepper
0m Args:
{
  "Expression": "bpf_trace\\.o",
  "PathPrefix": "kernel/trace/Makefile"
}

Results:
{
  "Output": "kernel/trace/Makefile=54=KCOV_INSTRUMENT_trace_irqsoff.o := n\nkernel/trace/Makefile-55-\nkernel/trace/Makefile:56:CFLAGS_bpf_trace.o := -I$(src)\nkernel/trace/Makefile-57-\n--\nkernel/trace/Makefile=109=obj-$(CONFIG_USER_EVENTS) += trace_events_user.o\nkernel/trace/Makefile:110:obj-$(CONFIG_BPF_EVENTS) += bpf_trace.o\nkernel/trace/Makefile-111-obj-$(CONFIG_KPROBE_EVENTS) += trace_kprobe.o\n"
}

15/2 2026/07/16 13:48 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5511
							output: 25
							thoughts: 316
						

Thoughts:
**Analyzing the BPF Trampoline Patch**

Okay, so this patch is clearly touching `kernel/bpf/trampoline.c`, specifically the `bpf_trampoline_multi_detach` and `bpf_trampoline_multi_attach_free` functions. That's immediately interesting; these are core components for managing BPF trampolines. The addition of rollback logic here is what's crucial. It means they're adding a safety net when `update_ftrace_direct_del` or `update_ftrace_direct_mod` fail, which is good. We're talking about a reachable code path through `bpf_tracing_multi_attach` and its associated release mechanisms. That makes sense for the use case.

From a configuration perspective, `CONFIG_BPF_JIT`, `CONFIG_BPF_EVENTS`, and `CONFIG_BPF_SYSCALL` are the key enabling configurations. Without those, this is all academic. The real meat of the changes is centered around these trampoline management functions: `bpf_trampoline_multi_detach`, `bpf_trampoline_multi_attach_free`, and also `bpf_trampoline_multi_attach_rollback`. Wait, what's this `bpf_trampoline_multi_attach_rollback`? It's being *called* in the patch, but is it *defined* here? The context isn't showing a definition, which is not unusual, it could be defined elsewhere.

Let's dig deeper. The patch calls `bpf_trampoline_multi_attach_rollback(tr)`. I need to ensure it's been defined, and ensure that the code is well-formed. It has to be defined. Let's make sure `bpf_trampoline_multi_attach_rollback` exists in the codebase!



16/2 2026/07/16 13:48 tool grepper
0m Args:
{
  "Expression": "bpf_trampoline_multi_attach_rollback"
}

Results:
{
  "Output": "kernel/bpf/trampoline.c=1586=static void bpf_trampoline_multi_attach_free(struct bpf_trampoline *tr)\n--\nkernel/bpf/trampoline.c-1604-\nkernel/bpf/trampoline.c:1605:static void bpf_trampoline_multi_attach_rollback(struct bpf_trampoline *tr)\nkernel/bpf/trampoline.c-1606-{\n--\nkernel/bpf/trampoline.c=1622=int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,\n--\nkernel/bpf/trampoline.c-1706-\t\tbpf_trampoline_remove_prog(mnode-\u003etrampoline, \u0026mnode-\u003enode);\nkernel/bpf/trampoline.c:1707:\t\tbpf_trampoline_multi_attach_rollback(mnode-\u003etrampoline);\nkernel/bpf/trampoline.c-1708-\t}\n--\nkernel/bpf/trampoline.c=1722=int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_link *link)\n--\nkernel/bpf/trampoline.c-1767-\t\t     (err_mod \u0026\u0026 tr-\u003ecur_image)))\nkernel/bpf/trampoline.c:1768:\t\t\tbpf_trampoline_multi_attach_rollback(tr);\nkernel/bpf/trampoline.c-1769-\t\telse\n"
}

17/2 2026/07/16 13:49 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6320
							output: 22
							thoughts: 412
						

Thoughts:
**Analyzing a BPF Trampoline UAF Fix**

Okay, so I'm looking at this patch and it's addressing a use-after-free vulnerability within the BPF trampoline multi-attach/detach logic. It appears to be a functional change focusing on `bpf_trampoline_multi_detach` and `bpf_trampoline_multi_attach_free` in `kernel/bpf/trampoline.c`. Specifically, the patch modifies `bpf_trampoline_multi_detach` to properly handle errors that might come from `update_ftrace_direct_del` and `update_ftrace_direct_mod`. This is critical, as failure to handle these errors would propagate and lead to UAF issues.

The other part modifies `bpf_trampoline_multi_attach_free` to be smarter about releasing `old_image`. It seems it only releases the `old_image` if it's *no longer* the active image. This prevents premature freeing, which is likely the root cause of the UAF. The BPF syscall `link_create` with something like `BPF_TRACE_FENTRY` or other multi-attach tracing programs seems to be the entry point for triggering this bug.

I need to quickly verify the build environment and dependencies. `CONFIG_BPF_SYSCALL`, `CONFIG_BPF_JIT`, and `CONFIG_BPF_EVENTS` are obviously relevant. `CONFIG_BPF_JIT` is key since it's required for `trampoline.o` and `kernel/bpf/trampoline.c`. I need to ensure that `CONFIG_BPF_EVENTS` is also enabled since it leads to `bpf_trace.o` which, in turn, calls `bpf_trampoline_multi_detach`. I'm guessing `CONFIG_FTRACE` is implied since we're dealing with `update_ftrace_direct_del`. And I need to check `CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS` as that is probably needed for direct ftrace calls.

Now, I'll quickly check the dependencies to make sure everything lines up. I need to make sure `kernel/bpf/trampoline.c` gets compiled if `CONFIG_BPF_JIT` is set and that `kernel/trace/bpf_trace.c` is compiled when `CONFIG_BPF_EVENTS` is enabled. Time to verify these configs...



18/2 2026/07/16 13:49 tool grepper
0m Args:
{
  "Expression": "update_ftrace_direct_del"
}

Results:
{
  "Output": "include/linux/ftrace.h=550=int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash);\ninclude/linux/ftrace.h:551:int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash);\ninclude/linux/ftrace.h-552-int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock);\n--\ninclude/linux/ftrace.h=582=static inline int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)\n--\ninclude/linux/ftrace.h-586-\ninclude/linux/ftrace.h:587:static inline int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash)\ninclude/linux/ftrace.h-588-{\n--\nkernel/bpf/trampoline.c=269=static int direct_ops_del(struct bpf_trampoline *tr, void *addr)\n--\nkernel/bpf/trampoline.c-275-\t\treturn -ENOMEM;\nkernel/bpf/trampoline.c:276:\terr = update_ftrace_direct_del(tr-\u003efops, hash);\nkernel/bpf/trampoline.c-277-\tfree_ftrace_hash(hash);\n--\nkernel/bpf/trampoline.c=1622=int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,\n--\nkernel/bpf/trampoline.c-1690-\t\t\tif (ftrace_hash_count(data-\u003ereg))\nkernel/bpf/trampoline.c:1691:\t\t\t\tWARN_ON_ONCE(update_ftrace_direct_del(\u0026direct_ops, data-\u003ereg));\nkernel/bpf/trampoline.c-1692-\t\t\tgoto rollback_unlink;\n--\nkernel/bpf/trampoline.c=1722=int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_link *link)\n--\nkernel/bpf/trampoline.c-1737-\tif (ftrace_hash_count(data-\u003eunreg)) {\nkernel/bpf/trampoline.c:1738:\t\terr_unreg = update_ftrace_direct_del(\u0026direct_ops, data-\u003eunreg);\nkernel/bpf/trampoline.c-1739-\t\tWARN_ON_ONCE(err_unreg);\n--\nkernel/bpf/trampoline.c-1753-\t\t *\nkernel/bpf/trampoline.c:1754:\t\t * This relies on update_ftrace_direct_del/mod being atomic:\nkernel/bpf/trampoline.c-1755-\t\t * on failure, NO IPs in the hash are modified in ftrace (all\n--\nkernel/trace/ftrace.c=6461=static struct ftrace_hash *hash_sub(struct ftrace_hash *a, struct ftrace_hash *b)\n--\nkernel/trace/ftrace.c-6486-/**\nkernel/trace/ftrace.c:6487: * update_ftrace_direct_del - Updates @ops by removing its direct\nkernel/trace/ftrace.c-6488- * callers provided in @hash\n--\nkernel/trace/ftrace.c-6499- */\nkernel/trace/ftrace.c:6500:int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash)\nkernel/trace/ftrace.c-6501-{\n"
}

19/2 2026/07/16 13:49 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 7811
							output: 22
							thoughts: 0
						

20/2 2026/07/16 13:49 tool grepper
0m Args:
{
  "Expression": "update_ftrace_direct_mod"
}

Results:
{
  "Output": "include/linux/ftrace.h=551=int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash);\ninclude/linux/ftrace.h:552:int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock);\ninclude/linux/ftrace.h-553-\n--\ninclude/linux/ftrace.h=587=static inline int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash)\n--\ninclude/linux/ftrace.h-591-\ninclude/linux/ftrace.h:592:static inline int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock)\ninclude/linux/ftrace.h-593-{\n--\nkernel/bpf/trampoline.c=281=static int direct_ops_mod(struct bpf_trampoline *tr, void *addr, bool lock_direct_mutex)\n--\nkernel/bpf/trampoline.c-287-\t\treturn -ENOMEM;\nkernel/bpf/trampoline.c:288:\terr = update_ftrace_direct_mod(tr-\u003efops, hash, lock_direct_mutex);\nkernel/bpf/trampoline.c-289-\tfree_ftrace_hash(hash);\n--\nkernel/bpf/trampoline.c=1622=int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,\n--\nkernel/bpf/trampoline.c-1687-\tif (ftrace_hash_count(data-\u003emodify)) {\nkernel/bpf/trampoline.c:1688:\t\terr = update_ftrace_direct_mod(\u0026direct_ops, data-\u003emodify, true);\nkernel/bpf/trampoline.c-1689-\t\tif (err) {\n--\nkernel/bpf/trampoline.c=1722=int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_link *link)\n--\nkernel/bpf/trampoline.c-1741-\tif (ftrace_hash_count(data-\u003emodify)) {\nkernel/bpf/trampoline.c:1742:\t\terr_mod = update_ftrace_direct_mod(\u0026direct_ops, data-\u003emodify, true);\nkernel/bpf/trampoline.c-1743-\t\tWARN_ON_ONCE(err_mod);\n--\nkernel/trace/ftrace.c=6500=int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash)\n--\nkernel/trace/ftrace.c-6583-/**\nkernel/trace/ftrace.c:6584: * update_ftrace_direct_mod - Updates @ops by modifing its direct\nkernel/trace/ftrace.c-6585- * callers provided in @hash\n--\nkernel/trace/ftrace.c-6600- */\nkernel/trace/ftrace.c:6601:int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock)\nkernel/trace/ftrace.c-6602-{\n"
}

21/2 2026/07/16 13:49 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 9813
							output: 36
							thoughts: 188
						

Thoughts:
**Patch Verification and Configuration Assessment**

Okay, so this patch is a solid fix; it definitely addresses that Use-After-Free bug in the BPF trampoline's multi-detach sequence. That's good to see. Now, let's talk about the required kernel configurations. My immediate thought is to list `BPF_SYSCALL`, `BPF_JIT`, and `BPF_EVENTS`. Those are the core components that need to be enabled for this functionality to work correctly.

However, I should probably also mention `DYNAMIC_FTRACE_WITH_DIRECT_CALLS`. While the core BPF elements are the main drivers here, the `update_ftrace_direct_del` function, which might be relevant in certain scenarios, has a full implementation only if `DYNAMIC_FTRACE_WITH_DIRECT_CALLS` is enabled; otherwise, it's just a stub. Just to be completely certain about that, I should double-check the definition of `update_ftrace_direct_del` within `include/linux/ftrace.h`. It's always best to be thorough, especially when dealing with kernel configuration dependencies.



22/2 2026/07/16 13:50 tool grepper
0m Args:
{
  "Expression": "update_ftrace_direct_del",
  "PathPrefix": "include/linux/ftrace.h"
}

Results:
{
  "Output": "include/linux/ftrace.h=550=int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash);\ninclude/linux/ftrace.h:551:int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash);\ninclude/linux/ftrace.h-552-int update_ftrace_direct_mod(struct ftrace_ops *ops, struct ftrace_hash *hash, bool do_direct_lock);\n--\ninclude/linux/ftrace.h=582=static inline int update_ftrace_direct_add(struct ftrace_ops *ops, struct ftrace_hash *hash)\n--\ninclude/linux/ftrace.h-586-\ninclude/linux/ftrace.h:587:static inline int update_ftrace_direct_del(struct ftrace_ops *ops, struct ftrace_hash *hash)\ninclude/linux/ftrace.h-588-{\n"
}

23/2 2026/07/16 13:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 10536
							output: 41
							thoughts: 0
						

24/2 2026/07/16 13:50 tool grepper
0m Args:
{
  "Expression": "CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS",
  "PathPrefix": "include/linux/ftrace.h"
}

Results:
{
  "Output": "include/linux/ftrace.h=449=struct ftrace_ops {\n--\ninclude/linux/ftrace.h-464-\tstruct ftrace_ops\t\t*managed;\ninclude/linux/ftrace.h:465:#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS\ninclude/linux/ftrace.h-466-\tunsigned long\t\t\tdirect_call;\n--\ninclude/linux/ftrace.h=536=struct ftrace_func_entry {\n--\ninclude/linux/ftrace.h-541-\ninclude/linux/ftrace.h:542:#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS\ninclude/linux/ftrace.h-543-unsigned long ftrace_find_rec_direct(unsigned long ip);\n--\ninclude/linux/ftrace.h=615=static inline void arch_ftrace_set_direct_caller(struct ftrace_regs *fregs,\ninclude/linux/ftrace.h-616-\t\t\t\t\t\t unsigned long addr) { }\ninclude/linux/ftrace.h:617:#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */\ninclude/linux/ftrace.h-618-\n--\ninclude/linux/ftrace.h=1017=extern int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr);\n--\ninclude/linux/ftrace.h-1020-\tdefined(CONFIG_DYNAMIC_FTRACE_WITH_CALL_OPS) || \\\ninclude/linux/ftrace.h:1021:\tdefined(CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS)\ninclude/linux/ftrace.h-1022-/**\n"
}

25/2 2026/07/16 13:50 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 11007
							output: 153
							thoughts: 383
						

Thoughts:
**My Analysis of the BPF Trampoline Multi-Detach Patch**

Okay, so I've confirmed that `CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS` is the key config for enabling direct calls in this BPF context. Now, to be absolutely clear on what needs to be enabled for effective testing, I need to list the required configurations: `BPF_SYSCALL`, `BPF_JIT`, `BPF_EVENTS`, and crucially, `DYNAMIC_FTRACE_WITH_DIRECT_CALLS`. And, of course, the underlying `CONFIG_FTRACE` is a requirement for the `DYNAMIC_FTRACE_WITH_DIRECT_CALLS` to function.

The core of this patch centers on addressing a use-after-free vulnerability within the BPF trampoline's multi-detach logic. The fix is about robustly handling ftrace update failures. The patch rolls back the active image if there's a problem. This is critical BPF tracing logic, and it's definitely reachable.

I've got a list of functions that need close attention: `bpf_trampoline_multi_detach`, `bpf_trampoline_multi_attach_free`, and `bpf_trampoline_multi_attach_rollback`.  Let's double check... Yes, `bpf_trampoline_multi_attach_rollback` is indeed defined in the source, I can see it at line 1605.

Given the nature of the fix, fuzzing this area seems worthwhile. And, as I said, the key symbols I need to focus on during fuzzing will be the `bpf_trampoline_multi_detach`, `bpf_trampoline_multi_attach_free`, and `bpf_trampoline_multi_attach_rollback` functions. So, I need to ensure those configurations are enabled.  That should give me the coverage I need to effectively test this patch. Let's get the results set.



26/2 2026/07/16 13:50 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT",
    "BPF_EVENTS",
    "DYNAMIC_FTRACE_WITH_DIRECT_CALLS"
  ],
  "FocusSymbols": [
    "bpf_trampoline_multi_detach",
    "bpf_trampoline_multi_attach_free",
    "bpf_trampoline_multi_attach_rollback"
  ],
  "Reasoning": "The patch fixes a use-after-free bug in the BPF trampoline multi-detach logic by correctly handling ftrace update failures and rolling back the active image. This is reachable and functional core kernel logic for BPF tracing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT",
    "BPF_EVENTS",
    "DYNAMIC_FTRACE_WITH_DIRECT_CALLS"
  ],
  "FocusSymbols": [
    "bpf_trampoline_multi_detach",
    "bpf_trampoline_multi_attach_free",
    "bpf_trampoline_multi_attach_rollback"
  ],
  "Reasoning": "The patch fixes a use-after-free bug in the BPF trampoline multi-detach logic by correctly handling ftrace update failures and rolling back the active image. This is reachable and functional core kernel logic for BPF tracing.",
  "WorthFuzzing": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)