AI Trajectory


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

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [
    "anon_pipe_write",
    "pipe_poll"
  ],
  "Reasoning": "The patch modifies the core pipe and epoll interaction by restricting per-write wakeups to only when EPOLLET is used and the file is opened for reading. This is a functional change in core kernel logic that affects pipe polling and writing behavior, making it worth fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/08/03 16:18 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit e009ed8dbb016ea1af5bd779113ed6e48cc6f0ce\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Aug 3 16:18:43 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/fs/pipe.c b/fs/pipe.c\nindex 429b0714ec575..84a81db39f8aa 100644\n--- a/fs/pipe.c\n+++ b/fs/pipe.c\n@@ -686,10 +686,9 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)\n \t * how (for example) the GNU make jobserver uses small writes to\n \t * wake up pending jobs\n \t *\n-\t * Epoll nonsensically wants a wakeup whether the pipe\n-\t * was already empty or not.\n+\t * -\u003epseudo_edgetrigger enables per-write wakeups, see pipe_poll()\n \t */\n-\tif (was_empty || pipe-\u003epoll_usage)\n+\tif (was_empty || READ_ONCE(pipe-\u003epseudo_edgetrigger))\n \t\twake_up_interruptible_sync_poll(\u0026pipe-\u003erd_wait, EPOLLIN | EPOLLRDNORM);\n \tkill_fasync(\u0026pipe-\u003efasync_readers, SIGIO, POLL_IN);\n \tif (wake_next_writer)\n@@ -752,7 +751,6 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)\n \t}\n }\n \n-/* No kernel lock held - fine */\n static __poll_t\n pipe_poll(struct file *filp, poll_table *wait)\n {\n@@ -760,9 +758,17 @@ pipe_poll(struct file *filp, poll_table *wait)\n \tstruct pipe_inode_info *pipe = filp-\u003eprivate_data;\n \tunion pipe_index idx;\n \n-\t/* Epoll has some historical nasty semantics, this enables them */\n-\tif (unlikely(!READ_ONCE(pipe-\u003epoll_usage)))\n-\t\tWRITE_ONCE(pipe-\u003epoll_usage, true);\n+\t/*\n+\t * Legacy epoll(EPOLLET) users depend on historical per-write wakeups,\n+\t * see 3a34b13a88ca (\"pipe: make pipe writes always wake up readers\")\n+\t * and the -\u003epseudo_edgetrigger check in anon_pipe_write().\n+\t * Currently io_uring sets EPOLLET for multishot polls, so it gets the\n+\t * same behaviour.\n+\t */\n+\tif ((filp-\u003ef_mode \u0026 FMODE_READ) \u0026\u0026\n+\t    wait \u0026\u0026 (wait-\u003e_key \u0026 EPOLLET) \u0026\u0026\n+\t    unlikely(!READ_ONCE(pipe-\u003epseudo_edgetrigger)))\n+\t\tWRITE_ONCE(pipe-\u003epseudo_edgetrigger, true);\n \n \t/*\n \t * Reading pipe state only -- no need for acquiring the semaphore.\ndiff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h\nindex 7f6a92ac97047..1acc76581af55 100644\n--- a/include/linux/pipe_fs_i.h\n+++ b/include/linux/pipe_fs_i.h\n@@ -74,7 +74,7 @@ union pipe_index {\n  *\t@files: number of struct file referring this pipe (protected by -\u003ei_lock)\n  *\t@r_counter: reader counter\n  *\t@w_counter: writer counter\n- *\t@poll_usage: is this pipe used for epoll, which has crazy wakeups?\n+ *\t@pseudo_edgetrigger: has an EPOLLET consumer, enable per-write wakeups\n  *\t@fasync_readers: reader side fasync\n  *\t@fasync_writers: writer side fasync\n  *\t@bufs: the circular array of pipe buffers\n@@ -95,7 +95,7 @@ struct pipe_inode_info {\n \tunsigned int files;\n \tunsigned int r_counter;\n \tunsigned int w_counter;\n-\tbool poll_usage;\n+\tbool pseudo_edgetrigger;\n #ifdef CONFIG_WATCH_QUEUE\n \tbool note_loss;\n #endif\n"
}

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

