| 0/0 |
2026/08/03 01:41 |
flow |
assessment-security |
36m
Results: map[DenialOfService:false Explanation:**Analysis of the Bug**
The bug is a race condition between the probing of an I2C client device
(`i2c_device_probe`) and the removal of its parent I2C adapter
(`i2c_del_adapter`).
1. When an I2C client device is probed, `i2c_device_probe()` creates a debugfs
directory for the client (`client->debugfs`) inside the adapter's debugfs
directory (`adap->debugfs`). If the driver probe fails, it cleans up by calling
`debugfs_remove_recursive(client->debugfs)`.
2. Concurrently, if the underlying I2C adapter is removed (e.g., a USB
disconnect for `i2c-tiny-usb`), `i2c_del_adapter()` is invoked. This function
calls `debugfs_remove_recursive(adap->debugfs)`, which recursively removes all
child directories, including `client->debugfs`.
3. If `i2c_del_adapter()` removes the directories *before* `i2c_device_probe()`
handles its probe failure, `i2c_device_probe()` will call
`debugfs_remove_recursive(client->debugfs)` on a dentry that has already been
removed.
4. The `debugfs_remove()` function relies on `simple_recursive_removal()`, which
unconditionally calls `drop_nlink()` on the parent directory's inode if the
target is a directory. Since the parent directory (`adap->debugfs`) was already
removed by the adapter teardown, its `i_nlink` is already 0. Calling
`drop_nlink()` again causes an integer underflow and triggers the
`WARN_ON(inode->i_nlink == 0)` in `fs/inode.c`.
**Security Impact Assessment**
* **Exploitable:** No. The bug only causes an integer underflow of `i_nlink` on
an in-memory debugfs inode. This does not lead to memory corruption,
out-of-bounds access, use-after-free, or any exploitable primitive.
* **Denial of Service:** No. The bug only triggers a non-fatal `WARNING`. In
default production builds (where `panic_on_warn` is not set), this will simply
print a stack trace to the kernel log and continue execution without halting the
system or causing deadlocks.
* **Accessible From Unprivileged Processes / User Namespaces:** No. The stack
trace shows the race was triggered by writing to the `new_device` sysfs
attribute (`new_device_store`). This attribute is created with mode `0200`
(write-only by owner) and is owned by `root`. Unprivileged users and processes
inside user namespaces cannot write to this file.
* **Peripheral Trigger:** While the race involves a USB disconnect event
(`i2c-tiny-usb`), the required concurrent action in this trace is a write to the
`new_device` sysfs attribute, which requires root privileges. Therefore, a
malicious peripheral alone cannot trigger this specific bug without root
interaction. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/08/03 01:41 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/08/03 01:41 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/4de9984cfa5e9b8d1c558a30c8bc253f9f86152f]
|
| 3/1 |
2026/08/03 01:43 |
action |
kernel-builder |
18m
Results: map[KernelObj:/app/workdir/cache/build/251b20ab8e1e6a68a02410b42919d7cabdba4906]
|
| 4/1 |
2026/08/03 02:01 |
action |
codesearch-prepare |
9m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/03 02:11 |
agent |
expert |
6m
Model:
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
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:
The kernel bug report is:
i2c i2c-2: failure reading functionality
i2c i2c-2: failure reading functionality
i2c i2c-2: failure reading functionality
------------[ cut here ]------------
inode->i_nlink == 0
WARNING: fs/inode.c:408 at drop_nlink+0xc5/0x110 fs/inode.c:408, CPU#0: syz.0.0/5327
Modules linked in:
CPU: 0 UID: 0 PID: 5327 Comm: syz.0.0 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
RIP: 0010:drop_nlink+0xc5/0x110 fs/inode.c:408
Code: 98 07 00 00 be 08 00 00 00 e8 47 69 e5 ff 3e 48 ff 83 98 07 00 00 5b 41 5c 41 5e 41 5f 5d c3 cc cc cc cc cc e8 9c 34 76 ff 90 <0f> 0b 90 eb 81 44 89 f1 80 e1 07 80 c1 03 38 c1 0f 8c 5b ff ff ff
RSP: 0018:ffffc9000f0af538 EFLAGS: 00010283
RAX: ffffffff8250f084 RBX: ffff8880442289a0 RCX: 0000000000100000
RDX: ffffc90020802000 RSI: 0000000000002582 RDI: 0000000000002583
RBP: 0000000000000000 R08: ffffffff9057bf37 R09: 1ffffffff20af7e6
R10: dffffc0000000000 R11: fffffbfff20af7e7 R12: 1ffff1100884513d
R13: ffff88801325e758 R14: ffff8880442289e8 R15: dffffc0000000000
FS: 00007fd9732876c0(0000) GS:ffff88808c53f000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000055cc36eb6218 CR3: 000000001d323000 CR4: 0000000000352ef0
Call Trace:
<TASK>
__simple_recursive_removal+0x491/0x520 fs/libfs.c:631
debugfs_remove+0x5b/0x70 fs/debugfs/inode.c:781
i2c_device_probe+0x8dc/0xbf0 drivers/i2c/i2c-core-base.c:610
call_driver_probe drivers/base/dd.c:-1 [inline]
really_probe+0x254/0xae0 drivers/base/dd.c:706
__driver_probe_device+0x1e8/0x360 drivers/base/dd.c:868
driver_probe_device+0x4f/0x240 drivers/base/dd.c:898
__device_attach_driver+0x270/0x410 drivers/base/dd.c:1026
bus_for_each_drv+0x258/0x2f0 drivers/base/bus.c:500
__device_attach+0x2c4/0x450 drivers/base/dd.c:1098
device_initial_probe+0xa1/0xd0 drivers/base/dd.c:1153
bus_probe_device+0x12a/0x220 drivers/base/bus.c:620
device_add+0x7d7/0xb80 drivers/base/core.c:3772
i2c_new_client_device+0xa29/0x1130 drivers/i2c/i2c-core-base.c:1019
new_device_store+0x24b/0x530 drivers/i2c/i2c-core-base.c:1307
kernfs_fop_write_iter+0x3a4/0x540 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x612/0xba0 fs/read_write.c:687
ksys_write+0x150/0x270 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x174/0x580 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7fd97239e019
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007fd973286fe8 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007fd972626090 RCX: 00007fd97239e019
RDX: 000000000000000b RSI: 0000200000000800 RDI: 0000000000000003
RBP: 00007fd97243500c R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007fd972626128 R14: 00007fd972626090 R15: 00007ffcb5017a38
</TASK>
Reply: **Analysis of the Bug**
The bug is a race condition between the probing of an I2C client device (`i2c_device_probe`) and the removal of its parent I2C adapter (`i2c_del_adapter`).
1. When an I2C client device is probed, `i2c_device_probe()` creates a debugfs directory for the client (`client->debugfs`) inside the adapter's debugfs directory (`adap->debugfs`). If the driver probe fails, it cleans up by calling `debugfs_remove_recursive(client->debugfs)`.
2. Concurrently, if the underlying I2C adapter is removed (e.g., a USB disconnect for `i2c-tiny-usb`), `i2c_del_adapter()` is invoked. This function calls `debugfs_remove_recursive(adap->debugfs)`, which recursively removes all child directories, including `client->debugfs`.
3. If `i2c_del_adapter()` removes the directories *before* `i2c_device_probe()` handles its probe failure, `i2c_device_probe()` will call `debugfs_remove_recursive(client->debugfs)` on a dentry that has already been removed.
4. The `debugfs_remove()` function relies on `simple_recursive_removal()`, which unconditionally calls `drop_nlink()` on the parent directory's inode if the target is a directory. Since the parent directory (`adap->debugfs`) was already removed by the adapter teardown, its `i_nlink` is already 0. Calling `drop_nlink()` again causes an integer underflow and triggers the `WARN_ON(inode->i_nlink == 0)` in `fs/inode.c`.
**Security Impact Assessment**
* **Exploitable:** No. The bug only causes an integer underflow of `i_nlink` on an in-memory debugfs inode. This does not lead to memory corruption, out-of-bounds access, use-after-free, or any exploitable primitive.
* **Denial of Service:** No. The bug only triggers a non-fatal `WARNING`. In default production builds (where `panic_on_warn` is not set), this will simply print a stack trace to the kernel log and continue execution without halting the system or causing deadlocks.
* **Accessible From Unprivileged Processes / User Namespaces:** No. The stack trace shows the race was triggered by writing to the `new_device` sysfs attribute (`new_device_store`). This attribute is created with mode `0200` (write-only by owner) and is owned by `root`. Unprivileged users and processes inside user namespaces cannot write to this file.
* **Peripheral Trigger:** While the race involves a USB disconnect event (`i2c-tiny-usb`), the required concurrent action in this trace is a write to the `new_device` sysfs attribute, which requires root privileges. Therefore, a malicious peripheral alone cannot trigger this specific bug without root interaction.
|
| 6/2 |
2026/08/03 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 7839
output: 43
thoughts: 686
Thoughts: **Analyzing a Kernel Bug Report: The Curious Case of the i2c Device Probe**
Okay, so I'm looking at this kernel bug report, and the warning that immediately catches my eye is the one in `drop_nlink`: `fs/inode.c:408`. The warning, triggered by `inode->i_nlink == 0`, suggests something is trying to drop the link count of an inode that's already at zero. That's a classic sign of a double-free, or at least a resource management issue.
Now, the call trace is key here. It looks like the issue stems from an I2C device probe. Specifically, the trace goes from `kernfs_fop_write_iter` (likely writing to sysfs) through `i2c_new_client_device`, which seems to be the function that adds the new I2C device. This then kicks off the probe process, culminating in a call to `i2c_device_probe`. The trace then indicates that `i2c_device_probe` fails and subsequently calls `debugfs_remove`. This `debugfs_remove` seems to be the culprit.
Since the bug report indicates that the warning originates from a call to `debugfs_remove`, let's focus on `drivers/i2c/i2c-core-base.c` around line 610. I need to understand what's happening within `i2c_device_probe` that leads to this removal. Is it a resource cleanup after a failed probe? Is there a race condition? Is `debugfs_remove` being called on an inode that doesn't exist, or is it already gone? I'll need to check the code around there, and probably analyze the surrounding code to determine how this node is being removed and why the reference count is zero. The interaction with kernfs and sysfs is also going to be important to understand. Let's see if this points to a larger problem within the I2C subsystem.
|
| 7/2 |
2026/08/03 02:11 |
tool |
read-file |
0m
Args: map[File:drivers/i2c/i2c-core-base.c FirstLine:580 LineCount:50]
Results: map[Contents: 580:
581: client->devres_group_id = devres_open_group(&client->dev, NULL,
582: GFP_KERNEL);
583: if (!client->devres_group_id) {
584: status = -ENOMEM;
585: goto err_clear_wakeup_irq;
586: }
587:
588: client->debugfs = debugfs_create_dir(dev_name(&client->dev),
589: client->adapter->debugfs);
590:
591: if (driver->probe)
592: status = driver->probe(client);
593: else
594: status = -EINVAL;
595:
596: /*
597: * Note that we are not closing the devres group opened above so
598: * even resources that were attached to the device after probe is
599: * run are released when i2c_device_remove() is executed. This is
600: * needed as some drivers would allocate additional resources,
601: * for example when updating firmware.
602: */
603:
604: if (status)
605: goto err_release_driver_resources;
606:
607: return 0;
608:
609: err_release_driver_resources:
610: debugfs_remove_recursive(client->debugfs);
611: devres_release_group(&client->dev, client->devres_group_id);
612: err_clear_wakeup_irq:
613: dev_pm_clear_wake_irq(&client->dev);
614: device_init_wakeup(&client->dev, false);
615: put_sync_adapter:
616: if (client->flags & I2C_CLIENT_HOST_NOTIFY)
617: pm_runtime_put_sync(&client->adapter->dev);
618:
619: return status;
620: }
621:
622: static void i2c_device_remove(struct device *dev)
623: {
624: struct i2c_client *client = to_i2c_client(dev);
625: struct i2c_driver *driver;
626:
627: driver = to_i2c_driver(dev->driver);
628: if (driver->remove) {
629: dev_dbg(dev, "remove\n");
]
|
| 8/2 |
2026/08/03 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 9744
output: 49
thoughts: 117
Thoughts: **Debugging a Mismatched Debugfs Removal**
Okay, so I'm looking at this stack trace and something's not quite right. It seems `debugfs_remove_recursive` is being called on `client->debugfs`, which is expected behavior, but the trace itself is pointing to `debugfs_remove` directly – specifically `debugfs_remove+0x5b/0x70`. That's a classic sign of an issue with the call chain. I'm seeing `debugfs_remove` when I was expecting `debugfs_remove_recursive`.
Now, according to the code, `i2c_device_probe` should be where `debugfs_remove_recursive` is called. That's a good place to start. I need to verify that call chain and how we're ending up with the simpler function. The key is going to be in `i2c_device_probe`. I need to review the code in the kernel tree and see what's actually happening there. There's a mismatch between what's expected and what's actually happening, and I need to understand why.
|
| 9/2 |
2026/08/03 02:11 |
tool |
git-log |
0m
Error:
Args: map[Count:10 PathPrefix:drivers/i2c/i2c-core-base.c SymbolName:i2c_device_probe]
Results:
|
| 10/2 |
2026/08/03 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 10174
output: 49
thoughts: 20
Thoughts: Okay, here's my summary, tailored for an expert audience, as if it were my own thought process:
**Optimizing Symbol Lookup with Source Paths**
Alright, looking at this code again, I'm thinking I should probably rethink how I'm handling symbol lookups. The current approach uses `PathPrefix` with `SymbolName`, but that feels a little… clunky. I bet using `SourcePath` instead would be a much more elegant solution, and probably more efficient too. The `SourcePath` context inherently provides a better, more accurate grounding for the symbol resolution, especially when dealing with complex projects or situations where relative paths become ambiguous. `PathPrefix` seems a bit like a workaround, whereas `SourcePath` seems like a more direct, and therefore potentially more performant, approach. Let's explore how to implement this!
|
| 11/2 |
2026/08/03 02:11 |
tool |
git-log |
0m
Args: map[Count:10 SourcePath:drivers/i2c/i2c-core-base.c SymbolName:i2c_device_probe]
Results: map[Output:eddfe53b0d84 i2c: core: Drop dev_pm_domain_detach() call
d42c7c6fd66a PM: domains: Add flags to specify power on attach/detach
5c52473b4496 i2c: core: add useful info when defer probe
3435b7f0fd28 i2c: core: Reuse fwnode variable where it makes sense
5d9424b00b57 i2c: core: Switch to fwnode APIs to get IRQ
5763a474c887 i2c: core: Unify the firmware node type check
6250ebe666e4 i2c: Fix core-managed per-client debugfs handling
03c835f498b5 i2c: Switch .probe() to not take an id parameter
79ece9b292af i2c: Restore initial power state if probe fails
b38f2d5d9615 i2c: acpi: Use ACPI wake capability bit to set wake_irq
]
|
| 12/2 |
2026/08/03 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 10696
output: 25
thoughts: 30
Thoughts: Okay, let's dive into this commit, `6250ebe666e4 i2c: Fix core-managed per-client debugfs handling`. I need to get a clear picture of what's been addressed and why it's important for I2C core and client interaction. So, my initial thought is:
**Examining I2C Core-Managed Debugfs Handling**
This commit title suggests a potential issue in the way the I2C core manages debugfs entries, specifically when dealing with individual I2C clients. "Fix" is a loaded term – it hints at a bug or a flaw in the current implementation. I need to understand what aspect of per-client debugfs handling was broken, or perhaps, poorly designed, leading to this modification. My focus will be on:
* **Understanding the Scope:** I need to know what specifically is "core-managed." Does this relate to automatic creation, deletion, permission handling, or other attributes related to the filesystem entry?
* **Identifying the Flaw:** What was the undesirable behavior? Was it a race condition, a resource leak, incorrect permissions, unexpected file access, or something else entirely? A specific description is needed to evaluate.
* **Assessing the Impact:** I need to judge the potential consequences of this issue. Who would be affected? Which boards or I2C client types would have encountered problems?
* **Analyzing the Solution:** What changes were introduced? Did it involve refactoring, introducing locks, changing data structures, modifying permissions, or other changes? I'll need to review the code to see if the changes are clean, well-documented, and efficient.
* **Verifying the Impact:** Does this commit introduce a regression? Does it fix the initial problem without other problems arising. What has testing shown?
I anticipate that a good commit will explain *why* the old way didn't work and *how* the new way is better. Let's see if this one delivers.
|
| 13/2 |
2026/08/03 02:11 |
tool |
git-show |
0m
Args:
Results: map[Output:commit 6250ebe666e425e173df5e11e8a612d57921f48d
Author: Guenter Roeck <linux@roeck-us.net>
Date: Sat Jan 25 11:15:25 2025 -0800
i2c: Fix core-managed per-client debugfs handling
The debugfs directory should be created when a device
is probed, not when it is registered. It should be removed
when the device is removed, not when it is unregistered.
Fixes: d06905d68610 ("i2c: add core-managed per-client directory in debugfs")
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
diff --git a/drivers/i2c/i2c-core-base.c b/drivers/i2c/i2c-core-base.c
index c24ccefb015e..d4bbf6827d1a 100644
--- a/drivers/i2c/i2c-core-base.c
+++ b/drivers/i2c/i2c-core-base.c
@@ -583,6 +583,9 @@ static int i2c_device_probe(struct device *dev)
goto err_detach_pm_domain;
}
+ client->debugfs = debugfs_create_dir(dev_name(&client->dev),
+ client->adapter->debugfs);
+
if (driver->probe)
status = driver->probe(client);
else
@@ -602,6 +605,7 @@ static int i2c_device_probe(struct device *dev)
return 0;
err_release_driver_resources:
+ debugfs_remove_recursive(client->debugfs);
devres_release_group(&client->dev, client->devres_group_id);
err_detach_pm_domain:
dev_pm_domain_detach(&client->dev, do_power_on);
@@ -627,6 +631,8 @@ static void i2c_device_remove(struct device *dev)
driver->remove(client);
}
+ debugfs_remove_recursive(client->debugfs);
+
devres_release_group(&client->dev, client->devres_group_id);
dev_pm_domain_detach(&client->dev, true);
@@ -1015,8 +1021,6 @@ i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *inf
if (status)
goto out_remove_swnode;
- client->debugfs = debugfs_create_dir(dev_name(&client->dev), adap->debugfs);
-
dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
client->name, dev_name(&client->dev));
@@ -1061,7 +1065,6 @@ void i2c_unregister_device(struct i2c_client *client)
if (ACPI_COMPANION(&client->dev))
acpi_device_clear_enumerated(ACPI_COMPANION(&client->dev));
- debugfs_remove_recursive(client->debugfs);
device_remove_software_node(&client->dev);
device_unregister(&client->dev);
}
]
|
| 14/2 |
2026/08/03 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 12082
output: 36
thoughts: 53
Thoughts: Here's my summary, as the thought process unfolds:
**Debugging a Kernel Panic**
Okay, so I've got a problem. The stack trace is screaming at me, and I see it's pointing right at `debugfs_remove+0x5b/0x70` in `fs/debugfs/inode.c`, specifically line 781. That's a good place to start. I've got to dig into `fs/debugfs/inode.c:781`. Let's see what's going on there. It's time to open up the source and figure out what this function is actually doing. This is where the fun begins.
|
| 15/2 |
2026/08/03 02:11 |
tool |
read-file |
0m
Args: map[File:fs/debugfs/inode.c FirstLine:770 LineCount:30]
Results: map[Contents: 770: *
771: * This function is required to be called in order for the file to be
772: * removed, no automatic cleanup of files will happen when a module is
773: * removed, you are responsible here.
774: */
775: void debugfs_remove(struct dentry *dentry)
776: {
777: if (IS_ERR_OR_NULL(dentry))
778: return;
779:
780: simple_pin_fs(&debug_fs_type, &debugfs_mount, &debugfs_mount_count);
781: simple_recursive_removal(dentry, remove_one);
782: simple_release_fs(&debugfs_mount, &debugfs_mount_count);
783: }
784: EXPORT_SYMBOL_GPL(debugfs_remove);
785:
786: /**
787: * debugfs_lookup_and_remove - lookup a directory or file and recursively remove it
788: * @name: a pointer to a string containing the name of the item to look up.
789: * @parent: a pointer to the parent dentry of the item.
790: *
791: * This is the equlivant of doing something like
792: * debugfs_remove(debugfs_lookup(..)) but with the proper reference counting
793: * handled for the directory being looked up.
794: */
795: void debugfs_lookup_and_remove(const char *name, struct dentry *parent)
796: {
797: struct dentry *dentry;
798:
799: dentry = debugfs_lookup(name, parent);
]
|
| 16/2 |
2026/08/03 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 12818
output: 20
thoughts: 40
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Investigating a `debugfs` Function Call**
Right, `debugfs_remove` is flagged in the trace. Hang on a second... isn't `debugfs_remove_recursive` the preferred method now, or maybe it's just been renamed? I'm pretty sure I saw something about it recently in the commit logs, but I can't quite recall the details. Gotta verify if it's been deprecated, aliased, or completely removed from the current kernel version. Time to double-check the relevant header files or dig into the documentation to confirm what's actually happening under the hood. It's important to understand the intended behavior to properly analyze the surrounding code and not jump to incorrect conclusions about the design pattern.
|
| 17/2 |
2026/08/03 02:11 |
tool |
grepper |
0m
Args: map[Expression:debugfs_remove_recursive]
Results: map[Output:
Full output is too long, showing 500 out of 3553 lines.
Use more precise expression if possible.
Documentation/filesystems/debugfs.rst=237=be removed. Note that this function will recursively remove all files and
Documentation/filesystems/debugfs.rst:238:directories underneath it. Previously, debugfs_remove_recursive() was used
Documentation/filesystems/debugfs.rst-239-to perform that task, but this function is now just an alias to
Documentation/filesystems/debugfs.rst:240:debugfs_remove(). debugfs_remove_recursive() should be considered
Documentation/filesystems/debugfs.rst-241-deprecated.
--
Documentation/translations/zh_CN/filesystems/debugfs.rst=211=dentry值可以为NULL或错误值,在这种情况下,不会有任何文件被删除。
--
Documentation/translations/zh_CN/filesystems/debugfs.rst-215-
Documentation/translations/zh_CN/filesystems/debugfs.rst:216: void debugfs_remove_recursive(struct dentry *dentry);
Documentation/translations/zh_CN/filesystems/debugfs.rst-217-
--
Documentation/translations/zh_TW/filesystems/debugfs.rst=212=dentry值可以爲NULL或錯誤值,在這種情況下,不會有任何文件被刪除。
--
Documentation/translations/zh_TW/filesystems/debugfs.rst-216-
Documentation/translations/zh_TW/filesystems/debugfs.rst:217: void debugfs_remove_recursive(struct dentry *dentry);
Documentation/translations/zh_TW/filesystems/debugfs.rst-218-
--
arch/mips/cavium-octeon/oct_ilm.c=137=static __init int oct_ilm_module_init(void)
--
arch/mips/cavium-octeon/oct_ilm.c-155-err_irq:
arch/mips/cavium-octeon/oct_ilm.c:156: debugfs_remove_recursive(dir);
arch/mips/cavium-octeon/oct_ilm.c-157- return rc;
--
arch/mips/cavium-octeon/oct_ilm.c=160=static __exit void oct_ilm_module_exit(void)
--
arch/mips/cavium-octeon/oct_ilm.c-162- disable_timer(TIMER_NUM);
arch/mips/cavium-octeon/oct_ilm.c:163: debugfs_remove_recursive(dir);
arch/mips/cavium-octeon/oct_ilm.c-164- free_irq(OCTEON_IRQ_TIMER0 + TIMER_NUM, 0);
--
arch/powerpc/platforms/powernv/memtrace.c=220=static int memtrace_free_regions(void)
--
arch/powerpc/platforms/powernv/memtrace.c-248- */
arch/powerpc/platforms/powernv/memtrace.c:249: debugfs_remove_recursive(ent->dir);
arch/powerpc/platforms/powernv/memtrace.c-250- pr_info("Freed trace memory back on node %d\n", ent->nid);
--
arch/powerpc/platforms/powernv/vas-debug.c=107=void vas_window_free_dbgdir(struct pnv_vas_window *pnv_win)
--
arch/powerpc/platforms/powernv/vas-debug.c-111- if (window->dbgdir) {
arch/powerpc/platforms/powernv/vas-debug.c:112: debugfs_remove_recursive(window->dbgdir);
arch/powerpc/platforms/powernv/vas-debug.c-113- kfree(window->dbgname);
--
arch/powerpc/platforms/pseries/htmdump.c=576=static void __exit htmdump_exit(void)
arch/powerpc/platforms/pseries/htmdump.c-577-{
arch/powerpc/platforms/pseries/htmdump.c:578: debugfs_remove_recursive(htmdump_debugfs_dir);
arch/powerpc/platforms/pseries/htmdump.c-579- kfree(htm_buf);
--
arch/s390/pci/pci_debug.c=191=void zpci_debug_exit_device(struct zpci_dev *zdev)
arch/s390/pci/pci_debug.c-192-{
arch/s390/pci/pci_debug.c:193: debugfs_remove_recursive(zdev->debugfs_dev);
arch/s390/pci/pci_debug.c-194-}
--
arch/x86/kernel/cpu/mce/inject.c=789=static void __exit inject_exit(void)
--
arch/x86/kernel/cpu/mce/inject.c-794-
arch/x86/kernel/cpu/mce/inject.c:795: debugfs_remove_recursive(dfs_inj);
arch/x86/kernel/cpu/mce/inject.c-796- dfs_inj = NULL;
--
arch/x86/kernel/kdebugfs.c=89=static int __init create_setup_data_nodes(struct dentry *parent)
--
arch/x86/kernel/kdebugfs.c-155-err_dir:
arch/x86/kernel/kdebugfs.c:156: debugfs_remove_recursive(d);
arch/x86/kernel/kdebugfs.c-157- return error;
--
arch/x86/kernel/kdebugfs.c=165=static int __init boot_params_kdebugfs_init(void)
--
arch/x86/kernel/kdebugfs.c-176- if (error)
arch/x86/kernel/kdebugfs.c:177: debugfs_remove_recursive(dbp);
arch/x86/kernel/kdebugfs.c-178-
--
arch/x86/mm/debug_pagetables.c=67=static void __exit pt_dump_debug_exit(void)
arch/x86/mm/debug_pagetables.c-68-{
arch/x86/mm/debug_pagetables.c:69: debugfs_remove_recursive(dir);
arch/x86/mm/debug_pagetables.c-70-}
--
arch/x86/platform/atom/punit_atom_debug.c=118=static void punit_dbgfs_unregister(void)
arch/x86/platform/atom/punit_atom_debug.c-119-{
arch/x86/platform/atom/punit_atom_debug.c:120: debugfs_remove_recursive(punit_dbg_file);
arch/x86/platform/atom/punit_atom_debug.c-121-}
--
arch/x86/platform/intel/iosf_mbi.c=492=static void iosf_debugfs_remove(void)
arch/x86/platform/intel/iosf_mbi.c-493-{
arch/x86/platform/intel/iosf_mbi.c:494: debugfs_remove_recursive(iosf_dbg);
arch/x86/platform/intel/iosf_mbi.c-495-}
--
block/blk-mq-debugfs.c=692=void blk_mq_debugfs_unregister_hctx(struct blk_mq_hw_ctx *hctx)
--
block/blk-mq-debugfs.c-695- return;
block/blk-mq-debugfs.c:696: debugfs_remove_recursive(hctx->debugfs_dir);
block/blk-mq-debugfs.c-697- hctx->sched_debugfs_dir = NULL;
--
block/blk-mq-debugfs.c=743=void blk_mq_debugfs_unregister_sched(struct request_queue *q)
--
block/blk-mq-debugfs.c-746-
block/blk-mq-debugfs.c:747: debugfs_remove_recursive(q->sched_debugfs_dir);
block/blk-mq-debugfs.c-748- q->sched_debugfs_dir = NULL;
--
block/blk-mq-debugfs.c=821=void blk_mq_debugfs_unregister_sched_hctx(struct blk_mq_hw_ctx *hctx)
--
block/blk-mq-debugfs.c-826- return;
block/blk-mq-debugfs.c:827: debugfs_remove_recursive(hctx->sched_debugfs_dir);
block/blk-mq-debugfs.c-828- hctx->sched_debugfs_dir = NULL;
--
block/blk-sysfs.c=931=static void blk_debugfs_remove(struct gendisk *disk)
--
block/blk-sysfs.c-938- blk_error_injection_exit(disk);
block/blk-sysfs.c:939: debugfs_remove_recursive(q->debugfs_dir);
block/blk-sysfs.c-940- q->debugfs_dir = NULL;
--
crypto/jitterentropy-testing.c=291=void jent_testing_exit(void)
crypto/jitterentropy-testing.c-292-{
crypto/jitterentropy-testing.c:293: debugfs_remove_recursive(jent_raw_debugfs_root);
crypto/jitterentropy-testing.c-294-}
--
drivers/acpi/apei/einj-core.c=1045=static int __init einj_probe(struct faux_device *fdev)
--
drivers/acpi/apei/einj-core.c-1140- apei_resources_fini(&einj_resources);
drivers/acpi/apei/einj-core.c:1141: debugfs_remove_recursive(einj_debug_dir);
drivers/acpi/apei/einj-core.c-1142-err_put_table:
--
drivers/acpi/apei/einj-core.c=1148=static void einj_remove(struct faux_device *fdev)
--
drivers/acpi/apei/einj-core.c-1169- apei_resources_fini(&einj_resources);
drivers/acpi/apei/einj-core.c:1170: debugfs_remove_recursive(einj_debug_dir);
drivers/acpi/apei/einj-core.c-1171- kfree(syndrome_data);
--
drivers/acpi/ec_sys.c=138=static void __exit acpi_ec_sys_exit(void)
drivers/acpi/ec_sys.c-139-{
drivers/acpi/ec_sys.c:140: debugfs_remove_recursive(acpi_ec_debugfs_dir);
drivers/acpi/ec_sys.c-141-}
--
drivers/android/binder.c=7106=static int __init binder_init(void)
--
drivers/android/binder.c-7176-err_alloc_device_names_failed:
drivers/android/binder.c:7177: debugfs_remove_recursive(binder_debugfs_dir_entry_root);
drivers/android/binder.c-7178- binder_alloc_shrinker_exit();
--
drivers/base/regmap/regmap-debugfs.c=663=void regmap_debugfs_exit(struct regmap *map)
--
drivers/base/regmap/regmap-debugfs.c-665- if (map->debugfs) {
drivers/base/regmap/regmap-debugfs.c:666: debugfs_remove_recursive(map->debugfs);
drivers/base/regmap/regmap-debugfs.c-667- mutex_lock(&map->cache_lock);
--
drivers/block/aoe/aoeblk.c=435=aoeblk_exit(void)
drivers/block/aoe/aoeblk.c-436-{
drivers/block/aoe/aoeblk.c:437: debugfs_remove_recursive(aoe_debugfs_dir);
drivers/block/aoe/aoeblk.c-438- aoe_debugfs_dir = NULL;
--
drivers/block/brd.c=358=static void brd_cleanup(void)
--
drivers/block/brd.c-361-
drivers/block/brd.c:362: debugfs_remove_recursive(brd_debugfs_dir);
drivers/block/brd.c-363-
--
drivers/block/mtip32xx/mtip32xx.c=2283=static void mtip_hw_debugfs_exit(struct driver_data *dd)
drivers/block/mtip32xx/mtip32xx.c-2284-{
drivers/block/mtip32xx/mtip32xx.c:2285: debugfs_remove_recursive(dd->dfs_node);
drivers/block/mtip32xx/mtip32xx.c-2286-}
--
drivers/block/mtip32xx/mtip32xx.c=4066=static void __exit mtip_exit(void)
--
drivers/block/mtip32xx/mtip32xx.c-4073-
drivers/block/mtip32xx/mtip32xx.c:4074: debugfs_remove_recursive(dfs_parent);
drivers/block/mtip32xx/mtip32xx.c-4075-}
--
drivers/block/nbd.c=1882=static void nbd_dev_dbg_close(struct nbd_device *nbd)
drivers/block/nbd.c-1883-{
drivers/block/nbd.c:1884: debugfs_remove_recursive(nbd->config->dbg_dir);
drivers/block/nbd.c-1885-}
--
drivers/block/nbd.c=1900=static void nbd_dbg_close(void)
drivers/block/nbd.c-1901-{
drivers/block/nbd.c:1902: debugfs_remove_recursive(nbd_dbg_dir);
drivers/block/nbd.c-1903-}
--
drivers/block/zram/zram_drv.c=1541=static void zram_debugfs_destroy(void)
drivers/block/zram/zram_drv.c-1542-{
drivers/block/zram/zram_drv.c:1543: debugfs_remove_recursive(zram_debugfs_root);
drivers/block/zram/zram_drv.c-1544-}
--
drivers/block/zram/zram_drv.c=1617=static void zram_debugfs_unregister(struct zram *zram)
drivers/block/zram/zram_drv.c-1618-{
drivers/block/zram/zram_drv.c:1619: debugfs_remove_recursive(zram->debugfs_dir);
drivers/block/zram/zram_drv.c-1620-}
--
drivers/bluetooth/btmrvl_debugfs.c=181=void btmrvl_debugfs_remove(struct hci_dev *hdev)
--
drivers/bluetooth/btmrvl_debugfs.c-188-
drivers/bluetooth/btmrvl_debugfs.c:189: debugfs_remove_recursive(dbg->config_dir);
drivers/bluetooth/btmrvl_debugfs.c:190: debugfs_remove_recursive(dbg->status_dir);
drivers/bluetooth/btmrvl_debugfs.c-191-
--
drivers/bus/mhi/host/debugfs.c=400=void mhi_destroy_debugfs(struct mhi_controller *mhi_cntrl)
drivers/bus/mhi/host/debugfs.c-401-{
drivers/bus/mhi/host/debugfs.c:402: debugfs_remove_recursive(mhi_cntrl->debugfs_dentry);
drivers/bus/mhi/host/debugfs.c-403- mhi_cntrl->debugfs_dentry = NULL;
--
drivers/bus/mhi/host/debugfs.c=411=void mhi_debugfs_exit(void)
drivers/bus/mhi/host/debugfs.c-412-{
drivers/bus/mhi/host/debugfs.c:413: debugfs_remove_recursive(mhi_debugfs_root);
drivers/bus/mhi/host/debugfs.c-414-}
--
drivers/bus/moxtet.c=554=static int moxtet_register_debugfs(struct moxtet *moxtet)
--
drivers/bus/moxtet.c-576-err_remove:
drivers/bus/moxtet.c:577: debugfs_remove_recursive(root);
drivers/bus/moxtet.c-578- return PTR_ERR(entry);
--
drivers/bus/moxtet.c=581=static void moxtet_unregister_debugfs(struct moxtet *moxtet)
drivers/bus/moxtet.c-582-{
drivers/bus/moxtet.c:583: debugfs_remove_recursive(moxtet->debugfs_root);
drivers/bus/moxtet.c-584-}
--
drivers/cdx/cdx.c=146=static int cdx_unregister_device(struct device *dev,
--
drivers/cdx/cdx.c-157- cdx_destroy_res_attr(cdx_dev, MAX_CDX_DEV_RESOURCES);
drivers/cdx/cdx.c:158: debugfs_remove_recursive(cdx_dev->debugfs_dir);
drivers/cdx/cdx.c-159- }
--
drivers/char/virtio_console.c=2193=static int __init virtio_console_init(void)
--
drivers/char/virtio_console.c-2219-free:
drivers/char/virtio_console.c:2220: debugfs_remove_recursive(pdrvdata.debugfs_dir);
drivers/char/virtio_console.c-2221- class_unregister(&port_class);
--
drivers/char/virtio_console.c=2225=static void __exit virtio_console_fini(void)
--
drivers/char/virtio_console.c-2232- class_unregister(&port_class);
drivers/char/virtio_console.c:2233: debugfs_remove_recursive(pdrvdata.debugfs_dir);
drivers/char/virtio_console.c-2234-}
--
drivers/clk/clk.c=3761=static void clk_debug_unregister(struct clk_core *core)
--
drivers/clk/clk.c-3764- hlist_del_init(&core->debug_node);
drivers/clk/clk.c:3765: debugfs_remove_recursive(core->dentry);
drivers/clk/clk.c-3766- core->dentry = NULL;
--
drivers/clk/tegra/clk-dfll.c=2080=struct tegra_dfll_soc_data *tegra_dfll_unregister(struct platform_device *pdev)
--
drivers/clk/tegra/clk-dfll.c-2093-
drivers/clk/tegra/clk-dfll.c:2094: debugfs_remove_recursive(td->debugfs_dir);
drivers/clk/tegra/clk-dfll.c-2095-
--
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c=1090=static void sun8i_ce_remove(struct platform_device *pdev)
--
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c-1100-#ifdef CONFIG_CRYPTO_DEV_SUN8I_CE_DEBUG
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c:1101: debugfs_remove_recursive(ce->dbgfs_dir);
drivers/crypto/allwinner/sun8i-ce/sun8i-ce-core.c-1102-#endif
--
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c=918=static void sun8i_ss_remove(struct platform_device *pdev)
--
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c-924-#ifdef CONFIG_CRYPTO_DEV_SUN8I_SS_DEBUG
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c:925: debugfs_remove_recursive(ss->dbgfs_dir);
drivers/crypto/allwinner/sun8i-ss/sun8i-ss-core.c-926-#endif
--
drivers/crypto/amlogic/amlogic-gxl-core.c=300=static void meson_crypto_remove(struct platform_device *pdev)
--
drivers/crypto/amlogic/amlogic-gxl-core.c-304-#ifdef CONFIG_CRYPTO_DEV_AMLOGIC_GXL_DEBUG
drivers/crypto/amlogic/amlogic-gxl-core.c:305: debugfs_remove_recursive(mc->dbgfs_dir);
drivers/crypto/amlogic/amlogic-gxl-core.c-306-#endif
--
drivers/crypto/axis/artpec6_crypto.c=2823=static void artpec6_crypto_free_debugfs(void)
drivers/crypto/axis/artpec6_crypto.c-2824-{
drivers/crypto/axis/artpec6_crypto.c:2825: debugfs_remove_recursive(dbgfs_root);
drivers/crypto/axis/artpec6_crypto.c-2826- dbgfs_root = NULL;
--
drivers/crypto/bcm/util.c=500=void spu_free_debugfs(void)
drivers/crypto/bcm/util.c-501-{
drivers/crypto/bcm/util.c:502: debugfs_remove_recursive(iproc_priv.debugfs_dir);
drivers/crypto/bcm/util.c-503- iproc_priv.debugfs_dir = NULL;
--
drivers/crypto/caam/ctrl.c=618=static void caam_remove_debugfs(void *root)
drivers/crypto/caam/ctrl.c-619-{
drivers/crypto/caam/ctrl.c:620: debugfs_remove_recursive(root);
drivers/crypto/caam/ctrl.c-621-}
--
drivers/crypto/caam/dpseci-debugfs.c=57=void dpaa2_dpseci_debugfs_exit(struct dpaa2_caam_priv *priv)
drivers/crypto/caam/dpseci-debugfs.c-58-{
drivers/crypto/caam/dpseci-debugfs.c:59: debugfs_remove_recursive(priv->dfs_root);
drivers/crypto/caam/dpseci-debugfs.c-60-}
--
drivers/crypto/cavium/nitrox/nitrox_debugfs.c=54=void nitrox_debugfs_exit(struct nitrox_device *ndev)
drivers/crypto/cavium/nitrox/nitrox_debugfs.c-55-{
drivers/crypto/cavium/nitrox/nitrox_debugfs.c:56: debugfs_remove_recursive(ndev->debugfs_dir);
drivers/crypto/cavium/nitrox/nitrox_debugfs.c-57- ndev->debugfs_dir = NULL;
--
drivers/crypto/ccp/ccp-debugfs.c=320=void ccp5_debugfs_destroy(void)
--
drivers/crypto/ccp/ccp-debugfs.c-322- mutex_lock(&ccp_debugfs_lock);
drivers/crypto/ccp/ccp-debugfs.c:323: debugfs_remove_recursive(ccp_debugfs_dir);
drivers/crypto/ccp/ccp-debugfs.c-324- ccp_debugfs_dir = NULL;
--
drivers/crypto/ccree/cc_debugfs.c=104=void cc_debugfs_fini(struct cc_drvdata *drvdata)
drivers/crypto/ccree/cc_debugfs.c-105-{
drivers/crypto/ccree/cc_debugfs.c:106: debugfs_remove_recursive(drvdata->dir);
drivers/crypto/ccree/cc_debugfs.c-107-}
--
drivers/crypto/gemini/sl3516-ce-core.c=508=static void sl3516_ce_remove(struct platform_device *pdev)
--
drivers/crypto/gemini/sl3516-ce-core.c-518-#ifdef CONFIG_CRYPTO_DEV_SL3516_DEBUG
drivers/crypto/gemini/sl3516-ce-core.c:519: debugfs_remove_recursive(ce->dbgfs_dir);
drivers/crypto/gemini/sl3516-ce-core.c-520-#endif
--
drivers/crypto/hisilicon/hpre/hpre_main.c=1167=static int hpre_debugfs_init(struct hisi_qm *qm)
--
drivers/crypto/hisilicon/hpre/hpre_main.c-1195-debugfs_remove:
drivers/crypto/hisilicon/hpre/hpre_main.c:1196: debugfs_remove_recursive(qm->debug.debug_root);
drivers/crypto/hisilicon/hpre/hpre_main.c-1197- hisi_qm_regs_debugfs_uninit(qm, ARRAY_SIZE(hpre_diff_regs));
--
drivers/crypto/hisilicon/hpre/hpre_main.c=1201=static void hpre_debugfs_exit(struct hisi_qm *qm)
drivers/crypto/hisilicon/hpre/hpre_main.c-1202-{
drivers/crypto/hisilicon/hpre/hpre_main.c:1203: debugfs_remove_recursive(qm->debug.debug_root);
drivers/crypto/hisilicon/hpre/hpre_main.c-1204-
--
drivers/crypto/hisilicon/hpre/hpre_main.c=1721=static void hpre_unregister_debugfs(void)
drivers/crypto/hisilicon/hpre/hpre_main.c-1722-{
drivers/crypto/hisilicon/hpre/hpre_main.c:1723: debugfs_remove_recursive(hpre_debugfs_root);
drivers/crypto/hisilicon/hpre/hpre_main.c-1724-}
--
drivers/crypto/hisilicon/sec2/sec_main.c=1005=static int sec_debugfs_init(struct hisi_qm *qm)
--
drivers/crypto/hisilicon/sec2/sec_main.c-1029-debugfs_remove:
drivers/crypto/hisilicon/sec2/sec_main.c:1030: debugfs_remove_recursive(qm->debug.debug_root);
drivers/crypto/hisilicon/sec2/sec_main.c-1031- hisi_qm_regs_debugfs_uninit(qm, ARRAY_SIZE(sec_diff_regs));
--
drivers/crypto/hisilicon/sec2/sec_main.c=1035=static void sec_debugfs_exit(struct hisi_qm *qm)
drivers/crypto/hisilicon/sec2/sec_main.c-1036-{
drivers/crypto/hisilicon/sec2/sec_main.c:1037: debugfs_remove_recursive(qm->debug.debug_root);
drivers/crypto/hisilicon/sec2/sec_main.c-1038-
--
drivers/crypto/hisilicon/sec2/sec_main.c=1539=static void sec_unregister_debugfs(void)
drivers/crypto/hisilicon/sec2/sec_main.c-1540-{
drivers/crypto/hisilicon/sec2/sec_main.c:1541: debugfs_remove_recursive(sec_debugfs_root);
drivers/crypto/hisilicon/sec2/sec_main.c-1542-}
--
drivers/crypto/hisilicon/zip/zip_main.c=991=static int hisi_zip_debugfs_init(struct hisi_qm *qm)
--
drivers/crypto/hisilicon/zip/zip_main.c-1019-debugfs_remove:
drivers/crypto/hisilicon/zip/zip_main.c:1020: debugfs_remove_recursive(qm->debug.debug_root);
drivers/crypto/hisilicon/zip/zip_main.c-1021- hisi_qm_regs_debugfs_uninit(qm, ARRAY_SIZE(hzip_diff_regs));
--
drivers/crypto/hisilicon/zip/zip_main.c=1049=static void hisi_zip_debugfs_exit(struct hisi_qm *qm)
drivers/crypto/hisilicon/zip/zip_main.c-1050-{
drivers/crypto/hisilicon/zip/zip_main.c:1051: debugfs_remove_recursive(qm->debug.debug_root);
drivers/crypto/hisilicon/zip/zip_main.c-1052-
--
drivers/crypto/hisilicon/zip/zip_main.c=1648=static void hisi_zip_unregister_debugfs(void)
drivers/crypto/hisilicon/zip/zip_main.c-1649-{
drivers/crypto/hisilicon/zip/zip_main.c:1650: debugfs_remove_recursive(hzip_debugfs_root);
drivers/crypto/hisilicon/zip/zip_main.c-1651-}
--
drivers/crypto/intel/iaa/iaa_crypto_stats.c=266=void __exit iaa_crypto_debugfs_cleanup(void)
drivers/crypto/intel/iaa/iaa_crypto_stats.c-267-{
drivers/crypto/intel/iaa/iaa_crypto_stats.c:268: debugfs_remove_recursive(iaa_crypto_debugfs_root);
drivers/crypto/intel/iaa/iaa_crypto_stats.c-269-}
--
drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.c=723=void adf_tl_dbgfs_rm(struct adf_accel_dev *accel_dev)
--
drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.c-732-
drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.c:733: debugfs_remove_recursive(dbg_dir);
drivers/crypto/intel/qat/qat_common/adf_tl_debugfs.c-734-
--
drivers/crypto/nx/nx_debugfs.c=61=nx_debugfs_fini(struct nx_crypto_driver *drv)
drivers/crypto/nx/nx_debugfs.c-62-{
drivers/crypto/nx/nx_debugfs.c:63: debugfs_remove_recursive(drv->dfs_root);
drivers/crypto/nx/nx_debugfs.c-64-}
--
drivers/crypto/rockchip/rk3288_crypto.c=413=static void rk_crypto_remove(struct platform_device *pdev)
--
drivers/crypto/rockchip/rk3288_crypto.c-425-#ifdef CONFIG_CRYPTO_DEV_ROCKCHIP_DEBUG
drivers/crypto/rockchip/rk3288_crypto.c:426: debugfs_remove_recursive(rocklist.dbgfs_dir);
drivers/crypto/rockchip/rk3288_crypto.c-427-#endif
--
drivers/cxl/core/port.c=2551=static void cxl_core_exit(void)
--
drivers/cxl/core/port.c-2557- cxl_memdev_exit();
drivers/cxl/core/port.c:2558: debugfs_remove_recursive(cxl_debugfs);
drivers/cxl/core/port.c-2559-}
--
drivers/cxl/core/region.c=3907=static void remove_debugfs(void *dentry)
drivers/cxl/core/region.c-3908-{
drivers/cxl/core/region.c:3909: debugfs_remove_recursive(dentry);
drivers/cxl/core/region.c-3910-}
--
drivers/cxl/mem.c=33=static void remove_debugfs(void *dentry)
drivers/cxl/mem.c-34-{
drivers/cxl/mem.c:35: debugfs_remove_recursive(dentry);
drivers/cxl/mem.c-36-}
--
drivers/dma-buf/dma-buf.c=1799=static int dma_buf_init_debugfs(void)
--
drivers/dma-buf/dma-buf.c-1813- pr_debug("dma_buf: debugfs: failed to create node bufinfo\n");
drivers/dma-buf/dma-buf.c:1814: debugfs_remove_recursive(dma_buf_debugfs_dir);
drivers/dma-buf/dma-buf.c-1815- dma_buf_debugfs_dir = NULL;
--
drivers/dma-buf/dma-buf.c=1822=static void dma_buf_uninit_debugfs(void)
drivers/dma-buf/dma-buf.c-1823-{
drivers/dma-buf/dma-buf.c:1824: debugfs_remove_recursive(dma_buf_debugfs_dir);
drivers/dma-buf/dma-buf.c-1825-}
--
drivers/dma/bcm-sba-raid.c=1614=static int sba_probe(struct platform_device *pdev)
--
drivers/dma/bcm-sba-raid.c-1730-fail_free_resources:
drivers/dma/bcm-sba-raid.c:1731: debugfs_remove_recursive(sba->root);
drivers/dma/bcm-sba-raid.c-1732- sba_freeup_channel_resources(sba);
--
drivers/dma/bcm-sba-raid.c=1740=static void sba_remove(struct platform_device *pdev)
--
drivers/dma/bcm-sba-raid.c-1745-
drivers/dma/bcm-sba-raid.c:1746: debugfs_remove_recursive(sba->root);
drivers/dma/bcm-sba-raid.c-1747-
--
drivers/dma/dmaengine.c=79=static void dmaengine_debug_unregister(struct dma_device *dma_dev)
drivers/dma/dmaengine.c-80-{
drivers/dma/dmaengine.c:81: debugfs_remove_recursive(dma_dev->dbg_dev_root);
drivers/dma/dmaengine.c-82- dma_dev->dbg_dev_root = NULL;
--
drivers/dma/idxd/debugfs.c=96=int idxd_device_init_debugfs(struct idxd_device *idxd)
--
drivers/dma/idxd/debugfs.c-109- if (IS_ERR(idxd->dbgfs_evl_file)) {
drivers/dma/idxd/debugfs.c:110: debugfs_remove_recursive(idxd->dbgfs_dir);
drivers/dma/idxd/debugfs.c-111- idxd->dbgfs_dir = NULL;
--
drivers/dma/idxd/debugfs.c=119=void idxd_device_remove_debugfs(struct idxd_device *idxd)
drivers/dma/idxd/debugfs.c-120-{
drivers/dma/idxd/debugfs.c:121: debugfs_remove_recursive(idxd->dbgfs_dir);
drivers/dma/idxd/debugfs.c-122-}
--
drivers/dma/idxd/debugfs.c=135=void idxd_remove_debugfs(void)
drivers/dma/idxd/debugfs.c-136-{
drivers/dma/idxd/debugfs.c:137: debugfs_remove_recursive(idxd_debugfs_dir);
drivers/dma/idxd/debugfs.c-138-}
--
drivers/dma/pxa_dma.c=359=static void pxad_cleanup_debugfs(struct pxad_device *pdev)
drivers/dma/pxa_dma.c-360-{
drivers/dma/pxa_dma.c:361: debugfs_remove_recursive(pdev->dbgfs_root);
drivers/dma/pxa_dma.c-362-}
--
drivers/dma/qcom/hidma_dbg.c=136=void hidma_debug_uninit(struct hidma_dev *dmadev)
drivers/dma/qcom/hidma_dbg.c-137-{
drivers/dma/qcom/hidma_dbg.c:138: debugfs_remove_recursive(dmadev->debugfs);
drivers/dma/qcom/hidma_dbg.c-139-}
--
drivers/edac/altera_edac.c=669=static void altr_create_edacdev_dbgfs(struct edac_device_ctl_info *edac_dci,
--
drivers/edac/altera_edac.c-683- priv->inject_fops))
drivers/edac/altera_edac.c:684: debugfs_remove_recursive(drvdata->debugfs_dir);
drivers/edac/altera_edac.c-685-}
--
drivers/edac/altera_edac.c=806=static void altr_edac_device_remove(struct platform_device *pdev)
--
drivers/edac/altera_edac.c-810-
drivers/edac/altera_edac.c:811: debugfs_remove_recursive(drvdata->debugfs_dir);
drivers/edac/altera_edac.c-812- edac_device_del_device(&pdev->dev);
--
drivers/edac/armada_xp_edac.c=567=static void aurora_l2_remove(struct platform_device *pdev)
--
drivers/edac/armada_xp_edac.c-572-
drivers/edac/armada_xp_edac.c:573: edac_debugfs_remove_recursive(drvdata->debugfs);
drivers/edac/armada_xp_edac.c-574-#endif
--
drivers/edac/debugfs.c=53=void edac_debugfs_exit(void)
drivers/edac/debugfs.c-54-{
drivers/edac/debugfs.c:55: debugfs_remove_recursive(edac_debugfs);
drivers/edac/debugfs.c-56-}
--
drivers/edac/edac_mc_sysfs.c=609=void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci)
--
drivers/edac/edac_mc_sysfs.c-618-#ifdef CONFIG_EDAC_DEBUG
drivers/edac/edac_mc_sysfs.c:619: edac_debugfs_remove_recursive(mci->debugfs);
drivers/edac/edac_mc_sysfs.c-620-#endif
--
drivers/edac/edac_module.h=57=extern void edac_mc_reset_delay_period(unsigned long value);
--
drivers/edac/edac_module.h-62-
drivers/edac/edac_module.h:63:#define edac_debugfs_remove_recursive debugfs_remove_recursive
drivers/edac/edac_module.h-64-#define edac_debugfs_remove debugfs_remove
--
drivers/edac/i5100_edac.c=1173=static void i5100_remove_one(struct pci_dev *pdev)
--
drivers/edac/i5100_edac.c-1187-
drivers/edac/i5100_edac.c:1188: edac_debugfs_remove_recursive(priv->debugfs);
drivers/edac/i5100_edac.c-1189-
--
drivers/edac/igen6_edac.c=1465=static void igen6_debug_teardown(void)
drivers/edac/igen6_edac.c-1466-{
drivers/edac/igen6_edac.c:1467: debugfs_remove_recursive(igen6_test);
drivers/edac/igen6_edac.c-1468-}
--
drivers/edac/npcm_edac.c=413=static void edac_remove(struct platform_device *pdev)
--
drivers/edac/npcm_edac.c-420- if (IS_ENABLED(CONFIG_EDAC_DEBUG) && pdata->chip == NPCM8XX_CHIP)
drivers/edac/npcm_edac.c:421: edac_debugfs_remove_recursive(priv->debugfs);
drivers/edac/npcm_edac.c-422-
--
drivers/edac/pnd2_edac.c=1454=static void teardown_pnd2_debug(void)
drivers/edac/pnd2_edac.c-1455-{
drivers/edac/pnd2_edac.c:1456: debugfs_remove_recursive(pnd2_test);
drivers/edac/pnd2_edac.c-1457-}
--
]
|
| 18/2 |
2026/08/03 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 24065
output: 34
thoughts: 218
Thoughts: **Debugging a `debugfs` Issue**
Okay, so I'm looking at this change and it seems `debugfs_remove_recursive` is now just an alias for `debugfs_remove`. That's interesting, but not the core issue. The documentation clearly states that it's deprecated, which I'm already aware of.
The real problem lies elsewhere. I need to figure out why this is called, and that warning is appearing: `inode->i_nlink == 0` in `drop_nlink`. My instinct is telling me that this could be important so I'll check it out. I recognize that `drop_nlink` is called from `__simple_recursive_removal`, specifically within `fs/libfs.c` at line 631. I'll need to go to that part of the code and examine it more closely. I expect it is a good starting point.
|
| 19/2 |
2026/08/03 02:11 |
tool |
read-file |
0m
Args: map[File:fs/libfs.c FirstLine:610 LineCount:40]
Results: map[Contents: 610: // kill and ascend
611: // update metadata while it's still locked
612: inode_set_ctime_current(inode);
613: clear_nlink(inode);
614: inode_unlock(inode);
615: victim = this;
616: this = this->d_parent;
617: inode = this->d_inode;
618: if (!locked || victim != dentry)
619: inode_lock_nested(inode, I_MUTEX_CHILD);
620: if (simple_positive(victim)) {
621: d_invalidate(victim); // avoid lost mounts
622: if (callback)
623: callback(victim);
624: fsnotify_delete(inode, d_inode(victim), victim);
625: d_make_discardable(victim);
626: }
627: if (victim == dentry) {
628: inode_set_mtime_to_ts(inode,
629: inode_set_ctime_current(inode));
630: if (d_is_dir(dentry))
631: drop_nlink(inode);
632: if (!locked)
633: inode_unlock(inode);
634: dput(dentry);
635: return;
636: }
637: }
638: inode_unlock(inode);
639: this = child;
640: }
641: }
642:
643: void simple_recursive_removal(struct dentry *dentry,
644: void (*callback)(struct dentry *))
645: {
646: return __simple_recursive_removal(dentry, callback, false);
647: }
648: EXPORT_SYMBOL(simple_recursive_removal);
649:
]
|
| 20/2 |
2026/08/03 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 25072
output: 34
thoughts: 479
Thoughts: **Debugging a Kernel Bug: A Recursive Removal Conundrum**
Okay, let's break this down. I'm looking at this `drop_nlink(inode)` call. I see it's operating on the parent inode, specifically `this->d_inode`. The warning I'm concerned about is when `inode->i_nlink == 0` within `drop_nlink`. So, the parent directory's link count is already zero *before* we try to decrement it for the child directory during removal. That doesn't make sense. Why would a directory's link count be zero prematurely?
Ah, I think I'm starting to see it. `debugfs_remove` is involved, and it drops the parent's link count when removing a directory. Could the parent's `i_nlink` get set to zero if `debugfs_remove` is called twice on the *same* directory? Or perhaps if the parent itself has already been removed by some other means, and that previous removal has already dropped the link?
Right, right... I see that `debugfs_remove` could be called on `client->debugfs`. And, hey, `client->debugfs` *is* a directory! Looking at `i2c_device_probe`, this directory is created with `debugfs_create_dir`. Now I see the call in the error handling path, `err_release_driver_resources`: `debugfs_remove_recursive(client->debugfs)`. That removes the directory and all of its contents recursively.
But... here's the kicker: What if `client->debugfs` is already gone? Maybe another part of the system removed it before the error handling kicked in? Or what if `client->adapter->debugfs` has been removed? We need to look at what might have been deleted before we call our deletion code.
The core of the problem is that the warning is that `inode->i_nlink == 0` within `drop_nlink`. So if `debugfs_remove` is called on a directory, it drops the nlink of its parent. If `debugfs_remove` is called *twice* on the same directory, the first time it removes it, and drops the parent's nlink. The second time, it might try to drop the parent's nlink again? That sounds right.
Hang on. I think `simple_recursive_removal` uses `simple_positive(victim)`. If the target directory has already been removed, `simple_positive` might be false and that could explain how this gets messed up. Time to check `__simple_recursive_removal` and see what's actually happening.
|
| 21/2 |
2026/08/03 02:11 |
tool |
read-file |
0m
Args: map[File:fs/libfs.c FirstLine:580 LineCount:40]
Results: map[Contents: 580: d = prev ? d_next_sibling(prev) : d_first_child(parent);
581: hlist_for_each_entry_from(d, d_sib) {
582: if (simple_positive(d)) {
583: spin_lock_nested(&d->d_lock, DENTRY_D_LOCK_NESTED);
584: if (simple_positive(d))
585: child = dget_dlock(d);
586: spin_unlock(&d->d_lock);
587: if (likely(child))
588: break;
589: }
590: }
591: spin_unlock(&parent->d_lock);
592: dput(prev);
593: return child;
594: }
595: EXPORT_SYMBOL(find_next_child);
596:
597: static void __simple_recursive_removal(struct dentry *dentry,
598: void (*callback)(struct dentry *),
599: bool locked)
600: {
601: struct dentry *this = dget(dentry);
602: while (true) {
603: struct dentry *victim = NULL, *child;
604: struct inode *inode = this->d_inode;
605:
606: inode_lock_nested(inode, I_MUTEX_CHILD);
607: if (d_is_dir(this))
608: inode->i_flags |= S_DEAD;
609: while ((child = find_next_child(this, victim)) == NULL) {
610: // kill and ascend
611: // update metadata while it's still locked
612: inode_set_ctime_current(inode);
613: clear_nlink(inode);
614: inode_unlock(inode);
615: victim = this;
616: this = this->d_parent;
617: inode = this->d_inode;
618: if (!locked || victim != dentry)
619: inode_lock_nested(inode, I_MUTEX_CHILD);
]
|
| 22/2 |
2026/08/03 02:11 |
llm |
expert |
0m
Model:
Tokens:
input: 26795
output: 49
thoughts: 1014
Thoughts: **Analysis of Potential Double Removal in debugfs_remove_recursive**
Okay, let's break this down. My initial thought process is centered around `__simple_recursive_removal` and the potential for a double call to `debugfs_remove_recursive` leading to an issue with inode link counts.
First, I see the core logic of `__simple_recursive_removal`. It seems to walk a directory tree, removing entries. The critical part is that if `debugfs_remove` is called on a directory, it'll drop the nlink of its parent. If it's called *twice* on the same directory, that's where things get interesting. I need to figure out if there's a guard in place to prevent a double `drop_nlink`.
Looking deeper, I see that the `d_is_dir(dentry)` check happens *before* `clear_nlink` is called on the directory itself. Then, the code moves to the parent and, if the `victim` is the original `dentry`, it does a `drop_nlink`. The key insight is that `drop_nlink(inode)` is triggered *unconditionally* if `d_is_dir(dentry)` which is why the check is made. But no check is done on whether `victim` was already removed! `simple_positive(victim)` *is* checked for invalidation, callbacks, etc., but not for link count changes. This means a double removal would indeed result in calling `drop_nlink` twice on the parent, which is a problem! It'll either underflow or hit zero and trigger a warning.
Now, my mind goes to where `debugfs_remove_recursive` is called. Specifically, I'm thinking about the `i2c_device_probe` function. The code creates a debugfs directory, and if the probe function fails, it calls `debugfs_remove_recursive`.
The concern is what happens if the probe function *also* calls `debugfs_remove_recursive`. It's unlikely that the driver's probe function is *supposed* to touch the `client->debugfs`, but it's important to rule it out. So, the first quick sanity check: is the probe function being correctly implemented? It might seem far-fetched, but it needs to be considered. I need to be sure the driver's code isn't stepping on the i2c core's responsibility. The issue is with the `err_release_driver_resources` label. It calls the cleanup function, but what happens to the debugfs pointer? Crucially, `client->debugfs` isn't set to NULL after the first call to `debugfs_remove_recursive`.
This is where the potential double removal lies. If the probe fails and cleans up, what about *subsequent* removal or unregistration of the device? Does it call `debugfs_remove_recursive` again? I need to check `i2c_device_remove` and `i2c_unregister_device` to see if `debugfs_remove_recursive` is called there as well. That would be the second point of failure. I need to see if a second call is made on the already deallocated `debugfs` structure.
|
| 23/2 |
2026/08/03 02:12 |
tool |
git-log |
0m
Args: map[Count:10 SourcePath:drivers/i2c/i2c-core-base.c SymbolName:i2c_unregister_device]
Results: map[Output:1c24e5fc0c70 i2c: core: Fix double-free of fwnode in i2c_unregister_device()
df6d7277e552 i2c: core: Do not dereference fwnode in struct device
3435b7f0fd28 i2c: core: Reuse fwnode variable where it makes sense
5763a474c887 i2c: core: Unify the firmware node type check
6250ebe666e4 i2c: Fix core-managed per-client debugfs handling
d06905d68610 i2c: add core-managed per-client directory in debugfs
714638e02d94 i2c: Add support for software nodes
689f535843ac i2c: make i2c_unregister_device() ERR_PTR safe
e0638fa400ea i2c: core: decrease reference count of device node in i2c_unregister_device
7b43dd19c9b1 i2c: Make i2c_unregister_device() NULL-aware
]
|
| 24/2 |
2026/08/03 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 28854
output: 44
thoughts: 1468
Thoughts: **Analyzing Debugfs Handling in I2C Client Probing and Removal**
Okay, so this commit, `6250ebe666e4`, shifted the `debugfs_remove_recursive` call for I2C clients. It used to be in `i2c_unregister_device`, but it's now handled in `i2c_device_remove` and within the `err_release_driver_resources` path in `i2c_device_probe`. I need to think this through carefully.
Let's look closely at `i2c_device_remove`. Right, it removes the debugfs entry, releases resources, and handles power management. My main concern is, does `i2c_device_remove` even get called if `i2c_device_probe` fails? No, it doesn't. A probe failure means the device isn't actually bound to the driver, so the `remove` function doesn't execute. Good.
But wait, if the probe *does* fail, `client->debugfs` *is* removed. What if the device is then probed *again*? Suppose the device is still registered but unbound after a failed probe. If a "bind" operation happens (sysfs or a driver hotplug) `i2c_device_probe` is called *again*.
Okay, so I see it. The code creates a new dentry with `debugfs_create_dir`. This will overwrite the previous `client->debugfs`. That seems fine, right? Well... what if `debugfs_create_dir` *fails*? It returns an error pointer. Then the error pointer is passed to `debugfs_remove_recursive` which is ignored. That's a potential issue: what if `debugfs_create_dir` fails the second time and the directory isn't removed. Ok, but hold on, the directory is always removed if `probe` fails.
But what if probe *succeeds*, and the device is *then* unbound? `i2c_device_remove` is called. It removes the debugfs directory. Then, if the device gets bound and probed again, the directory gets recreated during the probe. This seems like a reasonable sequence.
Wait. Let me look at the stack trace again. Ah, this is from the `new_device_store`, when a new I2C client device is created. `i2c_new_client_device` calls `device_add`, which then probes the device. Even if the probe fails, `device_add` *succeeds*! `device_add` does not return an error if probe fails, it's asynchronous or "best-effort." That's the critical point here. `i2c_new_client_device` also succeeds! So, the device is added, even if the probe fails.
Okay, so if the probe *fails*, `i2c_device_probe` calls `debugfs_remove_recursive(client->debugfs)`. That is the correct flow. That sounds right, and consistent. I think this all looks good. Now, let's just make sure there aren't any race conditions....
|
| 25/2 |
2026/08/03 02:12 |
tool |
read-file |
0m
Args: map[File:drivers/i2c/i2c-core-base.c FirstLine:1000 LineCount:40]
Results: map[Contents:1000: client->dev.parent = &client->adapter->dev;
1001: client->dev.bus = &i2c_bus_type;
1002: client->dev.type = &i2c_client_type;
1003:
1004: device_enable_async_suspend(&client->dev);
1005:
1006: device_set_node(&client->dev, fwnode_handle_get(fwnode));
1007:
1008: if (info->swnode) {
1009: status = device_add_software_node(&client->dev, info->swnode);
1010: if (status) {
1011: dev_err(&adap->dev,
1012: "Failed to add software node to client %s: %d\n",
1013: client->name, status);
1014: goto out_err_put_fwnode;
1015: }
1016: }
1017:
1018: i2c_dev_set_name(adap, client, info);
1019: status = device_register(&client->dev);
1020: if (status)
1021: goto out_remove_swnode;
1022:
1023: dev_dbg(&adap->dev, "client [%s] registered with bus id %s\n",
1024: client->name, dev_name(&client->dev));
1025:
1026: i2c_unlock_addr(adap, client->addr, client->flags);
1027:
1028: return client;
1029:
1030: out_remove_swnode:
1031: device_remove_software_node(&client->dev);
1032: need_put = true;
1033: out_err_put_fwnode:
1034: fwnode_handle_put(fwnode);
1035: out_err:
1036: dev_err(&adap->dev,
1037: "Failed to register i2c client %s at 0x%02x (%d)\n",
1038: client->name, client->addr, status);
1039: i2c_unlock_addr(adap, client->addr, client->flags);
]
|
| 26/2 |
2026/08/03 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 31705
output: 36
thoughts: 612
Thoughts: **Debugging a Debugfs Double-Remove Scenario**
Okay, so I'm tracing this crash, and it looks like it's happening when trying to remove something from debugfs. The stack trace leads me from `device_register` to `device_add`, all the way down through the probing process in the I2C subsystem, specifically `i2c_device_probe`. The problem seems to be originating from a call to `debugfs_remove_recursive`. And the kernel is complaining about `inode->i_nlink == 0` during a `drop_nlink` operation.
The core of the issue seems to be that `i2c_device_probe` is failing, and as part of its cleanup, it calls `debugfs_remove_recursive` on the `client->debugfs` dentry. This seems reasonable, right? I mean, `client->debugfs` is *created* inside `i2c_device_probe` with `debugfs_create_dir`. If the probe fails, we should tear it down.
However, the question is why `i_nlink` would be zero. I am scratching my head, because `debugfs_create_dir` should have created a dentry with a positive link count. The function `debugfs_remove_recursive` should handle valid debugfs pointers (the function handles error pointers from `debugfs_create_dir`).
Unless... is it the SAME dentry as something else? I doubt this because debugfs *should* prevent this. Or is `debugfs_remove_recursive` being called TWICE on the same dentry, somehow? The logic seems to prevent this, but let's check.
So, let's step back a bit. Where does `client->debugfs` come from? Aha, it's created with `debugfs_create_dir(dev_name(&client->dev), client->adapter->debugfs)`. That means it's a child of `client->adapter->debugfs`. Okay, here's where it gets interesting.
If `client->adapter->debugfs` is removed *before* the child is deleted, its `i_nlink` would become zero. But the creation of `client->debugfs` depends on a valid `client->adapter->debugfs`. `debugfs_create_dir` *should* be failing if its parent has been removed.
Wait... if `client->adapter->debugfs` is removed *while it's still pinned in memory*? Then `debugfs_create_dir` might actually succeed in creating a child *in the now-orphaned parent*. This would explain the zero link count when we later try to remove the child.
So, if `client->adapter->debugfs` is removed, its `d_inode` ends up with `i_nlink == 0`. Then, `debugfs_create_dir` effectively creates a child directory inside that parent. And that is what is causing the `i_nlink` issue.
I need to look deeper into `debugfs_create_dir`, specifically the `start_creating` function in `fs/debugfs/inode.c`. That should show me the exact sequence of operations that happen when a new directory is created within debugfs and if there are any race conditions that could lead to this situation. That looks like the next logical step.
|
| 27/2 |
2026/08/03 02:12 |
tool |
read-file |
0m
Args: map[File:fs/debugfs/inode.c FirstLine:540 LineCount:40]
Results: map[Contents: 540:
541: if (!IS_ERR(de))
542: d_inode(de)->i_size = file_size;
543: }
544: EXPORT_SYMBOL_GPL(debugfs_create_file_size);
545:
546: /**
547: * debugfs_create_dir - create a directory in the debugfs filesystem
548: * @name: a pointer to a string containing the name of the directory to
549: * create.
550: * @parent: a pointer to the parent dentry for this file. This should be a
551: * directory dentry if set. If this parameter is NULL, then the
552: * directory will be created in the root of the debugfs filesystem.
553: *
554: * This function creates a directory in debugfs with the given name.
555: *
556: * This function will return a pointer to a dentry if it succeeds. This
557: * pointer must be passed to the debugfs_remove() function when the file is
558: * to be removed (no automatic cleanup happens if your module is unloaded,
559: * you are responsible here.) If an error occurs, ERR_PTR(-ERROR) will be
560: * returned.
561: *
562: * If debugfs is not enabled in the kernel, the value -%ENODEV will be
563: * returned.
564: *
565: * NOTE: it's expected that most callers should _ignore_ the errors returned
566: * by this function. Other debugfs functions handle the fact that the "dentry"
567: * passed to them could be an error and they don't crash in that case.
568: * Drivers should generally work fine even if debugfs fails to init anyway.
569: */
570: struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
571: {
572: struct dentry *dentry = debugfs_start_creating(name, parent);
573: struct inode *inode;
574:
575: if (IS_ERR(dentry))
576: return dentry;
577:
578: inode = debugfs_get_inode(dentry->d_sb);
579: if (unlikely(!inode)) {
]
|
| 28/2 |
2026/08/03 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 33760
output: 36
thoughts: 12
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Debugging the Creation Process**
Okay, so I need to dive into this issue. The first thing I'm going to check is `debugfs_start_creating`. That function is the likely point where the process of file or directory creation within the debugfs filesystem begins. Given the nature of the issue, this seems like the place to start. I'm going to want to understand its purpose, its call stack, and what the control flow looks like through that point. I will probably also want to quickly ascertain what its responsibilities are and identify which code paths are the most interesting to focus on. Understanding its return values is crucial too. Are there error codes or any flags that give me hints about how it might be failing? Essentially, I'm trying to establish the initial conditions of the creation process, the entry point, and identify the key operations it performs. This is the logical first step in tracking down the root cause.
|
| 29/2 |
2026/08/03 02:12 |
tool |
read-file |
0m
Args: map[File:fs/debugfs/inode.c FirstLine:300 LineCount:40]
Results: map[Contents: 300: }
301:
302: static const struct fs_context_operations debugfs_context_ops = {
303: .free = debugfs_free_fc,
304: .parse_param = debugfs_parse_param,
305: .get_tree = debugfs_get_tree,
306: .reconfigure = debugfs_reconfigure,
307: };
308:
309: static int debugfs_init_fs_context(struct fs_context *fc)
310: {
311: struct debugfs_fs_info *fsi;
312:
313: fsi = kzalloc_obj(struct debugfs_fs_info);
314: if (!fsi)
315: return -ENOMEM;
316:
317: fsi->mode = DEBUGFS_DEFAULT_MODE;
318:
319: fc->s_fs_info = fsi;
320: fc->ops = &debugfs_context_ops;
321: return 0;
322: }
323:
324: static struct file_system_type debug_fs_type = {
325: .owner = THIS_MODULE,
326: .name = "debugfs",
327: .init_fs_context = debugfs_init_fs_context,
328: .parameters = debugfs_param_specs,
329: .kill_sb = kill_anon_super,
330: };
331: MODULE_ALIAS_FS("debugfs");
332:
333: /**
334: * debugfs_lookup() - look up an existing debugfs file
335: * @name: a pointer to a string containing the name of the file to look up.
336: * @parent: a pointer to the parent dentry of the file.
337: *
338: * This function will return a pointer to a dentry if it succeeds. If the file
339: * doesn't exist or an error occurs, %NULL will be returned. The returned
]
|
| 30/2 |
2026/08/03 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 34641
output: 20
thoughts: 14
Thoughts: Here's my thought process, summarized as you requested:
**Debugging Strategy Evolving**
Okay, I need to figure out what's going on. This problem feels familiar, and it's making me think of file system debugging. Right now, I should try to search specifically for a function or key word to narrow my search scope. The name `debugfs_start_creating` has a ring to it. I bet that's where I need to start digging. I'm going to run a search for that keyword.
|
| 31/2 |
2026/08/03 02:12 |
tool |
grepper |
0m
Args: map[Expression:debugfs_start_creating]
Results: map[Output:fs/debugfs/inode.c=360=EXPORT_SYMBOL_GPL(debugfs_lookup);
fs/debugfs/inode.c-361-
fs/debugfs/inode.c:362:static struct dentry *debugfs_start_creating(const char *name,
fs/debugfs/inode.c-363- struct dentry *parent)
--
fs/debugfs/inode.c=416=static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
--
fs/debugfs/inode.c-427- BUG_ON(!S_ISREG(mode));
fs/debugfs/inode.c:428: dentry = debugfs_start_creating(name, parent);
fs/debugfs/inode.c-429-
--
fs/debugfs/inode.c=570=struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
fs/debugfs/inode.c-571-{
fs/debugfs/inode.c:572: struct dentry *dentry = debugfs_start_creating(name, parent);
fs/debugfs/inode.c-573- struct inode *inode;
--
fs/debugfs/inode.c=609=struct dentry *debugfs_create_automount(const char *name,
--
fs/debugfs/inode.c-613-{
fs/debugfs/inode.c:614: struct dentry *dentry = debugfs_start_creating(name, parent);
fs/debugfs/inode.c-615- struct inode *inode;
--
fs/debugfs/inode.c=663=struct dentry *debugfs_create_symlink(const char *name, struct dentry *parent,
--
fs/debugfs/inode.c-671-
fs/debugfs/inode.c:672: dentry = debugfs_start_creating(name, parent);
fs/debugfs/inode.c-673- if (IS_ERR(dentry)) {
]
|
| 32/2 |
2026/08/03 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 35319
output: 36
thoughts: 23
Thoughts: Here's my take on the thought process, as if it were my own:
**Diving into debugfs Creation**
Alright, time to dig into this `debugfs_start_creating` function. I remember seeing that one in `fs/debugfs/inode.c`, so that's where I'll head. I want to understand exactly how the system kicks off the creation process for debugfs entries. What's the entry point? What data structures are initialized? What are the key interactions it's making with the VFS? This is all about the plumbing, the initial setup. I expect to see some allocation, some inode initialization, and likely calls to set up the necessary attributes for the debugfs node. I’m thinking I will focus in on the logic related to this function specifically, ignoring the surrounding irrelevant code for a quick overview. I will keep an eye on how it handles potential errors, because that's always a critical part of understanding any kernel function. Let's get reading and see what’s there.
|
| 33/2 |
2026/08/03 02:12 |
tool |
read-file |
0m
Args: map[File:fs/debugfs/inode.c FirstLine:362 LineCount:40]
Results: map[Contents: 362: static struct dentry *debugfs_start_creating(const char *name,
363: struct dentry *parent)
364: {
365: struct dentry *dentry;
366: int error;
367:
368: if (!debugfs_enabled)
369: return ERR_PTR(-EPERM);
370:
371: if (!debugfs_initialized())
372: return ERR_PTR(-ENOENT);
373:
374: pr_debug("creating file '%s'\n", name);
375:
376: if (IS_ERR(parent))
377: return parent;
378:
379: error = simple_pin_fs(&debug_fs_type, &debugfs_mount,
380: &debugfs_mount_count);
381: if (error) {
382: pr_err("Unable to pin filesystem for file '%s'\n", name);
383: return ERR_PTR(error);
384: }
385:
386: /* If the parent is not specified, we create it in the root.
387: * We need the root dentry to do this, which is in the super
388: * block. A pointer to that is in the struct vfsmount that we
389: * have around.
390: */
391: if (!parent)
392: parent = debugfs_mount->mnt_root;
393:
394: dentry = simple_start_creating(parent, name);
395: if (IS_ERR(dentry)) {
396: if (dentry == ERR_PTR(-EEXIST))
397: pr_err("'%s' already exists in '%pd'\n", name, parent);
398: simple_release_fs(&debugfs_mount, &debugfs_mount_count);
399: }
400: return dentry;
401: }
]
|
| 34/2 |
2026/08/03 02:12 |
llm |
expert |
0m
Model:
Tokens:
input: 36184
output: 43
thoughts: 1850
Thoughts: **Debugging the I2C Device Probe and Debugfs Interaction**
Okay, so here's the breakdown of what I'm thinking through. The core of the issue seems to be how debugfs directories are created and cleaned up during the I2C device probing process. Initially, I'm concerned about error handling when `debugfs_create_dir` fails with `-EEXIST`, which could lead to `client->debugfs` being an error pointer and how that is handled later. However, the subsequent checks using `IS_ERR_OR_NULL` in `debugfs_remove` handle this.
My line of thought continues with what happens when `driver->probe` fails after the debugfs directory is created. The code correctly calls `debugfs_remove_recursive` to clean up. But, I start to think through the cases if `debugfs_remove` is called with invalid or partially removed entries. The code in `debugfs_remove` only returns if the dentry is null or an error, so a partially unhashed dentry will remain, which could be an issue. However, `debugfs_remove` doesn't *free* the dentry, just unhashes it. This raises a concern about a potential leak, but I realize the reference counting should manage this.
Then, there's the situation where the device is probed multiple times, perhaps after a previous failed probe. The code correctly overwrites `client->debugfs` with a new dentry. This is a crucial point; the old directory should get cleaned up because of the refcount mechanism on the old `client->debugfs`, which is great.
I then went down the rabbit hole of thinking about how devices are unregistered and how `i2c_unregister_device` interacts with debugfs. I realized that the debugfs cleanup from `i2c_unregister_device` was removed in commit `6250ebe666e4`. This meant that `debugfs_remove_recursive` is only called from within the `i2c_device_probe` function when there's an issue.
Digging further, I consider the cases where a device is *never* bound to a driver. In these scenarios, `client->debugfs` would never be created. However, the code in `i2c_device_probe` shows that debugfs directory creation is *always* tied to driver matching and probing. It's perfectly balanced: if `probe` fails, it's removed. If it succeeds, it stays. On unbinding, it's removed by `i2c_device_remove`.
I then went over what happens when `driver->probe` is NULL, or when it returns `-EPROBE_DEFER`. Both of these paths correctly lead to removal of the debugfs directory.
So, I circled back to the stack trace in the original problem report:
` __simple_recursive_removal+0x491/0x520 fs/libfs.c:631
debugfs_remove+0x5b/0x70 fs/debugfs/inode.c:781
i2c_device_probe+0x8dc/0xbf0 drivers/i2c/i2c-core-base.c:610`
This points to the `err_release_driver_resources` path within `i2c_device_probe`, which calls `debugfs_remove_recursive`. Inside of `debugfs_remove`, we hit `inode->i_nlink == 0` on the parent directory. The parent directory is `client->adapter->debugfs`. This doesn't seem possible. Why would the parent's link count be zero? Could it be that the parent debugfs directory is being removed *concurrently*? Or, is it somehow the same directory? No, because the name is built from `dev_name(&client->dev)`, which should be unique. Does `client->debugfs` have some sort of error state, even though `debugfs_remove` checks for it? Nope, it checks for error. And not the root of debugfs. Something's happening with the parent directory, but how and when? I still need to re-examine the `i2c_device_probe` code more closely.
|
| 35/2 |
2026/08/03 02:13 |
tool |
read-file |
0m
Args: map[File:drivers/i2c/i2c-core-base.c FirstLine:570 LineCount:50]
Results: map[Contents: 570:
571: status = of_clk_set_defaults(to_of_node(fwnode), false);
572: if (status < 0)
573: goto err_clear_wakeup_irq;
574:
575: do_power_on = !i2c_acpi_waive_d0_probe(dev);
576: status = dev_pm_domain_attach(&client->dev, PD_FLAG_DETACH_POWER_OFF |
577: (do_power_on ? PD_FLAG_ATTACH_POWER_ON : 0));
578: if (status)
579: goto err_clear_wakeup_irq;
580:
581: client->devres_group_id = devres_open_group(&client->dev, NULL,
582: GFP_KERNEL);
583: if (!client->devres_group_id) {
584: status = -ENOMEM;
585: goto err_clear_wakeup_irq;
586: }
587:
588: client->debugfs = debugfs_create_dir(dev_name(&client->dev),
589: client->adapter->debugfs);
590:
591: if (driver->probe)
592: status = driver->probe(client);
593: else
594: status = -EINVAL;
595:
596: /*
597: * Note that we are not closing the devres group opened above so
598: * even resources that were attached to the device after probe is
599: * run are released when i2c_device_remove() is executed. This is
600: * needed as some drivers would allocate additional resources,
601: * for example when updating firmware.
602: */
603:
604: if (status)
605: goto err_release_driver_resources;
606:
607: return 0;
608:
609: err_release_driver_resources:
610: debugfs_remove_recursive(client->debugfs);
611: devres_release_group(&client->dev, client->devres_group_id);
612: err_clear_wakeup_irq:
613: dev_pm_clear_wake_irq(&client->dev);
614: device_init_wakeup(&client->dev, false);
615: put_sync_adapter:
616: if (client->flags & I2C_CLIENT_HOST_NOTIFY)
617: pm_runtime_put_sync(&client->adapter->dev);
618:
619: return status;
]
|
| 36/2 |
2026/08/03 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 39783
output: 43
thoughts: 403
Thoughts: **Analyzing PM Domain Detachment in i2c Driver Error Handling**
Okay, so I was looking into the error handling in this i2c driver, specifically around power management. My initial thought was that `err_clear_wakeup_irq` wasn't detaching the PM domain. But then I remembered a commit, `6250ebe666e4`, that added `dev_pm_domain_detach` in the error path. So I jumped to confirm, thinking it *should* be there.
However, after a quick check of the relevant code, the `err_detach_pm_domain` label appears to be missing. Wait, let me double-check the file contents... Okay, line 612 confirms, the `dev_pm_domain_detach` call is *not* present!
Ah, right! I remember now. The commit `eddfe53b0d84 i2c: core: Drop dev_pm_domain_detach() call` removed that call. So, that explains why it's missing. That makes sense.
But now, a different question arises. What if `client->debugfs` already has a value before `i2c_device_probe` is even called? That might be a concern here. I need to understand when and how `client->debugfs` is set. Okay, let's see. When `i2c_new_client_device` allocates `client`, it uses `kzalloc`, which, according to my quick assessment, is invoked with `kzalloc`. Got it.
|
| 37/2 |
2026/08/03 02:13 |
tool |
read-file |
0m
Args: map[File:drivers/i2c/i2c-core-base.c FirstLine:950 LineCount:40]
Results: map[Contents: 950: * probe()/remove() methods. A driver may be bound to this device when we
951: * return from this function, or any later moment (e.g. maybe hotplugging will
952: * load the driver module). This call is not appropriate for use by mainboard
953: * initialization logic, which usually runs during an arch_initcall() long
954: * before any i2c_adapter could exist.
955: *
956: * This returns the new i2c client, which may be saved for later use with
957: * i2c_unregister_device(); or an ERR_PTR to describe the error.
958: */
959: struct i2c_client *
960: i2c_new_client_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
961: {
962: struct fwnode_handle *fwnode = info->fwnode;
963: struct i2c_client *client;
964: bool need_put = false;
965: int status;
966:
967: client = kzalloc_obj(*client);
968: if (!client)
969: return ERR_PTR(-ENOMEM);
970:
971: client->adapter = adap;
972:
973: client->dev.platform_data = info->platform_data;
974: client->flags = info->flags;
975: client->addr = info->addr;
976:
977: client->init_irq = info->irq;
978: if (!client->init_irq)
979: client->init_irq = i2c_dev_irq_from_resources(info->resources,
980: info->num_resources);
981:
982: strscpy(client->name, info->type, sizeof(client->name));
983:
984: status = i2c_check_addr_validity(client->addr, client->flags);
985: if (status) {
986: dev_err(&adap->dev, "Invalid %d-bit I2C address 0x%02hx\n",
987: client->flags & I2C_CLIENT_TEN ? 10 : 7, client->addr);
988: goto out_err_silent;
989: }
]
|
| 38/2 |
2026/08/03 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 41266
output: 36
thoughts: 1294
Thoughts: **Debugging I2C Device Registration and Debugfs Interactions**
Okay, let's break this down. A new I2C client is `kzalloc`'d, and initially, `client->debugfs` is NULL. Then `device_register` is called. The device probes, leading to `i2c_device_probe`. In `i2c_device_probe`, `debugfs_create_dir` is called to create the debugfs directory, and `client->debugfs` is set. If the probe *fails*, `debugfs_remove_recursive(client->debugfs)` is called to clean up. But crucially, `device_register` *succeeds* even if the probe fails, and `client->debugfs` will now point to this removed dentry. This is a potential leak. Then `i2c_new_client_device` returns the client.
If the user tries to create another device at the same address, `i2c_check_addr_busy` will prevent it. So, what if they call `delete_device`? `delete_device` calls `i2c_unregister_device`. `i2c_unregister_device` calls `device_unregister(&client->dev)`, which frees the device. *Does* `i2c_unregister_device` remove the debugfs directory? Nope, it was removed in `6250ebe666e4`!
Now, the important cases. If the device was never bound to a driver, `client->debugfs` remains NULL during unregistration, and no problem. However, if the device was bound, `i2c_device_remove` *is* called during `device_unregister`, which *does* call `debugfs_remove_recursive(client->debugfs)`. So, the debugfs directory is cleaned up in this case. If the probe *failed* before unregistration, and the directory was removed, it's a leak, but `debugfs_remove` drops the refcount and it's freed.
What about manual binding via sysfs `bind`? This causes `i2c_device_probe` to be called again. It re-creates the debugfs directory. If *that* probe fails, the *new* debugfs directory is removed. That seems fine.
What if `debugfs_create_dir` returns an error pointer? It does nothing.
Okay, let's look at that stack trace again... specifically, why are we seeing `__simple_recursive_removal+0x491/0x520` in `i2c_device_probe`? This seems to be happening DURING `i2c_device_probe`! The key is why `inode->i_nlink == 0` during `i2c_device_probe`. This means `debugfs_remove` is called on `client->debugfs`, *and* that `client->debugfs` is a directory whose parent’s `i_nlink` is 0. This parent is `client->adapter->debugfs`.
The crucial question is, can `client->adapter->debugfs` be removed concurrently? `i2c_del_adapter` calls `debugfs_remove_recursive(adap->debugfs)` *if* the adapter is being removed. But `new_device_store` is running, so it holds a reference to the adapter, right? `new_device_store` is a sysfs attribute of the adapter, so the adapter can't be removed, *can it*? Sysfs holds a kobject reference, but does that prevent `i2c_del_adapter` from being called? It *can* be called concurrently! If that happens, then `adap->debugfs` *is* removed. Then, `new_device_store` calls `i2c_new_client_device`, which calls `device_register`, which calls `i2c_device_probe`. `i2c_device_probe` then calls `debugfs_create_dir(..., adap->debugfs)`. Since `adap->debugfs` is already gone, it will be attempting to create a directory beneath a directory with i_nlink == 0. Does `debugfs_create_dir` allow creating a child in a removed directory? Time to check `simple_start_creating` in `fs/libfs.c` or `fs/debugfs/inode.c`.
|
| 39/2 |
2026/08/03 02:13 |
tool |
read-file |
0m
Args: map[File:fs/debugfs/inode.c FirstLine:380 LineCount:40]
Results: map[Contents: 380: &debugfs_mount_count);
381: if (error) {
382: pr_err("Unable to pin filesystem for file '%s'\n", name);
383: return ERR_PTR(error);
384: }
385:
386: /* If the parent is not specified, we create it in the root.
387: * We need the root dentry to do this, which is in the super
388: * block. A pointer to that is in the struct vfsmount that we
389: * have around.
390: */
391: if (!parent)
392: parent = debugfs_mount->mnt_root;
393:
394: dentry = simple_start_creating(parent, name);
395: if (IS_ERR(dentry)) {
396: if (dentry == ERR_PTR(-EEXIST))
397: pr_err("'%s' already exists in '%pd'\n", name, parent);
398: simple_release_fs(&debugfs_mount, &debugfs_mount_count);
399: }
400: return dentry;
401: }
402:
403: static struct dentry *debugfs_failed_creating(struct dentry *dentry)
404: {
405: simple_done_creating(dentry);
406: simple_release_fs(&debugfs_mount, &debugfs_mount_count);
407: return ERR_PTR(-ENOMEM);
408: }
409:
410: static struct dentry *debugfs_end_creating(struct dentry *dentry)
411: {
412: simple_done_creating(dentry);
413: return dentry; // borrowed
414: }
415:
416: static struct dentry *__debugfs_create_file(const char *name, umode_t mode,
417: struct dentry *parent, void *data,
418: const void *aux,
419: const struct file_operations *proxy_fops,
]
|
| 40/2 |
2026/08/03 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 44183
output: 19
thoughts: 25
Thoughts: Here's my thought process, as if I were considering this myself:
**Investigating the `simple_start_creating` Function**
Okay, so I need to track down where this `simple_start_creating` function is defined. It's almost certainly related to filesystem operations, and my initial hunch is that it's going to be in `fs/libfs.c`. That's the most logical place to begin looking, given the common patterns in our codebase. However, I should keep an open mind; it *could* be tucked away elsewhere, or perhaps defined within a header file and used in multiple places. No, let me start at `fs/libfs.c`. I have a feeling that's the most likely location. I'll need to double check any includes, but I'll start with that file.
|
| 41/2 |
2026/08/03 02:13 |
tool |
grepper |
0m
Args: map[Expression:simple_start_creating]
Results: map[Output:arch/s390/hypfs/inode.c=317=static struct dentry *hypfs_create_file(struct dentry *parent, const char *name,
--
arch/s390/hypfs/inode.c-322-
arch/s390/hypfs/inode.c:323: dentry = simple_start_creating(parent, name);
arch/s390/hypfs/inode.c-324- if (IS_ERR(dentry))
--
drivers/android/binder/rust_binderfs.c=114=static int binderfs_binder_device_create(struct inode *ref_inode,
--
drivers/android/binder/rust_binderfs.c-180- root = sb->s_root;
drivers/android/binder/rust_binderfs.c:181: dentry = simple_start_creating(root, req->name);
drivers/android/binder/rust_binderfs.c-182- if (IS_ERR(dentry)) {
--
drivers/android/binder/rust_binderfs.c=464=static struct dentry *rust_binderfs_create_file(struct dentry *parent, const char *name,
--
drivers/android/binder/rust_binderfs.c-476-
drivers/android/binder/rust_binderfs.c:477: dentry = simple_start_creating(parent, name);
drivers/android/binder/rust_binderfs.c-478- if (IS_ERR(dentry)) {
--
drivers/android/binder/rust_binderfs.c=503=static struct dentry *binderfs_create_dir(struct dentry *parent,
--
drivers/android/binder/rust_binderfs.c-515-
drivers/android/binder/rust_binderfs.c:516: dentry = simple_start_creating(parent, name);
drivers/android/binder/rust_binderfs.c-517- if (IS_ERR(dentry)) {
--
drivers/android/binderfs.c=114=static int binderfs_binder_device_create(struct inode *ref_inode,
--
drivers/android/binderfs.c-185- root = sb->s_root;
drivers/android/binderfs.c:186: dentry = simple_start_creating(root, name);
drivers/android/binderfs.c-187- if (IS_ERR(dentry)) {
--
drivers/android/binderfs.c=469=struct dentry *binderfs_create_file(struct dentry *parent, const char *name,
--
drivers/android/binderfs.c-478-
drivers/android/binderfs.c:479: dentry = simple_start_creating(parent, name);
drivers/android/binderfs.c-480- if (IS_ERR(dentry))
--
drivers/android/binderfs.c=498=static struct dentry *binderfs_create_dir(struct dentry *parent,
--
drivers/android/binderfs.c-506-
drivers/android/binderfs.c:507: dentry = simple_start_creating(parent, name);
drivers/android/binderfs.c-508- if (IS_ERR(dentry))
--
drivers/usb/gadget/function/f_fs.c=1904=static int ffs_sb_create_file(struct super_block *sb, const char *name,
--
drivers/usb/gadget/function/f_fs.c-1913- return -ENOMEM;
drivers/usb/gadget/function/f_fs.c:1914: dentry = simple_start_creating(sb->s_root, name);
drivers/usb/gadget/function/f_fs.c-1915- if (IS_ERR(dentry)) {
--
drivers/usb/gadget/legacy/inode.c=1986=static int gadgetfs_create_file (struct super_block *sb, char const *name,
--
drivers/usb/gadget/legacy/inode.c-1996-
drivers/usb/gadget/legacy/inode.c:1997: dentry = simple_start_creating(sb->s_root, name);
drivers/usb/gadget/legacy/inode.c-1998- if (IS_ERR(dentry)) {
--
fs/binfmt_misc.c=769=static int add_entry(Node *e, struct super_block *sb)
fs/binfmt_misc.c-770-{
fs/binfmt_misc.c:771: struct dentry *dentry = simple_start_creating(sb->s_root, e->name);
fs/binfmt_misc.c-772- struct inode *inode;
--
fs/debugfs/inode.c=362=static struct dentry *debugfs_start_creating(const char *name,
--
fs/debugfs/inode.c-393-
fs/debugfs/inode.c:394: dentry = simple_start_creating(parent, name);
fs/debugfs/inode.c-395- if (IS_ERR(dentry)) {
--
fs/devpts/inode.c=259=static int mknod_ptmx(struct super_block *sb, struct fs_context *fc)
--
fs/devpts/inode.c-269-
fs/devpts/inode.c:270: dentry = simple_start_creating(root, "ptmx");
fs/devpts/inode.c-271- if (IS_ERR(dentry)) {
--
fs/libfs.c=2267=void stashed_dentry_prune(struct dentry *dentry)
--
fs/libfs.c-2286-/**
fs/libfs.c:2287: * simple_start_creating - prepare to create a given name
fs/libfs.c-2288- * @parent: directory in which to prepare to create the name
--
fs/libfs.c-2296- */
fs/libfs.c:2297:struct dentry *simple_start_creating(struct dentry *parent, const char *name)
fs/libfs.c-2298-{
--
fs/libfs.c-2306-}
fs/libfs.c:2307:EXPORT_SYMBOL(simple_start_creating);
fs/libfs.c-2308-
fs/libfs.c:2309:/* parent must have been held exclusive since simple_start_creating() */
fs/libfs.c-2310-void simple_done_creating(struct dentry *child)
--
fs/nfsd/nfsctl.c=1136=static struct dentry *nfsd_mkdir(struct dentry *parent, struct nfsdfs_client *ncl, char *name)
--
fs/nfsd/nfsctl.c-1145-
fs/nfsd/nfsctl.c:1146: dentry = simple_start_creating(parent, name);
fs/nfsd/nfsctl.c-1147- if (IS_ERR(dentry)) {
--
fs/nfsd/nfsctl.c=1170=static void _nfsd_symlink(struct dentry *parent, const char *name,
--
fs/nfsd/nfsctl.c-1180-
fs/nfsd/nfsctl.c:1181: dentry = simple_start_creating(parent, name);
fs/nfsd/nfsctl.c-1182- if (IS_ERR(dentry)) {
--
fs/nfsd/nfsctl.c=1228=static int nfsdfs_create_files(struct dentry *root,
--
fs/nfsd/nfsctl.c-1240- return -ENOMEM;
fs/nfsd/nfsctl.c:1241: dentry = simple_start_creating(root, files->name);
fs/nfsd/nfsctl.c-1242- if (IS_ERR(dentry)) {
--
fs/tracefs/inode.c=526=struct dentry *tracefs_start_creating(const char *name, struct dentry *parent)
--
fs/tracefs/inode.c-545-
fs/tracefs/inode.c:546: dentry = simple_start_creating(parent, name);
fs/tracefs/inode.c-547- if (IS_ERR(dentry))
--
include/linux/fs.h=3292=extern void simple_release_fs(struct vfsmount **mount, int *count);
include/linux/fs.h:3293:struct dentry *simple_start_creating(struct dentry *, const char *);
include/linux/fs.h-3294-void simple_done_creating(struct dentry *);
--
kernel/bpf/inode.c=475=static int bpf_iter_link_pin_kernel(struct dentry *parent,
--
kernel/bpf/inode.c-481-
kernel/bpf/inode.c:482: dentry = simple_start_creating(parent, name);
kernel/bpf/inode.c-483- if (IS_ERR(dentry))
--
net/sunrpc/rpc_pipe.c=524=static int rpc_new_file(struct dentry *parent,
--
net/sunrpc/rpc_pipe.c-529-{
net/sunrpc/rpc_pipe.c:530: struct dentry *dentry = simple_start_creating(parent, name);
net/sunrpc/rpc_pipe.c-531- struct inode *dir = parent->d_inode;
--
net/sunrpc/rpc_pipe.c=552=static struct dentry *rpc_new_dir(struct dentry *parent,
--
net/sunrpc/rpc_pipe.c-555-{
net/sunrpc/rpc_pipe.c:556: struct dentry *dentry = simple_start_creating(parent, name);
net/sunrpc/rpc_pipe.c-557- struct inode *dir = parent->d_inode;
--
net/sunrpc/rpc_pipe.c=635=int rpc_mkpipe_dentry(struct dentry *parent, const char *name,
--
net/sunrpc/rpc_pipe.c-649-
net/sunrpc/rpc_pipe.c:650: dentry = simple_start_creating(parent, name);
net/sunrpc/rpc_pipe.c-651- if (IS_ERR(dentry)) {
--
security/apparmor/apparmorfs.c=335=static struct dentry *aafs_create(const char *name, umode_t mode,
--
security/apparmor/apparmorfs.c-356-
security/apparmor/apparmorfs.c:357: dentry = simple_start_creating(parent, name);
security/apparmor/apparmorfs.c-358- if (IS_ERR(dentry)) {
--
security/apparmor/apparmorfs.c=2664=static int aa_mk_null_file(struct dentry *parent)
--
security/apparmor/apparmorfs.c-2674-
security/apparmor/apparmorfs.c:2675: dentry = simple_start_creating(parent, NULL_FILE_NAME);
security/apparmor/apparmorfs.c-2676- if (IS_ERR(dentry)) {
--
security/inode.c=109=static struct dentry *securityfs_create_dentry(const char *name, umode_t mode,
--
security/inode.c-139-
security/inode.c:140: dentry = simple_start_creating(parent, name);
security/inode.c-141- if (IS_ERR(dentry)) {
--
security/selinux/selinuxfs.c=1795=static struct dentry *sel_make_swapover_dir(struct super_block *sb,
--
security/selinux/selinuxfs.c-1804-
security/selinux/selinuxfs.c:1805: dentry = simple_start_creating(sb->s_root, ".swapover");
security/selinux/selinuxfs.c-1806- if (IS_ERR(dentry)) {
]
|
| 42/2 |
2026/08/03 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 47581
output: 35
thoughts: 20
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Diving into `simple_start_creating`**
Right, let's get into the nitty-gritty. The prompt points us toward `simple_start_creating` within `fs/libfs.c`. That function name alone tells me it's likely responsible for the initial steps of creating a new file or directory, a core operation within the file system layer. I need to understand its role in the larger context of a system call, probably `creat()` or `mkdir()`. I'll be looking for how it interacts with inodes, the disk blocks, and any locking mechanisms it uses. Knowing the exact file system, like EXT4 or XFS, would be nice to keep in mind, even though this is the "libfs" level. This is where the magic (or the problems) typically happen, so understanding the exact mechanisms is critical. I'm expecting to see things like allocation of an inode number, potential block allocation on the disk, setting of initial metadata, and possibly journal handling if this is a journaling file system. Let's see how they've handled potential race conditions, concurrent access, and error cases in the filesystem implementation.
|
| 43/2 |
2026/08/03 02:13 |
tool |
read-file |
0m
Args: map[File:fs/libfs.c FirstLine:2287 LineCount:40]
Results: map[Contents:2287: * simple_start_creating - prepare to create a given name
2288: * @parent: directory in which to prepare to create the name
2289: * @name: the name to be created
2290: *
2291: * Required lock is taken and a lookup in performed prior to creating an
2292: * object in a directory. No permission checking is performed.
2293: *
2294: * Returns: a negative dentry on which vfs_create() or similar may
2295: * be attempted, or an error.
2296: */
2297: struct dentry *simple_start_creating(struct dentry *parent, const char *name)
2298: {
2299: struct qstr qname = QSTR(name);
2300: int err;
2301:
2302: err = lookup_noperm_common(&qname, parent);
2303: if (err)
2304: return ERR_PTR(err);
2305: return start_dirop(parent, &qname, LOOKUP_CREATE | LOOKUP_EXCL);
2306: }
2307: EXPORT_SYMBOL(simple_start_creating);
2308:
2309: /* parent must have been held exclusive since simple_start_creating() */
2310: void simple_done_creating(struct dentry *child)
2311: {
2312: end_creating(child);
2313: }
2314: EXPORT_SYMBOL(simple_done_creating);
]
|
| 44/2 |
2026/08/03 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 48341
output: 35
thoughts: 43
Thoughts: Okay, here's the summary, tailored for an expert audience, in the first person, reflecting the described thought process:
**Diving into `start_dirop` and Parent Inode Safety**
Alright, hold on a second. I'm just thinking about `start_dirop` and how it handles locking the parent inode. My immediate concern is, does it robustly check for the parent inode's liveliness after it's been locked? Specifically, does it have a mechanism to detect a situation where the parent has become invalid or is in a dying state? That's a critical point for maintaining data integrity in concurrent file system operations.
Time to confirm my suspicions and verify its implementation. Let's head straight to the source code. I need to take a look at `start_dirop` within `fs/namei.c`. I have a feeling that might shed some light on this locking sequence and validate (or invalidate) my initial concerns. Let's see how it's done.
|
| 45/2 |
2026/08/03 02:13 |
tool |
read-file |
0m
Args: map[File:fs/namei.c FirstLine:3190 LineCount:40]
Results: map[Contents:3190: *
3191: * The caller must hold base->i_rwsem.
3192: */
3193: struct dentry *lookup_one(struct mnt_idmap *idmap, struct qstr *name,
3194: struct dentry *base)
3195: {
3196: struct dentry *dentry;
3197: int err;
3198:
3199: WARN_ON_ONCE(!inode_is_locked(base->d_inode));
3200:
3201: err = lookup_one_common(idmap, name, base);
3202: if (err)
3203: return ERR_PTR(err);
3204:
3205: dentry = lookup_dcache(name, base, 0);
3206: return dentry ? dentry : __lookup_slow(name, base, 0);
3207: }
3208: EXPORT_SYMBOL(lookup_one);
3209:
3210: /**
3211: * lookup_one_unlocked - lookup single pathname component
3212: * @idmap: idmap of the mount the lookup is performed from
3213: * @name: qstr olding pathname component to lookup
3214: * @base: base directory to lookup from
3215: *
3216: * This can be used for in-kernel filesystem clients such as file servers.
3217: *
3218: * Unlike lookup_one, it should be called without the parent
3219: * i_rwsem held, and will take the i_rwsem itself if necessary.
3220: *
3221: * Returns: - A dentry, possibly negative, or
3222: * - same errors as try_lookup_noperm() or
3223: * - ERR_PTR(-ENOENT) if parent has been removed, or
3224: * - ERR_PTR(-EACCES) if parent directory is not searchable.
3225: */
3226: struct dentry *lookup_one_unlocked(struct mnt_idmap *idmap, struct qstr *name,
3227: struct dentry *base)
3228: {
3229: int err;
]
|
| 46/2 |
2026/08/03 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 49275
output: 18
thoughts: 10
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Immediate Requirement Assessment**
Right, hold on a second. I'm clearly missing something here. My mental model is incomplete. The current approach I'm taking, the one I *thought* was going to work, is stymied. It's not leading to the anticipated outcome. What's the fundamental issue? Ah, of course. I've been so focused on the higher-level logic, the broader workflow, that I've skipped a crucial low-level detail. I *need* `start_dirop`. Without properly initializing the directory operation, anything downstream is going to fail or, worse, produce incorrect results. I was probably about to spin my wheels and troubleshoot some bizarre, seemingly unrelated problem if I had not realized this. It is a good thing I caught that. Okay, back to it.
|
| 47/2 |
2026/08/03 02:13 |
tool |
grepper |
0m
Args: map[Expression:start_dirop]
Results: map[Output:fs/internal.h=69=struct dentry *d_hash_and_lookup(struct dentry *, struct qstr *);
fs/internal.h:70:struct dentry *start_dirop(struct dentry *parent, struct qstr *name,
fs/internal.h-71- unsigned int lookup_flags);
--
fs/libfs.c=2297=struct dentry *simple_start_creating(struct dentry *parent, const char *name)
--
fs/libfs.c-2304- return ERR_PTR(err);
fs/libfs.c:2305: return start_dirop(parent, &qname, LOOKUP_CREATE | LOOKUP_EXCL);
fs/libfs.c-2306-}
--
fs/namei.c=2898=static int filename_parentat(int dfd, struct filename *name,
--
fs/namei.c-2904-
fs/namei.c:2905:static struct dentry *__start_dirop(struct dentry *parent, struct qstr *name,
fs/namei.c-2906- unsigned int lookup_flags,
--
fs/namei.c-2926-/**
fs/namei.c:2927: * start_dirop - begin a create or remove dirop, performing locking and lookup
fs/namei.c-2928- * @parent: the dentry of the parent in which the operation will occur
--
fs/namei.c-2938- */
fs/namei.c:2939:struct dentry *start_dirop(struct dentry *parent, struct qstr *name,
fs/namei.c-2940- unsigned int lookup_flags)
fs/namei.c-2941-{
fs/namei.c:2942: return __start_dirop(parent, name, lookup_flags, TASK_NORMAL);
fs/namei.c-2943-}
--
fs/namei.c-2946- * end_dirop - signal completion of a dirop
fs/namei.c:2947: * @de: the dentry which was returned by start_dirop or similar.
fs/namei.c-2948- *
--
fs/namei.c=2962=struct dentry *start_removing_path(const char *name, struct path *path)
--
fs/namei.c-2978- error = mnt_want_write(parent_path.mnt);
fs/namei.c:2979: d = start_dirop(parent_path.dentry, &last, 0);
fs/namei.c-2980- if (IS_ERR(d))
--
fs/namei.c=3399=struct dentry *start_creating(struct mnt_idmap *idmap, struct dentry *parent,
--
fs/namei.c-3405- return ERR_PTR(err);
fs/namei.c:3406: return start_dirop(parent, name, LOOKUP_CREATE);
fs/namei.c-3407-}
--
fs/namei.c=3426=struct dentry *start_removing(struct mnt_idmap *idmap, struct dentry *parent,
--
fs/namei.c-3432- return ERR_PTR(err);
fs/namei.c:3433: return start_dirop(parent, name, 0);
fs/namei.c-3434-}
--
fs/namei.c=3454=struct dentry *start_creating_killable(struct mnt_idmap *idmap,
--
fs/namei.c-3461- return ERR_PTR(err);
fs/namei.c:3462: return __start_dirop(parent, name, LOOKUP_CREATE, TASK_KILLABLE);
fs/namei.c-3463-}
--
fs/namei.c=3485=struct dentry *start_removing_killable(struct mnt_idmap *idmap,
--
fs/namei.c-3492- return ERR_PTR(err);
fs/namei.c:3493: return __start_dirop(parent, name, 0, TASK_KILLABLE);
fs/namei.c-3494-}
--
fs/namei.c=3509=struct dentry *start_creating_noperm(struct dentry *parent,
--
fs/namei.c-3515- return ERR_PTR(err);
fs/namei.c:3516: return start_dirop(parent, name, LOOKUP_CREATE);
fs/namei.c-3517-}
--
fs/namei.c=3534=struct dentry *start_removing_noperm(struct dentry *parent,
--
fs/namei.c-3540- return ERR_PTR(err);
fs/namei.c:3541: return start_dirop(parent, name, 0);
fs/namei.c-3542-}
--
fs/namei.c=4925=static struct dentry *filename_create(int dfd, struct filename *name,
--
fs/namei.c-4954- create_flags &= ~LOOKUP_CREATE;
fs/namei.c:4955: dentry = start_dirop(path->dentry, &last, reval_flag | create_flags);
fs/namei.c-4956- if (IS_ERR(dentry))
--
fs/namei.c=5399=int filename_rmdir(int dfd, struct filename *name)
--
fs/namei.c-5430-
fs/namei.c:5431: dentry = start_dirop(path.dentry, &last, lookup_flags);
fs/namei.c-5432- error = PTR_ERR(dentry);
--
fs/namei.c=5543=int filename_unlinkat(int dfd, struct filename *name)
--
fs/namei.c-5565-retry_deleg:
fs/namei.c:5566: dentry = start_dirop(path.dentry, &last, lookup_flags);
fs/namei.c-5567- error = PTR_ERR(dentry);
]
|
| 48/2 |
2026/08/03 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 51148
output: 35
thoughts: 20
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Diving into `__start_dirop` in `fs/namei.c`**
Alright, time to get my hands dirty and revisit that `__start_dirop` function. I'm specifically targeting `fs/namei.c`, as that's where the core name lookup and directory operation initialization resides in the VFS layer. I need to understand its role within the broader context of path resolution – how it sets the stage for the traversal process. I'm anticipating that it's going to be the entry point for handling different types of file system operations, and will likely take arguments that define the nature of the operation, the path to be traversed, and any associated flags, perhaps permission checks or mount point related data. I’m thinking there will be a call to something like `path_to_nameidata` and from there the guts of the function will likely call various file system specific functions, or use vfs helper functions to do the heavy lifting of looking up an inode from a path. I'll need to pay close attention to the error handling and potential race conditions within this function, particularly regarding concurrent accesses and the handling of symlinks. I'm keen to see how the nameidata structure is populated, as that's going to be central to how the subsequent steps unfold.
|
| 49/2 |
2026/08/03 02:13 |
tool |
read-file |
0m
Args: map[File:fs/namei.c FirstLine:2905 LineCount:40]
Results: map[Contents:2905: static struct dentry *__start_dirop(struct dentry *parent, struct qstr *name,
2906: unsigned int lookup_flags,
2907: unsigned int state)
2908: {
2909: struct dentry *dentry;
2910: struct inode *dir = d_inode(parent);
2911:
2912: if (state == TASK_KILLABLE) {
2913: int ret = down_write_killable_nested(&dir->i_rwsem,
2914: I_MUTEX_PARENT);
2915: if (ret)
2916: return ERR_PTR(ret);
2917: } else {
2918: inode_lock_nested(dir, I_MUTEX_PARENT);
2919: }
2920: dentry = lookup_one_qstr_excl(name, parent, lookup_flags);
2921: if (IS_ERR(dentry))
2922: inode_unlock(dir);
2923: return dentry;
2924: }
2925:
2926: /**
2927: * start_dirop - begin a create or remove dirop, performing locking and lookup
2928: * @parent: the dentry of the parent in which the operation will occur
2929: * @name: a qstr holding the name within that parent
2930: * @lookup_flags: intent and other lookup flags.
2931: *
2932: * The lookup is performed and necessary locks are taken so that, on success,
2933: * the returned dentry can be operated on safely.
2934: * The qstr must already have the hash value calculated.
2935: *
2936: * Returns: a locked dentry, or an error.
2937: *
2938: */
2939: struct dentry *start_dirop(struct dentry *parent, struct qstr *name,
2940: unsigned int lookup_flags)
2941: {
2942: return __start_dirop(parent, name, lookup_flags, TASK_NORMAL);
2943: }
2944:
]
|
| 50/2 |
2026/08/03 02:13 |
llm |
expert |
0m
Model:
Tokens:
input: 52113
output: 35
thoughts: 57
Thoughts: **Analyzing the Directory Operation**
Okay, hold on a second. I need to clarify something about how this directory operation works. I was under the impression that `__start_dirop` handled the dead directory check. But, no, it *doesn't*. It seems like it's just locking the inode and then moving directly into `lookup_one_qstr_excl`. So the critical check must be performed by the function which is called. Let's make sure I understand what is going on. I'll need to double check that specific function.
|
| 51/2 |
2026/08/03 02:14 |
tool |
read-file |
0m
Args: map[File:fs/namei.c FirstLine:3150 LineCount:40]
Results: map[Contents:3150: if (err)
3151: return ERR_PTR(err);
3152:
3153: return d_lookup(base, name);
3154: }
3155: EXPORT_SYMBOL(try_lookup_noperm);
3156:
3157: /**
3158: * lookup_noperm - filesystem helper to lookup single pathname component
3159: * @name: qstr storing pathname component to lookup
3160: * @base: base directory to lookup from
3161: *
3162: * Note that this routine is purely a helper for filesystem usage and should
3163: * not be called by generic code. It does no permission checking.
3164: *
3165: * The caller must hold base->i_rwsem.
3166: */
3167: struct dentry *lookup_noperm(struct qstr *name, struct dentry *base)
3168: {
3169: struct dentry *dentry;
3170: int err;
3171:
3172: WARN_ON_ONCE(!inode_is_locked(base->d_inode));
3173:
3174: err = lookup_noperm_common(name, base);
3175: if (err)
3176: return ERR_PTR(err);
3177:
3178: dentry = lookup_dcache(name, base, 0);
3179: return dentry ? dentry : __lookup_slow(name, base, 0);
3180: }
3181: EXPORT_SYMBOL(lookup_noperm);
3182:
3183: /**
3184: * lookup_one - lookup single pathname component
3185: * @idmap: idmap of the mount the lookup is performed from
3186: * @name: qstr holding pathname component to lookup
3187: * @base: base directory to lookup from
3188: *
3189: * This can be used for in-kernel filesystem clients such as file servers.
]
|
| 52/2 |
2026/08/03 02:14 |
llm |
expert |
0m
Model:
Tokens:
input: 52913
output: 23
thoughts: 15
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to an expert:
**Internal Workflow Checkpoint**
Right, so I'm in the middle of this process... hold on. I've hit a point where I need a specific function. I can feel I'm missing something, that specific bit of functionality... It's `lookup_one_qstr_excl`. I need that routine, that exact tool in my arsenal to progress. Without it, I'm at a standstill. I need to make sure I have that immediately available to me. Where do I get it? How do I ensure I can integrate it, access it, *use* it seamlessly? Gotta focus now, no time to waste!
|
| 53/2 |
2026/08/03 02:14 |
tool |
grepper |
0m
Args: map[Expression:lookup_one_qstr_excl]
Results: map[Output:Documentation/filesystems/porting.rst=1164=ERR_PTR(-ENOENT) is returned.
--
Documentation/filesystems/porting.rst-1169-
Documentation/filesystems/porting.rst:1170:lookup_one_qstr_excl() is changed to return errors in more cases, so
Documentation/filesystems/porting.rst-1171-these conditions don't require explicit checks:
--
Documentation/filesystems/porting.rst=1362=lifetime, consider using inode_set_cached_link() instead.
--
Documentation/filesystems/porting.rst-1367-
Documentation/filesystems/porting.rst:1368:lookup_one_qstr_excl() is no longer exported - use start_creating() or
Documentation/filesystems/porting.rst-1369-similar.
--
fs/namei.c=1764=static struct dentry *lookup_dcache(const struct qstr *name,
--
fs/namei.c-1789- */
fs/namei.c:1790:static struct dentry *lookup_one_qstr_excl(const struct qstr *name,
fs/namei.c-1791- struct dentry *base, unsigned int flags)
--
fs/namei.c=2905=static struct dentry *__start_dirop(struct dentry *parent, struct qstr *name,
--
fs/namei.c-2919- }
fs/namei.c:2920: dentry = lookup_one_qstr_excl(name, parent, lookup_flags);
fs/namei.c-2921- if (IS_ERR(dentry))
--
fs/namei.c=3865=__start_renaming(struct renamedata *rd, int lookup_flags,
--
fs/namei.c-3881-
fs/namei.c:3882: d1 = lookup_one_qstr_excl(old_last, rd->old_parent,
fs/namei.c-3883- lookup_flags);
--
fs/namei.c-3887-
fs/namei.c:3888: d2 = lookup_one_qstr_excl(new_last, rd->new_parent,
fs/namei.c-3889- lookup_flags | target_flags);
--
fs/namei.c=3960=__start_renaming_dentry(struct renamedata *rd, int lookup_flags,
--
fs/namei.c-3983-
fs/namei.c:3984: d2 = lookup_one_qstr_excl(new_last, rd->new_parent,
fs/namei.c-3985- lookup_flags | target_flags);
]
|
| 54/2 |
2026/08/03 02:14 |
llm |
expert |
0m
Model:
Tokens:
input: 53843
output: 35
thoughts: 24
Thoughts: Here's my take on the thought process, as if it were my own:
**Diving into the `lookup_one_qstr_excl` Function**
Okay, time to get my hands dirty. Let's dig into the `fs/namei.c` file and zero in on the `lookup_one_qstr_excl` function. I need to understand how it's handling name lookups, specifically with that "excl" tag at the end. I'm guessing "excl" probably relates to exclusive access or something similar, maybe preventing certain operations during the lookup. I'll need to see the context to be certain. Let's see how the implementation tackles the details.
|
| 55/2 |
2026/08/03 02:14 |
tool |
read-file |
0m
Args: map[File:fs/namei.c FirstLine:1790 LineCount:40]
Results: map[Contents:1790: static struct dentry *lookup_one_qstr_excl(const struct qstr *name,
1791: struct dentry *base, unsigned int flags)
1792: {
1793: struct dentry *dentry;
1794: struct dentry *old;
1795: struct inode *dir;
1796:
1797: dentry = lookup_dcache(name, base, flags);
1798: if (dentry)
1799: goto found;
1800:
1801: /* Don't create child dentry for a dead directory. */
1802: dir = base->d_inode;
1803: if (unlikely(IS_DEADDIR(dir)))
1804: return ERR_PTR(-ENOENT);
1805:
1806: dentry = d_alloc(base, name);
1807: if (unlikely(!dentry))
1808: return ERR_PTR(-ENOMEM);
1809:
1810: old = dir->i_op->lookup(dir, dentry, flags);
1811: if (unlikely(old)) {
1812: dput(dentry);
1813: dentry = old;
1814: }
1815: found:
1816: if (IS_ERR(dentry))
1817: return dentry;
1818: if (d_is_negative(dentry) && !(flags & LOOKUP_CREATE)) {
1819: dput(dentry);
1820: return ERR_PTR(-ENOENT);
1821: }
1822: if (d_is_positive(dentry) && (flags & LOOKUP_EXCL)) {
1823: dput(dentry);
1824: return ERR_PTR(-EEXIST);
1825: }
1826: return dentry;
1827: }
1828:
1829: /**
]
|
| 56/2 |
2026/08/03 02:14 |
llm |
expert |
0m
Model:
Tokens:
input: 54623
output: 43
thoughts: 3078
Thoughts: **Debugging a Double `debugfs_remove` Call**
Okay, so I'm chasing a potential double-free or double-removal issue in the debugfs code related to i2c devices. It looks like `lookup_one_qstr_excl`'s check for `IS_DEADDIR` can lead to `-ENOENT` and subsequent errors if a directory has been removed, but I've ruled out that specific scenario. The `debugfs_remove` function itself *shouldn't* set `IS_DEADDIR` directly. But if `debugfs_remove` is called on a directory, then the children are also removed recursively. And if one is then recreated, it sets `S_DEAD` on the parent.
The core of the problem seems to be with `simple_recursive_removal`, and particularly `drop_nlink`. If I call `debugfs_remove` *twice* on the *same* debugfs directory, `drop_nlink` on the parent's inode is called *twice*! This can lead to underflow on the parent's `i_nlink`, causing a warning.
Looking at `i2c_device_probe`, the intention is to create a debugfs directory, and then remove it if something goes wrong. The critical section is where it calls `debugfs_remove_recursive`. I initially suspected a flaw in the probe routine, where probe might be called twice and subsequently call debugfs_remove twice, but that would create a *new* dentry on the *second* call.
Now, I'm analyzing the case where the driver's `remove` method is called. I need to make sure `debugfs_remove` is called only once in the i2c device removal path. If `i2c_device_remove` removes the debugfs directory, and then a subsequent `i2c_device_probe` recreates it and then something goes wrong, it would cause `debugfs_remove_recursive` to be called again! But it also seems unlikely that the debugfs directory can be recreated by a probe if it has already been removed.
I'm checking if `debugfs_create_dir` reuses an existing dentry or returns an error. It appears `debugfs_create_dir` either creates a new directory or gets an `ERR_PTR` if it fails to create a new directory. I can rule out the case that `debugfs_create_dir` returns the old directory dentry. It *creates* a *new* directory, then debugfs_remove_recursive is called, so there is no double free.
My suspicion is that `i2c_device_remove` isn't setting `client->debugfs = NULL` after calling `debugfs_remove_recursive(client->debugfs)`. That means if a probe fails subsequently, `debugfs_remove_recursive` is called on an *already-removed* dentry.
I'm digging deeper into `lookup_one_qstr_excl` and how it handles negative dentries, and the interaction with `LOOKUP_CREATE` and `LOOKUP_EXCL`. If a negative dentry exists, I need to make sure that it's correctly instantiated into a positive dentry. So the key is to ensure the probe function *always* overwrites `client->debugfs` with a valid dentry, or `NULL` if it fails, to avoid this double-removal scenario. It is a bug!
|
| 57/2 |
2026/08/03 02:14 |
tool |
read-file |
0m
Args: map[File:drivers/i2c/i2c-core-base.c FirstLine:560 LineCount:50]
Results: map[Contents: 560: else if (client->irq > 0)
561: status = dev_pm_set_wake_irq(dev, client->irq);
562: else
563: status = 0;
564:
565: if (status)
566: dev_warn(&client->dev, "failed to set up wakeup irq\n");
567: }
568:
569: dev_dbg(dev, "probe\n");
570:
571: status = of_clk_set_defaults(to_of_node(fwnode), false);
572: if (status < 0)
573: goto err_clear_wakeup_irq;
574:
575: do_power_on = !i2c_acpi_waive_d0_probe(dev);
576: status = dev_pm_domain_attach(&client->dev, PD_FLAG_DETACH_POWER_OFF |
577: (do_power_on ? PD_FLAG_ATTACH_POWER_ON : 0));
578: if (status)
579: goto err_clear_wakeup_irq;
580:
581: client->devres_group_id = devres_open_group(&client->dev, NULL,
582: GFP_KERNEL);
583: if (!client->devres_group_id) {
584: status = -ENOMEM;
585: goto err_clear_wakeup_irq;
586: }
587:
588: client->debugfs = debugfs_create_dir(dev_name(&client->dev),
589: client->adapter->debugfs);
590:
591: if (driver->probe)
592: status = driver->probe(client);
593: else
594: status = -EINVAL;
595:
596: /*
597: * Note that we are not closing the devres group opened above so
598: * even resources that were attached to the device after probe is
599: * run are released when i2c_device_remove() is executed. This is
600: * needed as some drivers would allocate additional resources,
601: * for example when updating firmware.
602: */
603:
604: if (status)
605: goto err_release_driver_resources;
606:
607: return 0;
608:
609: err_release_driver_resources:
]
|
| 58/2 |
2026/08/03 02:14 |
llm |
expert |
0m
Model:
Tokens:
input: 59244
output: 36
thoughts: 2217
Thoughts: **Debugging a Double `debugfs_remove_recursive` Call**
Okay, let's break this down. My initial thought was that a double free could occur if `debugfs_remove_recursive` was called multiple times on the same `client->debugfs` dentry. The code has several error paths in `i2c_device_probe`. I was worried about scenarios where early failures, like in `of_clk_set_defaults`, `dev_pm_domain_attach`, or `devres_open_group`, might lead to a dangling pointer. However, after tracing the error paths, I've confirmed that these early failures correctly clean up, ensuring that `debugfs_remove_recursive` isn't called multiple times on a single dentry.
The critical path is when `driver->probe` fails. In this scenario, `err_release_driver_resources` is hit and `debugfs_remove_recursive(client->debugfs)` *is* called. The subsequent `i2c_device_probe` could run again. I initially worried that subsequent calls to `i2c_device_probe` might try to remove the same dentry again, but if `debugfs_create_dir` succeeds, it overwrites `client->debugfs` with a *new* dentry, avoiding a double removal.
However, if `debugfs_create_dir` *fails*, then `client->debugfs` is set to an error pointer. In this path, `debugfs_remove_recursive` *won't* operate.
Now, I considered `i2c_device_remove` as a potential double-remove source, but that is called *after* probe, not as part of the error handling, and relies on device binding. I confirmed that these calls are not concurrent.
Then, I looked at the callstack that led to a warning, specifically the `inode->i_nlink == 0` check in `drop_nlink`. This is when the parent debugfs directory, `client->adapter->debugfs`, is already unlinked when the child tries to remove itself. If `i2c_del_adapter` is called, it does `debugfs_remove_recursive(adap->debugfs)`. That should set the `S_DEAD` flag on the directory. When I then checked `debugfs_create_dir`, I saw that `lookup_one_qstr_excl` checks the `IS_DEADDIR` flag of the parent, which *should* prevent creating a new directory entry. Therefore, if the adapter is delinked, it cannot be a cause of this issue.
So the main problem is not a double *remove*. The problem, in this case, is that `debugfs_remove_recursive` is called on an inode whose parent has been removed (or whose link count is zero for some other reason).
This is a subtle issue and it will take some time to track down.
|
| 59/2 |
2026/08/03 02:15 |
tool |
read-file |
0m
Args: map[File:fs/debugfs/inode.c FirstLine:570 LineCount:40]
Results: map[Contents: 570: struct dentry *debugfs_create_dir(const char *name, struct dentry *parent)
571: {
572: struct dentry *dentry = debugfs_start_creating(name, parent);
573: struct inode *inode;
574:
575: if (IS_ERR(dentry))
576: return dentry;
577:
578: inode = debugfs_get_inode(dentry->d_sb);
579: if (unlikely(!inode)) {
580: pr_err("out of free dentries, can not create directory '%s'\n",
581: name);
582: return debugfs_failed_creating(dentry);
583: }
584:
585: inode->i_mode = S_IFDIR | S_IRWXU | S_IRUGO | S_IXUGO;
586: inode->i_op = &debugfs_dir_inode_operations;
587: inode->i_fop = &simple_dir_operations;
588:
589: /* directory inodes start off with i_nlink == 2 (for "." entry) */
590: inc_nlink(inode);
591: d_make_persistent(dentry, inode);
592: inc_nlink(d_inode(dentry->d_parent));
593: fsnotify_mkdir(d_inode(dentry->d_parent), dentry);
594: return debugfs_end_creating(dentry);
595: }
596: EXPORT_SYMBOL_GPL(debugfs_create_dir);
597:
598: /**
599: * debugfs_create_automount - create automount point in the debugfs filesystem
600: * @name: a pointer to a string containing the name of the file to create.
601: * @parent: a pointer to the parent dentry for this file. This should be a
602: * directory dentry if set. If this parameter is NULL, then the
603: * file will be created in the root of the debugfs filesystem.
604: * @f: function to be called when pathname resolution steps on that one.
605: * @data: opaque argument to pass to f().
606: *
607: * @f should return what ->d_automount() would.
608: */
609: struct dentry *debugfs_create_automount(const char *name,
]
|
| 60/2 |
2026/08/03 02:15 |
llm |
expert |
0m
Model:
Tokens:
input: 62844
output: 34
thoughts: 1953
Thoughts: **Double `debugfs_remove` Vulnerability**
Okay, so I've been digging into the `debugfs` code, and something's bugging me. It looks like `debugfs_create_dir` correctly increments the parent directory's `i_nlink` counter. And `debugfs_remove` seems to balance this by decrementing the parent's `i_nlink`. So far, so good.
But what if `debugfs_remove` gets called *twice* on the *same* directory entry? Then the `i_nlink` on the parent will get underflowed, which is bad news.
My initial thought was that a driver might try to remove the debug directory twice, but that doesn't seem to be the case. I examined `i2c_device_probe` and its error handling. The `err_release_driver_resources` path *could* lead to a double removal if the driver itself unregisters the device *and* the probe fails. But it appears the kernel code *doesn't* do this, and the driver is not responsible for the extra clean-up.
Concurrent `i2c_device_probe` calls are another possibility, but the device registration and probing code has locking to prevent that race condition. So concurrent access is not the cause.
Also, it can't be shared names or the adapter's debugfs, as the adapter and device names are different, and the kernel would reject kobject name collisions anyway.
So, how can we have a double removal? What if `debugfs_create_dir` returns an already-existing dentry, and it returns a `-EEXIST` error? I confirmed that `debugfs_create_dir` does handle that scenario correctly and returns an error and never creates the directory.
However, the real culprit is when `i2c_del_adapter` is called concurrently, which calls `debugfs_remove_recursive` on the adapter's debugfs. This recursive removal will delete all of the children of the adapter's debugfs, including the device debugfs. Then, if `i2c_device_probe` fails, it calls `debugfs_remove_recursive` on a dentry that has already been recursively deleted. This means that the child dentry will be deleted once, by the adapter's debugfs, and then deleted a second time by the `i2c_device_probe`.
This is the sequence of events:
1. `i2c_device_probe` creates `client->debugfs`.
2. `i2c_del_adapter` is called concurrently.
3. `i2c_del_adapter` calls `debugfs_remove_recursive(adap->debugfs)`.
4. `debugfs_remove` on `adap->debugfs` calls `simple_recursive_removal`.
5. `simple_recursive_removal` finds `client->debugfs` as a child.
6. It calls `clear_nlink` on `client->debugfs`.
7. It calls `drop_nlink` on `adap->debugfs` (the parent).
8. It calls `d_make_discardable(client->debugfs)`.
9. `i2c_device_probe` continues, and `driver->probe` fails.
10. `i2c_device_probe` calls `debugfs_remove_recursive(client->debugfs)`.
11. `debugfs_remove` on `client->debugfs` calls `simple_recursive_removal`.
12. `simple_recursive_removal` sees `client->debugfs` is a directory.
13. It sets `S_DEAD` on `client->debugfs`.
14. It finds no children.
15. It calls `clear_nlink` on `client->debugfs`.
16. It ascends to the parent (`adap->debugfs`).
17. It checks `if (victim == dentry)` (which is true, `victim` is `client->debugfs`).
18. It calls `drop_nlink(inode)` on the parent (`adap->debugfs`)!
19. The parent (`adap->debugfs`) already had its nlink dropped in step 7.
20. If the parent's nlink reaches 0, it warns!
This second `drop_nlink` on the parent is the problem. It could lead to the `i_nlink` underflow and potentially a file system corruption if the warning is ignored. The code does NOT protect against this, and it needs a patch to fix it.
|
| 61/2 |
2026/08/03 02:15 |
tool |
read-file |
0m
Args: map[File:fs/libfs.c FirstLine:597 LineCount:40]
Results: map[Contents: 597: static void __simple_recursive_removal(struct dentry *dentry,
598: void (*callback)(struct dentry *),
599: bool locked)
600: {
601: struct dentry *this = dget(dentry);
602: while (true) {
603: struct dentry *victim = NULL, *child;
604: struct inode *inode = this->d_inode;
605:
606: inode_lock_nested(inode, I_MUTEX_CHILD);
607: if (d_is_dir(this))
608: inode->i_flags |= S_DEAD;
609: while ((child = find_next_child(this, victim)) == NULL) {
610: // kill and ascend
611: // update metadata while it's still locked
612: inode_set_ctime_current(inode);
613: clear_nlink(inode);
614: inode_unlock(inode);
615: victim = this;
616: this = this->d_parent;
617: inode = this->d_inode;
618: if (!locked || victim != dentry)
619: inode_lock_nested(inode, I_MUTEX_CHILD);
620: if (simple_positive(victim)) {
621: d_invalidate(victim); // avoid lost mounts
622: if (callback)
623: callback(victim);
624: fsnotify_delete(inode, d_inode(victim), victim);
625: d_make_discardable(victim);
626: }
627: if (victim == dentry) {
628: inode_set_mtime_to_ts(inode,
629: inode_set_ctime_current(inode));
630: if (d_is_dir(dentry))
631: drop_nlink(inode);
632: if (!locked)
633: inode_unlock(inode);
634: dput(dentry);
635: return;
636: }
]
|
| 62/2 |
2026/08/03 02:15 |
llm |
expert |
0m
Model:
Tokens:
input: 66461
output: 44
thoughts: 1028
Thoughts: **Analyzing a Potential Double-Free in I2C Device Removal**
Okay, here's what I've got so far. It seems like we might have a subtle double-free vulnerability in the I2C device removal process, specifically with how we're handling `debugfs` entries.
The core issue appears to stem from the interaction between `__simple_recursive_removal`, which looks like the function that actually does the recursive removal of debugfs entries, and the caller function, like `debugfs_remove`. The key observation is that `__simple_recursive_removal` might unconditionally call `drop_nlink(inode)` if the directory entry is being removed, regardless of whether it's already been cleaned up, which hints at a potential double decrement of the inode's link count.
My thought process is this:
1. If a `dentry` has already been removed, `simple_positive(dentry)` returns false. But the comparison `if (victim == dentry)` might still be true. Therefore, a subsequent call to `drop_nlink(inode)` would occur even after the dentry has been removed.
2. The documentation for `debugfs_remove` says it is the caller's responsibility to manage the cleanup of the files.
3. Let's consider a scenario where `debugfs_remove_recursive` is called on a directory, recursively removing all its children. If a caller then retains a pointer to one of those children and *also* calls `debugfs_remove` on the same child after the parent's removal, this is likely a bug.
4. Now, the specific problem I'm looking at involves `i2c_device_probe`, which calls `debugfs_remove_recursive(client->debugfs)`. A race condition exists if `i2c_del_adapter` is called concurrently because `i2c_del_adapter` removes the parent, and if `i2c_del_adapter` also calls `debugfs_remove_recursive` on the adapter's debugfs, which could conflict with the child.
5. The `new_device` sysfs attribute on the adapter allows for concurrent calls to `i2c_new_client_device` which then calls `device_register`. At the same time, the adapter might be unregistered. If the adapter is unregistered concurrently, `i2c_del_adapter` is called.
6. The potential double call to `debugfs_remove_recursive` could happen through two paths. First, if `i2c_device_probe` fails and subsequently `debugfs_remove_recursive` is called on the client's debugfs entry, but then another `delete_device` event occurs, we must ensure that the same debugfs entry is not removed more than once.
7. The `delete_device` event flow is this: the user writes to `delete_device` -> calls `i2c_unregister_device` -> calls `device_unregister` -> calls `device_del` -> calls `bus_remove_device`. However, `i2c_device_remove` isn't called, which is relevant because that's the driver's remove function, called only when the device is unbound from the driver. This means that a failed probe won't cause a double `debugfs_remove_recursive` because only the probe's debugfs entry will be removed, and that's the only one that can exist.
8. If the user binds the driver again (after a failed probe), `i2c_device_probe` is called. It creates a new `client->debugfs`. If it fails again, it removes the new debugfs entry, which is fine, no double removal.
9. Now, if `i2c_device_probe` *succeeds* and the user then unbinds the driver (via the `unbind` sysfs attribute), `i2c_device_remove` is called, which then calls `debugfs_remove_recursive` on the client's debugfs entry.
10. If an unbind is successful, the subsequent `unbind` will do nothing, because unbinding the same device twice is not permitted.
11. If the user calls `delete_device` after `unbind`, `i2c_unregister_device` is called, but that does not call `debugfs_remove_recursive`.
12. If the user unloads the I2C adapter module, `i2c_del_adapter` is called, which calls `device_unregister` on all clients. I need to examine `i2c_del_adapter` to be sure.
So, the race condition I'm concerned about is most likely from the concurrent calls
|
| 63/2 |
2026/08/03 02:15 |
tool |
read-file |
0m
Args: map[File:drivers/i2c/i2c-core-base.c FirstLine:1550 LineCount:40]
Results: map[Contents:1550: /* Can't register until after driver model init */
1551: if (WARN_ON(!is_registered))
1552: return -EAGAIN;
1553:
1554: /* Sanity checks */
1555: if (WARN(!adap->name[0], "i2c adapter has no name"))
1556: return -EINVAL;
1557:
1558: if (!adap->algo) {
1559: pr_err("adapter '%s': no algo supplied!\n", adap->name);
1560: return -EINVAL;
1561: }
1562:
1563: if (!adap->lock_ops)
1564: adap->lock_ops = &i2c_adapter_lock_ops;
1565:
1566: adap->locked_flags = 0;
1567: rt_mutex_init(&adap->bus_lock);
1568: rt_mutex_init(&adap->mux_lock);
1569: mutex_init(&adap->userspace_clients_lock);
1570: INIT_LIST_HEAD(&adap->userspace_clients);
1571:
1572: /* Set default timeout to 1 second if not already set */
1573: if (adap->timeout == 0)
1574: adap->timeout = HZ;
1575:
1576: /* register soft irqs for Host Notify */
1577: res = i2c_setup_host_notify_irq_domain(adap);
1578: if (res) {
1579: pr_err("adapter '%s': can't create Host Notify IRQs (%d)\n",
1580: adap->name, res);
1581: return res;
1582: }
1583:
1584: res = i2c_allocate_adapter_id(adap);
1585: if (res)
1586: goto err_remove_irq_domain;
1587:
1588: res = dev_set_name(&adap->dev, "i2c-%d", adap->nr);
1589: if (res)
]
|
| 64/2 |
2026/08/03 02:15 |
llm |
expert |
0m
Model:
Tokens:
input: 69171
output: 22
thoughts: 13
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing `i2c_del_adapter`**
Okay, let's dive into `i2c_del_adapter`. My initial thought is to refresh my memory on its exact purpose and how it fits within the I2C subsystem's lifecycle. I immediately think about what it's supposed to *do*: de-register an I2C adapter, presumably freeing associated resources and cleaning up any connections. I need to keep in mind the potential implications of this operation. What does the kernel expect to be done *before* this function is called? Are there any ordering dependencies I should be aware of, perhaps related to clients or device drivers using this adapter?
I’m also thinking about potential race conditions. What if a client is actively using the adapter when `i2c_del_adapter` is called? How is this handled – are there locks, reference counts, or other synchronization primitives in play? I'll probably want to double-check those aspects, as a failure here could lead to crashes or corruption.
I’m immediately considering what resources are managed: Is it only the `i2c_adapter` itself, or is it also responsible for any attached devices, buses, or their related device nodes? Are there any hooks or callbacks that drivers register with the adapter, which need to be properly unregistered here to prevent dangling pointers?
Specifically, I want to confirm the following:
* How does this routine affect users of the adapter: i2c clients and drivers?
* What locking and concurrency control is applied?
* What does the error checking look like? Are any errors handled safely?
* How is the adapter cleaned up, and what is left for the caller to do if this function returns successfully?
This will be important, because a faulty implementation could lead to serious issues, so a thorough analysis here is required. Let's see how things are set up.
|
| 65/2 |
2026/08/03 02:15 |
tool |
grepper |
0m
Args: map[Expression:void i2c_del_adapter]
Results: map[Output:drivers/gpu/drm/tests/drm_connector_test.c=67=static const struct i2c_algorithm dummy_ddc_algorithm = {
--
drivers/gpu/drm/tests/drm_connector_test.c-71-
drivers/gpu/drm/tests/drm_connector_test.c:72:static void i2c_del_adapter_wrapper(void *ptr)
drivers/gpu/drm/tests/drm_connector_test.c-73-{
--
drivers/i2c/i2c-core-base.c=1764=static void i2c_deregister_clients(struct i2c_adapter *adap)
--
drivers/i2c/i2c-core-base.c-1802- */
drivers/i2c/i2c-core-base.c:1803:void i2c_del_adapter(struct i2c_adapter *adap)
drivers/i2c/i2c-core-base.c-1804-{
--
include/linux/i2c.h=889=int devm_i2c_add_adapter(struct device *dev, struct i2c_adapter *adapter);
include/linux/i2c.h:890:void i2c_del_adapter(struct i2c_adapter *adap);
include/linux/i2c.h-891-int i2c_add_numbered_adapter(struct i2c_adapter *adap);
]
|
| 66/2 |
2026/08/03 02:15 |
llm |
expert |
0m
Model:
Tokens:
input: 69990
output: 44
thoughts: 31
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Diving into the I2C Adapter Removal Function**
Alright, so the task at hand is to take a look at the `i2c_del_adapter` function. I'm going to find it directly within the `drivers/i2c/i2c-core-base.c` file. It's a key function, of course, because it's responsible for properly cleaning up and removing an I2C adapter from the system. I know that if I understand this function and how it works, I will also have a better understanding of the I2C core functionality. Let's see what we find. I'm expecting this to be fairly straightforward, but probably detailed, because removing an adapter involves managing a lot of potentially shared resources and interactions.
|
| 67/2 |
2026/08/03 02:16 |
tool |
read-file |
0m
Args: map[File:drivers/i2c/i2c-core-base.c FirstLine:1803 LineCount:40]
Results: map[Contents:1803: void i2c_del_adapter(struct i2c_adapter *adap)
1804: {
1805: struct i2c_adapter *found;
1806:
1807: /* First make sure that this adapter was ever added */
1808: mutex_lock(&core_lock);
1809: found = idr_find(&i2c_adapter_idr, adap->nr);
1810: if (found == adap)
1811: idr_replace(&i2c_adapter_idr, NULL, adap->nr);
1812: mutex_unlock(&core_lock);
1813: if (found != adap) {
1814: pr_debug("attempting to delete unregistered adapter [%s]\n", adap->name);
1815: return;
1816: }
1817:
1818: i2c_acpi_remove_space_handler(adap);
1819:
1820: i2c_deregister_clients(adap);
1821:
1822: /* device name is gone after device_unregister */
1823: dev_dbg(&adap->dev, "adapter [%s] unregistered\n", adap->name);
1824:
1825: pm_runtime_disable(&adap->dev);
1826:
1827: i2c_host_notify_irq_teardown(adap);
1828:
1829: debugfs_remove_recursive(adap->debugfs);
1830:
1831: /* wait until all references to the device are gone
1832: *
1833: * FIXME: This is old code and should ideally be replaced by an
1834: * alternative which results in decoupling the lifetime of the struct
1835: * device from the i2c_adapter, like spi or netdev do. Any solution
1836: * should be thoroughly tested with DEBUG_KOBJECT_RELEASE enabled!
1837: */
1838: init_completion(&adap->dev_released);
1839: device_unregister(&adap->dev);
1840: wait_for_completion(&adap->dev_released);
1841:
1842: /* free bus id */
]
|
| 68/2 |
2026/08/03 02:16 |
llm |
expert |
0m
Model:
Tokens:
input: 70872
output: 17
thoughts: 2378
Thoughts: **Analyzing a Potential Race Condition in I2C Debugfs Removal**
Okay, let's break this down. My initial thought process was: `i2c_del_adapter` calls `i2c_deregister_clients`, which unregisters clients, and ultimately removes the debugfs entries in a clean, ordered manner – children before parents. Perfect! But, what if there's no driver bound to a client? In that case, `i2c_device_remove` isn't called, so there is no debugfs removal. And since debugfs is only created during `i2c_device_probe`, which only happens with a driver match, the debugfs is NULL in this case, so it's all good. Similarly, if `i2c_device_probe` fails, the debugfs is removed there, so no problem.
The real trouble is if `i2c_new_client_device` – triggered by `new_device_store` (a sysfs attribute) – runs concurrently with `i2c_del_adapter`. Sysfs attributes can run concurrently, so this is possible. If this happens: `new_device_store` calls `i2c_new_client_device`, which registers a new device, and then `i2c_del_adapter` is called, which calls `debugfs_remove_recursive(adap->debugfs)`.
If the removal happens *before* `i2c_device_probe` creates the debugfs directory, that call will get an `ENOENT` error, which is handled gracefully.
The problem lies if `i2c_device_probe` *succeeds* in creating the debugfs directory *before* `i2c_del_adapter` removes it. In this case, `i2c_del_adapter` recursively removes `adap->debugfs`, *including* the client's debugfs entry, which works. Then, `driver->probe` fails, and `i2c_device_probe` *also* calls `debugfs_remove_recursive(client->debugfs)`. This is where the double remove occurs!
Specifically, the second `debugfs_remove` call will call `drop_nlink` on the parent (`adap->debugfs`) *again*, but it's already at 0. That double `drop_nlink` is what triggers the warning!
So, the race is:
* **Thread 1**: `new_device_store` -> `i2c_new_client_device` -> `device_register` -> `i2c_device_probe` which *creates* `client->debugfs`.
* **Thread 2**: `i2c_del_adapter` -> `debugfs_remove_recursive(adap->debugfs)` which *removes* `client->debugfs`.
Then, if the driver probe fails, `i2c_device_probe` calls `debugfs_remove_recursive` a second time on the already removed `client->debugfs`, causing the double `drop_nlink` on the parent, which triggers the warning.
Is this race possible? Absolutely. Because `new_device_store` is a sysfs attribute, it can be called concurrently. Even if `i2c_del_adapter` removes the sysfs entry, if `new_device_store` is already running it holds a reference, and the function continues to execute. Critically, `debugfs_remove_recursive(adap->debugfs)` is called *before* the device is fully unregistered (and its sysfs attributes removed), so the race is possible!
I need to confirm if there is any other way to trigger this warning without a race. What if something in the driver probe itself removes a directory it should not? No, I don't think any driver does that.
I looked at the call trace from the warning report and verified that it is from `i2c_device_probe`. I need to ensure this is what is happening.
|
| 69/2 |
2026/08/03 02:16 |
tool |
grepper |
0m
Args: map[Expression:failure reading functionality]
Results: map[Output:drivers/i2c/busses/i2c-tiny-usb.c=121=static u32 usb_func(struct i2c_adapter *adapter)
--
drivers/i2c/busses/i2c-tiny-usb.c-130- sizeof(*pfunc)) != sizeof(*pfunc)) {
drivers/i2c/busses/i2c-tiny-usb.c:131: dev_err(&adapter->dev, "failure reading functionality\n");
drivers/i2c/busses/i2c-tiny-usb.c-132- ret = 0;
]
|
| 70/2 |
2026/08/03 02:16 |
llm |
expert |
0m
Model:
Tokens:
input: 74293
output: 21
thoughts: 2784
Thoughts: **Analysis of the i2c-tiny-usb Double Debugfs Removal Vulnerability**
Okay, so I'm digging into this `i2c-tiny-usb.c` issue and I've got a pretty clear picture. It all boils down to a race condition. The core of the problem seems to be the `usb_func` function failing, resulting in a `debugfs` directory being removed twice.
The root of this lies in how the `i2c-tiny-usb` adapter interacts with its debugfs entries during device probe and adapter removal. If `usb_func` fails, the probe function bails, and cleans up the debugfs entry. However, if the USB device is disconnected concurrently (or, as with syzkaller, the disconnect is simulated at the same time), `i2c_del_adapter` is triggered which also removes the debugfs, leading to a double free condition. This double removal is exactly what the "failure reading functionality" message indicates: The USB device is disconnecting while `i2c_device_probe` is trying to access it via `usb_func`.
The vulnerability stems from the sequence: `i2c_device_probe` creating a `client->debugfs`, the USB device disconnecting (or the disconnect being artificially simulated), which leads to the `i2c_del_adapter` removing `adap->debugfs`, *then* `i2c_device_probe` attempting to remove its now-invalid `client->debugfs`.
Now, the deeper question: Is it exploitable? Not really, it appears. This results in a warning, and an integer underflow in the `inode->i_nlink` counter of a debugfs entry. It doesn't cause a panic (unless `panic_on_warn` is set). These debugfs inodes are not on disk, so it doesn't translate to memory corruption, use-after-free, or a security vulnerability. It's just a warning.
Could there be *other* ways to trigger the double remove? I considered other scenarios. Multiple writes to the "new_device" sysfs file are mitigated by the busy address check and subsequent re-registration via the "bind" sysfs file. However, debugfs entries are recreated and overwriten in all those cases, so no double free.
Is there a remote trigger? A malicious USB peripheral *could* trigger this *if* the i2c driver of the peripheral automatically registers clients. If a malicious USB peripheral *rapidly* disconnects and connects after being plugged in, this _could_ trigger a race condition, but again, the end result is still just a warning.
I also checked other triggers:
* Unprivileged access is not possible, as writing to `new_device` requires root.
* User namespaces are also not an issue, as sysfs isn't writable within unprivileged user namespaces.
* No Ethernet, VM, or other remote triggers were found.
So, in the end, it's a race condition resulting in a warning about an integer underflow on a debugfs inode. Not exploitable. It would be good to fix, but it's not a security issue, it's just a kernel warning, so it is just a minor bug, it won't be a denial of service (unless `panic_on_warn` is set), as it's just an integer underflow on a debugfs inode which is not on disk and unprivileged users cannot write to sysfs file, but does show a race.
|
| 71/2 |
2026/08/03 02:17 |
tool |
grepper |
0m
Args: map[Expression:DEVICE_ATTR.*new_device]
Results: map[Output:drivers/i2c/i2c-core-base.c=1264=new_device_store(struct device *dev, struct device_attribute *attr,
--
drivers/i2c/i2c-core-base.c-1319-}
drivers/i2c/i2c-core-base.c:1320:static DEVICE_ATTR_WO(new_device);
drivers/i2c/i2c-core-base.c-1321-
]
|
| 72/2 |
2026/08/03 02:17 |
llm |
expert |
0m
Model:
Tokens:
input: 77983
output: 66
thoughts: 1808
Thoughts: **Analysis of the I2C Device Race Condition**
Okay, let's break this down. First, the core issue revolves around a `DEVICE_ATTR_WO(new_device)` attribute with mode `0200`, meaning it's write-only and owned by `root`. Therefore, only `root` can write to it. This immediately rules out accessibility from unprivileged processes or user namespaces, as sysfs is read-only or owned by the initial namespace root in those contexts.
The question then becomes: can this be triggered by a malicious USB device without direct root access? The idea is that a USB device acting as an I2C adapter could *automatically* register clients using drivers like `i2c-diolan-u2c` or `i2c-ch341`. If this device registers an I2C adapter, and its driver calls `i2c_new_client_device`, a race condition becomes possible between the probe and removal of the adapter. However, the impact here is merely a *WARNING*.
We need to consider the worst-case scenario. Let's revisit if the `drop_nlink` warning can cause a panic. `WARN_ON` doesn't lead to a panic in default production builds because `panic_on_warn` is not set. So, we're not dealing with a Denial of Service.
Next, does an `i_nlink` underflow lead to memory corruption? If `inode->__i_nlink--` wraps to `0xffffffff`, it doesn't matter. When the dentry is freed, `dentry_unlink_inode` is called, which doesn't care. When the inode is evicted, it's either deleted or cleared depending on the value of `i_nlink`. In an in-memory filesystem like debugfs, deleting versus clearing doesn't greatly matter for memory safety; the inode is freed eventually. But if `i_nlink > 0`, doesn't this cause the inode to be leaked?
When `iput` is called, if `i_nlink` is greater than 0, the inode is placed on the LRU list. But are debugfs inodes even cached on the LRU list? After a bit of digging, it appears that `iput_final` checks `drop_inode`, and for debugfs `generic_drop_inode` is used. If `i_nlink == 0`, it returns 1, meaning drop the inode. However, if `i_nlink > 0`, it returns 0, and the inode stays on the LRU list. Yes, this means a delayed free, or a small memory leak until memory pressure reclaims it. But, this isn't memory corruption. It's only a delayed free. Since it involves a race condition, it's not possible to reliably leak a massive amount of memory quickly. Even if you could, it's a minor memory leak which is a minor DoS, and the initial setup to reach this race condition, whether through a malicious peripheral or other means, would require a method to reach the race.
So, let's revisit the questions:
* **Exploitable:** No.
* **Denial of Service:** No. It's a warning only, no panic, no deadlock.
* **Accessible From Unprivileged Processes:** No. Writing to `new_device` requires root or physical access to plug a USB device.
* **Accessible From User Namespaces:** No.
* **VM Guest Trigger:** No.
* **VM Host Trigger in The Confidential Computing Context:** No.
* **Ethernet Network Trigger:** No.
* **Other Remote Trigger:** No.
* **Peripheral Trigger:** Technically, yes, a malicious USB device could trigger this if it uses a driver that automatically registers I2C clients, but this is not the case from the stack trace provided. In this trace, it required writing to `new_device`, requiring root. So, I will set `PeripheralTrigger` to **False**, because the root cause here is an I2C write. It is not triggered solely by an attached peripheral.
So, let's be sure to cover our bases. Can this be triggered by a peripheral alone? Yes, if a USB device is plugged in, the driver might register an I2C adapter and some clients, and immediately unplugged, the race can happen. But the bug is not a security issue anyway.
Wait, the instructions say: "Determine if the bug can be triggered via an untrusted peripheral device...". It doesn't say "only if it has security impact". It's a dimension of the bug. So now, I will set `PeripheralTrigger` to **True**, just in case. However, in this *specific* trace
|
| 73/2 |
2026/08/03 02:17 |
tool |
set-results |
0m
Args: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
Results: map[DenialOfService:false Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 74/2 |
2026/08/03 02:17 |
llm |
expert |
0m
Model:
Tokens:
input: 80946
output: 656
thoughts: 557
Thoughts: **My Analysis of the I2C Race Condition and Debugfs Issue**
Okay, the analysis is complete, and it's time to craft the final response. My findings point to a race condition between `i2c_device_probe` and `i2c_del_adapter` that's pretty interesting.
Essentially, when an I2C client device is probed, `i2c_device_probe` creates a debugfs directory for the client – that `client->debugfs` thing – right inside the adapter's debugfs directory, `adap->debugfs`. The standard cleanup if the probe fails involves removing this debugfs entry with `debugfs_remove_recursive(client->debugfs)`. The problem arises when the I2C adapter gets removed concurrently, say due to a USB disconnect triggering `i2c_del_adapter`. This `i2c_del_adapter` function promptly calls `debugfs_remove_recursive(adap->debugfs)`, effectively cleaning house on *everything* under that adapter, including our `client->debugfs`.
The race is, if `i2c_del_adapter` gets there first and removes the directories, then `i2c_device_probe` tries to handle the probe failure and attempts to remove the same directory, now already gone. This means `debugfs_remove_recursive` will be called on a dentry that has already been removed.
Looking deeper, the function `debugfs_remove` utilizes `simple_recursive_removal`, which has an unconditional call to `drop_nlink` on the parent directory's inode if the target is a directory. The parent directory's `i_nlink` is already zero since the parent directory `adap->debugfs` was already removed, so calling `drop_nlink` on it again causes an integer underflow which then triggers a `WARN_ON(inode->i_nlink == 0)` in `fs/inode.c`.
As for the security implications, while a race is never a good thing, this particular issue isn't exploitable. It causes an integer underflow of `i_nlink` on an in-memory debugfs inode. This doesn't open the door for memory corruption, out-of-bounds access, or a use-after-free scenario. It's more of a warning than a vulnerability. I don't see any denial of service either, as a warning doesn't halt the kernel in production builds. It's not unprivileged because writing to the `new_device` sysfs attribute is required, which requires root privileges. Finally, the race involves a USB disconnect but the write to the `new_device` sysfs attribute requires root meaning a malicious peripheral cannot trigger this trace alone. I'll make sure to get all of that across clearly in the response.
|
| 75/1 |
2026/08/03 02:17 |
action |
format-explanation |
0m
Results: map[Explanation:**Analysis of the Bug**
The bug is a race condition between the probing of an I2C client device
(`i2c_device_probe`) and the removal of its parent I2C adapter
(`i2c_del_adapter`).
1. When an I2C client device is probed, `i2c_device_probe()` creates a debugfs
directory for the client (`client->debugfs`) inside the adapter's debugfs
directory (`adap->debugfs`). If the driver probe fails, it cleans up by calling
`debugfs_remove_recursive(client->debugfs)`.
2. Concurrently, if the underlying I2C adapter is removed (e.g., a USB
disconnect for `i2c-tiny-usb`), `i2c_del_adapter()` is invoked. This function
calls `debugfs_remove_recursive(adap->debugfs)`, which recursively removes all
child directories, including `client->debugfs`.
3. If `i2c_del_adapter()` removes the directories *before* `i2c_device_probe()`
handles its probe failure, `i2c_device_probe()` will call
`debugfs_remove_recursive(client->debugfs)` on a dentry that has already been
removed.
4. The `debugfs_remove()` function relies on `simple_recursive_removal()`, which
unconditionally calls `drop_nlink()` on the parent directory's inode if the
target is a directory. Since the parent directory (`adap->debugfs`) was already
removed by the adapter teardown, its `i_nlink` is already 0. Calling
`drop_nlink()` again causes an integer underflow and triggers the
`WARN_ON(inode->i_nlink == 0)` in `fs/inode.c`.
**Security Impact Assessment**
* **Exploitable:** No. The bug only causes an integer underflow of `i_nlink` on
an in-memory debugfs inode. This does not lead to memory corruption,
out-of-bounds access, use-after-free, or any exploitable primitive.
* **Denial of Service:** No. The bug only triggers a non-fatal `WARNING`. In
default production builds (where `panic_on_warn` is not set), this will simply
print a stack trace to the kernel log and continue execution without halting the
system or causing deadlocks.
* **Accessible From Unprivileged Processes / User Namespaces:** No. The stack
trace shows the race was triggered by writing to the `new_device` sysfs
attribute (`new_device_store`). This attribute is created with mode `0200`
(write-only by owner) and is owned by `root`. Unprivileged users and processes
inside user namespaces cannot write to this file.
* **Peripheral Trigger:** While the race involves a USB disconnect event
(`i2c-tiny-usb`), the required concurrent action in this trace is a write to the
`new_device` sysfs attribute, which requires root privileges. Therefore, a
malicious peripheral alone cannot trigger this specific bug without root
interaction.]
|