AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/08/13 03:56 flow patch-triage
0m Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "KMSANReasoning": "",
  "NeedsKMSAN": false,
  "Reasoning": "The patch only adds a tracepoint (`refcount_final_put`) and its associated KUnit test. It does not functionally alter the execution flow of the kernel.",
  "WorthFuzzing": false
}

1/1 2026/08/13 03:56 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit 498d780feb54fc45f06a961d7dae704e6127b469\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Thu Aug 13 03:56:31 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 10e8253181d32..c37e4cd0e4ff6 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -4210,7 +4210,10 @@ S:\tMaintained\n F:\tDocumentation/atomic_*.txt\n F:\tarch/*/include/asm/atomic*.h\n F:\tinclude/*/atomic*.h\n+F:\tinclude/linux/refcount_trace.h\n F:\tinclude/linux/refcount.h\n+F:\tlib/refcount_trace.c\n+F:\tlib/tests/refcount_trace_kunit.c\n F:\tscripts/atomic/\n F:\trust/kernel/sync/atomic.rs\n F:\trust/kernel/sync/atomic/\ndiff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h\nindex d73a1c08c3e3c..244992b2f746e 100644\n--- a/include/linux/percpu-refcount.h\n+++ b/include/linux/percpu-refcount.h\n@@ -55,6 +55,7 @@\n #include \u003clinux/rcupdate.h\u003e\n #include \u003clinux/types.h\u003e\n #include \u003clinux/gfp.h\u003e\n+#include \u003clinux/refcount_trace.h\u003e\n \n struct percpu_ref;\n typedef void (percpu_ref_func_t)(struct percpu_ref *);\n@@ -331,8 +332,10 @@ static inline void percpu_ref_put_many(struct percpu_ref *ref, unsigned long nr)\n \n \tif (__ref_is_percpu(ref, \u0026percpu_count))\n \t\tthis_cpu_sub(*percpu_count, nr);\n-\telse if (unlikely(atomic_long_sub_and_test(nr, \u0026ref-\u003edata-\u003ecount)))\n+\telse if (unlikely(atomic_long_sub_and_test(nr, \u0026ref-\u003edata-\u003ecount))) {\n+\t\tdo_trace_refcount_final_put(ref);\n \t\tref-\u003edata-\u003erelease(ref);\n+\t}\n \n \trcu_read_unlock();\n }\ndiff --git a/include/linux/refcount.h b/include/linux/refcount.h\nindex ba7657ced2817..23291a31d1be2 100644\n--- a/include/linux/refcount.h\n+++ b/include/linux/refcount.h\n@@ -107,6 +107,7 @@\n #include \u003clinux/limits.h\u003e\n #include \u003clinux/refcount_types.h\u003e\n #include \u003clinux/spinlock_types.h\u003e\n+#include \u003clinux/refcount_trace.h\u003e\n \n struct mutex;\n \n@@ -393,6 +394,7 @@ bool __refcount_sub_and_test(int i, refcount_t *r, int *oldp)\n \n \tif (old \u003e 0 \u0026\u0026 old == i) {\n \t\tsmp_acquire__after_ctrl_dep();\n+\t\tdo_trace_refcount_final_put(r);\n \t\treturn true;\n \t}\n \ndiff --git a/include/linux/refcount_trace.h b/include/linux/refcount_trace.h\nnew file mode 100644\nindex 0000000000000..6f8d0ba910f05\n--- /dev/null\n+++ b/include/linux/refcount_trace.h\n@@ -0,0 +1,33 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#ifndef _LINUX_REFCOUNT_TRACE_H\n+#define _LINUX_REFCOUNT_TRACE_H\n+\n+#include \u003clinux/tracepoint-defs.h\u003e\n+#include \u003clinux/instruction_pointer.h\u003e\n+\n+#ifdef CONFIG_REFCOUNT_TRACE_FINAL_PUT\n+/* Declare the tracepoint so tracepoint_enabled() can be used */\n+DECLARE_TRACEPOINT(refcount_final_put);\n+\n+/* Wrapper function implemented in lib/ref_trace.c */\n+extern void do_refcount_trace_final_put(unsigned long caller, unsigned long ip, const void *obj);\n+\n+#define do_trace_refcount_final_put(obj)\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\t\\\n+\t\tif (tracepoint_enabled(refcount_final_put))\t\t\t\\\n+\t\t\tdo_refcount_trace_final_put(_RET_IP_, _THIS_IP_, obj);\t\\\n+\t} while (0)\n+\n+#define do_trace_refcount_final_put_cond(cond, obj)\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\t\\\n+\t\tif (tracepoint_enabled(refcount_final_put) \u0026\u0026 cond)\t\t\\\n+\t\t\tdo_refcount_trace_final_put(_RET_IP_, _THIS_IP_, obj);\t\\\n+\t} while (0)\n+\n+#else /* !CONFIG_REFCOUNT_TRACE_FINAL_PUT */\n+extern void do_refcount_trace_final_put(unsigned long caller, unsigned long ip, const void *obj);\n+#define do_trace_refcount_final_put(obj) do { } while (0)\n+#define do_trace_refcount_final_put_cond(cond, obj) do { } while (0)\n+#endif\n+\n+#endif /* _LINUX_REFCOUNT_TRACE_H */\ndiff --git a/include/trace/events/refcount.h b/include/trace/events/refcount.h\nnew file mode 100644\nindex 0000000000000..a4dc23aff93b6\n--- /dev/null\n+++ b/include/trace/events/refcount.h\n@@ -0,0 +1,55 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#undef TRACE_SYSTEM\n+#define TRACE_SYSTEM refcount\n+\n+#if !defined(_TRACE_REFCOUNT_H) || defined(TRACE_HEADER_MULTI_READ)\n+#define _TRACE_REFCOUNT_H\n+\n+#include \u003clinux/tracepoint.h\u003e\n+\n+#ifdef CONFIG_REFCOUNT_TRACE_FINAL_PUT\n+\n+/**\n+ * refcount_final_put - trace when a reference count reaches zero\n+ * @caller: return address of refcount\n+ * function(refcount_sub_and_test, percpu_ref_put_many)\n+ * @ip: return address of trace wrapper macro call,\n+ * inlining may cause it to be something else tho\n+ * @obj: refcount object(struct percpu_ref, refcount_t)\n+ *\n+ * Tracepoint instrumentation can be added using the do_refcount_trace_final_put\n+ * macro defined in include/linux/refcount_trace.h\n+ * which uses _RET_IP_ and _THIS_IP_ for caller and ip arguments respectively,\n+ * thus only requiring obj arg to be supplied\n+ */\n+TRACE_EVENT(refcount_final_put,\n+\n+\tTP_PROTO(unsigned long caller, unsigned long ip, const void *obj),\n+\n+\tTP_ARGS(caller, ip, obj),\n+\n+\tTP_STRUCT__entry(\n+\t\t__field(\tunsigned long,\t\tcaller\t)\n+\t\t__field(\tunsigned long,\t\tip\t)\n+\t\t__field(\tconst void *,\t\tobj\t)\n+\t),\n+\n+\tTP_fast_assign(\n+\t\t__entry-\u003ecaller = caller;\n+\t\t__entry-\u003eip = ip;\n+\t\t__entry-\u003eobj = obj;\n+\t),\n+\n+\tTP_printk(\"caller=%pS ip=%pS obj=%p\",\n+\t\t  (void *)__entry-\u003ecaller,\n+\t\t  (void *)__entry-\u003eip,\n+\t\t  __entry-\u003eobj\n+\t)\n+);\n+\n+#endif /* CONFIG_REFCOUNT_TRACE_FINAL_PUT */\n+\n+#endif /* _TRACE_REFCOUNT_H */\n+\n+/* This part must be outside protection */\n+#include \u003ctrace/define_trace.h\u003e\ndiff --git a/lib/Kconfig b/lib/Kconfig\nindex 00a9509636c18..ee12e8f951589 100644\n--- a/lib/Kconfig\n+++ b/lib/Kconfig\n@@ -52,6 +52,24 @@ config PACKING_KUNIT_TEST\n \n \t  When in doubt, say N.\n \n+config REFCOUNT_TRACE_FINAL_PUT\n+\tbool \"Trace final puts on multiple refcount implementations\"\n+\tdepends on TRACEPOINTS \u0026\u0026 DEBUG_KERNEL\n+\thelp\n+\t  Trace when a refcount implementation considers refcounted object dead.\n+\n+\t  If unsure, say N.\n+\n+config REFCOUNT_TRACE_KUNIT_TEST\n+\ttristate \"refcount trace kunit test\" if !KUNIT_ALL_TESTS\n+\tdepends on REFCOUNT_TRACE_FINAL_PUT \u0026\u0026 KUNIT \u0026\u0026 TRACEPOINTS\n+\tdefault KUNIT_ALL_TESTS\n+\thelp\n+\t  This option enables the KUnit test suite for the refcount_final_put\n+\t  tracepoint.\n+\n+\t  If unsure, say N.\n+\n config BITREVERSE\n \ttristate\n \ndiff --git a/lib/Makefile b/lib/Makefile\nindex f33a24bf1c19a..e75e88f0c8708 100644\n--- a/lib/Makefile\n+++ b/lib/Makefile\n@@ -335,3 +335,5 @@ CONTEXT_ANALYSIS_test_context-analysis.o := y\n obj-$(CONFIG_CONTEXT_ANALYSIS_TEST) += test_context-analysis.o\n \n subdir-$(CONFIG_FORTIFY_SOURCE) += test_fortify\n+\n+obj-$(CONFIG_REFCOUNT_TRACE_FINAL_PUT) += refcount_trace.o\ndiff --git a/lib/refcount.c b/lib/refcount.c\nindex a207a8f22b3ca..8b148b5765037 100644\n--- a/lib/refcount.c\n+++ b/lib/refcount.c\n@@ -7,6 +7,7 @@\n #include \u003clinux/refcount.h\u003e\n #include \u003clinux/spinlock.h\u003e\n #include \u003clinux/bug.h\u003e\n+#include \u003clinux/refcount_trace.h\u003e\n \n #define REFCOUNT_WARN(str)\tWARN_ONCE(1, \"refcount_t: \" str \".\\n\")\n \n@@ -56,7 +57,10 @@ bool refcount_dec_if_one(refcount_t *r)\n {\n \tint val = 1;\n \n-\treturn atomic_try_cmpxchg_release(\u0026r-\u003erefs, \u0026val, 0);\n+\tbool ret = atomic_try_cmpxchg_release(\u0026r-\u003erefs, \u0026val, 0);\n+\n+\tdo_trace_refcount_final_put_cond(ret, r);\n+\treturn ret;\n }\n EXPORT_SYMBOL(refcount_dec_if_one);\n \ndiff --git a/lib/refcount_trace.c b/lib/refcount_trace.c\nnew file mode 100644\nindex 0000000000000..2476bb5f2f603\n--- /dev/null\n+++ b/lib/refcount_trace.c\n@@ -0,0 +1,14 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#define CREATE_TRACE_POINTS\n+#include \u003ctrace/events/refcount.h\u003e\n+#include \u003clinux/refcount_trace.h\u003e\n+\n+//Wrapper function for functions defined entirely in header files\n+void do_refcount_trace_final_put(unsigned long caller,\n+\t\t\t\t unsigned long ip,\n+\t\t\t\t const void *obj)\n+{\n+\ttrace_call__refcount_final_put(caller, ip, obj);\n+}\n+EXPORT_SYMBOL_GPL(do_refcount_trace_final_put);\n+EXPORT_TRACEPOINT_SYMBOL_GPL(refcount_final_put);\ndiff --git a/lib/tests/Makefile b/lib/tests/Makefile\nindex 7e9c2fa52e35a..1ef41c0b6a0c1 100644\n--- a/lib/tests/Makefile\n+++ b/lib/tests/Makefile\n@@ -57,5 +57,6 @@ obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o\n obj-$(CONFIG_UTIL_MACROS_KUNIT) += util_macros_kunit.o\n obj-$(CONFIG_RATELIMIT_KUNIT_TEST) += test_ratelimit.o\n obj-$(CONFIG_UUID_KUNIT_TEST) += uuid_kunit.o\n+obj-$(CONFIG_REFCOUNT_TRACE_KUNIT_TEST) += refcount_trace_kunit.o\n \n obj-$(CONFIG_TEST_RUNTIME_MODULE)\t\t+= module/\ndiff --git a/lib/tests/refcount_trace_kunit.c b/lib/tests/refcount_trace_kunit.c\nnew file mode 100644\nindex 0000000000000..a57e9e4cfa427\n--- /dev/null\n+++ b/lib/tests/refcount_trace_kunit.c\n@@ -0,0 +1,141 @@\n+// SPDX-License-Identifier: GPL-2.0\n+#include \u003clinux/kernel.h\u003e\n+#include \u003ckunit/test.h\u003e\n+#include \u003clinux/compiler_attributes.h\u003e\n+#include \u003clinux/wait_bit.h\u003e\n+#include \u003clinux/instruction_pointer.h\u003e\n+#include \u003clinux/kallsyms.h\u003e\n+#include \u003clinux/jiffies.h\u003e\n+#include \u003clinux/percpu-refcount.h\u003e\n+#include \u003clinux/refcount.h\u003e\n+#include \u003clinux/types.h\u003e\n+#include \u003clinux/atomic.h\u003e\n+#include \u003ctrace/events/refcount.h\u003e\n+\n+struct data {\n+\tunsigned long caller;\n+\tunsigned long ip;\n+\tconst void *obj;\n+\tatomic_t count;\n+};\n+\n+struct data capture;\n+\n+const void *chk_obj;\n+\n+#define test_init()\t\t\t\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\t\\\n+\t\tKUNIT_EXPECT_FALSE(\t\t\t\t\t\t\\\n+\t\t\ttest, register_trace_refcount_final_put(probe, NULL));\t\\\n+\t\t\t\t\t\t\t\t\t\t\\\n+\t\tatomic_set_release(\u0026capture.count, 0);\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\t\\\n+\t\tchk_obj = \u0026obj;\t\t\t\t\t\t\t\\\n+\t} while (0)\n+\n+\n+#define test_exit()\t\t\t\t\t\t\t\t\\\n+\tdo {\t\t\t\t\t\t\t\t\t\\\n+\t\t/* wait for probe completion */\t\t\t\t\t\\\n+\t\tint notimeout = wait_var_event_timeout(\t\t\t\t\\\n+\t\t\t\u0026capture.count,\t\t\t\t\t\t\\\n+\t\t\tatomic_read_acquire(\u0026capture.count),\t\t\t\\\n+\t\t\tmsecs_to_jiffies(10000)\t\t\t\t\t\\\n+\t\t);\t\t\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\t\\\n+\t\tunregister_trace_refcount_final_put(probe, NULL);\t\t\\\n+\t\ttracepoint_synchronize_unregister();\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\t\\\n+\t\tKUNIT_ASSERT_TRUE(test, notimeout);\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\t\t\\\n+\t\tKUNIT_EXPECT_EQ(test, atomic_read_acquire(\u0026capture.count), 1);\t\\\n+\t\t\t\t\t\t\t\t\t\t\\\n+\t\tKUNIT_EXPECT_TRUE(test, __kernel_text_address(capture.caller));\t\\\n+\t\tKUNIT_EXPECT_TRUE(test, __kernel_text_address(capture.ip));\t\\\n+\t\t\t\t\t\t\t\t\t\t\\\n+\t\tKUNIT_EXPECT_PTR_EQ(test, capture.obj, \u0026obj);\t\t\t\\\n+\t} while (0)\n+\n+static void probe(\n+\t  void *ignore,\n+\t  unsigned long caller,\n+\t  unsigned long ip,\n+\t  const void *obj)\n+{\n+\t//prevent non test func final_puts from changing captured values\n+\tif (chk_obj != obj)\n+\t\treturn;\n+\n+\tcapture.caller = caller;\n+\tcapture.ip = ip;\n+\tcapture.obj = obj;\n+\n+\tatomic_inc_return_release(\u0026capture.count); //increase count\n+}\n+\n+static void test_refcount_sub_and_test(struct kunit *test)\n+{\n+\trefcount_t obj;\n+\n+\ttest_init();\n+\trefcount_set(\u0026obj, 2);\n+\n+\tKUNIT_EXPECT_FALSE(test, refcount_dec_and_test(\u0026obj));\n+\tKUNIT_EXPECT_TRUE(test, refcount_dec_and_test(\u0026obj));\n+\n+\ttest_exit();\n+}\n+\n+static void test_refcount_dec_if_one(struct kunit *test)\n+{\n+\trefcount_t obj;\n+\n+\ttest_init();\n+\trefcount_set(\u0026obj, 2);\n+\n+\tKUNIT_EXPECT_FALSE(test, refcount_dec_and_test(\u0026obj));\n+\tKUNIT_EXPECT_TRUE(test, refcount_dec_if_one(\u0026obj));\n+\n+\ttest_exit();\n+}\n+static void dummy_release(struct percpu_ref *ref) {}\n+\n+static void test_percpu_ref_put_many(struct kunit *test)\n+{\n+\tstruct percpu_ref obj;\n+\n+\ttest_init();\n+\n+\tKUNIT_ASSERT_FALSE(test, percpu_ref_init(\u0026obj, dummy_release, 0, GFP_KERNEL));\n+\n+\tpercpu_ref_get(\u0026obj);\n+\tpercpu_ref_get(\u0026obj);\n+\n+\tpercpu_ref_put(\u0026obj);\n+\tpercpu_ref_put(\u0026obj);\n+\n+\tpercpu_ref_switch_to_atomic_sync(\u0026obj);\n+\n+\tpercpu_ref_put(\u0026obj);\n+\n+\ttest_exit();\n+\tpercpu_ref_exit(\u0026obj);\n+}\n+\n+static struct kunit_case refcount_trace_test_cases[] = {\n+\tKUNIT_CASE(test_refcount_sub_and_test),\n+\tKUNIT_CASE(test_refcount_dec_if_one),\n+\tKUNIT_CASE(test_percpu_ref_put_many),\n+\t{}\n+};\n+\n+static struct kunit_suite refcount_trace_test_suite = {\n+\t.name = \"refcount-trace\",\n+\t.test_cases = refcount_trace_test_cases\n+};\n+\n+kunit_test_suites(\u0026refcount_trace_test_suite);\n+\n+MODULE_AUTHOR(\"Eugene Mavick \u003cm@mavick.dev\u003e\");\n+MODULE_DESCRIPTION(\"KUnit test for ref_trace\");\n+MODULE_LICENSE(\"GPL\");\n"
}