Args:
null

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [
    "anon_pipe_write",
    "pipe_poll"
  ],
  "Reasoning": "The patch modifies the core pipe and epoll interaction by restricting per-write wakeups to only when EPOLLET is used and the file is opened for reading. This is a functional change in core kernel logic that affects pipe polling and writing behavior, making it worth fuzzing.",
  "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 e009ed8dbb016ea1af5bd779113ed6e48cc6f0ce
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Aug 3 16:18:43 2026 +0000

    syz-cluster: applied patch under review

diff --git a/fs/pipe.c b/fs/pipe.c
index 429b0714ec575..84a81db39f8aa 100644
--- a/fs/pipe.c
+++ b/fs/pipe.c
@@ -686,10 +686,9 @@ anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)
 	 * how (for example) the GNU make jobserver uses small writes to
 	 * wake up pending jobs
 	 *
-	 * Epoll nonsensically wants a wakeup whether the pipe
-	 * was already empty or not.
+	 * ->pseudo_edgetrigger enables per-write wakeups, see pipe_poll()
 	 */
-	if (was_empty || pipe->poll_usage)
+	if (was_empty || READ_ONCE(pipe->pseudo_edgetrigger))
 		wake_up_interruptible_sync_poll(&pipe->rd_wait, EPOLLIN | EPOLLRDNORM);
 	kill_fasync(&pipe->fasync_readers, SIGIO, POLL_IN);
 	if (wake_next_writer)
@@ -752,7 +751,6 @@ static long pipe_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
 	}
 }
 
