AI Trajectory


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

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT",
    "FTRACE",
    "DYNAMIC_FTRACE",
    "KPROBES"
  ],
  "FocusSymbols": [
    "bpf_trampoline_link_prog",
    "bpf_trampoline_unlink_prog",
    "bpf_trampoline_multi_attach",
    "bpf_trampoline_multi_detach",
    "register_kprobe",
    "unregister_kprobe",
    "register_kretprobe",
    "unregister_kretprobe",
    "ftrace_enable_sysctl"
  ],
  "Reasoning": "The patch sets FTRACE_OPS_FL_PERMANENT on direct_ops and kprobe_ftrace_ops/kprobe_ipmodify_ops. This prevents disabling ftrace globally via /proc/sys/kernel/ftrace_enabled while BPF trampolines or kprobes are active. This is a functional change in core kernel logic (BPF trampoline and kprobes) and should be fuzzed to ensure it correctly prevents ftrace disabling and doesn't introduce regressions or race conditions during attach/detach operations.",
  "WorthFuzzing": true
}

1/1 2026/07/30 06:47 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit c6200124fe3d948e4c823c401382932a057b1ab6\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Jul 30 06:47:40 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c\nindex 129d07db117ec..3d5069091db76 100644\n--- a/kernel/bpf/trampoline.c\n+++ b/kernel/bpf/trampoline.c\n@@ -224,6 +224,8 @@ void bpf_image_ksym_del(struct bpf_ksym *ksym)\n  */\n struct ftrace_ops direct_ops = {\n \t.ops_func = bpf_tramp_ftrace_ops_func,\n+\t/* Same protection livepatch gives its own ftrace_ops. */\n+\t.flags = FTRACE_OPS_FL_PERMANENT,\n };\n \n static int direct_ops_alloc(struct bpf_trampoline *tr)\n@@ -303,6 +305,8 @@ static int direct_ops_alloc(struct bpf_trampoline *tr)\n \t\treturn -ENOMEM;\n \ttr-\u003efops-\u003eprivate = tr;\n \ttr-\u003efops-\u003eops_func = bpf_tramp_ftrace_ops_func;\n+\t/* See the direct_ops initializer above for why. */\n+\ttr-\u003efops-\u003eflags |= FTRACE_OPS_FL_PERMANENT;\n \treturn 0;\n }\n \ndiff --git a/kernel/kprobes.c b/kernel/kprobes.c\nindex bfc89083daa93..5a54511eee791 100644\n--- a/kernel/kprobes.c\n+++ b/kernel/kprobes.c\n@@ -1122,14 +1122,15 @@ static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)\n #endif /* CONFIG_OPTPROBES */\n \n #ifdef CONFIG_KPROBES_ON_FTRACE\n+/* Same protection livepatch gives its own ftrace_ops. */\n static struct ftrace_ops kprobe_ftrace_ops __read_mostly = {\n \t.func = kprobe_ftrace_handler,\n-\t.flags = FTRACE_OPS_FL_SAVE_REGS,\n+\t.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_PERMANENT,\n };\n \n static struct ftrace_ops kprobe_ipmodify_ops __read_mostly = {\n \t.func = kprobe_ftrace_handler,\n-\t.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY,\n+\t.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY | FTRACE_OPS_FL_PERMANENT,\n };\n \n static int kprobe_ipmodify_enabled;\ndiff --git a/tools/testing/selftests/bpf/prog_tests/ftrace_permanent.c b/tools/testing/selftests/bpf/prog_tests/ftrace_permanent.c\nnew file mode 100644\nindex 0000000000000..dbe78009a2913\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/prog_tests/ftrace_permanent.c\n@@ -0,0 +1,144 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 CrowdStrike */\n+#include \u003ctest_progs.h\u003e\n+#include \"ftrace_permanent.skel.h\"\n+\n+/*\n+ * fentry/fexit/kprobe/kretprobe now carry FTRACE_OPS_FL_PERMANENT, so\n+ * kernel.ftrace_enabled=0 must be refused with EBUSY while any of them\n+ * is attached, and allowed again once detached.\n+ *\n+ * kprobe.multi/kretprobe.multi/kprobe.session are out of scope.\n+ * kprobe/kretprobe subtests need CONFIG_KPROBES_ON_FTRACE (e.g. not on\n+ * arm64) and are skipped otherwise.\n+ */\n+\n+#define FTRACE_ENABLED_PATH \"/proc/sys/kernel/ftrace_enabled\"\n+\n+static int read_ftrace_enabled(int *val)\n+{\n+\tchar buf[16] = {};\n+\tint fd, n;\n+\n+\tfd = open(FTRACE_ENABLED_PATH, O_RDONLY);\n+\tif (fd \u003c 0)\n+\t\treturn -errno;\n+\tn = read(fd, buf, sizeof(buf) - 1);\n+\tclose(fd);\n+\tif (n \u003c= 0)\n+\t\treturn -EIO;\n+\t*val = atoi(buf);\n+\treturn 0;\n+}\n+\n+/* Returns 0 on success, or -errno on write failure. */\n+static int write_ftrace_enabled(int val)\n+{\n+\tchar buf[4];\n+\tint fd, n, len, err = 0;\n+\n+\tfd = open(FTRACE_ENABLED_PATH, O_WRONLY);\n+\tif (fd \u003c 0)\n+\t\treturn -errno;\n+\tlen = snprintf(buf, sizeof(buf), \"%d\", val);\n+\tn = write(fd, buf, len);\n+\tif (n \u003c 0)\n+\t\terr = -errno;\n+\tclose(fd);\n+\treturn err;\n+}\n+\n+/*\n+ * Attach @prog, assert kernel.ftrace_enabled=0 is refused while attached\n+ * and stays at 1, then detach and assert the disable now succeeds.\n+ */\n+static void check_blocks_disable(struct bpf_program *prog, const char *name)\n+{\n+\tstruct bpf_link *link;\n+\tint val, err;\n+\n+\tlink = bpf_program__attach(prog);\n+\tif (!ASSERT_OK_PTR(link, name))\n+\t\treturn;\n+\n+\terr = write_ftrace_enabled(0);\n+\tASSERT_EQ(err, -EBUSY, \"disable_refused\");\n+\tif (!ASSERT_OK(read_ftrace_enabled(\u0026val), \"read_back\"))\n+\t\tgoto detach;\n+\tASSERT_EQ(val, 1, \"still_enabled\");\n+\n+detach:\n+\tbpf_link__destroy(link);\n+\n+\tASSERT_OK(write_ftrace_enabled(0), \"disable_after_detach\");\n+\tASSERT_OK(write_ftrace_enabled(1), \"reenable\");\n+}\n+\n+/* Attach @prog while ftrace_enabled=0 and assert it is refused. */\n+static void check_attach_while_disabled_refused(struct bpf_program *prog, const char *name)\n+{\n+\tstruct bpf_link *link;\n+\n+\tif (!ASSERT_OK(write_ftrace_enabled(0), \"disable\"))\n+\t\treturn;\n+\n+\tlink = bpf_program__attach(prog);\n+\tif (!ASSERT_ERR_PTR(link, name))\n+\t\tbpf_link__destroy(link);\n+\n+\tASSERT_OK(write_ftrace_enabled(1), \"reenable\");\n+}\n+\n+void test_ftrace_permanent(void)\n+{\n+\tstruct ftrace_permanent *skel;\n+\tbool kprobes_on_ftrace;\n+\tint orig = 1;\n+\n+\t/* Save and always restore ftrace_enabled. */\n+\tif (read_ftrace_enabled(\u0026orig)) {\n+\t\ttest__skip();\n+\t\treturn;\n+\t}\n+\n+\tskel = ftrace_permanent__open_and_load();\n+\tif (!ASSERT_OK_PTR(skel, \"skel_open_and_load\"))\n+\t\tgoto restore;\n+\n+\tkprobes_on_ftrace = skel-\u003ekconfig-\u003eCONFIG_KPROBES_ON_FTRACE;\n+\n+\tif (test__start_subtest(\"fentry_blocks_disable\"))\n+\t\tcheck_blocks_disable(skel-\u003eprogs.test_fentry, \"attach_fentry\");\n+\n+\tif (test__start_subtest(\"fexit_blocks_disable\"))\n+\t\tcheck_blocks_disable(skel-\u003eprogs.test_fexit, \"attach_fexit\");\n+\n+\tif (test__start_subtest(\"kprobe_blocks_disable\")) {\n+\t\tif (kprobes_on_ftrace)\n+\t\t\tcheck_blocks_disable(skel-\u003eprogs.test_kprobe, \"attach_kprobe\");\n+\t\telse\n+\t\t\ttest__skip();\n+\t}\n+\n+\tif (test__start_subtest(\"kretprobe_blocks_disable\")) {\n+\t\tif (kprobes_on_ftrace)\n+\t\t\tcheck_blocks_disable(skel-\u003eprogs.test_kretprobe, \"attach_kretprobe\");\n+\t\telse\n+\t\t\ttest__skip();\n+\t}\n+\n+\tif (test__start_subtest(\"fentry_attach_while_disabled_refused\"))\n+\t\tcheck_attach_while_disabled_refused(skel-\u003eprogs.test_fentry, \"attach_fentry\");\n+\n+\tif (test__start_subtest(\"kprobe_attach_while_disabled_refused\")) {\n+\t\tif (kprobes_on_ftrace)\n+\t\t\tcheck_attach_while_disabled_refused(skel-\u003eprogs.test_kprobe,\n+\t\t\t\t\t\t\t    \"attach_kprobe\");\n+\t\telse\n+\t\t\ttest__skip();\n+\t}\n+\n+\tftrace_permanent__destroy(skel);\n+restore:\n+\twrite_ftrace_enabled(orig);\n+}\ndiff --git a/tools/testing/selftests/bpf/progs/ftrace_permanent.c b/tools/testing/selftests/bpf/progs/ftrace_permanent.c\nnew file mode 100644\nindex 0000000000000..ca706d8edc23b\n--- /dev/null\n+++ b/tools/testing/selftests/bpf/progs/ftrace_permanent.c\n@@ -0,0 +1,43 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/* Copyright (c) 2026 CrowdStrike */\n+#include \"vmlinux.h\"\n+#include \u003cbpf/bpf_helpers.h\u003e\n+#include \u003cbpf/bpf_tracing.h\u003e\n+#include \u003cstdbool.h\u003e\n+\n+char _license[] SEC(\"license\") = \"GPL\";\n+\n+extern bool CONFIG_KPROBES_ON_FTRACE __kconfig __weak;\n+\n+/* This function is here to have CONFIG_KPROBES_ON_FTRACE used and\n+ * added to object BTF, so the userspace side can read it back via\n+ * skel-\u003ekconfig-\u003eCONFIG_KPROBES_ON_FTRACE.\n+ */\n+int unused(void)\n+{\n+\treturn CONFIG_KPROBES_ON_FTRACE ? 0 : 1;\n+}\n+\n+SEC(\"fentry/bpf_fentry_test1\")\n+int BPF_PROG(test_fentry, int a)\n+{\n+\treturn 0;\n+}\n+\n+SEC(\"fexit/bpf_fentry_test2\")\n+int BPF_PROG(test_fexit, int a, __u64 b)\n+{\n+\treturn 0;\n+}\n+\n+SEC(\"kprobe/bpf_fentry_test3\")\n+int test_kprobe(struct pt_regs *ctx)\n+{\n+\treturn 0;\n+}\n+\n+SEC(\"kretprobe/bpf_fentry_test4\")\n+int BPF_KRETPROBE(test_kretprobe)\n+{\n+\treturn 0;\n+}\n"
}