2/1 2026/08/13 03:56 agent patch-evaluator
0m Model:
best-expensive

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a tracepoint (`refcount_final_put`) and its associated KUnit test. It does not functionally alter the execution flow of the kernel.",
  "WorthFuzzing": false
}

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 498d780feb54fc45f06a961d7dae704e6127b469
Author: syz-cluster <triage@syzkaller.com>
Date:   Thu Aug 13 03:56:31 2026 +0000

    syz-cluster: applied patch under review

diff --git a/MAINTAINERS b/MAINTAINERS
index 10e8253181d32..c37e4cd0e4ff6 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4210,7 +4210,10 @@ S:	Maintained
 F:	Documentation/atomic_*.txt
 F:	arch/*/include/asm/atomic*.h
 F:	include/*/atomic*.h
+F:	include/linux/refcount_trace.h
 F:	include/linux/refcount.h
+F:	lib/refcount_trace.c
+F:	lib/tests/refcount_trace_kunit.c
 F:	scripts/atomic/
 F:	rust/kernel/sync/atomic.rs
 F:	rust/kernel/sync/atomic/
diff --git a/include/linux/percpu-refcount.h b/include/linux/percpu-refcount.h
index d73a1c08c3e3c..244992b2f746e 100644
--- a/include/linux/percpu-refcount.h
+++ b/include/linux/percpu-refcount.h
@@ -55,6 +55,7 @@
 #include <linux/rcupdate.h>
 #include <linux/types.h>
 #include <linux/gfp.h>