-/* No kernel lock held - fine */
 static __poll_t
 pipe_poll(struct file *filp, poll_table *wait)
 {
@@ -760,9 +758,17 @@ pipe_poll(struct file *filp, poll_table *wait)
 	struct pipe_inode_info *pipe = filp->private_data;
 	union pipe_index idx;
 
-	/* Epoll has some historical nasty semantics, this enables them */
-	if (unlikely(!READ_ONCE(pipe->poll_usage)))
-		WRITE_ONCE(pipe->poll_usage, true);
+	/*
+	 * Legacy epoll(EPOLLET) users depend on historical per-write wakeups,
+	 * see 3a34b13a88ca ("pipe: make pipe writes always wake up readers")
+	 * and the ->pseudo_edgetrigger check in anon_pipe_write().
+	 * Currently io_uring sets EPOLLET for multishot polls, so it gets the
+	 * same behaviour.
+	 */
+	if ((filp->f_mode & FMODE_READ) &&
+	    wait && (wait->_key & EPOLLET) &&
+	    unlikely(!READ_ONCE(pipe->pseudo_edgetrigger)))
+		WRITE_ONCE(pipe->pseudo_edgetrigger, true);
 
 	/*
 	 * Reading pipe state only -- no need for acquiring the semaphore.
diff --git a/include/linux/pipe_fs_i.h b/include/linux/pipe_fs_i.h
index 7f6a92ac97047..1acc76581af55 100644
--- a/include/linux/pipe_fs_i.h
+++ b/include/linux/pipe_fs_i.h
@@ -74,7 +74,7 @@ union pipe_index {
  *	@files: number of struct file referring this pipe (protected by ->i_lock)
  *	@r_counter: reader counter
  *	@w_counter: writer counter
- *	@poll_usage: is this pipe used for epoll, which has crazy wakeups?
+ *	@pseudo_edgetrigger: has an EPOLLET consumer, enable per-write wakeups
  *	@fasync_readers: reader side fasync
  *	@fasync_writers: writer side fasync
  *	@bufs: the circular array of pipe buffers
@@ -95,7 +95,7 @@ struct pipe_inode_info {
 	unsigned int files;
 	unsigned int r_counter;
 	unsigned int w_counter;
-	bool poll_usage;
+	bool pseudo_edgetrigger;
 #ifdef CONFIG_WATCH_QUEUE
 	bool note_loss;
 #endif

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

Args:
null

Results:
null

Tokens:
							input: 2852
							output: 30
							thoughts: 504
						

Thoughts:
**Analyzing a Pipe and Epoll Interaction Patch**

Okay, so I'm looking at a patch that tweaks how pipes and epoll interact in the kernel. Seems like a functional change, specifically in `fs/pipe.c` and `include/linux/pipe_fs_i.h`. The core of it revolves around renaming `poll_usage` to `pseudo_edgetrigger` within the `pipe_inode_info` structure.

The more interesting part is how `pipe_poll()` and `anon_pipe_write()` are modified. It looks like the patch is tightening up the conditions for per-write wakeups related to the `pseudo_edgetrigger` flag. It seems the old behavior, which always enabled these wakeups, is now restricted. Now, these wakeups will only occur if the file is opened for reading and `EPOLLET` (edge-triggered) mode is active. This change is tied to some legacy epoll behavior, specifically historical per-write wakeups. From the looks of it, io_uring, which uses EPOLLET for multishot polls, is now getting the same treatment.

This looks like a solid candidate for some fuzzing. It's a functional change in the core kernel pipe and epoll interaction, and subtle changes in wake-up behavior can sometimes reveal interesting edge cases.

I'll want to focus my attention on `anon_pipe_write` and `pipe_poll`, as those are the functions directly affected. This is core kernel code, so no specific configuration requirements that I can see, at least initially.

Just a quick sanity check before I go any further, I'll double-check whether `anon_pipe_write` is a static function or not. It's easy to miss those details. I'll take a quick look inside `fs/pipe.c` to confirm.



4/2 2026/08/03 16:18 tool grepper
0m Args:
{
  "Expression": "anon_pipe_write",
  "PathPrefix": "fs/pipe.c"
}

Results:
{
  "Output": "fs/pipe.c=522=static ssize_t\nfs/pipe.c:523:anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)\nfs/pipe.c-524-{\n--\nfs/pipe.c=700=fifo_pipe_write(struct kiocb *iocb, struct iov_iter *from)\nfs/pipe.c-701-{\nfs/pipe.c:702:\tint ret = anon_pipe_write(iocb, from);\nfs/pipe.c-703-\tif (ret \u003e 0) {\n--\nfs/pipe.c=755=pipe_poll(struct file *filp, poll_table *wait)\n--\nfs/pipe.c-763-\t * see 3a34b13a88ca (\"pipe: make pipe writes always wake up readers\")\nfs/pipe.c:764:\t * and the -\u003epseudo_edgetrigger check in anon_pipe_write().\nfs/pipe.c-765-\t * Currently io_uring sets EPOLLET for multishot polls, so it gets the\n--\nfs/pipe.c=1361=static const struct file_operations pipeanon_fops = {\n--\nfs/pipe.c-1363-\t.read_iter\t= anon_pipe_read,\nfs/pipe.c:1364:\t.write_iter\t= anon_pipe_write,\nfs/pipe.c-1365-\t.poll\t\t= pipe_poll,\n"
}

5/2 2026/08/03 16:18 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 4173
							output: 28
							thoughts: 188
						

Thoughts:
**Analyzing Pipe File Operations**

Okay, so I'm looking at how these pipe file operations are set up. I see that `anon_pipe_write` is declared as `static`, but then it's assigned to `.write_iter` in `pipeanon_fops`. Hmm, I need to remind myself of the style guidelines here... Right, the instructions say to prefer non-static, non-inlined API entrypoint functions over internal static helper functions. But wait, since `anon_pipe_write` is *assigned* to a function pointer (`.write_iter`), it's *not* inlined by the compiler and has a distinct address. So, even though it's declared static, it effectively acts as an API entrypoint for pipe writes, right? Got it.

Now, I'm thinking about `pipe_poll`. I see it's also `static`, and it's assigned to `.poll`. Seems similar to `anon_pipe_write`. But, just to be crystal clear, `vfs_write` and `vfs_poll` are *the* entry points at the VFS level, but *within* the pipe implementation, `anon_pipe_write` and `pipe_poll` are the key functions. To be sure about `pipe_poll`, let me just quickly check that it's declared `static` as well.



6/2 2026/08/03 16:19 tool grepper
0m Args:
{
  "Expression": "pipe_poll",
  "PathPrefix": "fs/pipe.c"
}

Results:
{
  "Output": "fs/pipe.c=523=anon_pipe_write(struct kiocb *iocb, struct iov_iter *from)\n--\nfs/pipe.c-688-\t *\nfs/pipe.c:689:\t * -\u003epseudo_edgetrigger enables per-write wakeups, see pipe_poll()\nfs/pipe.c-690-\t */\n--\nfs/pipe.c=754=static __poll_t\nfs/pipe.c:755:pipe_poll(struct file *filp, poll_table *wait)\nfs/pipe.c-756-{\n--\nfs/pipe.c=1350=const struct file_operations pipefifo_fops = {\n--\nfs/pipe.c-1353-\t.write_iter\t= fifo_pipe_write,\nfs/pipe.c:1354:\t.poll\t\t= pipe_poll,\nfs/pipe.c-1355-\t.unlocked_ioctl\t= pipe_ioctl,\n--\nfs/pipe.c=1361=static const struct file_operations pipeanon_fops = {\n--\nfs/pipe.c-1364-\t.write_iter\t= anon_pipe_write,\nfs/pipe.c:1365:\t.poll\t\t= pipe_poll,\nfs/pipe.c-1366-\t.unlocked_ioctl\t= pipe_ioctl,\n"
}

7/2 2026/08/03 16:19 llm patch-evaluator
0m Model:
gemini-3.1-pro-preview

Args:
null

Results:
null

Tokens:
							input: 5040
							output: 100
							thoughts: 265
						

Thoughts:
**My Analysis of Pipe and Epoll Interaction**

Okay, so I'm looking at this patch and the core of its impact seems to be on how pipes interact with epoll, specifically the wake-up behavior during writes. I've noted that `anon_pipe_write` and `pipe_poll` are the key entry points; they are *static* but critically, they're assigned to `file_operations` and thus, aren't inlined by the compiler. That's good, because I need distinct symbols to focus on. They are the primary targets for analyzing the changes.

No specific configuration changes are needed; this is down in the kernel's core plumbing.

The reasoning here is solid. The patch fundamentally alters the wake-up logic, restricting per-write wakeups to situations where `EPOLLET` is *and* a file is opened for reading. That's a definite functional change affecting both polling and writing, making it prime fuzzing material.

I'm making sure I have a complete picture. `fifo_pipe_write` calls `anon_pipe_write`, and `pipe_poll` is used by both `pipefifo_fops` and `pipeanon_fops`.

Since they're not inlined, they are not a problem.

So, I will focus on `anon_pipe_write` and `pipe_poll`. I'm using `set-results` to record this choice.



8/2 2026/08/03 16:19 tool set-results
0m Args:
{
  "EnableConfigs": [],
  "FocusSymbols": [
    "anon_pipe_write",
    "pipe_poll"
  ],
  "Reasoning": "The patch modifies the core pipe and epoll interaction by restricting per-write wakeups to only when EPOLLET is used and the file is opened for reading. This is a functional change in core kernel logic that affects pipe polling and writing behavior, making it worth fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": null,
  "FocusSymbols": [
    "anon_pipe_write",
    "pipe_poll"
  ],
  "Reasoning": "The patch modifies the core pipe and epoll interaction by restricting per-write wakeups to only when EPOLLET is used and the file is opened for reading. This is a functional change in core kernel logic that affects pipe polling and writing behavior, making it worth fuzzing.",
  "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)