| 0/0 |
2026/05/29 22:11 |
flow |
assessment-security |
31m
Results: map[DenialOfService:false Explanation:Based on the analysis of the kernel source code and the provided bug report,
here is the assessment of the bug:
**Root Cause Analysis:**
The warning `class_for_each_device called for class 'devlink' before it was
registered` is triggered in `class_for_each_device()` when it attempts to
iterate over devices of a class that has not been registered (i.e.,
`class_to_subsys()` returns `NULL`).
In this specific case, it is attempting to iterate over `devlink_class`. The
registration of `devlink_class` happens in `devlink_class_init()`, which is a
`postcore_initcall` executed exactly once during the early stages of kernel
boot. If this initialization fails (e.g., due to an out-of-memory condition or
artificial memory allocation fault injection by a fuzzer like syzkaller), the
class remains unregistered.
Later in the boot process (or when the deferred probe timeout expires),
`deferred_probe_timeout_work_func()` is executed. This function calls
`fw_devlink_drivers_done()`, which unconditionally invokes
`class_for_each_device(&devlink_class, ...)`. Because the class was never
successfully registered, the kernel hits the `WARN(1, ...)` statement in
`class_for_each_device()`.
**Security Impact:**
1. **Exploitability:** The bug is not exploitable. It is a simple state mismatch
where a subsystem tries to iterate over an unregistered class. The
`class_for_each_device()` function safely handles this by printing a warning and
returning `-EINVAL`. There is no memory corruption, out-of-bounds access, or
use-after-free involved.
2. **Denial of Service:** In production builds (where `panic_on_warn` is
disabled), this bug merely prints a warning to the kernel log and continues
execution normally. It does not crash the system, cause deadlocks, or lead to
resource leaks.
3. **Triggerability:** The failure of `devlink_class_init()` can only occur
during kernel boot. An unprivileged user, remote attacker, or malicious device
cannot trigger this initialization failure post-boot. The bug report is a direct
result of syzkaller injecting memory allocation failures (`fault-injection`)
during the boot process.
**Conclusion:**
This is a benign warning caused by simulated resource exhaustion during kernel
initialization. It has no security implications and cannot be triggered or
exploited by any user or external entity on a running production system. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/29 22:11 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/05/29 22:11 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/49a09bb054f54d1c103c80632ce3f2ebbe7db207]
|
| 3/1 |
2026/05/29 22:12 |
action |
kernel-builder |
16m
Results: map[KernelObj:/app/workdir/cache/build/632dfc0ffbe1e712f0e318b6f0e7a85231528071]
|
| 4/1 |
2026/05/29 22:28 |
action |
codesearch-prepare |
12m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/29 22:41 |
agent |
expert |
1m
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 or elevated privileges.
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.
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:
------------[ cut here ]------------
class_for_each_device called for class 'devlink' before it was registered
WARNING: drivers/base/class.c:416 at class_for_each_device+0x2d8/0x390 drivers/base/class.c:415, CPU#1: kworker/1:1/37
Modules linked in:
CPU: 1 UID: 0 PID: 37 Comm: kworker/1:1 Not tainted syzkaller #0 PREEMPT_{RT,(full)}
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 04/18/2026
Workqueue: events deferred_probe_timeout_work_func
Code: 8d 1d d8 14 0e 0a 4c 89 e8 48 c1 e8 03 42 80 3c 38 00 74 08 4c 89 ef e8 62 ad a5 fc 49 8b 55 00 48 89 df 48 c7 c6 0e 5f 5d 8d <67> 48 0f b9 3a b8 ea ff ff ff eb 1b e8 43 16 3b fc 49 81 c6 90 00
RSP: 0000:ffffc90000ac7a20 EFLAGS: 00010246
RAX: 1ffffffff17868c0 RBX: ffffffff8f986560 RCX: ffff88801e2c9f00
RDX: ffffffff8bc35ac0 RSI: ffffffff8d5d5f0e RDI: ffffffff8f986560
RBP: ffffc90000ac7af8 R08: ffffc90000ac7a7f R09: 0000000000000000
R10: ffffc90000ac7a60 R11: fffff52000158f50 R12: ffffc90000ac7a60
R13: ffffffff8bc34600 R14: 1ffff92000158f48 R15: dffffc0000000000
FS: 0000000000000000(0000) GS:ffff888125fd7000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 0000000000000000 CR3: 000000000ddb8000 CR4: 00000000003506f0
Call Trace:
<TASK>
fw_devlink_drivers_done+0x35/0x50 drivers/base/core.c:1837
deferred_probe_timeout_work_func+0x21/0x220 drivers/base/dd.c:305
process_one_work+0x98b/0x1630 kernel/workqueue.c:3318
process_scheduled_works kernel/workqueue.c:3401 [inline]
worker_thread+0xb49/0x1140 kernel/workqueue.c:3482
kthread+0x388/0x470 kernel/kthread.c:436
ret_from_fork+0x514/0xb70 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
----------------
Code disassembly (best guess):
0: 8d 1d d8 14 0e 0a lea 0xa0e14d8(%rip),%ebx # 0xa0e14de
6: 4c 89 e8 mov %r13,%rax
9: 48 c1 e8 03 shr $0x3,%rax
d: 42 80 3c 38 00 cmpb $0x0,(%rax,%r15,1)
12: 74 08 je 0x1c
14: 4c 89 ef mov %r13,%rdi
17: e8 62 ad a5 fc call 0xfca5ad7e
1c: 49 8b 55 00 mov 0x0(%r13),%rdx
20: 48 89 df mov %rbx,%rdi
23: 48 c7 c6 0e 5f 5d 8d mov $0xffffffff8d5d5f0e,%rsi
* 2a: 67 48 0f b9 3a ud1 (%edx),%rdi <-- trapping instruction
2f: b8 ea ff ff ff mov $0xffffffea,%eax
34: eb 1b jmp 0x51
36: e8 43 16 3b fc call 0xfc3b167e
3b: 49 rex.WB
3c: 81 .byte 0x81
3d: c6 (bad)
3e: 90 nop
Reply: Based on the analysis of the kernel source code and the provided bug report, here is the assessment of the bug:
**Root Cause Analysis:**
The warning `class_for_each_device called for class 'devlink' before it was registered` is triggered in `class_for_each_device()` when it attempts to iterate over devices of a class that has not been registered (i.e., `class_to_subsys()` returns `NULL`).
In this specific case, it is attempting to iterate over `devlink_class`. The registration of `devlink_class` happens in `devlink_class_init()`, which is a `postcore_initcall` executed exactly once during the early stages of kernel boot. If this initialization fails (e.g., due to an out-of-memory condition or artificial memory allocation fault injection by a fuzzer like syzkaller), the class remains unregistered.
Later in the boot process (or when the deferred probe timeout expires), `deferred_probe_timeout_work_func()` is executed. This function calls `fw_devlink_drivers_done()`, which unconditionally invokes `class_for_each_device(&devlink_class, ...)`. Because the class was never successfully registered, the kernel hits the `WARN(1, ...)` statement in `class_for_each_device()`.
**Security Impact:**
1. **Exploitability:** The bug is not exploitable. It is a simple state mismatch where a subsystem tries to iterate over an unregistered class. The `class_for_each_device()` function safely handles this by printing a warning and returning `-EINVAL`. There is no memory corruption, out-of-bounds access, or use-after-free involved.
2. **Denial of Service:** In production builds (where `panic_on_warn` is disabled), this bug merely prints a warning to the kernel log and continues execution normally. It does not crash the system, cause deadlocks, or lead to resource leaks.
3. **Triggerability:** The failure of `devlink_class_init()` can only occur during kernel boot. An unprivileged user, remote attacker, or malicious device cannot trigger this initialization failure post-boot. The bug report is a direct result of syzkaller injecting memory allocation failures (`fault-injection`) during the boot process.
**Conclusion:**
This is a benign warning caused by simulated resource exhaustion during kernel initialization. It has no security implications and cannot be triggered or exploited by any user or external entity on a running production system.
|
| 6/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 6964
output: 39
thoughts: 201
Thoughts: **Analyzing a Kernel Warning: A Driver Registration Timing Issue**
Okay, so I'm seeing this kernel warning: "class_for_each_device called for class 'devlink' before it was registered." And, from the trace, it's pretty clear it originates in `drivers/base/class.c`, specifically at line 416 within `class_for_each_device`. I recognize the general area, though I'll need to dig in for the specifics.
The call trace is crucial here. It shows `fw_devlink_drivers_done` in `drivers/base/core.c` as the immediate culprit, and then cascades through the deferred probing mechanism. That's interesting, suggesting a race condition or timing issue related to device link driver initialization and class registration. It looks like `devlink` is being accessed before its class has been fully initialized. I'm seeing `deferred_probe_timeout_work_func` so a delayed or deferred action may be at play.
My immediate next step is to examine the `fw_devlink_drivers_done` function in `drivers/base/core.c`. I need to understand what's happening there to see how it's interacting with the `devlink` class and, more importantly, how it's trying to iterate or operate on it *before* the class is fully set up. I'll be looking for assumptions or implicit dependencies on the class's registration state. It seems that the assumption may not be holding.
|
| 7/2 |
2026/05/29 22:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/core.c Name:fw_devlink_drivers_done]
Results: map[SourceCode:1833: void fw_devlink_drivers_done(void)
1834: {
1835: fw_devlink_drv_reg_done = true;
1836: device_links_write_lock();
1837: class_for_each_device(&devlink_class, NULL, NULL,
1838: fw_devlink_no_driver);
1839: device_links_write_unlock();
1840: }
SourceFile:drivers/base/core.c]
|
| 8/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 7680
output: 48
thoughts: 40
Thoughts: Okay, here's my take on those thoughts, framed as an expert's inner monologue:
**Investigating the Device Class Registration Timing**
Alright, the warning flag is popping up in `class_for_each_device`, which immediately tells me something about the timing of the `devlink_class` registration. This isn't ideal, but it's often a symptom of something trying to access a component before it's fully initialized. So, my primary concern is to figure out the **precise moment and location of `devlink_class`'s registration** within the driver or kernel initialization sequence. It's got to be happening later than we'd expect, causing that function to fail. I need to trace this back. Let's dig into the registration function itself and see where it's being called.
|
| 9/2 |
2026/05/29 22:41 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/base/core.c IncludeSnippetLines:2 Name:devlink_class SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 10/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 7959
output: 28
thoughts: 41
Thoughts: Okay, here's my interpretation of that thought process, assuming I'm already pretty familiar with kernel driver development:
**Investigating the Scope of `devlink_class`**
Hmm, interesting. I recall seeing `devlink_class` being used somewhere... wasn't it in `drivers/base/core.c`? Okay, let's confirm that. I need to refresh my memory on the interactions around that specific structure. The best way to do that is to quickly locate all uses of `devlink_class` within that file. I'll need to use my editor's search functionality, or maybe grep if I'm on the command line, to pinpoint precisely where and how it's being employed in `drivers/base/core.c`. This will give me a solid starting point for understanding its role in the system.
|
| 11/2 |
2026/05/29 22:41 |
tool |
grepper |
0m
Args: map[Expression:devlink_class PathPrefix:drivers/base/]
Results: map[Output:drivers/base/core.c=625=EXPORT_SYMBOL_GPL(device_link_wait_removal);
drivers/base/core.c-626-
drivers/base/core.c:627:static const struct class devlink_class = {
drivers/base/core.c-628- .name = "devlink",
--
drivers/base/core.c=681=static void devlink_remove_symlinks(struct device *dev)
--
drivers/base/core.c-708-
drivers/base/core.c:709:static struct class_interface devlink_class_intf = {
drivers/base/core.c:710: .class = &devlink_class,
drivers/base/core.c-711- .add_dev = devlink_add_symlinks,
--
drivers/base/core.c-714-
drivers/base/core.c:715:static int __init devlink_class_init(void)
drivers/base/core.c-716-{
--
drivers/base/core.c-718-
drivers/base/core.c:719: ret = class_register(&devlink_class);
drivers/base/core.c-720- if (ret)
--
drivers/base/core.c-722-
drivers/base/core.c:723: ret = class_interface_register(&devlink_class_intf);
drivers/base/core.c-724- if (ret)
drivers/base/core.c:725: class_unregister(&devlink_class);
drivers/base/core.c-726-
--
drivers/base/core.c-728-}
drivers/base/core.c:729:postcore_initcall(devlink_class_init);
drivers/base/core.c-730-
--
drivers/base/core.c=800=struct device_link *device_link_add(struct device *consumer,
--
drivers/base/core.c-934-
drivers/base/core.c:935: link->link_dev.class = &devlink_class;
drivers/base/core.c-936- device_set_pm_not_required(&link->link_dev);
--
drivers/base/core.c=1687=static void device_links_purge(struct device *dev)
--
drivers/base/core.c-1690-
drivers/base/core.c:1691: if (dev->class == &devlink_class)
drivers/base/core.c-1692- return;
--
drivers/base/core.c=1833=void fw_devlink_drivers_done(void)
--
drivers/base/core.c-1836- device_links_write_lock();
drivers/base/core.c:1837: class_for_each_device(&devlink_class, NULL, NULL,
drivers/base/core.c-1838- fw_devlink_no_driver);
--
drivers/base/core.c=1869=void fw_devlink_probing_done(void)
--
drivers/base/core.c-1873- device_links_write_lock();
drivers/base/core.c:1874: class_for_each_device(&devlink_class, NULL, &sync_list,
drivers/base/core.c-1875- fw_devlink_dev_sync_state);
]
|
| 12/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 9110
output: 40
thoughts: 95
Thoughts: Here's my thought process, as it stands:
**Debugging the Device Linking Initialization**
Okay, so `devlink_class_init` is firing up during the `postcore_initcall` phase. That's good to know, sets the stage. But I'm also seeing something about `deferred_probe_timeout_work_func` being scheduled within a `deferred_probe_initcall` which is a `late_initcall`. Interesting β that suggests some device probing is happening later, potentially after some core system initialization is complete. Hang on... isn't `deferred_probe_timeout_work_func` a workqueue function? Right, that means it's running asynchronously.
Alright, time to get my hands dirty. I need to understand what this timeout function is doing. Let's dive into the code. I'll take a look at `drivers/base/dd.c` to understand the implementation of `deferred_probe_timeout_work_func`. I need to understand *why* these late probes are happening and what timeout this concerns. Is this related to some device hotplugging mechanism or something more specific? Time to investigate!
|
| 13/2 |
2026/05/29 22:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/dd.c Name:deferred_probe_timeout_work_func]
Results: map[SourceCode: 301: static void deferred_probe_timeout_work_func(struct work_struct *work)
302: {
303: struct device_private *p;
304:
305: fw_devlink_drivers_done();
306:
307: driver_deferred_probe_timeout = 0;
308: driver_deferred_probe_trigger();
309: flush_work(&deferred_probe_work);
310:
311: mutex_lock(&deferred_probe_mutex);
312: list_for_each_entry(p, &deferred_probe_pending_list, deferred_probe)
313: dev_warn(p->device, "deferred probe pending: %s", p->deferred_probe_reason ?: "(reason unknown)\n");
314: mutex_unlock(&deferred_probe_mutex);
315:
316: fw_devlink_probing_done();
317: }
SourceFile:drivers/base/dd.c]
|
| 14/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 9790
output: 53
thoughts: 15
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing the Scheduling of `deferred_probe_timeout_work_func`**
Okay, so I'm trying to nail down exactly *when* the `deferred_probe_timeout_work_func` is scheduled. That's the core question here. I need to understand its relationship to the overall probe sequence. Is it triggered directly by the driver's state machine, tied to a specific interrupt, or managed by some higher-level framework? My immediate concerns are about race conditions and how its execution impacts power management or resource availability.
I'm thinking, is this a kthread, a workqueue item, or something else entirely? What context does it run in? If it's a workqueue item, which one, and what implications does that have for concurrency and priority? Is there any chance of it being preempted during a critical section? I'll need to trace back from the function's definition and identify where it's being enqueued, and under what conditions. Are there any configuration parameters or module options that influence its scheduling behavior? What are the potential consequences if it's delayed or missed? Thatβs going to inform what I need to do to debug, and where to look next.
|
| 15/2 |
2026/05/29 22:41 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/base/dd.c IncludeSnippetLines:2 Name:deferred_probe_timeout_work_func SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 16/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 10147
output: 33
thoughts: 17
Thoughts: Here's a summary of my thought process, as requested:
**Deconstructing the Problem: `deferred_probe_timeout_work_func`**
Okay, so I'm thinking about something related to deferred probe timeouts, and the first thing that jumps to mind is that I should immediately look for the function `deferred_probe_timeout_work_func`. This is the logical first step. Whenever I'm tackling something like this, a direct search for the core function handling the deferred timeout mechanism is almost always the best starting point. Understanding how this function works will give me a solid foundation for tracing the flow and understanding where any issues might be arising. It's the most likely entry point to understanding the whole system, so that's where I need to start.
|
| 17/2 |
2026/05/29 22:41 |
tool |
grepper |
0m
Args: map[Expression:deferred_probe_timeout_work_func PathPrefix:drivers/base/]
Results: map[Output:drivers/base/dd.c=299=EXPORT_SYMBOL_GPL(driver_deferred_probe_check_state);
drivers/base/dd.c-300-
drivers/base/dd.c:301:static void deferred_probe_timeout_work_func(struct work_struct *work)
drivers/base/dd.c-302-{
--
drivers/base/dd.c-317-}
drivers/base/dd.c:318:static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_work_func);
drivers/base/dd.c-319-
]
|
| 18/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 10526
output: 31
thoughts: 17
Thoughts: Here's my take on those thoughts:
**Analyzing `deferred_probe_timeout_work` Usage**
Alright, let's dive into this `deferred_probe_timeout_work` function. My immediate instinct is to figure out its scope β where is this thing actually being *used*? I need to understand its context. I need to trace its callers. Knowing the call graph will give me a solid picture of its role in the system. The devil's always in the details, so let's start digging. Gotta find all the places it's being invoked to truly grasp its significance.
|
| 19/2 |
2026/05/29 22:41 |
tool |
grepper |
0m
Args: map[Expression:deferred_probe_timeout_work PathPrefix:drivers/base/]
Results: map[Output:drivers/base/dd.c=299=EXPORT_SYMBOL_GPL(driver_deferred_probe_check_state);
drivers/base/dd.c-300-
drivers/base/dd.c:301:static void deferred_probe_timeout_work_func(struct work_struct *work)
drivers/base/dd.c-302-{
--
drivers/base/dd.c-317-}
drivers/base/dd.c:318:static DECLARE_DELAYED_WORK(deferred_probe_timeout_work, deferred_probe_timeout_work_func);
drivers/base/dd.c-319-
drivers/base/dd.c=320=void deferred_probe_extend_timeout(void)
--
drivers/base/dd.c-325- */
drivers/base/dd.c:326: if (mod_delayed_work(system_wq, &deferred_probe_timeout_work,
drivers/base/dd.c-327- driver_deferred_probe_timeout))
--
drivers/base/dd.c=339=static int deferred_probe_initcall(void)
--
drivers/base/dd.c-360- if (driver_deferred_probe_timeout > 0) {
drivers/base/dd.c:361: schedule_delayed_work(&deferred_probe_timeout_work,
drivers/base/dd.c-362- driver_deferred_probe_timeout * HZ);
]
|
| 20/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 11076
output: 38
thoughts: 129
Thoughts: **Debugging a Driver Initialization Order Mismatch**
Okay, so I'm scratching my head over this warning message. It's saying that I'm trying to iterate through devices of the 'devlink' class *before* it's registered. The odd thing is, I thought I had the initialization order nailed down. Let's see... the `deferred_probe_initcall` function that kicks off the probe is a `late_initcall`. Right, `late_initcall` means it runs later in the boot process. Then `devlink_class_init` is a `postcore_initcall`. And *postcore_initcall* comes *before* `late_initcall`. So, theoretically, the 'devlink' class should already be registered by the time I'm attempting to look for devices.
This doesn't make any sense. Why am I seeing this warning then? Time to dig into the source code and confirm my understanding of what is going on. I'll need to check the `class_for_each_device` function to see exactly how it's determining the class registration status. Let's go look in `drivers/base/class.c`.
|
| 21/2 |
2026/05/29 22:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/class.c Name:class_for_each_device]
Results: map[SourceCode: 386: /**
387: * class_for_each_device - device iterator
388: * @class: the class we're iterating
389: * @start: the device to start with in the list, if any.
390: * @data: data for the callback
391: * @fn: function to be called for each device
392: *
393: * Iterate over @class's list of devices, and call @fn for each,
394: * passing it @data. If @start is set, the list iteration will start
395: * there, otherwise if it is NULL, the iteration starts at the
396: * beginning of the list.
397: *
398: * We check the return of @fn each time. If it returns anything
399: * other than 0, we break out and return that value.
400: *
401: * @fn is allowed to do anything including calling back into class
402: * code. There's no locking restriction.
403: */
404: int class_for_each_device(const struct class *class, const struct device *start,
405: void *data, device_iter_t fn)
406: {
407: struct subsys_private *sp = class_to_subsys(class);
408: struct class_dev_iter iter;
409: struct device *dev;
410: int error = 0;
411:
412: if (!class)
413: return -EINVAL;
414: if (!sp) {
415: WARN(1, "%s called for class '%s' before it was registered",
416: __func__, class->name);
417: return -EINVAL;
418: }
419:
420: class_dev_iter_init(&iter, class, start, NULL);
421: while ((dev = class_dev_iter_next(&iter))) {
422: error = fn(dev, data);
423: if (error)
424: break;
425: }
426: class_dev_iter_exit(&iter);
427: subsys_put(sp);
428:
429: return error;
430: }
SourceFile:drivers/base/class.c]
|
| 22/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 12194
output: 37
thoughts: 26
Thoughts: Here's a summary of my immediate thought process, focusing on the core issue:
**Debugging a Null Pointer from `class_to_subsys`**
Okay, so `class_to_subsys(class)` just returned NULL. That's a classic sign of trouble. Since I'm getting a null pointer, I need to immediately isolate the problem. The most logical next step, considering the function's responsibility, is to investigate `class_to_subsys` itself. I need to understand why it might be returning NULL in this specific scenario. It's time to dig into the implementation and parameters to find the root cause, assuming there may have been an edge case or missing coverage.
|
| 23/2 |
2026/05/29 22:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/class.c Name:class_to_subsys]
Results: map[SourceCode: 28: /**
29: * class_to_subsys - Turn a struct class into a struct subsys_private
30: *
31: * @class: pointer to the struct bus_type to look up
32: *
33: * The driver core internals need to work on the subsys_private structure, not
34: * the external struct class pointer. This function walks the list of
35: * registered classes in the system and finds the matching one and returns the
36: * internal struct subsys_private that relates to that class.
37: *
38: * Note, the reference count of the return value is INCREMENTED if it is not
39: * NULL. A call to subsys_put() must be done when finished with the pointer in
40: * order for it to be properly freed.
41: */
42: struct subsys_private *class_to_subsys(const struct class *class)
43: {
44: struct subsys_private *sp = NULL;
45: struct kobject *kobj;
46:
47: if (!class || !class_kset)
48: return NULL;
49:
50: spin_lock(&class_kset->list_lock);
51:
52: if (list_empty(&class_kset->list))
53: goto done;
54:
55: list_for_each_entry(kobj, &class_kset->list, entry) {
56: struct kset *kset = container_of(kobj, struct kset, kobj);
57:
58: sp = container_of_const(kset, struct subsys_private, subsys);
59: if (sp->class == class)
60: goto done;
61: }
62: sp = NULL;
63: done:
64: sp = subsys_get(sp);
65: spin_unlock(&class_kset->list_lock);
66: return sp;
67: }
SourceFile:drivers/base/class.c]
|
| 24/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 13005
output: 37
thoughts: 64
Thoughts: Okay, let me think this through.
**Debugging a Possible `devlink_class` Initialization Failure**
Alright, so I'm digging into this code, and I'm seeing a possible issue when `class_to_subsys` returns NULL. The key takeaway here is that if that happens, it indicates that `devlink_class` isn't found within the `class_kset->list`. That's a red flag. Now, why would that be the case? It should be there.
My immediate thought goes to the initialization. The most likely culprit is that `devlink_class_init` didn't succeed as expected. If the initialization routine failed, perhaps due to a resource issue or some other error condition during setup, `devlink_class` might not have been properly added to the `class_kset->list` in the first place. Thatβs where I'd start looking β back at the `devlink_class_init` function, reviewing its return values and error handling to see if something went sideways there. I'd need to trace the initialization process to confirm if there was a failure that resulted in the expected object not being where it should be. The next step is to examine the return values of `devlink_class_init` and any associated error logs, to identify if any problems were encountered.
|
| 25/2 |
2026/05/29 22:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/core.c Name:devlink_class_init]
Results: map[SourceCode: 715: static int __init devlink_class_init(void)
716: {
717: int ret;
718:
719: ret = class_register(&devlink_class);
720: if (ret)
721: return ret;
722:
723: ret = class_interface_register(&devlink_class_intf);
724: if (ret)
725: class_unregister(&devlink_class);
726:
727: return ret;
728: }
SourceFile:drivers/base/core.c]
|
| 26/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 13587
output: 35
thoughts: 233
Thoughts: **Analyzing the `devlink_class` Registration and Potential Failure Points**
Okay, so I'm digging into this `devlink_class` registration process and I need to understand the potential failure points. My initial understanding is that if `class_register` or `class_interface_register` fails, then `devlink_class` gets unregistered, which seems straightforward. The question then becomes, can `class_register` actually fail? Of course, memory allocation issues could cause it to fail, I need to keep that in mind.
Now, thinking a bit deeper, `devlink_class_init` is also crucial here. The code returns an error if that fails, but the fact that it's an `initcall` is important. The kernel doesn't panic on a failed `initcall`; it just logs an error. But wait, I'm just reminded `devlink_class_init` is actually a `postcore_initcall`. If *that* fails, then `devlink_class` isn't registered in the first place, which is significantly different.
The implication here is that later on, `deferred_probe_timeout_work_func` runs, and it calls `fw_devlink_drivers_done()`. And that routine unconditionally calls `class_for_each_device(&devlink_class, ...)` which could cause problems.
So, I need to solidify that `devlink_class_init` is the *only* place the `devlink_class` could be unregistered. Let me confirm, the code seems to indicate that it is, given what I'm seeing now.
Okay, let's circle back and focus on this `devlink_class_init` and its vulnerabilities. I need to figure out if it can fail and what the consequences are. Let me go back and see if `class_register` can fail. The code seems to be indicating that it calls `__class_register`, so I'll need to follow that path.
|
| 27/2 |
2026/05/29 22:41 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/base/class.c Name:__class_register]
Results: map[SourceCode: SourceFile:]
|
| 28/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 14302
output: 27
thoughts: 12
Thoughts: Okay, here's my summary, in the first person, as if these are my thoughts:
**Investigating the Implementation**
Hmm, I'm just looking at this code and trying to get a handle on how it works. I'm wondering if the behavior I'm seeing is related to how `class_register` is defined. Something about its functionality feels... indirect. So, my gut reaction is to check if `class_register` might be implemented as a macro. That would certainly explain some of the more opaque aspects of its behavior, especially if there's some expansion happening under the hood. It's a common pattern, and a macro could be hiding some complexities related to registration. I'll need to confirm that, but that's my initial hypothesis.
|
| 29/2 |
2026/05/29 22:41 |
tool |
grepper |
0m
Args: map[Expression:class_register PathPrefix:drivers/base/]
Results: map[Output:drivers/base/class.c=171=static void klist_class_dev_put(struct klist_node *n)
--
drivers/base/class.c-177-
drivers/base/class.c:178:int class_register(const struct class *cls)
drivers/base/class.c-179-{
--
drivers/base/class.c-230-}
drivers/base/class.c:231:EXPORT_SYMBOL_GPL(class_register);
drivers/base/class.c-232-
--
drivers/base/class.c=266=struct class *class_create(const char *name)
--
drivers/base/class.c-279-
drivers/base/class.c:280: retval = class_register(cls);
drivers/base/class.c-281- if (retval)
--
drivers/base/core.c=715=static int __init devlink_class_init(void)
--
drivers/base/core.c-718-
drivers/base/core.c:719: ret = class_register(&devlink_class);
drivers/base/core.c-720- if (ret)
--
drivers/base/devcoredump.c=469=static int __init devcoredump_init(void)
drivers/base/devcoredump.c-470-{
drivers/base/devcoredump.c:471: return class_register(&devcd_class);
drivers/base/devcoredump.c-472-}
--
drivers/base/firmware_loader/sysfs.c=114=int register_sysfs_loader(void)
drivers/base/firmware_loader/sysfs.c-115-{
drivers/base/firmware_loader/sysfs.c:116: int ret = class_register(&firmware_class);
drivers/base/firmware_loader/sysfs.c-117-
--
drivers/base/transport_class.c=33=static int transport_remove_classdev(struct attribute_container *cont,
--
drivers/base/transport_class.c-37-/**
drivers/base/transport_class.c:38: * transport_class_register - register an initial transport class
drivers/base/transport_class.c-39- *
--
drivers/base/transport_class.c-50- */
drivers/base/transport_class.c:51:int transport_class_register(struct transport_class *tclass)
drivers/base/transport_class.c-52-{
drivers/base/transport_class.c:53: return class_register(&tclass->class);
drivers/base/transport_class.c-54-}
drivers/base/transport_class.c:55:EXPORT_SYMBOL_GPL(transport_class_register);
drivers/base/transport_class.c-56-
--
drivers/base/transport_class.c=71=static int anon_transport_dummy_function(struct transport_container *tc,
--
drivers/base/transport_class.c-79-/**
drivers/base/transport_class.c:80: * anon_transport_class_register - register an anonymous class
drivers/base/transport_class.c-81- *
--
drivers/base/transport_class.c-90- */
drivers/base/transport_class.c:91:void anon_transport_class_register(struct anon_transport_class *atc)
drivers/base/transport_class.c-92-{
--
drivers/base/transport_class.c-98-}
drivers/base/transport_class.c:99:EXPORT_SYMBOL_GPL(anon_transport_class_register);
drivers/base/transport_class.c-100-
]
|
| 30/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 15452
output: 34
thoughts: 19
Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience:
**Diving into the Kernel's Class Registration**
Right, let's pull up the source code for `class_register` in `drivers/base/class.c`. I need to get a handle on how a new device class is actually integrated into the kernel's device model. What's the core logic? Where are the hooks and data structures?
I immediately want to understand how it interfaces with the generic device model. Does it use the same kobject infrastructure that other drivers employ, or does it have its own specialized mechanisms? I'm particularly interested in how it manages the sysfs entries. Does it handle permissions correctly? Are there any potential race conditions I should be mindful of, especially during class unregistration?
Iβll also need to check how it interacts with the underlying bus drivers. Does it provide any special support for PCI, USB, or other specific buses? Are there any design patterns at play, like callbacks or inheritance, that I can use as examples in my own code?
I need to understand what aspects of the class it makes configurable. Is it a configurable parameter like a class name or a specific property, or does it offer finer control. What are the various attributes?
Ultimately, I'm looking for the 'golden path' β the most common and robust flow β but also any error paths and exception handling. I expect there to be a good level of detail in the comments, but I'll be looking for implicit assumptions or gotchas that might not be immediately apparent. Time to dig in!
|
| 31/2 |
2026/05/29 22:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/class.c Name:class_register]
Results: map[SourceCode: 178: int class_register(const struct class *cls)
179: {
180: struct subsys_private *cp;
181: struct lock_class_key *key;
182: int error;
183:
184: pr_debug("device class '%s': registering\n", cls->name);
185:
186: if (cls->ns_type && !cls->namespace) {
187: pr_err("%s: class '%s' does not have namespace\n",
188: __func__, cls->name);
189: return -EINVAL;
190: }
191: if (!cls->ns_type && cls->namespace) {
192: pr_err("%s: class '%s' does not have ns_type\n",
193: __func__, cls->name);
194: return -EINVAL;
195: }
196:
197: cp = kzalloc_obj(*cp);
198: if (!cp)
199: return -ENOMEM;
200: klist_init(&cp->klist_devices, klist_class_dev_get, klist_class_dev_put);
201: INIT_LIST_HEAD(&cp->interfaces);
202: kset_init(&cp->glue_dirs);
203: key = &cp->lock_key;
204: lockdep_register_key(key);
205: __mutex_init(&cp->mutex, "subsys mutex", key);
206: error = kobject_set_name(&cp->subsys.kobj, "%s", cls->name);
207: if (error)
208: goto err_out;
209:
210: cp->subsys.kobj.kset = class_kset;
211: cp->subsys.kobj.ktype = &class_ktype;
212: cp->class = cls;
213:
214: error = kset_register(&cp->subsys);
215: if (error)
216: goto err_out;
217:
218: error = sysfs_create_groups(&cp->subsys.kobj, cls->class_groups);
219: if (error) {
220: kobject_del(&cp->subsys.kobj);
221: kfree_const(cp->subsys.kobj.name);
222: goto err_out;
223: }
224: return 0;
225:
226: err_out:
227: lockdep_unregister_key(key);
228: kfree(cp);
229: return error;
230: }
SourceFile:drivers/base/class.c]
|
| 32/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 16667
output: 34
thoughts: 344
Thoughts: **Debugging a Potential Devlink Class Registration Issue**
Okay, so I'm looking at this error message: "class\_for\_each\_device called for class 'devlink' before it was registered." That's a classic race condition issue. My initial thought is the memory allocation. The code allocates memory for the `devlink_class` structure using `kzalloc_obj`, and if that fails, it returns `-ENOMEM`. Then, `devlink_class_init` could fail and returns `-ENOMEM`, or another error. But, `devlink_class_init` is a `postcore_initcall`, so the kernel won't panic if it fails, it will just log an error and continue which seems like it could lead to this issue.
Now, the debugger pointed to `deferred_probe_timeout_work_func`. That runs, calls `fw_devlink_drivers_done()`, and that in turn calls `class_for_each_device` on `devlink_class`. This makes sense β a probe timeout might be triggering this sequence.
`class_for_each_device` is the key. It ultimately tries to access `class_to_subsys`, which is expected to return a subsystem structure. But, if `devlink_class` isn't registered, `class_to_subsys` returns `NULL`. That triggers the `WARN(1, ...)` message we're seeing.
The bug report confirms this: the warning about the `class_for_each_device` call before registration, specifically pointing to `drivers/base/class.c:416`.
So, the question is, why is `devlink_class` not registered? It *should* be. The primary place for registration is, I think, through the `class_register` function, which initializes the structure. So if the memory allocation is successful, how does it fail? Is there another way the `devlink_class` could be considered unregistered? Could `class_kset` be `NULL`? I need to check `classes_init()` to make sure the `class_kset` is initialized correctly. The kset is initialized at boot time. I need to trace this back. This could be subtle; a timing issue between initialization and the deferred probe. I have to look deeper.
|
| 33/2 |
2026/05/29 22:41 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/class.c Name:classes_init]
Results: map[SourceCode: 648: int __init classes_init(void)
649: {
650: class_kset = kset_create_and_add("class", NULL, NULL);
651: if (!class_kset)
652: return -ENOMEM;
653: return 0;
654: }
SourceFile:drivers/base/class.c]
|
| 34/2 |
2026/05/29 22:41 |
llm |
expert |
0m
Model:
Tokens:
input: 17672
output: 51
thoughts: 424
Thoughts: **Analyzing a Devlink Class Initialization Warning**
Okay, so `classes_init` is kicked off by `driver_init()`. But wait, `devlink_class_init` is a `postcore_initcall`, meaning it runs pretty late in the game. The core concern seems to be that if `class_register` fails, potentially due to something like a memory allocation issue introduced by, say, syzkaller fault injection, then `devlink_class` won't be registered. This then leads to `deferred_probe_timeout_work_func` throwing that annoying warning.
My initial thought is, "Is this exploitable?" Let's see... It's just a WARNING, not a crash. The code shows that if the check `!sp` happens, it generates a warning and returns `-EINVAL`. But, `fw_devlink_drivers_done` ignores this return value from `class_for_each_device`. So, it just keeps on trucking, albeit with a log entry.
Alright, so no immediate memory corruption there. Also, no obvious denial-of-service, it just skips iterating over `devlink_class` devices. The question then becomes, can an unprivileged process trigger this? Looks like it happens during boot or via fault injection during boot. So that's a no.
Wait... `deferred_probe_timeout_work_func` is scheduled by `deferred_probe_initcall` or `deferred_probe_extend_timeout`. Hmm, so if an unprivileged user can trigger `deferred_probe_extend_timeout`, then could there be some kind of access path for this? Let's check `deferred_probe_extend_timeout`.
|
| 35/2 |
2026/05/29 22:42 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/base/dd.c IncludeSnippetLines:2 Name:deferred_probe_extend_timeout SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:driver_register SourceFile:drivers/base/driver.c SourceLine:258 SourceSnippet: 256: }
257: kobject_uevent(&drv->p->kobj, KOBJ_ADD);
258: deferred_probe_extend_timeout();
259:
260: return ret;
]] TruncatedOutput:false]
|
| 36/2 |
2026/05/29 22:42 |
llm |
expert |
0m
Model:
Tokens:
input: 18647
output: 37
thoughts: 315
Thoughts: **Analyzing a Curious Kernel Warning**
Okay, so I'm seeing this warning related to `devlink_class` and I need to figure out the likely cause. The trace starts with `driver_register` calling `deferred_probe_extend_timeout`. Immediately, my brain jumps to: could an unprivileged user be responsible for this? Is there a user-space driver registration problem? No, that's not it. Driver registration is locked down; it usually involves root privileges (like loading a module) or happens during the initial boot sequence.
Wait a minute, what about hotplug devices? If a user plugs in a USB device, does *that* trigger something? No, that registers the *device*, not the *driver* directly. And what about using something like `bpf` to register a driver? No, that's not how it works. Okay, what about triggering module loading? Ah, but module loading needs `CAP_SYS_MODULE`, or happens through an internal kernel call like `request_module`. Even *if* a user somehow triggered this (unlikely), the underlying issue is `devlink_class` not being registered in the first place.
So, how can `devlink_class` *not* be registered? The only place that it could go wrong is in `devlink_class_init`. And that's a `postcore_initcall`, meaning it runs once, during the early boot phase. So, if `devlink_class_init` fails, it's pretty much always going to be related to some kind of critical failure that prevented the module from registering properly during boot. And what would typically cause *that*? Memory allocation failures during boot, which is exactly what Syzkaller's fault injection attempts to do.
So, in essence, this warning is almost certainly triggered by a memory allocation failing *during boot*, a highly unlikely scenario in a production environment. Even if it *does* happen, the kernel's being smart about it. It just prints a warning and then keeps on chugging along, so it's not a critical issue, even if it does happen.
Just to be thorough, I'm going to check for other possible causes. I need to make sure I haven't missed something. Is `devlink_class` unregistered somewhere? Ah, yes, it seems `class_unregister(&devlink_class)` is called in `devlink_class_init` if `class_interface_register` fails. So, a failure there could also be a root cause.
|
| 37/2 |
2026/05/29 22:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/core.c Name:devlink_class_init]
Results: map[SourceCode: 715: static int __init devlink_class_init(void)
716: {
717: int ret;
718:
719: ret = class_register(&devlink_class);
720: if (ret)
721: return ret;
722:
723: ret = class_interface_register(&devlink_class_intf);
724: if (ret)
725: class_unregister(&devlink_class);
726:
727: return ret;
728: }
SourceFile:drivers/base/core.c]
|
| 38/2 |
2026/05/29 22:42 |
llm |
expert |
0m
Model:
Tokens:
input: 19732
output: 36
thoughts: 32
Thoughts: **My Analysis of the `devlink_class` Registration**
Okay, so if the `class_interface_register` function fails, the system unregisters `devlink_class`. That's a pretty straightforward dependency. But, and this is where I need to dig deeper, the critical question is: under what circumstances can `class_interface_register` actually *fail*? I need to understand the failure modes of that function. Are there resource limitations, conflicting registrations, or other potential issues that could trigger a failure? I need to review the code path within `class_interface_register` to determine the likely scenarios. Understanding this is key to building a robust system.
|
| 39/2 |
2026/05/29 22:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/class.c Name:class_interface_register]
Results: map[SourceCode: 482: int class_interface_register(struct class_interface *class_intf)
483: {
484: struct subsys_private *sp;
485: const struct class *parent;
486: struct class_dev_iter iter;
487: struct device *dev;
488:
489: if (!class_intf || !class_intf->class)
490: return -ENODEV;
491:
492: parent = class_intf->class;
493: sp = class_to_subsys(parent);
494: if (!sp)
495: return -EINVAL;
496:
497: /*
498: * Reference in sp is now incremented and will be dropped when
499: * the interface is removed in the call to class_interface_unregister()
500: */
501:
502: mutex_lock(&sp->mutex);
503: list_add_tail(&class_intf->node, &sp->interfaces);
504: if (class_intf->add_dev) {
505: class_dev_iter_init(&iter, parent, NULL, NULL);
506: while ((dev = class_dev_iter_next(&iter)))
507: class_intf->add_dev(dev);
508: class_dev_iter_exit(&iter);
509: }
510: mutex_unlock(&sp->mutex);
511:
512: return 0;
513: }
SourceFile:drivers/base/class.c]
|
| 40/2 |
2026/05/29 22:42 |
llm |
expert |
0m
Model:
Tokens:
input: 20423
output: 36
thoughts: 346
Thoughts: **Debugging a Potential Devlink Class Registration Issue**
Okay, so I'm trying to wrap my head around this `devlink_class` registration and why we're seeing this warning. It seems like the core issue is potentially related to a failure in registering the class in the first place, or perhaps an unexpected unregistration.
Initially, I was worried about `class_interface_register` failing because it depends on `class_to_subsys` β that could return NULL, but that can *only* happen if the initial `class_register` had bombed. But, wait a minute, `class_interface_register` doesn't actually allocate memory; it just adds to a list. It only returns `-ENODEV` if the arguments are bad, and it only returns `-EINVAL` if `class_to_subsys` returns NULL. So it appears that `class_interface_register` *cannot* fail in `devlink_class_init` under normal circumstances.
That means `devlink_class` is only unregistered if `class_register` fails. And `class_register`, well, that can fail due to memory allocation β all those `kzalloc_obj`, `kobject_set_name`, `kset_register`, `sysfs_create_groups` calls are potential failure points. If any of those fail, `devlink_class` *won't* be registered in the first place.
Then, the probe timeout work function (`deferred_probe_timeout_work_func`) runs, which calls `fw_devlink_drivers_done`, which *then* calls `class_for_each_device` on `devlink_class` β that's when the warning gets triggered. Right, that makes sense.
So, the next question is: is `devlink_class` used anywhere else? I know `device_link_add` does set `link->link_dev.class = &devlink_class;`. This is a crucial link between devices. If `devlink_class` isn't registered, what exactly happens in `device_link_add` when a device uses this class? I need to check the code there. Let's dig into `device_link_add` and see what's going on.
|
| 41/2 |
2026/05/29 22:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/core.c Name:device_link_add]
Results: map[SourceCode: 741: /**
742: * device_link_add - Create a link between two devices.
743: * @consumer: Consumer end of the link.
744: * @supplier: Supplier end of the link.
745: * @flags: Link flags.
746: *
747: * Return: On success, a device_link struct will be returned.
748: * On error or invalid flag settings, NULL will be returned.
749: *
750: * The caller is responsible for the proper synchronization of the link creation
751: * with runtime PM. First, setting the DL_FLAG_PM_RUNTIME flag will cause the
752: * runtime PM framework to take the link into account. Second, if the
753: * DL_FLAG_RPM_ACTIVE flag is set in addition to it, the supplier devices will
754: * be forced into the active meta state and reference-counted upon the creation
755: * of the link. If DL_FLAG_PM_RUNTIME is not set, DL_FLAG_RPM_ACTIVE will be
756: * ignored.
757: *
758: * If DL_FLAG_STATELESS is set in @flags, the caller of this function is
759: * expected to release the link returned by it directly with the help of either
760: * device_link_del() or device_link_remove().
761: *
762: * If that flag is not set, however, the caller of this function is handing the
763: * management of the link over to the driver core entirely and its return value
764: * can only be used to check whether or not the link is present. In that case,
765: * the DL_FLAG_AUTOREMOVE_CONSUMER and DL_FLAG_AUTOREMOVE_SUPPLIER device link
766: * flags can be used to indicate to the driver core when the link can be safely
767: * deleted. Namely, setting one of them in @flags indicates to the driver core
768: * that the link is not going to be used (by the given caller of this function)
769: * after unbinding the consumer or supplier driver, respectively, from its
770: * device, so the link can be deleted at that point. If none of them is set,
771: * the link will be maintained until one of the devices pointed to by it (either
772: * the consumer or the supplier) is unregistered.
773: *
774: * Also, if DL_FLAG_STATELESS, DL_FLAG_AUTOREMOVE_CONSUMER and
775: * DL_FLAG_AUTOREMOVE_SUPPLIER are not set in @flags (that is, a persistent
776: * managed device link is being added), the DL_FLAG_AUTOPROBE_CONSUMER flag can
777: * be used to request the driver core to automatically probe for a consumer
778: * driver after successfully binding a driver to the supplier device.
779: *
780: * The combination of DL_FLAG_STATELESS and one of DL_FLAG_AUTOREMOVE_CONSUMER,
781: * DL_FLAG_AUTOREMOVE_SUPPLIER, or DL_FLAG_AUTOPROBE_CONSUMER set in @flags at
782: * the same time is invalid and will cause NULL to be returned upfront.
783: * However, if a device link between the given @consumer and @supplier pair
784: * exists already when this function is called for them, the existing link will
785: * be returned regardless of its current type and status (the link's flags may
786: * be modified then). The caller of this function is then expected to treat
787: * the link as though it has just been created, so (in particular) if
788: * DL_FLAG_STATELESS was passed in @flags, the link needs to be released
789: * explicitly when not needed any more (as stated above).
790: *
791: * A side effect of the link creation is re-ordering of dpm_list and the
792: * devices_kset list by moving the consumer device and all devices depending
793: * on it to the ends of these lists (that does not happen to devices that have
794: * not been registered when this function is called).
795: *
796: * The supplier device is required to be registered when this function is called
797: * and NULL will be returned if that is not the case. The consumer device need
798: * not be registered, however.
799: */
800: struct device_link *device_link_add(struct device *consumer,
801: struct device *supplier, u32 flags)
802: {
803: struct device_link *link;
804:
805: if (!consumer || !supplier || consumer == supplier ||
806: flags & ~DL_ADD_VALID_FLAGS ||
807: (flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) ||
808: (flags & DL_FLAG_AUTOPROBE_CONSUMER &&
809: flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
810: DL_FLAG_AUTOREMOVE_SUPPLIER)))
811: return NULL;
812:
813: if (flags & DL_FLAG_PM_RUNTIME && flags & DL_FLAG_RPM_ACTIVE) {
814: if (pm_runtime_get_sync(supplier) < 0) {
815: pm_runtime_put_noidle(supplier);
816: return NULL;
817: }
818: }
819:
820: if (!(flags & DL_FLAG_STATELESS))
821: flags |= DL_FLAG_MANAGED;
822:
823: if (flags & DL_FLAG_SYNC_STATE_ONLY &&
824: !device_link_flag_is_sync_state_only(flags))
825: return NULL;
826:
827: device_links_write_lock();
828: device_pm_lock();
829:
830: /*
831: * If the supplier has not been fully registered yet or there is a
832: * reverse (non-SYNC_STATE_ONLY) dependency between the consumer and
833: * the supplier already in the graph, return NULL. If the link is a
834: * SYNC_STATE_ONLY link, we don't check for reverse dependencies
835: * because it only affects sync_state() callbacks.
836: */
837: if (!device_pm_initialized(supplier)
838: || (!(flags & DL_FLAG_SYNC_STATE_ONLY) &&
839: device_is_dependent(consumer, supplier))) {
840: link = NULL;
841: goto out;
842: }
843:
844: /*
845: * SYNC_STATE_ONLY links are useless once a consumer device has probed.
846: * So, only create it if the consumer hasn't probed yet.
847: */
848: if (flags & DL_FLAG_SYNC_STATE_ONLY &&
849: consumer->links.status != DL_DEV_NO_DRIVER &&
850: consumer->links.status != DL_DEV_PROBING) {
851: link = NULL;
852: goto out;
853: }
854:
855: /*
856: * DL_FLAG_AUTOREMOVE_SUPPLIER indicates that the link will be needed
857: * longer than for DL_FLAG_AUTOREMOVE_CONSUMER and setting them both
858: * together doesn't make sense, so prefer DL_FLAG_AUTOREMOVE_SUPPLIER.
859: */
860: if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER)
861: flags &= ~DL_FLAG_AUTOREMOVE_CONSUMER;
862:
863: list_for_each_entry(link, &supplier->links.consumers, s_node) {
864: if (link->consumer != consumer)
865: continue;
866:
867: if (device_link_test(link, DL_FLAG_INFERRED) &&
868: !(flags & DL_FLAG_INFERRED))
869: link->flags &= ~DL_FLAG_INFERRED;
870:
871: if (flags & DL_FLAG_PM_RUNTIME) {
872: if (!device_link_test(link, DL_FLAG_PM_RUNTIME)) {
873: pm_runtime_new_link(consumer);
874: link->flags |= DL_FLAG_PM_RUNTIME;
875: }
876: if (flags & DL_FLAG_RPM_ACTIVE)
877: refcount_inc(&link->rpm_active);
878: }
879:
880: if (flags & DL_FLAG_STATELESS) {
881: kref_get(&link->kref);
882: if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY) &&
883: !device_link_test(link, DL_FLAG_STATELESS)) {
884: link->flags |= DL_FLAG_STATELESS;
885: goto reorder;
886: } else {
887: link->flags |= DL_FLAG_STATELESS;
888: goto out;
889: }
890: }
891:
892: /*
893: * If the life time of the link following from the new flags is
894: * longer than indicated by the flags of the existing link,
895: * update the existing link to stay around longer.
896: */
897: if (flags & DL_FLAG_AUTOREMOVE_SUPPLIER) {
898: if (device_link_test(link, DL_FLAG_AUTOREMOVE_CONSUMER)) {
899: link->flags &= ~DL_FLAG_AUTOREMOVE_CONSUMER;
900: link->flags |= DL_FLAG_AUTOREMOVE_SUPPLIER;
901: }
902: } else if (!(flags & DL_FLAG_AUTOREMOVE_CONSUMER)) {
903: link->flags &= ~(DL_FLAG_AUTOREMOVE_CONSUMER |
904: DL_FLAG_AUTOREMOVE_SUPPLIER);
905: }
906: if (!device_link_test(link, DL_FLAG_MANAGED)) {
907: kref_get(&link->kref);
908: link->flags |= DL_FLAG_MANAGED;
909: device_link_init_status(link, consumer, supplier);
910: }
911: if (device_link_test(link, DL_FLAG_SYNC_STATE_ONLY) &&
912: !(flags & DL_FLAG_SYNC_STATE_ONLY)) {
913: link->flags &= ~DL_FLAG_SYNC_STATE_ONLY;
914: goto reorder;
915: }
916:
917: goto out;
918: }
919:
920: link = kzalloc_obj(*link);
921: if (!link)
922: goto out;
923:
924: refcount_set(&link->rpm_active, 1);
925:
926: get_device(supplier);
927: link->supplier = supplier;
928: INIT_LIST_HEAD(&link->s_node);
929: get_device(consumer);
930: link->consumer = consumer;
931: INIT_LIST_HEAD(&link->c_node);
932: link->flags = flags;
933: kref_init(&link->kref);
934:
935: link->link_dev.class = &devlink_class;
936: device_set_pm_not_required(&link->link_dev);
937: dev_set_name(&link->link_dev, "%s:%s--%s:%s",
938: dev_bus_name(supplier), dev_name(supplier),
939: dev_bus_name(consumer), dev_name(consumer));
940: if (device_register(&link->link_dev)) {
941: put_device(&link->link_dev);
942: link = NULL;
943: goto out;
944: }
945:
946: if (flags & DL_FLAG_PM_RUNTIME) {
947: if (flags & DL_FLAG_RPM_ACTIVE)
948: refcount_inc(&link->rpm_active);
949:
950: pm_runtime_new_link(consumer);
951: }
952:
953: /* Determine the initial link state. */
954: if (flags & DL_FLAG_STATELESS)
955: link->status = DL_STATE_NONE;
956: else
957: device_link_init_status(link, consumer, supplier);
958:
959: /*
960: * Some callers expect the link creation during consumer driver probe to
961: * resume the supplier even without DL_FLAG_RPM_ACTIVE.
962: */
963: if (link->status == DL_STATE_CONSUMER_PROBE &&
964: flags & DL_FLAG_PM_RUNTIME)
965: pm_runtime_resume(supplier);
966:
967: list_add_tail_rcu(&link->s_node, &supplier->links.consumers);
968: list_add_tail_rcu(&link->c_node, &consumer->links.suppliers);
969:
970: if (flags & DL_FLAG_SYNC_STATE_ONLY) {
971: dev_dbg(consumer,
972: "Linked as a sync state only consumer to %s\n",
973: dev_name(supplier));
974: goto out;
975: }
976:
977: reorder:
978: /*
979: * Move the consumer and all of the devices depending on it to the end
980: * of dpm_list and the devices_kset list.
981: *
982: * It is necessary to hold dpm_list locked throughout all that or else
983: * we may end up suspending with a wrong ordering of it.
984: */
985: device_reorder_to_tail(consumer, NULL);
986:
987: dev_dbg(consumer, "Linked as a consumer to %s\n", dev_name(supplier));
988:
989: out:
990: device_pm_unlock();
991: device_links_write_unlock();
992:
993: if ((flags & DL_FLAG_PM_RUNTIME && flags & DL_FLAG_RPM_ACTIVE) && !link)
994: pm_runtime_put(supplier);
995:
996: return link;
997: }
SourceFile:drivers/base/core.c]
|
| 42/2 |
2026/05/29 22:42 |
llm |
expert |
0m
Model:
Tokens:
input: 25613
output: 34
thoughts: 42
Thoughts: Okay, let's break this down.
**Diving into the Device Registration Flow**
So, if the `devlink_class` isn't already set up and registered, then `device_register(&link->link_dev)` is going to be invoked. My brain is already jumping ahead... I need to see *exactly* what `device_register` does. Right, so I know I need to trace that call through. Okay, `device_register` leads straight to `device_add`. Perfect. Now I know where to look in the code.
|
| 43/2 |
2026/05/29 22:42 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/base/core.c Name:device_add]
Results: map[SourceCode:3612: /**
3613: * device_add - add device to device hierarchy.
3614: * @dev: device.
3615: *
3616: * This is part 2 of device_register(), though may be called
3617: * separately _iff_ device_initialize() has been called separately.
3618: *
3619: * This adds @dev to the kobject hierarchy via kobject_add(), adds it
3620: * to the global and sibling lists for the device, then
3621: * adds it to the other relevant subsystems of the driver model.
3622: *
3623: * Do not call this routine or device_register() more than once for
3624: * any device structure. The driver model core is not designed to work
3625: * with devices that get unregistered and then spring back to life.
3626: * (Among other things, it's very hard to guarantee that all references
3627: * to the previous incarnation of @dev have been dropped.) Allocate
3628: * and register a fresh new struct device instead.
3629: *
3630: * NOTE: _Never_ directly free @dev after calling this function, even
3631: * if it returned an error! Always use put_device() to give up your
3632: * reference instead.
3633: *
3634: * Rule of thumb is: if device_add() succeeds, you should call
3635: * device_del() when you want to get rid of it. If device_add() has
3636: * *not* succeeded, use *only* put_device() to drop the reference
3637: * count.
3638: */
3639: int device_add(struct device *dev)
3640: {
3641: struct subsys_private *sp;
3642: struct device *parent;
3643: struct kobject *kobj;
3644: struct class_interface *class_intf;
3645: int error = -EINVAL;
3646: struct kobject *glue_dir = NULL;
3647:
3648: dev = get_device(dev);
3649: if (!dev)
3650: goto done;
3651:
3652: if (!dev->p) {
3653: error = device_private_init(dev);
3654: if (error)
3655: goto done;
3656: }
3657:
3658: /*
3659: * for statically allocated devices, which should all be converted
3660: * some day, we need to initialize the name. We prevent reading back
3661: * the name, and force the use of dev_name()
3662: */
3663: if (dev->init_name) {
3664: error = dev_set_name(dev, "%s", dev->init_name);
3665: dev->init_name = NULL;
3666: }
3667:
3668: if (dev_name(dev))
3669: error = 0;
3670: /* subsystems can specify simple device enumeration */
3671: else if (dev->bus && dev->bus->dev_name)
3672: error = dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
3673: else
3674: error = -EINVAL;
3675: if (error)
3676: goto name_error;
3677:
3678: pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
3679:
3680: parent = get_device(dev->parent);
3681: kobj = get_device_parent(dev, parent);
3682: if (IS_ERR(kobj)) {
3683: error = PTR_ERR(kobj);
3684: goto parent_error;
3685: }
3686: if (kobj)
3687: dev->kobj.parent = kobj;
3688:
3689: /* use parent numa_node */
3690: if (parent && (dev_to_node(dev) == NUMA_NO_NODE))
3691: set_dev_node(dev, dev_to_node(parent));
3692:
3693: /* first, register with generic layer. */
3694: /* we require the name to be set before, and pass NULL */
3695: error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
3696: if (error) {
3697: glue_dir = kobj;
3698: goto Error;
3699: }
3700:
3701: /* notify platform of device entry */
3702: device_platform_notify(dev);
3703:
3704: error = device_create_file(dev, &dev_attr_uevent);
3705: if (error)
3706: goto attrError;
3707:
3708: error = device_add_class_symlinks(dev);
3709: if (error)
3710: goto SymlinkError;
3711: error = device_add_attrs(dev);
3712: if (error)
3713: goto AttrsError;
3714: error = bus_add_device(dev);
3715: if (error)
3716: goto BusError;
3717: error = dpm_sysfs_add(dev);
3718: if (error)
3719: goto DPMError;
3720: device_pm_add(dev);
3721:
3722: if (MAJOR(dev->devt)) {
3723: error = device_create_file(dev, &dev_attr_dev);
3724: if (error)
3725: goto DevAttrError;
3726:
3727: error = device_create_sys_dev_entry(dev);
3728: if (error)
3729: goto SysEntryError;
3730:
3731: devtmpfs_create_node(dev);
3732: }
3733:
3734: /* Notify clients of device addition. This call must come
3735: * after dpm_sysfs_add() and before kobject_uevent().
3736: */
3737: bus_notify(dev, BUS_NOTIFY_ADD_DEVICE);
3738: kobject_uevent(&dev->kobj, KOBJ_ADD);
3739:
3740: /*
3741: * Check if any of the other devices (consumers) have been waiting for
3742: * this device (supplier) to be added so that they can create a device
3743: * link to it.
3744: *
3745: * This needs to happen after device_pm_add() because device_link_add()
3746: * requires the supplier be registered before it's called.
3747: *
3748: * But this also needs to happen before bus_probe_device() to make sure
3749: * waiting consumers can link to it before the driver is bound to the
3750: * device and the driver sync_state callback is called for this device.
3751: */
3752: if (dev->fwnode && !dev->fwnode->dev) {
3753: dev->fwnode->dev = dev;
3754: fw_devlink_link_device(dev);
3755: }
3756:
3757: /*
3758: * The moment the device was linked into the bus's "klist_devices" in
3759: * bus_add_device() then it's possible that probe could have been
3760: * attempted in a different thread via userspace loading a driver
3761: * matching the device. "ready_to_probe" being unset would have
3762: * blocked those attempts. Now that all of the above initialization has
3763: * happened, unblock probe. If probe happens through another thread
3764: * after this point but before bus_probe_device() runs then it's fine.
3765: * bus_probe_device() -> device_initial_probe() -> __device_attach()
3766: * will notice (under device_lock) that the device is already bound.
3767: */
3768: device_lock(dev);
3769: dev_set_ready_to_probe(dev);
3770: device_unlock(dev);
3771:
3772: bus_probe_device(dev);
3773:
3774: /*
3775: * If all driver registration is done and a newly added device doesn't
3776: * match with any driver, don't block its consumers from probing in
3777: * case the consumer device is able to operate without this supplier.
3778: */
3779: if (dev->fwnode && fw_devlink_drv_reg_done && !dev_can_match(dev))
3780: fw_devlink_unblock_consumers(dev);
3781:
3782: if (parent)
3783: klist_add_tail(&dev->p->knode_parent,
3784: &parent->p->klist_children);
3785:
3786: sp = class_to_subsys(dev->class);
3787: if (sp) {
3788: mutex_lock(&sp->mutex);
3789: /* tie the class to the device */
3790: klist_add_tail(&dev->p->knode_class, &sp->klist_devices);
3791:
3792: /* notify any interfaces that the device is here */
3793: list_for_each_entry(class_intf, &sp->interfaces, node)
3794: if (class_intf->add_dev)
3795: class_intf->add_dev(dev);
3796: mutex_unlock(&sp->mutex);
3797: subsys_put(sp);
3798: }
3799: done:
3800: put_device(dev);
3801: return error;
3802: SysEntryError:
3803: if (MAJOR(dev->devt))
3804: device_remove_file(dev, &dev_attr_dev);
3805: DevAttrError:
3806: device_pm_remove(dev);
3807: dpm_sysfs_remove(dev);
3808: DPMError:
3809: device_set_driver(dev, NULL);
3810: bus_remove_device(dev);
3811: BusError:
3812: device_remove_attrs(dev);
3813: AttrsError:
3814: device_remove_class_symlinks(dev);
3815: SymlinkError:
3816: device_remove_file(dev, &dev_attr_uevent);
3817: attrError:
3818: device_platform_notify_remove(dev);
3819: kobject_uevent(&dev->kobj, KOBJ_REMOVE);
3820: glue_dir = get_glue_dir(dev);
3821: kobject_del(&dev->kobj);
3822: Error:
3823: cleanup_glue_dir(dev, glue_dir);
3824: parent_error:
3825: put_device(parent);
3826: name_error:
3827: kfree(dev->p);
3828: dev->p = NULL;
3829: goto done;
3830: }
SourceFile:drivers/base/core.c]
|
| 44/2 |
2026/05/29 22:42 |
llm |
expert |
0m
Model:
Tokens:
input: 29154
output: 34
thoughts: 622
Thoughts: **Analyzing a Potential Sysfs Issue**
Okay, so I'm looking at this `device_add` function and how it interacts with the `devlink_class`. My initial thought is about the scenario where `devlink_class` isn't registered β that `sp` is NULL. The code clearly handles this case by skipping the usual class-device association and interface notification steps. This immediately raises a few questions in my mind: Could this lead to a crash? Memory corruption? Denial of service?
After some consideration, the answer is no, no, and well, maybe a *very* limited denial of service. The code doesn't crash or corrupt memory directly. What *does* happen, if `devlink_class` fails to initialize, is that the device links won't have the expected sysfs symlinks. This, in turn, prevents `fw_devlink_drivers_done` from iterating over them. But this only happens if `devlink_class_init` failed. If that's the case, we're already in a pretty dire situation β a fundamental memory allocation failure during the boot process. Everything will likely be failing at that point.
The more I look at this, the more this points to a fault injection bug, likely from syzkaller. Syzkaller is injecting a memory allocation failure during `devlink_class_init`, which causes the initialization to fail. Later, `deferred_probe_timeout_work_func` gets triggered and throws a warning. However, a `WARN(1, ...)` in `class_for_each_device` is the only result, unless `panic_on_warn` is set, a debug setting. In production builds, this just results in a warning printed to the console, and execution continues. There's no security impact here.
I need to confirm there is no other way to trigger this.
My next step is to examine whether user-space can unregister `devlink_class`. Good news, the `class_unregister` function isn't even exposed to userspace. They can't unregister it. What about `deferred_probe_timeout_work_func`? Even if a user could somehow trigger *that*, it will only produce the warning if `devlink_class` *isn't* registered. If `devlink_class` *is* registered, everything works as expected. Therefore, the only way to get this warning is through `devlink_class` not being registered in the first place, which only happens from a failure in `devlink_class_init` during boot.
I should also check if any other code paths could unregister the `devlink_class`.
|
| 45/2 |
2026/05/29 22:42 |
tool |
grepper |
0m
Args: map[Expression:class_unregister\(&devlink_class\) PathPrefix:drivers/base/]
Results: map[Output:drivers/base/core.c=715=static int __init devlink_class_init(void)
--
drivers/base/core.c-724- if (ret)
drivers/base/core.c:725: class_unregister(&devlink_class);
drivers/base/core.c-726-
]
|
| 46/2 |
2026/05/29 22:42 |
llm |
expert |
0m
Model:
Tokens:
input: 30472
output: 66
thoughts: 503
Thoughts: **My Assessment of the "devlink" Class Registration Issue**
Okay, let's break this down. My analysis points to a very specific and limited issue centered around the `devlink_class`. It looks like the core problem is rooted in the initialization, specifically within `devlink_class_init`. I'm concerned with whether or not the initialization can be called multiple times or unloaded, but my thoughts lead me to conclude that it's an `__init` function, so it's only ever called once, during system boot, after which its resources are freed. It *can't* be called again. That means the state of the `devlink_class` is fundamentally fixed at boot time.
Therefore, this "bug" is entirely the result of injected faults during the boot process. It's not something that can be exploited in a typical, running system.
So, here's the quick summary I've come up with:
1. The warning "class\_for\_each\_device called for class 'devlink' before it was registered" stems from `class_for_each_device` when `class_to_subsys` returns NULL.
2. `class_to_subsys` returns NULL for `devlink_class` only if `devlink_class` was never successfully registered in the first place.
3. `devlink_class` is registered within `devlink_class_init`, which is a `postcore_initcall` that's executed precisely once during the kernel's boot sequence.
4. If `devlink_class_init` fails (e.g., due to a memory allocation problem injected by something like syzkaller), `devlink_class` remains unregistered.
5. Later, during a function called `deferred_probe_timeout_work_func` (scheduled by `deferred_probe_initcall`) calls `fw_devlink_drivers_done`, which unconditionally calls `class_for_each_device(&devlink_class, ...)`.
6. This sequence triggers the warning message.
7. Because `devlink_class_init` only runs during boot, an unprivileged user cannot influence whether it succeeds or fails. The failure is artificially introduced, by fault injection tools.
8. The warning doesn't lead to any memory corruption, crashes in production builds, or any kind of exploitable situation.
9. In short, there is no real security impact.
So, given this assessment, I'm confident in concluding that there's no actual security risk. I'll set all results to false.
|
| 47/2 |
2026/05/29 22:42 |
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]
|
| 48/2 |
2026/05/29 22:42 |
llm |
expert |
0m
Model:
Tokens:
input: 31667
output: 539
thoughts: 15
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Finalizing the Analysis & Drafting the Response**
Alright, the deep dive is done. Every angle, every nuance, the evidence, the counter-arguments β I've processed it all. The analysis is, if I may say so myself, robust and comprehensive. It's solid. There are no loose ends that I can see, no glaring omissions, nothing that's nagging at the back of my mind. The methodology was sound, the data was rigorously interrogated, and the conclusions are defensible.
Now it's time to translate all that internal processing into a crisp, authoritative response. This isnβt about just regurgitating the findings; it's about synthesizing them into a clear, concise, and persuasive argument. I'll need to articulate the essence of the work, and distill the complexities. Itβs about building a compelling narrative that demonstrates not just *what* Iβve found, but *why* it matters, and *what* it means in the broader context of the field. Let's get this finalized.
|
| 49/1 |
2026/05/29 22:42 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the kernel source code and the provided bug report,
here is the assessment of the bug:
**Root Cause Analysis:**
The warning `class_for_each_device called for class 'devlink' before it was
registered` is triggered in `class_for_each_device()` when it attempts to
iterate over devices of a class that has not been registered (i.e.,
`class_to_subsys()` returns `NULL`).
In this specific case, it is attempting to iterate over `devlink_class`. The
registration of `devlink_class` happens in `devlink_class_init()`, which is a
`postcore_initcall` executed exactly once during the early stages of kernel
boot. If this initialization fails (e.g., due to an out-of-memory condition or
artificial memory allocation fault injection by a fuzzer like syzkaller), the
class remains unregistered.
Later in the boot process (or when the deferred probe timeout expires),
`deferred_probe_timeout_work_func()` is executed. This function calls
`fw_devlink_drivers_done()`, which unconditionally invokes
`class_for_each_device(&devlink_class, ...)`. Because the class was never
successfully registered, the kernel hits the `WARN(1, ...)` statement in
`class_for_each_device()`.
**Security Impact:**
1. **Exploitability:** The bug is not exploitable. It is a simple state mismatch
where a subsystem tries to iterate over an unregistered class. The
`class_for_each_device()` function safely handles this by printing a warning and
returning `-EINVAL`. There is no memory corruption, out-of-bounds access, or
use-after-free involved.
2. **Denial of Service:** In production builds (where `panic_on_warn` is
disabled), this bug merely prints a warning to the kernel log and continues
execution normally. It does not crash the system, cause deadlocks, or lead to
resource leaks.
3. **Triggerability:** The failure of `devlink_class_init()` can only occur
during kernel boot. An unprivileged user, remote attacker, or malicious device
cannot trigger this initialization failure post-boot. The bug report is a direct
result of syzkaller injecting memory allocation failures (`fault-injection`)
during the boot process.
**Conclusion:**
This is a benign warning caused by simulated resource exhaustion during kernel
initialization. It has no security implications and cannot be triggered or
exploited by any user or external entity on a running production system.]
|