+#include <linux/refcount_trace.h>
 
 struct percpu_ref;
 typedef void (percpu_ref_func_t)(struct percpu_ref *);
@@ -331,8 +332,10 @@ static inline void percpu_ref_put_many(struct percpu_ref *ref, unsigned long nr)
 
 	if (__ref_is_percpu(ref, &percpu_count))
 		this_cpu_sub(*percpu_count, nr);
-	else if (unlikely(atomic_long_sub_and_test(nr, &ref->data->count)))
+	else if (unlikely(atomic_long_sub_and_test(nr, &ref->data->count))) {
+		do_trace_refcount_final_put(ref);
 		ref->data->release(ref);
+	}
 
 	rcu_read_unlock();
 }
diff --git a/include/linux/refcount.h b/include/linux/refcount.h
index ba7657ced2817..23291a31d1be2 100644
--- a/include/linux/refcount.h
+++ b/include/linux/refcount.h
@@ -107,6 +107,7 @@
 #include <linux/limits.h>
 #include <linux/refcount_types.h>
 #include <linux/spinlock_types.h>
+#include <linux/refcount_trace.h>
 
 struct mutex;
 
@@ -393,6 +394,7 @@ bool __refcount_sub_and_test(int i, refcount_t *r, int *oldp)
 
 	if (old > 0 && old == i) {
 		smp_acquire__after_ctrl_dep();
+		do_trace_refcount_final_put(r);
 		return true;
 	}
 