2/1 2026/07/30 06:47 agent patch-evaluator
1m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT",
    "FTRACE",
    "DYNAMIC_FTRACE",
    "KPROBES"
  ],
  "FocusSymbols": [
    "bpf_trampoline_link_prog",
    "bpf_trampoline_unlink_prog",
    "bpf_trampoline_multi_attach",
    "bpf_trampoline_multi_detach",
    "register_kprobe",
    "unregister_kprobe",
    "register_kretprobe",
    "unregister_kretprobe",
    "ftrace_enable_sysctl"
  ],
  "Reasoning": "The patch sets FTRACE_OPS_FL_PERMANENT on direct_ops and kprobe_ftrace_ops/kprobe_ipmodify_ops. This prevents disabling ftrace globally via /proc/sys/kernel/ftrace_enabled while BPF trampolines or kprobes are active. This is a functional change in core kernel logic (BPF trampoline and kprobes) and should be fuzzed to ensure it correctly prevents ftrace disabling and doesn't introduce regressions or race conditions during attach/detach operations.",
  "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).
- Code in vendor-specific PCIe switch, SmartNIC, or GPU drivers (e.g., mlxsw, pds_core, qed,
  ionic, amdgpu) that require physical PCIe hardware cards not emulated in standard QEMU.
- Driver .remove, .shutdown, or pci_unregister_driver teardown callbacks (e.g., igb_remove)
  that are executed only during PCI hot-unplug or sysfs driver unbind operations.

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.
   Prefer non-static, non-inlined API entrypoint functions over internal static helper functions
   (which are inlined by the compiler and do not have distinct symbol addresses).
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.
   Also check "depends on" lines in Kconfig to include any non-standard parent subsystem configs
   needed for Kbuild to compile the code statically into vmlinux. 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 c6200124fe3d948e4c823c401382932a057b1ab6
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Jul 30 06:47:40 2026 +0000

    syz-cluster: applied patch under review

diff --git a/kernel/bpf/trampoline.c b/kernel/bpf/trampoline.c
index 129d07db117ec..3d5069091db76 100644
--- a/kernel/bpf/trampoline.c
+++ b/kernel/bpf/trampoline.c
@@ -224,6 +224,8 @@ void bpf_image_ksym_del(struct bpf_ksym *ksym)
  */
 struct ftrace_ops direct_ops = {
 	.ops_func = bpf_tramp_ftrace_ops_func,
+	/* Same protection livepatch gives its own ftrace_ops. */
+	.flags = FTRACE_OPS_FL_PERMANENT,
 };
 
 static int direct_ops_alloc(struct bpf_trampoline *tr)
@@ -303,6 +305,8 @@ static int direct_ops_alloc(struct bpf_trampoline *tr)
 		return -ENOMEM;
 	tr->fops->private = tr;
 	tr->fops->ops_func = bpf_tramp_ftrace_ops_func;
+	/* See the direct_ops initializer above for why. */
+	tr->fops->flags |= FTRACE_OPS_FL_PERMANENT;
 	return 0;
 }
 
diff --git a/kernel/kprobes.c b/kernel/kprobes.c
index bfc89083daa93..5a54511eee791 100644
--- a/kernel/kprobes.c
+++ b/kernel/kprobes.c
@@ -1122,14 +1122,15 @@ static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)
 #endif /* CONFIG_OPTPROBES */
 
 #ifdef CONFIG_KPROBES_ON_FTRACE
+/* Same protection livepatch gives its own ftrace_ops. */
 static struct ftrace_ops kprobe_ftrace_ops __read_mostly = {
 	.func = kprobe_ftrace_handler,
-	.flags = FTRACE_OPS_FL_SAVE_REGS,
+	.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_PERMANENT,
 };
 
 static struct ftrace_ops kprobe_ipmodify_ops __read_mostly = {
 	.func = kprobe_ftrace_handler,
-	.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY,
+	.flags = FTRACE_OPS_FL_SAVE_REGS | FTRACE_OPS_FL_IPMODIFY | FTRACE_OPS_FL_PERMANENT,
 };
 
 static int kprobe_ipmodify_enabled;