diff --git a/include/linux/refcount_trace.h b/include/linux/refcount_trace.h
new file mode 100644
index 0000000000000..6f8d0ba910f05
--- /dev/null
+++ b/include/linux/refcount_trace.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_REFCOUNT_TRACE_H
+#define _LINUX_REFCOUNT_TRACE_H
+
+#include <linux/tracepoint-defs.h>
+#include <linux/instruction_pointer.h>
+
+#ifdef CONFIG_REFCOUNT_TRACE_FINAL_PUT
+/* Declare the tracepoint so tracepoint_enabled() can be used */
+DECLARE_TRACEPOINT(refcount_final_put);
+
+/* Wrapper function implemented in lib/ref_trace.c */
+extern void do_refcount_trace_final_put(unsigned long caller, unsigned long ip, const void *obj);
+
+#define do_trace_refcount_final_put(obj)					\
+	do {									\
+		if (tracepoint_enabled(refcount_final_put))			\
+			do_refcount_trace_final_put(_RET_IP_, _THIS_IP_, obj);	\
+	} while (0)
+
+#define do_trace_refcount_final_put_cond(cond, obj)				\
+	do {									\
+		if (tracepoint_enabled(refcount_final_put) && cond)		\
+			do_refcount_trace_final_put(_RET_IP_, _THIS_IP_, obj);	\
+	} while (0)
+
+#else /* !CONFIG_REFCOUNT_TRACE_FINAL_PUT */
+extern void do_refcount_trace_final_put(unsigned long caller, unsigned long ip, const void *obj);
+#define do_trace_refcount_final_put(obj) do { } while (0)
+#define do_trace_refcount_final_put_cond(cond, obj) do { } while (0)
+#endif
+
+#endif /* _LINUX_REFCOUNT_TRACE_H */
diff --git a/include/trace/events/refcount.h b/include/trace/events/refcount.h
new file mode 100644
index 0000000000000..a4dc23aff93b6
--- /dev/null
+++ b/include/trace/events/refcount.h
@@ -0,0 +1,55 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM refcount
+
+#if !defined(_TRACE_REFCOUNT_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _TRACE_REFCOUNT_H
+
+#include <linux/tracepoint.h>
+
+#ifdef CONFIG_REFCOUNT_TRACE_FINAL_PUT
+
+/**
+ * refcount_final_put - trace when a reference count reaches zero
+ * @caller: return address of refcount
+ * function(refcount_sub_and_test, percpu_ref_put_many)
+ * @ip: return address of trace wrapper macro call,
+ * inlining may cause it to be something else tho
+ * @obj: refcount object(struct percpu_ref, refcount_t)
+ *
+ * Tracepoint instrumentation can be added using the do_refcount_trace_final_put
+ * macro defined in include/linux/refcount_trace.h
+ * which uses _RET_IP_ and _THIS_IP_ for caller and ip arguments respectively,
+ * thus only requiring obj arg to be supplied
+ */
+TRACE_EVENT(refcount_final_put,
+
+	TP_PROTO(unsigned long caller, unsigned long ip, const void *obj),
+
+	TP_ARGS(caller, ip, obj),
+
+	TP_STRUCT__entry(
+		__field(	unsigned long,		caller	)
+		__field(	unsigned long,		ip	)
+		__field(	const void *,		obj	)
+	),
+
+	TP_fast_assign(
+		__entry->caller = caller;
+		__entry->ip = ip;
+		__entry->obj = obj;
+	),
+
+	TP_printk("caller=%pS ip=%pS obj=%p",
+		  (void *)__entry->caller,
+		  (void *)__entry->ip,
+		  __entry->obj
+	)
+);
+
+#endif /* CONFIG_REFCOUNT_TRACE_FINAL_PUT */
+
+#endif /* _TRACE_REFCOUNT_H */
+
+/* This part must be outside protection */
+#include <trace/define_trace.h>
diff --git a/lib/Kconfig b/lib/Kconfig
index 00a9509636c18..ee12e8f951589 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -52,6 +52,24 @@ config PACKING_KUNIT_TEST
 
 	  When in doubt, say N.
 
+config REFCOUNT_TRACE_FINAL_PUT
+	bool "Trace final puts on multiple refcount implementations"
+	depends on TRACEPOINTS && DEBUG_KERNEL
+	help
+	  Trace when a refcount implementation considers refcounted object dead.
+
+	  If unsure, say N.
+
+config REFCOUNT_TRACE_KUNIT_TEST
+	tristate "refcount trace kunit test" if !KUNIT_ALL_TESTS
+	depends on REFCOUNT_TRACE_FINAL_PUT && KUNIT && TRACEPOINTS
+	default KUNIT_ALL_TESTS
+	help
+	  This option enables the KUnit test suite for the refcount_final_put
+	  tracepoint.
+
+	  If unsure, say N.
+
 config BITREVERSE
 	tristate
 
diff --git a/lib/Makefile b/lib/Makefile
index f33a24bf1c19a..e75e88f0c8708 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -335,3 +335,5 @@ CONTEXT_ANALYSIS_test_context-analysis.o := y
 obj-$(CONFIG_CONTEXT_ANALYSIS_TEST) += test_context-analysis.o
 
 subdir-$(CONFIG_FORTIFY_SOURCE) += test_fortify
+
+obj-$(CONFIG_REFCOUNT_TRACE_FINAL_PUT) += refcount_trace.o
diff --git a/lib/refcount.c b/lib/refcount.c
index a207a8f22b3ca..8b148b5765037 100644
--- a/lib/refcount.c
+++ b/lib/refcount.c
@@ -7,6 +7,7 @@
 #include <linux/refcount.h>
 #include <linux/spinlock.h>
 #include <linux/bug.h>