diff --git a/tools/testing/selftests/bpf/prog_tests/ftrace_permanent.c b/tools/testing/selftests/bpf/prog_tests/ftrace_permanent.c
new file mode 100644
index 0000000000000..dbe78009a2913
--- /dev/null
+++ b/tools/testing/selftests/bpf/prog_tests/ftrace_permanent.c
@@ -0,0 +1,144 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 CrowdStrike */
+#include <test_progs.h>
+#include "ftrace_permanent.skel.h"
+
+/*
+ * fentry/fexit/kprobe/kretprobe now carry FTRACE_OPS_FL_PERMANENT, so
+ * kernel.ftrace_enabled=0 must be refused with EBUSY while any of them
+ * is attached, and allowed again once detached.
+ *
+ * kprobe.multi/kretprobe.multi/kprobe.session are out of scope.
+ * kprobe/kretprobe subtests need CONFIG_KPROBES_ON_FTRACE (e.g. not on
+ * arm64) and are skipped otherwise.
+ */
+
+#define FTRACE_ENABLED_PATH "/proc/sys/kernel/ftrace_enabled"
+
+static int read_ftrace_enabled(int *val)
+{
+	char buf[16] = {};
+	int fd, n;
+
+	fd = open(FTRACE_ENABLED_PATH, O_RDONLY);
+	if (fd < 0)
+		return -errno;
+	n = read(fd, buf, sizeof(buf) - 1);
+	close(fd);
+	if (n <= 0)
+		return -EIO;
+	*val = atoi(buf);
+	return 0;
+}
+
+/* Returns 0 on success, or -errno on write failure. */
+static int write_ftrace_enabled(int val)
+{
+	char buf[4];
+	int fd, n, len, err = 0;
+
+	fd = open(FTRACE_ENABLED_PATH, O_WRONLY);
+	if (fd < 0)
+		return -errno;
+	len = snprintf(buf, sizeof(buf), "%d", val);
+	n = write(fd, buf, len);
+	if (n < 0)
+		err = -errno;
+	close(fd);
+	return err;
+}
+
+/*
+ * Attach @prog, assert kernel.ftrace_enabled=0 is refused while attached
+ * and stays at 1, then detach and assert the disable now succeeds.
+ */
+static void check_blocks_disable(struct bpf_program *prog, const char *name)
+{
+	struct bpf_link *link;
+	int val, err;
+
+	link = bpf_program__attach(prog);
+	if (!ASSERT_OK_PTR(link, name))
+		return;
+
+	err = write_ftrace_enabled(0);
+	ASSERT_EQ(err, -EBUSY, "disable_refused");
+	if (!ASSERT_OK(read_ftrace_enabled(&val), "read_back"))
+		goto detach;
+	ASSERT_EQ(val, 1, "still_enabled");
+
+detach:
+	bpf_link__destroy(link);
+
+	ASSERT_OK(write_ftrace_enabled(0), "disable_after_detach");
+	ASSERT_OK(write_ftrace_enabled(1), "reenable");
+}
+
+/* Attach @prog while ftrace_enabled=0 and assert it is refused. */
+static void check_attach_while_disabled_refused(struct bpf_program *prog, const char *name)
+{
+	struct bpf_link *link;
+
+	if (!ASSERT_OK(write_ftrace_enabled(0), "disable"))
+		return;
+
+	link = bpf_program__attach(prog);
+	if (!ASSERT_ERR_PTR(link, name))
+		bpf_link__destroy(link);
+
+	ASSERT_OK(write_ftrace_enabled(1), "reenable");
+}
+
+void test_ftrace_permanent(void)
+{
+	struct ftrace_permanent *skel;
+	bool kprobes_on_ftrace;
+	int orig = 1;
+
+	/* Save and always restore ftrace_enabled. */
+	if (read_ftrace_enabled(&orig)) {
+		test__skip();
+		return;
+	}
+
+	skel = ftrace_permanent__open_and_load();
+	if (!ASSERT_OK_PTR(skel, "skel_open_and_load"))
+		goto restore;
+
+	kprobes_on_ftrace = skel->kconfig->CONFIG_KPROBES_ON_FTRACE;
+
+	if (test__start_subtest("fentry_blocks_disable"))
+		check_blocks_disable(skel->progs.test_fentry, "attach_fentry");
+
+	if (test__start_subtest("fexit_blocks_disable"))
+		check_blocks_disable(skel->progs.test_fexit, "attach_fexit");
+
+	if (test__start_subtest("kprobe_blocks_disable")) {
+		if (kprobes_on_ftrace)
+			check_blocks_disable(skel->progs.test_kprobe, "attach_kprobe");
+		else
+			test__skip();
+	}
+
+	if (test__start_subtest("kretprobe_blocks_disable")) {
+		if (kprobes_on_ftrace)
+			check_blocks_disable(skel->progs.test_kretprobe, "attach_kretprobe");
+		else
+			test__skip();
+	}
+
+	if (test__start_subtest("fentry_attach_while_disabled_refused"))
+		check_attach_while_disabled_refused(skel->progs.test_fentry, "attach_fentry");
+
+	if (test__start_subtest("kprobe_attach_while_disabled_refused")) {
+		if (kprobes_on_ftrace)
+			check_attach_while_disabled_refused(skel->progs.test_kprobe,
+							    "attach_kprobe");
+		else
+			test__skip();
+	}
+
+	ftrace_permanent__destroy(skel);
+restore:
+	write_ftrace_enabled(orig);
+}
diff --git a/tools/testing/selftests/bpf/progs/ftrace_permanent.c b/tools/testing/selftests/bpf/progs/ftrace_permanent.c
new file mode 100644
index 0000000000000..ca706d8edc23b
--- /dev/null
+++ b/tools/testing/selftests/bpf/progs/ftrace_permanent.c
@@ -0,0 +1,43 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2026 CrowdStrike */
+#include "vmlinux.h"
+#include <bpf/bpf_helpers.h>
+#include <bpf/bpf_tracing.h>
+#include <stdbool.h>
+
+char _license[] SEC("license") = "GPL";
+
+extern bool CONFIG_KPROBES_ON_FTRACE __kconfig __weak;
+
+/* This function is here to have CONFIG_KPROBES_ON_FTRACE used and
+ * added to object BTF, so the userspace side can read it back via
+ * skel->kconfig->CONFIG_KPROBES_ON_FTRACE.
+ */
+int unused(void)
+{
+	return CONFIG_KPROBES_ON_FTRACE ? 0 : 1;
+}
+
+SEC("fentry/bpf_fentry_test1")
+int BPF_PROG(test_fentry, int a)
+{
+	return 0;
+}
+
+SEC("fexit/bpf_fentry_test2")
+int BPF_PROG(test_fexit, int a, __u64 b)
+{
+	return 0;
+}
+
+SEC("kprobe/bpf_fentry_test3")
+int test_kprobe(struct pt_regs *ctx)
+{
+	return 0;
+}
+
+SEC("kretprobe/bpf_fentry_test4")
+int BPF_KRETPROBE(test_kretprobe)
+{
+	return 0;
+}

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

Args:
null

Results:
null

Tokens:
							input: 4752
							output: 32
							thoughts: 261
						

Thoughts:
**Analyzing the Impact of the FTRACE_OPS_FL_PERMANENT Patch**

Okay, so I'm looking at this patch and my initial thought is, "Ah, they're adding `FTRACE_OPS_FL_PERMANENT` to the `direct_ops.flags` within `kernel/bpf/trampoline.c`, and also to `kprobe_ftrace_ops.flags` and `kprobe_ipmodify_ops.flags` in `kernel/kprobes.c`." That immediately flags a functional change. Adding that flag means these ftrace operations, in the context of BPF and kprobes respectively, are now *permanent*. They can't be disabled once registered, which is a significant behavioral modification.

To fully grasp the implications, I need to understand where these are being employed. Let me quickly examine `kernel/bpf/trampoline.c` and `kernel/kprobes.c`. I recall that `direct_ops` is definitely used in `bpf_trampoline.c` (or a similar file) to register the ftrace operations within the BPF trampoline. And obviously, `kprobe_ftrace_ops` and `kprobe_ipmodify_ops` are utilized in `kprobes.c` itself.

Alright, let's fire up `grepper` and see where these structures and variables are referenced beyond those immediate files. Knowing the broader usage context will be crucial for assessing the patch's overall impact and any potential side effects. The addition of the BPF selftest `ftrace_permanent.c` confirms this is a change with specific consequences.



4/2 2026/07/30 06:47 tool grepper
0m Args:
{
  "Expression": "direct_ops",
  "PathPrefix": "kernel/bpf/trampoline.c"
}