+#include <linux/refcount_trace.h>
 
 #define REFCOUNT_WARN(str)	WARN_ONCE(1, "refcount_t: " str ".\n")
 
@@ -56,7 +57,10 @@ bool refcount_dec_if_one(refcount_t *r)
 {
 	int val = 1;
 
-	return atomic_try_cmpxchg_release(&r->refs, &val, 0);
+	bool ret = atomic_try_cmpxchg_release(&r->refs, &val, 0);
+
+	do_trace_refcount_final_put_cond(ret, r);
+	return ret;
 }
 EXPORT_SYMBOL(refcount_dec_if_one);
 
diff --git a/lib/refcount_trace.c b/lib/refcount_trace.c
new file mode 100644
index 0000000000000..2476bb5f2f603
--- /dev/null
+++ b/lib/refcount_trace.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+#define CREATE_TRACE_POINTS
+#include <trace/events/refcount.h>
+#include <linux/refcount_trace.h>
+
+//Wrapper function for functions defined entirely in header files
+void do_refcount_trace_final_put(unsigned long caller,
+				 unsigned long ip,
+				 const void *obj)
+{
+	trace_call__refcount_final_put(caller, ip, obj);
+}
+EXPORT_SYMBOL_GPL(do_refcount_trace_final_put);
+EXPORT_TRACEPOINT_SYMBOL_GPL(refcount_final_put);
diff --git a/lib/tests/Makefile b/lib/tests/Makefile
index 7e9c2fa52e35a..1ef41c0b6a0c1 100644
--- a/lib/tests/Makefile
+++ b/lib/tests/Makefile
@@ -57,5 +57,6 @@ obj-$(CONFIG_USERCOPY_KUNIT_TEST) += usercopy_kunit.o
 obj-$(CONFIG_UTIL_MACROS_KUNIT) += util_macros_kunit.o
 obj-$(CONFIG_RATELIMIT_KUNIT_TEST) += test_ratelimit.o
 obj-$(CONFIG_UUID_KUNIT_TEST) += uuid_kunit.o
+obj-$(CONFIG_REFCOUNT_TRACE_KUNIT_TEST) += refcount_trace_kunit.o
 
 obj-$(CONFIG_TEST_RUNTIME_MODULE)		+= module/
diff --git a/lib/tests/refcount_trace_kunit.c b/lib/tests/refcount_trace_kunit.c
new file mode 100644
index 0000000000000..a57e9e4cfa427
--- /dev/null
+++ b/lib/tests/refcount_trace_kunit.c
@@ -0,0 +1,141 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <kunit/test.h>
+#include <linux/compiler_attributes.h>
+#include <linux/wait_bit.h>
+#include <linux/instruction_pointer.h>
+#include <linux/kallsyms.h>
+#include <linux/jiffies.h>
+#include <linux/percpu-refcount.h>
+#include <linux/refcount.h>
+#include <linux/types.h>
+#include <linux/atomic.h>
+#include <trace/events/refcount.h>
+
+struct data {
+	unsigned long caller;
+	unsigned long ip;
+	const void *obj;
+	atomic_t count;
+};
+
+struct data capture;
+
+const void *chk_obj;
+
+#define test_init()								\
+	do {									\
+		KUNIT_EXPECT_FALSE(						\
+			test, register_trace_refcount_final_put(probe, NULL));	\
+										\
+		atomic_set_release(&capture.count, 0);				\
+										\
+		chk_obj = &obj;							\
+	} while (0)
+
+
+#define test_exit()								\
+	do {									\
+		/* wait for probe completion */					\
+		int notimeout = wait_var_event_timeout(				\
+			&capture.count,						\
+			atomic_read_acquire(&capture.count),			\
+			msecs_to_jiffies(10000)					\
+		);								\
+										\
+		unregister_trace_refcount_final_put(probe, NULL);		\
+		tracepoint_synchronize_unregister();				\
+										\
+		KUNIT_ASSERT_TRUE(test, notimeout);				\
+										\
+		KUNIT_EXPECT_EQ(test, atomic_read_acquire(&capture.count), 1);	\
+										\
+		KUNIT_EXPECT_TRUE(test, __kernel_text_address(capture.caller));	\
+		KUNIT_EXPECT_TRUE(test, __kernel_text_address(capture.ip));	\
+										\
+		KUNIT_EXPECT_PTR_EQ(test, capture.obj, &obj);			\
+	} while (0)
+
+static void probe(
+	  void *ignore,
+	  unsigned long caller,
+	  unsigned long ip,
+	  const void *obj)
+{
+	//prevent non test func final_puts from changing captured values
+	if (chk_obj != obj)
+		return;
+
+	capture.caller = caller;
+	capture.ip = ip;
+	capture.obj = obj;
+
+	atomic_inc_return_release(&capture.count); //increase count
+}
+
+static void test_refcount_sub_and_test(struct kunit *test)
+{
+	refcount_t obj;
+
+	test_init();
+	refcount_set(&obj, 2);
+
+	KUNIT_EXPECT_FALSE(test, refcount_dec_and_test(&obj));
+	KUNIT_EXPECT_TRUE(test, refcount_dec_and_test(&obj));
+
+	test_exit();
+}
+
+static void test_refcount_dec_if_one(struct kunit *test)
+{
+	refcount_t obj;
+
+	test_init();
+	refcount_set(&obj, 2);
+
+	KUNIT_EXPECT_FALSE(test, refcount_dec_and_test(&obj));
+	KUNIT_EXPECT_TRUE(test, refcount_dec_if_one(&obj));
+
+	test_exit();
+}
+static void dummy_release(struct percpu_ref *ref) {}
+
+static void test_percpu_ref_put_many(struct kunit *test)
+{
+	struct percpu_ref obj;
+
+	test_init();
+
+	KUNIT_ASSERT_FALSE(test, percpu_ref_init(&obj, dummy_release, 0, GFP_KERNEL));
+
+	percpu_ref_get(&obj);
+	percpu_ref_get(&obj);
+
+	percpu_ref_put(&obj);
+	percpu_ref_put(&obj);
+
+	percpu_ref_switch_to_atomic_sync(&obj);
+
+	percpu_ref_put(&obj);
+
+	test_exit();
+	percpu_ref_exit(&obj);
+}
+
+static struct kunit_case refcount_trace_test_cases[] = {
+	KUNIT_CASE(test_refcount_sub_and_test),
+	KUNIT_CASE(test_refcount_dec_if_one),
+	KUNIT_CASE(test_percpu_ref_put_many),
+	{}
+};
+
+static struct kunit_suite refcount_trace_test_suite = {
+	.name = "refcount-trace",
+	.test_cases = refcount_trace_test_cases
+};
+
+kunit_test_suites(&refcount_trace_test_suite);
+
+MODULE_AUTHOR("Eugene Mavick <m@mavick.dev>");
+MODULE_DESCRIPTION("KUnit test for ref_trace");
+MODULE_LICENSE("GPL");