Results:
{
  "Output": "kernel/bpf/trampoline.c=72=static const struct bpf_trampoline_ops trampoline_ops;\n--\nkernel/bpf/trampoline.c-74-#ifdef CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS\nkernel/bpf/trampoline.c:75:static struct bpf_trampoline *direct_ops_ip_lookup(struct ftrace_ops *ops, unsigned long ip)\nkernel/bpf/trampoline.c-76-{\n--\nkernel/bpf/trampoline.c-91-#else\nkernel/bpf/trampoline.c:92:static struct bpf_trampoline *direct_ops_ip_lookup(struct ftrace_ops *ops, unsigned long ip)\nkernel/bpf/trampoline.c-93-{\n--\nkernel/bpf/trampoline.c=98=static int bpf_tramp_ftrace_ops_func(struct ftrace_ops *ops, unsigned long ip,\n--\nkernel/bpf/trampoline.c-103-\nkernel/bpf/trampoline.c:104:\ttr = direct_ops_ip_lookup(ops, ip);\nkernel/bpf/trampoline.c-105-\tif (!tr)\n--\nkernel/bpf/trampoline.c=210=void bpf_image_ksym_del(struct bpf_ksym *ksym)\n--\nkernel/bpf/trampoline.c-219-/*\nkernel/bpf/trampoline.c:220: * We have only single direct_ops which contains all the direct call\nkernel/bpf/trampoline.c-221- * sites and is the only global ftrace_ops for all trampolines.\n--\nkernel/bpf/trampoline.c-224- */\nkernel/bpf/trampoline.c:225:struct ftrace_ops direct_ops = {\nkernel/bpf/trampoline.c-226-\t.ops_func = bpf_tramp_ftrace_ops_func,\n--\nkernel/bpf/trampoline.c-230-\nkernel/bpf/trampoline.c:231:static int direct_ops_alloc(struct bpf_trampoline *tr)\nkernel/bpf/trampoline.c-232-{\nkernel/bpf/trampoline.c:233:\ttr-\u003efops = \u0026direct_ops;\nkernel/bpf/trampoline.c-234-\treturn 0;\n--\nkernel/bpf/trampoline.c-236-\nkernel/bpf/trampoline.c:237:static void direct_ops_free(struct bpf_trampoline *tr) { }\nkernel/bpf/trampoline.c-238-\nkernel/bpf/trampoline.c=239=static struct ftrace_hash *hash_from_ip(struct bpf_trampoline *tr, void *ptr)\n--\nkernel/bpf/trampoline.c-258-\nkernel/bpf/trampoline.c:259:static int direct_ops_add(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-260-{\n--\nkernel/bpf/trampoline.c-270-\nkernel/bpf/trampoline.c:271:static int direct_ops_del(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-272-{\n--\nkernel/bpf/trampoline.c-282-\nkernel/bpf/trampoline.c:283:static int direct_ops_mod(struct bpf_trampoline *tr, void *addr, bool lock_direct_mutex)\nkernel/bpf/trampoline.c-284-{\n--\nkernel/bpf/trampoline.c-300- */\nkernel/bpf/trampoline.c:301:static int direct_ops_alloc(struct bpf_trampoline *tr)\nkernel/bpf/trampoline.c-302-{\n--\nkernel/bpf/trampoline.c-307-\ttr-\u003efops-\u003eops_func = bpf_tramp_ftrace_ops_func;\nkernel/bpf/trampoline.c:308:\t/* See the direct_ops initializer above for why. */\nkernel/bpf/trampoline.c-309-\ttr-\u003efops-\u003eflags |= FTRACE_OPS_FL_PERMANENT;\n--\nkernel/bpf/trampoline.c-312-\nkernel/bpf/trampoline.c:313:static void direct_ops_free(struct bpf_trampoline *tr)\nkernel/bpf/trampoline.c-314-{\n--\nkernel/bpf/trampoline.c-320-\nkernel/bpf/trampoline.c:321:static int direct_ops_add(struct bpf_trampoline *tr, void *ptr)\nkernel/bpf/trampoline.c-322-{\n--\nkernel/bpf/trampoline.c-335-\nkernel/bpf/trampoline.c:336:static int direct_ops_del(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-337-{\n--\nkernel/bpf/trampoline.c-340-\nkernel/bpf/trampoline.c:341:static int direct_ops_mod(struct bpf_trampoline *tr, void *ptr, bool lock_direct_mutex)\nkernel/bpf/trampoline.c-342-{\n--\nkernel/bpf/trampoline.c-353-#else\nkernel/bpf/trampoline.c:354:static void direct_ops_free(struct bpf_trampoline *tr) { }\nkernel/bpf/trampoline.c-355-\nkernel/bpf/trampoline.c:356:static int direct_ops_alloc(struct bpf_trampoline *tr)\nkernel/bpf/trampoline.c-357-{\n--\nkernel/bpf/trampoline.c-360-\nkernel/bpf/trampoline.c:361:static int direct_ops_add(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-362-{\n--\nkernel/bpf/trampoline.c-365-\nkernel/bpf/trampoline.c:366:static int direct_ops_del(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-367-{\n--\nkernel/bpf/trampoline.c-370-\nkernel/bpf/trampoline.c:371:static int direct_ops_mod(struct bpf_trampoline *tr, void *ptr, bool lock_direct_mutex)\nkernel/bpf/trampoline.c-372-{\n--\nkernel/bpf/trampoline.c=377=static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip)\n--\nkernel/bpf/trampoline.c-393-\t\tgoto out;\nkernel/bpf/trampoline.c:394:\tif (direct_ops_alloc(tr)) {\nkernel/bpf/trampoline.c-395-\t\tkfree(tr);\n--\nkernel/bpf/trampoline.c=436=static int unregister_fentry(struct bpf_trampoline *tr, u32 orig_flags, void *data __maybe_unused)\n--\nkernel/bpf/trampoline.c-441-\tif (tr-\u003efunc.ftrace_managed)\nkernel/bpf/trampoline.c:442:\t\tret = direct_ops_del(tr, old_addr);\nkernel/bpf/trampoline.c-443-\telse\n--\nkernel/bpf/trampoline.c=454=static int modify_fentry(struct bpf_trampoline *tr, u32 orig_flags, struct bpf_tramp_image *im,\n--\nkernel/bpf/trampoline.c-461-\tif (tr-\u003efunc.ftrace_managed) {\nkernel/bpf/trampoline.c:462:\t\tret = direct_ops_mod(tr, new_addr, lock_direct_mutex);\nkernel/bpf/trampoline.c-463-\t} else {\n--\nkernel/bpf/trampoline.c=477=static int register_fentry(struct bpf_trampoline *tr, struct bpf_tramp_image *im,\n--\nkernel/bpf/trampoline.c-492-\tif (tr-\u003efunc.ftrace_managed) {\nkernel/bpf/trampoline.c:493:\t\tret = direct_ops_add(tr, new_addr);\nkernel/bpf/trampoline.c-494-\t} else {\n--\nkernel/bpf/trampoline.c=1198=void bpf_trampoline_put(struct bpf_trampoline *tr)\n--\nkernel/bpf/trampoline.c-1219-\thlist_del(\u0026tr-\u003ehlist_ip);\nkernel/bpf/trampoline.c:1220:\tdirect_ops_free(tr);\nkernel/bpf/trampoline.c-1221-\tkfree(tr);\n--\nkernel/bpf/trampoline.c=1623=int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,\n--\nkernel/bpf/trampoline.c-1682-\tif (ftrace_hash_count(data-\u003ereg)) {\nkernel/bpf/trampoline.c:1683:\t\terr = update_ftrace_direct_add(\u0026direct_ops, data-\u003ereg);\nkernel/bpf/trampoline.c-1684-\t\tif (err)\n--\nkernel/bpf/trampoline.c-1688-\tif (ftrace_hash_count(data-\u003emodify)) {\nkernel/bpf/trampoline.c:1689:\t\terr = update_ftrace_direct_mod(\u0026direct_ops, data-\u003emodify, true);\nkernel/bpf/trampoline.c-1690-\t\tif (err) {\nkernel/bpf/trampoline.c-1691-\t\t\tif (ftrace_hash_count(data-\u003ereg))\nkernel/bpf/trampoline.c:1692:\t\t\t\tWARN_ON_ONCE(update_ftrace_direct_del(\u0026direct_ops, data-\u003ereg));\nkernel/bpf/trampoline.c-1693-\t\t\tgoto rollback_unlink;\n--\nkernel/bpf/trampoline.c=1723=int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_link *link)\n--\nkernel/bpf/trampoline.c-1738-\tif (ftrace_hash_count(data-\u003eunreg))\nkernel/bpf/trampoline.c:1739:\t\tWARN_ON_ONCE(update_ftrace_direct_del(\u0026direct_ops, data-\u003eunreg));\nkernel/bpf/trampoline.c-1740-\tif (ftrace_hash_count(data-\u003emodify))\nkernel/bpf/trampoline.c:1741:\t\tWARN_ON_ONCE(update_ftrace_direct_mod(\u0026direct_ops, data-\u003emodify, true));\nkernel/bpf/trampoline.c-1742-\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 8233
							output: 34
							thoughts: 269
						

Thoughts:
**My Analysis of Ftrace Permanence and Potential Fuzzing Targets**

Okay, so the code's setting `FTRACE_OPS_FL_PERMANENT` in a few key places. It's doing this on `direct_ops` and `tr->fops` within `kernel/bpf/trampoline.c`, and also on `kprobe_ftrace_ops` and `kprobe_ipmodify_ops` in `kernel/kprobes.c`. The core implication here is that this prevents someone from globally disabling Ftrace through `/proc/sys/kernel/ftrace_enabled` if BPF trampolines or kprobes are active. This is a deliberate functional change, and therefore, definitely worth a closer look from a security standpoint.

The selftest `ftrace_permanent.c` confirms this behavior by using `bpf_program__attach` for `fentry`, `fexit`, `kprobe`, and `kretprobe` attachments. That’s helpful; it means we have a known testing baseline. Since the aim is to find vulnerabilities related to this "permanent" state, the functions handling BPF trampoline and kprobe attachment/detachment are going to be my primary focus for fuzzing.

For BPF trampolines specifically, I need to zoom in on `bpf_trampoline_link_prog`, `bpf_trampoline_unlink_prog`, `bpf_trampoline_multi_attach`, and `bpf_trampoline_multi_detach`. These are the obvious entry points to trigger the permanence behavior. On the kprobe side, `register_kprobe` and `unregister_kprobe` are going to be key.

I think I'll start by taking a look at `kernel/kprobes.c` and specifically `kprobe_ftrace_ops`. Understanding how this is set up will give a good context.



6/2 2026/07/30 06:47 tool grepper
0m Args:
{
  "Expression": "kprobe_ftrace_ops",
  "PathPrefix": "kernel/kprobes.c"
}

Results:
{
  "Output": "kernel/kprobes.c=1118=static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)\n--\nkernel/kprobes.c-1125-/* Same protection livepatch gives its own ftrace_ops. */\nkernel/kprobes.c:1126:static struct ftrace_ops kprobe_ftrace_ops __read_mostly = {\nkernel/kprobes.c-1127-\t.func = kprobe_ftrace_handler,\n--\nkernel/kprobes.c=1167=static int arm_kprobe_ftrace(struct kprobe *p)\n--\nkernel/kprobes.c-1171-\treturn __arm_kprobe_ftrace(p,\nkernel/kprobes.c:1172:\t\tipmodify ? \u0026kprobe_ipmodify_ops : \u0026kprobe_ftrace_ops,\nkernel/kprobes.c-1173-\t\tipmodify ? \u0026kprobe_ipmodify_enabled : \u0026kprobe_ftrace_enabled);\n--\nkernel/kprobes.c=1201=static int disarm_kprobe_ftrace(struct kprobe *p)\n--\nkernel/kprobes.c-1205-\treturn __disarm_kprobe_ftrace(p,\nkernel/kprobes.c:1206:\t\tipmodify ? \u0026kprobe_ipmodify_ops : \u0026kprobe_ftrace_ops,\nkernel/kprobes.c-1207-\t\tipmodify ? \u0026kprobe_ipmodify_enabled : \u0026kprobe_ftrace_enabled);\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 9341
							output: 38
							thoughts: 115
						

Thoughts:
Okay, let's break this down.

**Kprobe/Ftrace Integration with BPF Trampolines**

Right, so I'm seeing that `arm_kprobe_ftrace` and `disarm_kprobe_ftrace` are specifically used in the context of kprobe registration and unregistration when dealing with ftrace. That's pretty straightforward, it's the glue between the kprobe mechanism and the ftrace infrastructure.

Now, the interesting bit is the BPF trampoline functions. We have `bpf_trampoline_link_prog`, `bpf_trampoline_unlink_prog`, `bpf_trampoline_multi_attach`, and `bpf_trampoline_multi_detach`. These are the core BPF functions involved. Specifically the linking/unlinking, and multi-attach/detach suggest they are handling the injection/removal of BPF programs into the execution flow.

I need to dive into the implementation. Let me pull up `kernel/bpf/trampoline.c` and take a look at `bpf_trampoline_link_prog`. That's where the real magic happens, I need to see exactly how these programs are being hooked and unhooked. I'll need to understand the specifics of the trampoline architecture, how it interacts with the instruction patching, and any potential caveats in that process. I suspect `bpf_trampoline_link_prog` does the initial setup, and then the others handle cleanup and multi-program scenarios. Let's see...



8/2 2026/07/30 06:47 tool grepper
0m Args:
{
  "Expression": "bpf_trampoline_link_prog",
  "PathPrefix": "kernel/bpf/trampoline.c"
}

Results:
{
  "Output": "kernel/bpf/trampoline.c=897=static void bpf_trampoline_remove_prog(struct bpf_trampoline *tr,\n--\nkernel/bpf/trampoline.c-915-\nkernel/bpf/trampoline.c:916:static int __bpf_trampoline_link_prog(struct bpf_tramp_node *node,\nkernel/bpf/trampoline.c-917-\t\t\t\t      struct bpf_trampoline *tr,\n--\nkernel/bpf/trampoline.c-956-\nkernel/bpf/trampoline.c:957:int bpf_trampoline_link_prog(struct bpf_tramp_node *node,\nkernel/bpf/trampoline.c-958-\t\t\t     struct bpf_trampoline *tr,\n--\nkernel/bpf/trampoline.c-963-\ttrampoline_lock(tr);\nkernel/bpf/trampoline.c:964:\terr = __bpf_trampoline_link_prog(node, tr, tgt_prog, \u0026trampoline_ops, NULL);\nkernel/bpf/trampoline.c-965-\ttrampoline_unlock(tr);\n--\nkernel/bpf/trampoline.c=1087=int bpf_trampoline_link_cgroup_shim(struct bpf_prog *prog,\n--\nkernel/bpf/trampoline.c-1129-\nkernel/bpf/trampoline.c:1130:\terr = __bpf_trampoline_link_prog(\u0026shim_link-\u003elink.node, tr, NULL, \u0026trampoline_ops, NULL);\nkernel/bpf/trampoline.c-1131-\tif (err)\n--\nkernel/bpf/trampoline.c=1623=int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,\n--\nkernel/bpf/trampoline.c-1672-\t\tdata-\u003eentry = \u0026mnode-\u003eentry;\nkernel/bpf/trampoline.c:1673:\t\terr = __bpf_trampoline_link_prog(\u0026mnode-\u003enode, mnode-\u003etrampoline, NULL,\nkernel/bpf/trampoline.c-1674-\t\t\t\t\t\t \u0026trampoline_multi_ops, data);\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 10391
							output: 31
							thoughts: 114
						

Thoughts:
**Targeting Key Functions for Analysis**

Okay, let's break this down systematically. Thinking about where to start my deep dive, I'm leaning heavily towards those BPF trampoline functions: `bpf_trampoline_link_prog`, `bpf_trampoline_unlink_prog`, `bpf_trampoline_multi_attach`, and `bpf_trampoline_multi_detach`. They seem like prime candidates for understanding how BPF programs are linked and unlinked, especially in complex multi-attach scenarios. I'll need to scrutinize how those hooks are implemented.

Then, moving on to kprobes, the obvious suspects are `register_kprobe`, `unregister_kprobe`, `register_kretprobe`, and `unregister_kretprobe`. That whole registration and unregistration lifecycle for kernel probes is crucial, and understanding the interactions there will be key.

Finally, let's not forget that `ftrace_enable_sysctl` function, associated with that `ftrace_enabled` sysctl write. I need to trace its location and what it's really doing under the hood. Ftrace's control of tracing is a powerful mechanism, and I need to see how it ties in with everything else.



10/2 2026/07/30 06:48 tool grepper
0m Args:
{
  "Expression": "ftrace_enable_sysctl",
  "PathPrefix": "kernel/trace/"
}

Results:
{
  "Output": "kernel/trace/ftrace.c=9392=static int\nkernel/trace/ftrace.c:9393:ftrace_enable_sysctl(const struct ctl_table *table, int write,\nkernel/trace/ftrace.c-9394-\t\t     void *buffer, size_t *lenp, loff_t *ppos)\n--\nkernel/trace/ftrace.c=9433=static const struct ctl_table ftrace_sysctls[] = {\n--\nkernel/trace/ftrace.c-9438-\t\t.mode           = 0644,\nkernel/trace/ftrace.c:9439:\t\t.proc_handler   = ftrace_enable_sysctl,\nkernel/trace/ftrace.c-9440-\t},\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 11006
							output: 42
							thoughts: 129
						

Thoughts:
**Analyzing the Impact of the `ftrace_enable_sysctl` Patch**

Okay, so this patch's success or failure hinges directly on `ftrace_enable_sysctl`, making it a prime candidate for a focused investigation. The first step is to nail down the necessary configurations. My experience tells me we're likely looking at some key BPF and tracing options. I'll need to check for the presence of `CONFIG_BPF_SYSCALL`, `CONFIG_KPROBES`, `CONFIG_KPROBES_ON_FTRACE`, `CONFIG_FTRACE`, `CONFIG_DYNAMIC_FTRACE`, and crucially, `CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS`.

Given that last one, I should really check `kernel/bpf/trampoline.c`. I have a feeling that `CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS` will come into play there.



12/2 2026/07/30 06:48 tool grepper
0m Args:
{
  "Expression": "CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS",
  "PathPrefix": "kernel/bpf/trampoline.c"
}

Results:
{
  "Output": "kernel/bpf/trampoline.c=62=struct bpf_trampoline_ops {\n--\nkernel/bpf/trampoline.c-68-\nkernel/bpf/trampoline.c:69:#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS\nkernel/bpf/trampoline.c-70-static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex,\n--\nkernel/bpf/trampoline.c=210=void bpf_image_ksym_del(struct bpf_ksym *ksym)\n--\nkernel/bpf/trampoline.c-216-\nkernel/bpf/trampoline.c:217:#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS\nkernel/bpf/trampoline.c-218-#ifdef CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS\n--\nkernel/bpf/trampoline.c=371=static int direct_ops_mod(struct bpf_trampoline *tr, void *ptr, bool lock_direct_mutex)\n--\nkernel/bpf/trampoline.c-374-}\nkernel/bpf/trampoline.c:375:#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */\nkernel/bpf/trampoline.c-376-\n--\nkernel/bpf/trampoline.c=684=static int bpf_trampoline_update(struct bpf_trampoline *tr, bool lock_direct_mutex,\n--\nkernel/bpf/trampoline.c-717-\nkernel/bpf/trampoline.c:718:#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS\nkernel/bpf/trampoline.c-719-again:\n--\nkernel/bpf/trampoline.c-770-\nkernel/bpf/trampoline.c:771:#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS\nkernel/bpf/trampoline.c-772-\tif (err == -EAGAIN) {\n--\nkernel/bpf/trampoline.c=1478=int __weak arch_bpf_trampoline_size(const struct btf_func_model *m, u32 flags,\n--\nkernel/bpf/trampoline.c-1483-\nkernel/bpf/trampoline.c:1484:#if defined(CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS) \u0026\u0026 \\\nkernel/bpf/trampoline.c-1485-    defined(CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS) \u0026\u0026 \\\n--\nkernel/bpf/trampoline.c=1723=int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_link *link)\n--\nkernel/bpf/trampoline.c-1757-\nkernel/bpf/trampoline.c:1758:#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS \u0026\u0026\nkernel/bpf/trampoline.c-1759-\t  CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS \u0026\u0026\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 12159
							output: 37
							thoughts: 71
						

Thoughts:
Here's my summary, as though I'm thinking through this myself:

**Analyzing a Kernel Patch**

Okay, so this patch is digging into `direct_ops`, which immediately tells me we're dealing with direct calls in ftrace. The `#ifdef` directives, specifically those related to `CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS` and `CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS`, confirm this. I need to understand the implications of these configurations being active or inactive, as they'll obviously condition the behavior of the code. My immediate thought is to check if these preprocessor directives are being applied and what effect that will have on the functionality.

Given the connection to direct calls and ftrace, I should also double check the related kprobe functionality. I'll need to confirm that `CONFIG_KPROBES_ON_FTRACE` is appropriately handled, since kprobes often work in tandem with function tracing. I should probably start looking in `kernel/kprobes.c` for any related logic or potential interactions.



14/2 2026/07/30 06:48 tool grepper
0m Args:
{
  "Expression": "CONFIG_KPROBES_ON_FTRACE",
  "PathPrefix": "kernel/kprobes.c"
}

Results:
{
  "Output": "kernel/kprobes.c=1118=static struct kprobe *alloc_aggr_kprobe(struct kprobe *p)\n--\nkernel/kprobes.c-1123-\nkernel/kprobes.c:1124:#ifdef CONFIG_KPROBES_ON_FTRACE\nkernel/kprobes.c-1125-/* Same protection livepatch gives its own ftrace_ops. */\n--\nkernel/kprobes.c=1210=void kprobe_ftrace_kill(void)\n--\nkernel/kprobes.c-1213-}\nkernel/kprobes.c:1214:#else\t/* !CONFIG_KPROBES_ON_FTRACE */\nkernel/kprobes.c-1215-static inline int arm_kprobe_ftrace(struct kprobe *p)\n--\nkernel/kprobes.c=1594=static int check_ftrace_location(struct kprobe *p)\n--\nkernel/kprobes.c-1598-\tif (ftrace_location(addr) == addr) {\nkernel/kprobes.c:1599:#ifdef CONFIG_KPROBES_ON_FTRACE\nkernel/kprobes.c-1600-\t\tp-\u003eflags |= KPROBE_FLAG_FTRACE;\n"
}

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

Args:
null

Results:
null

Tokens:
							input: 12825
							output: 33
							thoughts: 181
						

Thoughts:
**Configuration Conundrums**

Okay, so I'm trying to nail down the essential configurations for this tracing setup. Initially, I thought I needed `DYNAMIC_FTRACE_WITH_DIRECT_CALLS`, `HAVE_SINGLE_FTRACE_DIRECT_OPS`, and `KPROBES_ON_FTRACE`. But then it hits me: hold on, `HAVE_SINGLE_FTRACE_DIRECT_OPS` is an architecture-specific setting, right? We don't need to explicitly define it if the architecture already takes care of it. And `DYNAMIC_FTRACE_WITH_DIRECT_CALLS`... that's often architecture-selected too, isn't it? Same goes for `KPROBES_ON_FTRACE`; it's selected by architectures that support it.

Alright, a cleaner, more generalized approach then. Seems like I should just specify the core, architecture-agnostic options: `FTRACE`, `DYNAMIC_FTRACE`, `KPROBES`, `BPF_SYSCALL`, and `BPF_JIT`. That should give us the foundation we need. But, just to be absolutely sure, I'm going to double check if `DYNAMIC_FTRACE_WITH_DIRECT_CALLS` is ever actually a user-selectable config; just to be thorough.



16/2 2026/07/30 06:48 tool grepper
0m Args:
{
  "Expression": "config DYNAMIC_FTRACE_WITH_DIRECT_CALLS",
  "PathPrefix": ""
}

Results:
{
  "Output": "kernel/trace/Kconfig=324=config DYNAMIC_FTRACE_WITH_REGS\n--\nkernel/trace/Kconfig-328-\nkernel/trace/Kconfig:329:config DYNAMIC_FTRACE_WITH_DIRECT_CALLS\nkernel/trace/Kconfig-330-\tdef_bool y\n"
}

17/2 2026/07/30 06:48 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 13409
							output: 37
							thoughts: 22
						

Thoughts:
Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it's my own internal dialogue:

**Dependency Evaluation for `def_bool y`**

Alright, so we've got this `def_bool y`. The core concept here, as I understand it, is that its defined state depends on the successful fulfillment of its dependencies. This is classic, right? It's not magic, it's just how any logical construction works. If the inputs aren't valid, the output can't be trusted.

My immediate next step, logically, is to dive right into validating those dependencies. What are they? Are they satisfied? What's the state of the underlying functions or variables or whatever they're linked to? This isn't just a cursory glance; I need to drill down to confirm the validity of each element upon which `y` relies. No shortcuts. We need to be rigorously thorough to avoid any cascading failures down the line. We can't have `y` returning a value based on a faulty predicate. This is where the real work begins. We need to be meticulous.



18/2 2026/07/30 06:48 tool grepper
0m Args:
{
  "Expression": "DYNAMIC_FTRACE_WITH_DIRECT_CALLS",
  "PathPrefix": "kernel/trace/Kconfig"
}

Results:
{
  "Output": "kernel/trace/Kconfig=47=config HAVE_DYNAMIC_FTRACE_WITH_REGS\n--\nkernel/trace/Kconfig-49-\nkernel/trace/Kconfig:50:config HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS\nkernel/trace/Kconfig-51-\tbool\n--\nkernel/trace/Kconfig=324=config DYNAMIC_FTRACE_WITH_REGS\n--\nkernel/trace/Kconfig-328-\nkernel/trace/Kconfig:329:config DYNAMIC_FTRACE_WITH_DIRECT_CALLS\nkernel/trace/Kconfig-330-\tdef_bool y\nkernel/trace/Kconfig-331-\tdepends on DYNAMIC_FTRACE_WITH_REGS || DYNAMIC_FTRACE_WITH_ARGS\nkernel/trace/Kconfig:332:\tdepends on HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS\nkernel/trace/Kconfig-333-\n--\nkernel/trace/Kconfig=343=config DYNAMIC_FTRACE_WITH_JMP\n--\nkernel/trace/Kconfig-345-\tdepends on DYNAMIC_FTRACE\nkernel/trace/Kconfig:346:\tdepends on DYNAMIC_FTRACE_WITH_DIRECT_CALLS\nkernel/trace/Kconfig-347-\tdepends on HAVE_DYNAMIC_FTRACE_WITH_JMP\n"
}

19/2 2026/07/30 06:48 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14051
							output: 29
							thoughts: 75
						

Thoughts:
Okay, let's break this down.

**My Analysis of Kernel Tracing Configuration**

Right, so the enablement of this feature hinges on `HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS`. And that's arch-dependent, which means the underlying architecture is going to dictate whether we can even use this effectively.

To be thorough, I need to make sure we've also got `FTRACE`, `DYNAMIC_FTRACE`, `KPROBES`, `BPF_SYSCALL`, and `BPF_JIT` all set. They're all related, and I'll need all those pieces to be in place for a robust solution.

Okay, and now, before I move forward, I should really check the status of `KPROBES_ON_FTRACE`. Gotta make sure I'm not overlooking any configuration conflicts, especially since this relies on Kprobes.



20/2 2026/07/30 06:48 tool grepper
0m Args:
{
  "Expression": "config KPROBES_ON_FTRACE",
  "PathPrefix": ""
}

Results:
{
  "Output": "arch/Kconfig=168=config OPTPROBES\n--\narch/Kconfig-172-\narch/Kconfig:173:config KPROBES_ON_FTRACE\narch/Kconfig-174-\tdef_bool y\n"
}

21/2 2026/07/30 06:48 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 14420
							output: 38
							thoughts: 123
						

Thoughts:
**Checking for Core Functionality**

Okay, so I'm thinking we're dealing with some core BPF trampoline functionality, and it looks like `def_bool y` is also relevant. The key focus areas for my analysis right now are going to be those BPF trampoline functions: `bpf_trampoline_link_prog`, `bpf_trampoline_unlink_prog`, `bpf_trampoline_multi_attach`, and `bpf_trampoline_multi_detach`. We've also got some classic tracing hooks in there: `register_kprobe`, `unregister_kprobe`, `register_kretprobe`, `unregister_kretprobe`, and `ftrace_enable_sysctl`. Before I go any further, I should check if `bpf_trampoline_unlink_prog` exists, just to be absolutely sure this is a viable path.



22/2 2026/07/30 06:48 tool grepper
0m Args:
{
  "Expression": "bpf_trampoline_unlink_prog",
  "PathPrefix": "kernel/bpf/trampoline.c"
}

Results:
{
  "Output": "kernel/bpf/trampoline.c=957=int bpf_trampoline_link_prog(struct bpf_tramp_node *node,\n--\nkernel/bpf/trampoline.c-968-\nkernel/bpf/trampoline.c:969:static int __bpf_trampoline_unlink_prog(struct bpf_tramp_node *node,\nkernel/bpf/trampoline.c-970-\t\t\t\t\tstruct bpf_trampoline *tr,\n--\nkernel/bpf/trampoline.c-992-\nkernel/bpf/trampoline.c:993:/* bpf_trampoline_unlink_prog() should never fail. */\nkernel/bpf/trampoline.c:994:int bpf_trampoline_unlink_prog(struct bpf_tramp_node *node,\nkernel/bpf/trampoline.c-995-\t\t\t       struct bpf_trampoline *tr,\n--\nkernel/bpf/trampoline.c-1000-\ttrampoline_lock(tr);\nkernel/bpf/trampoline.c:1001:\terr = __bpf_trampoline_unlink_prog(node, tr, tgt_prog, \u0026trampoline_ops, NULL);\nkernel/bpf/trampoline.c-1002-\ttrampoline_unlock(tr);\n--\nkernel/bpf/trampoline.c=1007=static void bpf_shim_tramp_link_release(struct bpf_link *link)\n--\nkernel/bpf/trampoline.c-1015-\nkernel/bpf/trampoline.c:1016:\tWARN_ON_ONCE(bpf_trampoline_unlink_prog(\u0026shim_link-\u003elink.node, shim_link-\u003etrampoline, NULL));\nkernel/bpf/trampoline.c-1017-\tbpf_trampoline_put(shim_link-\u003etrampoline);\n--\nkernel/bpf/trampoline.c=1723=int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_link *link)\n--\nkernel/bpf/trampoline.c-1733-\t\tbpf_trampoline_multi_attach_init(mnode-\u003etrampoline);\nkernel/bpf/trampoline.c:1734:\t\tWARN_ON_ONCE(__bpf_trampoline_unlink_prog(\u0026mnode-\u003enode, mnode-\u003etrampoline,\nkernel/bpf/trampoline.c-1735-\t\t\t\t\tNULL, \u0026trampoline_multi_ops, data));\n"
}

23/2 2026/07/30 06:48 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 15407
							output: 34
							thoughts: 490
						

Thoughts:
**Analyzing Potential Kernel Symbols for Patch Impact**

Okay, so I'm looking at this patch and confirming that, yes, it exists. The core of it seems to revolve around `ftrace_enable_sysctl`. My first thought was to check if it's exported or static, and I see it's static in `kernel/trace/ftrace.c`. While a static function isn't *ideal* as a focus, it *is* the sysctl handler, so it's relevant. I considered if there was a better symbol, like `ftrace_set_filter`, but the patch specifically targets `ftrace_enabled`, which is managed by `ftrace_enable_sysctl`. Since it is static I better check if `ftrace_sysctls` is registered somewhere. I need to keep in mind, however, that the function that actually *does* the work is `ftrace_enable_sysctl`. So that needs to be included.

Then I shifted gears a bit. I remembered `bpf_trampoline_link_prog` and `bpf_trampoline_unlink_prog`. Those are definitely good candidates for inclusion in the analysis. Also, `register_kprobe` and `unregister_kprobe` are definitely on the list.

I need to confirm the relevant kernel configurations. I'm thinking: `FTRACE`, `DYNAMIC_FTRACE`, `KPROBES`, `BPF_SYSCALL`, and `BPF_JIT`.

I need to make sure I haven't missed any configs. The selftests use `fentry` and `fexit`, which require `BPF_JIT` and `DYNAMIC_FTRACE_WITH_DIRECT_CALLS`. So, yes, that confirms: `BPF_SYSCALL`, `BPF_JIT`, `FTRACE`, `DYNAMIC_FTRACE`, and `KPROBES`.

I'm now considering `bpf_trampoline_multi_attach` and `bpf_trampoline_multi_detach`. They're relevant to `kprobe.multi` and `kretprobe.multi`. But, wait, the patch *explicitly* says "kprobe.multi/kretprobe.multi/kprobe.session are out of scope." Okay, the commit message also says that they are out of scope. Therefore, the patch doesn't affect them. Why? Because `kprobe.multi` uses `fprobe`, which likely has its own `ftrace_ops`. I should confirm that `fprobe` uses `FTRACE_OPS_FL_PERMANENT`.



24/2 2026/07/30 06:48 tool grepper
0m Args:
{
  "Expression": "FTRACE_OPS_FL_PERMANENT",
  "PathPrefix": "kernel/trace/"
}

Results:
{
  "Output": "kernel/trace/ftrace.c=330=int __register_ftrace_function(struct ftrace_ops *ops)\n--\nkernel/trace/ftrace.c-350-#endif\nkernel/trace/ftrace.c:351:\tif (!ftrace_enabled \u0026\u0026 (ops-\u003eflags \u0026 FTRACE_OPS_FL_PERMANENT))\nkernel/trace/ftrace.c-352-\t\treturn -EBUSY;\n--\nkernel/trace/ftrace.c=9380=static bool is_permanent_ops_registered(void)\n--\nkernel/trace/ftrace.c-9384-\tdo_for_each_ftrace_op(op, ftrace_ops_list) {\nkernel/trace/ftrace.c:9385:\t\tif (op-\u003eflags \u0026 FTRACE_OPS_FL_PERMANENT)\nkernel/trace/ftrace.c-9386-\t\t\treturn true;\n"
}

25/2 2026/07/30 06:48 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 16726
							output: 32
							thoughts: 118
						

Thoughts:
**Analyzing the Implications of FTRACE_OPS_FL_PERMANENT**

Okay, so I'm focusing on `FTRACE_OPS_FL_PERMANENT`. From what I see, it's exclusively added to `direct_ops`, and then within the context of kprobes, to `kprobe_ftrace_ops` and `kprobe_ipmodify_ops`. This means my main points of entry for observing this behavior would be `bpf_trampoline_link_prog` and `register_kprobe`, right? I need to be sure I understand where this flag is being used.

Hold on... I seem to recall `bpf_trampoline_multi_attach` making use of `direct_ops`. That warrants immediate investigation. I should double check that and confirm my understanding. Let me jump into `kernel/bpf/trampoline.c` and have a look at the code for `bpf_trampoline_multi_attach`. I need to ensure I've accounted for every place that this flag is set and used.



26/2 2026/07/30 06:48 tool grepper
0m Args:
{
  "Expression": "direct_ops",
  "PathPrefix": "kernel/bpf/trampoline.c"
}

Results:
{
  "Output": "kernel/bpf/trampoline.c=72=static const struct bpf_trampoline_ops trampoline_ops;\n--\nkernel/bpf/trampoline.c-74-#ifdef CONFIG_HAVE_SINGLE_FTRACE_DIRECT_OPS\nkernel/bpf/trampoline.c:75:static struct bpf_trampoline *direct_ops_ip_lookup(struct ftrace_ops *ops, unsigned long ip)\nkernel/bpf/trampoline.c-76-{\n--\nkernel/bpf/trampoline.c-91-#else\nkernel/bpf/trampoline.c:92:static struct bpf_trampoline *direct_ops_ip_lookup(struct ftrace_ops *ops, unsigned long ip)\nkernel/bpf/trampoline.c-93-{\n--\nkernel/bpf/trampoline.c=98=static int bpf_tramp_ftrace_ops_func(struct ftrace_ops *ops, unsigned long ip,\n--\nkernel/bpf/trampoline.c-103-\nkernel/bpf/trampoline.c:104:\ttr = direct_ops_ip_lookup(ops, ip);\nkernel/bpf/trampoline.c-105-\tif (!tr)\n--\nkernel/bpf/trampoline.c=210=void bpf_image_ksym_del(struct bpf_ksym *ksym)\n--\nkernel/bpf/trampoline.c-219-/*\nkernel/bpf/trampoline.c:220: * We have only single direct_ops which contains all the direct call\nkernel/bpf/trampoline.c-221- * sites and is the only global ftrace_ops for all trampolines.\n--\nkernel/bpf/trampoline.c-224- */\nkernel/bpf/trampoline.c:225:struct ftrace_ops direct_ops = {\nkernel/bpf/trampoline.c-226-\t.ops_func = bpf_tramp_ftrace_ops_func,\n--\nkernel/bpf/trampoline.c-230-\nkernel/bpf/trampoline.c:231:static int direct_ops_alloc(struct bpf_trampoline *tr)\nkernel/bpf/trampoline.c-232-{\nkernel/bpf/trampoline.c:233:\ttr-\u003efops = \u0026direct_ops;\nkernel/bpf/trampoline.c-234-\treturn 0;\n--\nkernel/bpf/trampoline.c-236-\nkernel/bpf/trampoline.c:237:static void direct_ops_free(struct bpf_trampoline *tr) { }\nkernel/bpf/trampoline.c-238-\nkernel/bpf/trampoline.c=239=static struct ftrace_hash *hash_from_ip(struct bpf_trampoline *tr, void *ptr)\n--\nkernel/bpf/trampoline.c-258-\nkernel/bpf/trampoline.c:259:static int direct_ops_add(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-260-{\n--\nkernel/bpf/trampoline.c-270-\nkernel/bpf/trampoline.c:271:static int direct_ops_del(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-272-{\n--\nkernel/bpf/trampoline.c-282-\nkernel/bpf/trampoline.c:283:static int direct_ops_mod(struct bpf_trampoline *tr, void *addr, bool lock_direct_mutex)\nkernel/bpf/trampoline.c-284-{\n--\nkernel/bpf/trampoline.c-300- */\nkernel/bpf/trampoline.c:301:static int direct_ops_alloc(struct bpf_trampoline *tr)\nkernel/bpf/trampoline.c-302-{\n--\nkernel/bpf/trampoline.c-307-\ttr-\u003efops-\u003eops_func = bpf_tramp_ftrace_ops_func;\nkernel/bpf/trampoline.c:308:\t/* See the direct_ops initializer above for why. */\nkernel/bpf/trampoline.c-309-\ttr-\u003efops-\u003eflags |= FTRACE_OPS_FL_PERMANENT;\n--\nkernel/bpf/trampoline.c-312-\nkernel/bpf/trampoline.c:313:static void direct_ops_free(struct bpf_trampoline *tr)\nkernel/bpf/trampoline.c-314-{\n--\nkernel/bpf/trampoline.c-320-\nkernel/bpf/trampoline.c:321:static int direct_ops_add(struct bpf_trampoline *tr, void *ptr)\nkernel/bpf/trampoline.c-322-{\n--\nkernel/bpf/trampoline.c-335-\nkernel/bpf/trampoline.c:336:static int direct_ops_del(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-337-{\n--\nkernel/bpf/trampoline.c-340-\nkernel/bpf/trampoline.c:341:static int direct_ops_mod(struct bpf_trampoline *tr, void *ptr, bool lock_direct_mutex)\nkernel/bpf/trampoline.c-342-{\n--\nkernel/bpf/trampoline.c-353-#else\nkernel/bpf/trampoline.c:354:static void direct_ops_free(struct bpf_trampoline *tr) { }\nkernel/bpf/trampoline.c-355-\nkernel/bpf/trampoline.c:356:static int direct_ops_alloc(struct bpf_trampoline *tr)\nkernel/bpf/trampoline.c-357-{\n--\nkernel/bpf/trampoline.c-360-\nkernel/bpf/trampoline.c:361:static int direct_ops_add(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-362-{\n--\nkernel/bpf/trampoline.c-365-\nkernel/bpf/trampoline.c:366:static int direct_ops_del(struct bpf_trampoline *tr, void *addr)\nkernel/bpf/trampoline.c-367-{\n--\nkernel/bpf/trampoline.c-370-\nkernel/bpf/trampoline.c:371:static int direct_ops_mod(struct bpf_trampoline *tr, void *ptr, bool lock_direct_mutex)\nkernel/bpf/trampoline.c-372-{\n--\nkernel/bpf/trampoline.c=377=static struct bpf_trampoline *bpf_trampoline_lookup(u64 key, unsigned long ip)\n--\nkernel/bpf/trampoline.c-393-\t\tgoto out;\nkernel/bpf/trampoline.c:394:\tif (direct_ops_alloc(tr)) {\nkernel/bpf/trampoline.c-395-\t\tkfree(tr);\n--\nkernel/bpf/trampoline.c=436=static int unregister_fentry(struct bpf_trampoline *tr, u32 orig_flags, void *data __maybe_unused)\n--\nkernel/bpf/trampoline.c-441-\tif (tr-\u003efunc.ftrace_managed)\nkernel/bpf/trampoline.c:442:\t\tret = direct_ops_del(tr, old_addr);\nkernel/bpf/trampoline.c-443-\telse\n--\nkernel/bpf/trampoline.c=454=static int modify_fentry(struct bpf_trampoline *tr, u32 orig_flags, struct bpf_tramp_image *im,\n--\nkernel/bpf/trampoline.c-461-\tif (tr-\u003efunc.ftrace_managed) {\nkernel/bpf/trampoline.c:462:\t\tret = direct_ops_mod(tr, new_addr, lock_direct_mutex);\nkernel/bpf/trampoline.c-463-\t} else {\n--\nkernel/bpf/trampoline.c=477=static int register_fentry(struct bpf_trampoline *tr, struct bpf_tramp_image *im,\n--\nkernel/bpf/trampoline.c-492-\tif (tr-\u003efunc.ftrace_managed) {\nkernel/bpf/trampoline.c:493:\t\tret = direct_ops_add(tr, new_addr);\nkernel/bpf/trampoline.c-494-\t} else {\n--\nkernel/bpf/trampoline.c=1198=void bpf_trampoline_put(struct bpf_trampoline *tr)\n--\nkernel/bpf/trampoline.c-1219-\thlist_del(\u0026tr-\u003ehlist_ip);\nkernel/bpf/trampoline.c:1220:\tdirect_ops_free(tr);\nkernel/bpf/trampoline.c-1221-\tkfree(tr);\n--\nkernel/bpf/trampoline.c=1623=int bpf_trampoline_multi_attach(struct bpf_prog *prog, u32 *ids,\n--\nkernel/bpf/trampoline.c-1682-\tif (ftrace_hash_count(data-\u003ereg)) {\nkernel/bpf/trampoline.c:1683:\t\terr = update_ftrace_direct_add(\u0026direct_ops, data-\u003ereg);\nkernel/bpf/trampoline.c-1684-\t\tif (err)\n--\nkernel/bpf/trampoline.c-1688-\tif (ftrace_hash_count(data-\u003emodify)) {\nkernel/bpf/trampoline.c:1689:\t\terr = update_ftrace_direct_mod(\u0026direct_ops, data-\u003emodify, true);\nkernel/bpf/trampoline.c-1690-\t\tif (err) {\nkernel/bpf/trampoline.c-1691-\t\t\tif (ftrace_hash_count(data-\u003ereg))\nkernel/bpf/trampoline.c:1692:\t\t\t\tWARN_ON_ONCE(update_ftrace_direct_del(\u0026direct_ops, data-\u003ereg));\nkernel/bpf/trampoline.c-1693-\t\t\tgoto rollback_unlink;\n--\nkernel/bpf/trampoline.c=1723=int bpf_trampoline_multi_detach(struct bpf_prog *prog, struct bpf_tracing_multi_link *link)\n--\nkernel/bpf/trampoline.c-1738-\tif (ftrace_hash_count(data-\u003eunreg))\nkernel/bpf/trampoline.c:1739:\t\tWARN_ON_ONCE(update_ftrace_direct_del(\u0026direct_ops, data-\u003eunreg));\nkernel/bpf/trampoline.c-1740-\tif (ftrace_hash_count(data-\u003emodify))\nkernel/bpf/trampoline.c:1741:\t\tWARN_ON_ONCE(update_ftrace_direct_mod(\u0026direct_ops, data-\u003emodify, true));\nkernel/bpf/trampoline.c-1742-\n"
}

27/2 2026/07/30 06:48 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 19940
							output: 263
							thoughts: 691
						

Thoughts:
**Reasoning for Fuzzing a Kernel Patch**

Okay, so I've been looking at this patch and here's my train of thought.  Initially, the code utilizes `direct_ops` within `bpf_trampoline_multi_attach`, which makes `bpf_trampoline_multi_attach` and `bpf_trampoline_multi_detach` interesting targets for fuzzing, along with the standard `bpf_trampoline_link_prog` and `bpf_trampoline_unlink_prog`. We also need to consider the kprobe functions. This also makes the kprobe-related symbols of `register_kprobe`, `unregister_kprobe`, `register_kretprobe`, and `unregister_kretprobe` important. Lastly, we need to consider `ftrace_enable_sysctl` and its interaction with the other functions.

Now, concerning configurations, we definitely need `BPF_SYSCALL`, `BPF_JIT`, `FTRACE`, `DYNAMIC_FTRACE`, and `KPROBES` to enable the relevant functionality.  The `ftrace_enable_sysctl` is a bit of a wrinkle since it's static, but Syzkaller *can* fuzz it through `/proc/sys/kernel/ftrace_enabled`, so that's something to keep in mind. The important thing is that `ftrace_enable_sysctl` uses the sysctl handler `ftrace_enable_sysctl`.

I did a bit of cross-checking, and the selftests use `fentry` and `fexit`, which depend on `BPF_JIT` and `DYNAMIC_FTRACE_WITH_DIRECT_CALLS`.  However, `DYNAMIC_FTRACE_WITH_DIRECT_CALLS` is selected automatically if `HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS` is met, so we don't need to specify it directly. The same applies for `CONFIG_KPROBES_ON_FTRACE`, as it's automatically enabled with `HAVE_KPROBES_ON_FTRACE`.

Finally, the core reason to fuzz this patch is because it's a functional change affecting fundamental kernel logic. Specifically, the patch sets `FTRACE_OPS_FL_PERMANENT` on `direct_ops` and `kprobe_ftrace_ops` / `kprobe_ipmodify_ops`. This has the effect of preventing global ftrace disabling via `/proc/sys/kernel/ftrace_enabled` when BPF trampolines or kprobes are active. This kind of change is exactly the sort that warrants deep fuzzing. We want to ensure it works as intended, prevents unexpected side-effects, and doesn't introduce any races.

In short, I'm going to focus on these symbols:

*   `bpf_trampoline_link_prog`
*   `bpf_trampoline_unlink_prog`
*   `bpf_trampoline_multi_attach`
*   `bpf_trampoline_multi_detach`
*   `register_kprobe`
*   `unregister_kprobe`
*   `register_kretprobe`
*   `unregister_kretprobe`
*   `ftrace_enable_sysctl`

with these enabled configs:

*   `BPF_SYSCALL`
*   `BPF_JIT`
*   `FTRACE`
*   `DYNAMIC_FTRACE`
*   `KPROBES`

And then I'll call `set-results`.



28/2 2026/07/30 06:48 tool set-results
0m Args:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT",
    "FTRACE",
    "DYNAMIC_FTRACE",
    "KPROBES"
  ],
  "FocusSymbols": [
    "bpf_trampoline_link_prog",
    "bpf_trampoline_unlink_prog",
    "bpf_trampoline_multi_attach",
    "bpf_trampoline_multi_detach",
    "register_kprobe",
    "unregister_kprobe",
    "register_kretprobe",
    "unregister_kretprobe",
    "ftrace_enable_sysctl"
  ],
  "Reasoning": "The patch sets FTRACE_OPS_FL_PERMANENT on direct_ops and kprobe_ftrace_ops/kprobe_ipmodify_ops. This prevents disabling ftrace globally via /proc/sys/kernel/ftrace_enabled while BPF trampolines or kprobes are active. This is a functional change in core kernel logic (BPF trampoline and kprobes) and should be fuzzed to ensure it correctly prevents ftrace disabling and doesn't introduce regressions or race conditions during attach/detach operations.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "BPF_SYSCALL",
    "BPF_JIT",
    "FTRACE",
    "DYNAMIC_FTRACE",
    "KPROBES"
  ],
  "FocusSymbols": [
    "bpf_trampoline_link_prog",
    "bpf_trampoline_unlink_prog",
    "bpf_trampoline_multi_attach",
    "bpf_trampoline_multi_detach",
    "register_kprobe",
    "unregister_kprobe",
    "register_kretprobe",
    "unregister_kretprobe",
    "ftrace_enable_sysctl"
  ],
  "Reasoning": "The patch sets FTRACE_OPS_FL_PERMANENT on direct_ops and kprobe_ftrace_ops/kprobe_ipmodify_ops. This prevents disabling ftrace globally via /proc/sys/kernel/ftrace_enabled while BPF trampolines or kprobes are active. This is a functional change in core kernel logic (BPF trampoline and kprobes) and should be fuzzed to ensure it correctly prevents ftrace disabling and doesn't introduce regressions or race conditions during attach/detach operations.",
  "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)