3/2 2026/08/13 03:56 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 6477
							output: 65
							thoughts: 661
						

Thoughts:
**Analyzing the Patch for Fuzzing Eligibility**

Okay, so I'm looking at this patch and my initial thought is, based on the prompt's rules, this *should* be marked as not worth fuzzing. It's adding a tracepoint, `refcount_final_put`, to track when a refcount object is considered dead. I see modifications to several core files: `percpu-refcount.h`, `refcount.h`, and `refcount.c`, all aimed at inserting calls to either `do_trace_refcount_final_put` or `do_trace_refcount_final_put_cond`.  It's also introducing new files for the tracepoint itself: `refcount_trace.h`, `events/refcount.h`, `refcount_trace.c`, and a KUnit test `refcount_trace_kunit.c`, as well as Kconfig options.

The prompt specifically says to mark patches as `WorthFuzzing=false` if they only contain modifications like this – additions to documentation, Kconfig, comments, logging, *or* tracepoints. I need to make sure this doesn't actually *change* how anything runs.

Alright, I need to really confirm this. I'm going through the diffs specifically looking for code alterations.

*   `percpu_ref_put_many`: I see it's adding `do_trace_refcount_final_put(ref);` inside an `if` block that checks if the refcount becomes zero. It's *not* changing the core logic for the refcount itself.

*   `__refcount_sub_and_test`: Again, only adding the tracepoint call `do_trace_refcount_final_put(r);` around the conditional return. This doesn't change when a reference is considered to be final, it's just adding an observation point.

*   `refcount_dec_if_one`: Similar pattern. It adds `do_trace_refcount_final_put_cond(ret, r);` based on the return of `atomic_try_cmpxchg_release`. The core atomic operations are unchanged.

The rest is just the tracepoint implementation and its test. So, I'm confident that these additions are *purely* for tracing and don't introduce any new functional behavior or alter existing execution flow.  Therefore, I'm concluding that this patch is definitely not worth fuzzing. My final answer is `WorthFuzzing=false`.



4/2 2026/08/13 03:56 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a tracepoint (`refcount_final_put`) and its associated KUnit test. It does not functionally alter the execution flow of the kernel.",
  "WorthFuzzing": false
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [],
  "Reasoning": "The patch only adds a tracepoint (`refcount_final_put`) and its associated KUnit test. It does not functionally alter the execution flow of the kernel.",
  "WorthFuzzing": false
}

5/1 2026/08/13 03:56 action If
0m Args:
{
  "WorthFuzzing": false
}

Results:
null

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)