| 0/0 |
2026/05/19 05:33 |
flow |
assessment-security |
29m
Results: map[DenialOfService:false Explanation:Based on the analysis of the provided kernel bug report and source code, here is
the assessment of the bug's security impact:
**1. Not Accessible to Unprivileged Users or User Namespaces:**
The bug is triggered via the `COMEDI_DEVCONFIG` ioctl on a comedi device node
(e.g., `/dev/comedi0`), which is handled by `do_devconfig_ioctl()` in
`drivers/comedi/comedi_fops.c`.
Looking at the source code for `do_devconfig_ioctl()`:
```c
static int do_devconfig_ioctl(struct comedi_device *dev,
struct comedi_devconfig __user *arg)
{
struct comedi_devconfig it;
lockdep_assert_held(&dev->mutex);
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
// ...
```
The function explicitly checks for `CAP_SYS_ADMIN` using `capable()`, which
checks the capability against the initial user namespace (`init_user_ns`).
Therefore, an unprivileged user cannot trigger this bug, even if they create a
new user namespace and gain capabilities within it.
**2. Not Exploitable and No Denial of Service:**
The warning is triggered in `request_threaded_irq()` in `kernel/irq/manage.c`
when the user-provided IRQ number corresponds to a per-CPU devid IRQ:
```c
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
```
When the `WARN_ON` condition is met, the kernel simply prints a warning stack
trace to the kernel log and returns `-EINVAL`. It does not cause a kernel panic,
memory corruption, deadlocks, or any other exploitable condition. The system
continues to operate normally, meaning there is no Denial of Service (DoS) or
privilege escalation risk.
**Conclusion:**
This bug is a harmless warning that can only be triggered by a highly privileged
user (root / `CAP_SYS_ADMIN` in the initial namespace) providing an invalid IRQ
number to a legacy comedi driver configuration ioctl. It has no security impact. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:false RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:false]
|
| 1/1 |
2026/05/19 05:33 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/05/19 05:33 |
action |
kernel-checkouter |
1m
Results: map[KernelSrc:/app/workdir/cache/src/f22188f4d35f9a8b749bc492da99199be9f53a79]
|
| 3/1 |
2026/05/19 05:35 |
action |
kernel-builder |
17m
Results: map[KernelObj:/app/workdir/cache/build/6ea572b9689785f8cb601f55a0c69576791b9ea4]
|
| 4/1 |
2026/05/19 05:53 |
action |
codesearch-prepare |
8m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/05/19 06:01 |
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 greeping
".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).
Don't make assumptions; verify them with source code access. 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 completly 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 tranfers, that's 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 exploitabability 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 Confidetial 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.
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 ]------------
WARNING: [irq_settings_is_per_cpu_devid(desc)] kernel/irq/manage.c:2150 at request_threaded_irq+0x320/0x38c kernel/irq/manage.c:2150, CPU#0: syz.6.830/10032
Modules linked in:
CPU: 0 UID: 0 PID: 10032 Comm: syz.6.830 Not tainted syzkaller #0 PREEMPT
Hardware name: riscv-virtio,qemu (DT)
epc : request_threaded_irq+0x320/0x38c kernel/irq/manage.c:2150
ra : request_threaded_irq+0x320/0x38c kernel/irq/manage.c:2150
epc : ffffffff8033dfb0 ra : ffffffff8033dfb0 sp : ffff8f80028577c0
gp : ffffffff8a22a0c0 tp : ffffaf801c3bb500 t0 : 0000000000000000
t1 : ffffffff91601820 t2 : ffffffff91601820 s0 : ffff8f8002857820
s1 : 0000000000000000 a0 : 0000000000000005 a1 : 0000000000000000
a2 : 0000000000080000 a3 : ffffffff8033dfb0 a4 : ffff8f8004cee020
a5 : 0000000000014020 a6 : 0000000000000003 a7 : ffffffff86864460
s2 : 0000000000200000 s3 : ffffaf8011d1a000 s4 : 0000000000000002
s5 : ffffffff84bcbc14 s6 : ffffaf801d69b000 s7 : 0000000000000000
s8 : ffffffff877b5b00 s9 : 0000000000020000 s10: ffffaf801d69b000
s11: 0000000000000001 t3 : 0000000000000001 t4 : 0000000000001fff
t5 : 00000000000000c8 t6 : 0000000000000002 ssp : 0000000000000000
status: 0000000200000120 badaddr: ffffffff8033dfb0 cause: 0000000000000003
[<ffffffff8033dfb0>] request_threaded_irq+0x320/0x38c kernel/irq/manage.c:2150
[<ffffffff84bcd638>] request_irq include/linux/interrupt.h:176 [inline]
[<ffffffff84bcd638>] parport_attach drivers/comedi/drivers/comedi_parport.c:235 [inline]
[<ffffffff84bcd638>] parport_attach+0x780/0xb14 drivers/comedi/drivers/comedi_parport.c:224
[<ffffffff84bbdd20>] comedi_device_attach+0x350/0x7ec drivers/comedi/drivers.c:1069
[<ffffffff84ba9776>] do_devconfig_ioctl+0x1a2/0x654 drivers/comedi/comedi_fops.c:928
[<ffffffff84bb3adc>] comedi_unlocked_ioctl+0x750/0x30e0 drivers/comedi/comedi_fops.c:2300
[<ffffffff80cfa9cc>] vfs_ioctl fs/ioctl.c:51 [inline]
[<ffffffff80cfa9cc>] __do_sys_ioctl fs/ioctl.c:597 [inline]
[<ffffffff80cfa9cc>] __se_sys_ioctl fs/ioctl.c:583 [inline]
[<ffffffff80cfa9cc>] __riscv_sys_ioctl+0x17c/0x1e4 fs/ioctl.c:583
[<ffffffff80077f1e>] syscall_handler+0x92/0x114 arch/riscv/include/asm/syscall.h:112
[<ffffffff8642fdbe>] do_trap_ecall_u+0x402/0x680 arch/riscv/kernel/traps.c:342
[<ffffffff8645a152>] handle_exception+0x15e/0x16a arch/riscv/kernel/entry.S:232
Reply: Based on the analysis of the provided kernel bug report and source code, here is the assessment of the bug's security impact:
**1. Not Accessible to Unprivileged Users or User Namespaces:**
The bug is triggered via the `COMEDI_DEVCONFIG` ioctl on a comedi device node (e.g., `/dev/comedi0`), which is handled by `do_devconfig_ioctl()` in `drivers/comedi/comedi_fops.c`.
Looking at the source code for `do_devconfig_ioctl()`:
```c
static int do_devconfig_ioctl(struct comedi_device *dev,
struct comedi_devconfig __user *arg)
{
struct comedi_devconfig it;
lockdep_assert_held(&dev->mutex);
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
// ...
```
The function explicitly checks for `CAP_SYS_ADMIN` using `capable()`, which checks the capability against the initial user namespace (`init_user_ns`). Therefore, an unprivileged user cannot trigger this bug, even if they create a new user namespace and gain capabilities within it.
**2. Not Exploitable and No Denial of Service:**
The warning is triggered in `request_threaded_irq()` in `kernel/irq/manage.c` when the user-provided IRQ number corresponds to a per-CPU devid IRQ:
```c
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
```
When the `WARN_ON` condition is met, the kernel simply prints a warning stack trace to the kernel log and returns `-EINVAL`. It does not cause a kernel panic, memory corruption, deadlocks, or any other exploitable condition. The system continues to operate normally, meaning there is no Denial of Service (DoS) or privilege escalation risk.
**Conclusion:**
This bug is a harmless warning that can only be triggered by a highly privileged user (root / `CAP_SYS_ADMIN` in the initial namespace) providing an invalid IRQ number to a legacy comedi driver configuration ioctl. It has no security impact.
|
| 6/2 |
2026/05/19 06:01 |
llm |
expert |
0m
Model:
Tokens:
input: 5783
output: 137
thoughts: 397
Thoughts: **Analyzing a Syzkaller Bug Report**
Okay, here's what I'm seeing: A syzkaller bug report flagged a warning related to interrupt settings within the kernel's `irq` management code. The specific warning points to `irq_settings_is_per_cpu_devid(desc)` being called from `request_threaded_irq` in `kernel/irq/manage.c` at line 2150. That's immediately interesting, especially with the CPU context (CPU#0) and the specific syzkaller process ID (syz.6.830/10032). It gives me a good starting point for reproducing this issue.
The stack trace gives us a clear path: it originates in `request_threaded_irq`, which then traces back through `request_irq` (an inline function, which makes it a little trickier, but still manageable), into `parport_attach` within `drivers/comedi/drivers/comedi_parport.c`. From there, it jumps into `comedi_device_attach` and ultimately lands in `do_devconfig_ioctl` in `drivers/comedi/comedi_fops.c`. Finally `comedi_unlocked_ioctl` and some standard kernel functions for handling ioctls.
So, it seems we're dealing with an issue related to how the Comedi drivers interact with interrupt requests, specifically when configuring a device via an ioctl.
Now I need to zero in on the relevant code. I need to examine `drivers/comedi/drivers/comedi_parport.c` line 235 and `drivers/comedi/comedi_fops.c` line 928, to see exactly how these functions call and use the `request_threaded_irq` function. This should give me some context about what happens when the interrupt request is created. I expect to see the key to the problem in the interaction between these drivers, and how they handle interrupts.
|
| 7/2 |
2026/05/19 06:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/comedi/drivers/comedi_parport.c IncludeLines:true Name:parport_attach]
Results: map[SourceCode: 224: static int parport_attach(struct comedi_device *dev,
225: struct comedi_devconfig *it)
226: {
227: struct comedi_subdevice *s;
228: int ret;
229:
230: ret = comedi_request_region(dev, it->options[0], 0x03);
231: if (ret)
232: return ret;
233:
234: if (it->options[1]) {
235: ret = request_irq(it->options[1], parport_interrupt, 0,
236: dev->board_name, dev);
237: if (ret == 0)
238: dev->irq = it->options[1];
239: }
240:
241: ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3);
242: if (ret)
243: return ret;
244:
245: /* Digial I/O subdevice - Parallel port DATA register */
246: s = &dev->subdevices[0];
247: s->type = COMEDI_SUBD_DIO;
248: s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
249: s->n_chan = 8;
250: s->maxdata = 1;
251: s->range_table = &range_digital;
252: s->insn_bits = parport_data_reg_insn_bits;
253: s->insn_config = parport_data_reg_insn_config;
254:
255: /* Digial Input subdevice - Parallel port STATUS register */
256: s = &dev->subdevices[1];
257: s->type = COMEDI_SUBD_DI;
258: s->subdev_flags = SDF_READABLE;
259: s->n_chan = 5;
260: s->maxdata = 1;
261: s->range_table = &range_digital;
262: s->insn_bits = parport_status_reg_insn_bits;
263:
264: /* Digial Output subdevice - Parallel port CONTROL register */
265: s = &dev->subdevices[2];
266: s->type = COMEDI_SUBD_DO;
267: s->subdev_flags = SDF_WRITABLE;
268: s->n_chan = 4;
269: s->maxdata = 1;
270: s->range_table = &range_digital;
271: s->insn_bits = parport_ctrl_reg_insn_bits;
272:
273: if (dev->irq) {
274: /* Digial Input subdevice - Interrupt support */
275: s = &dev->subdevices[3];
276: dev->read_subdev = s;
277: s->type = COMEDI_SUBD_DI;
278: s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
279: s->n_chan = 1;
280: s->maxdata = 1;
281: s->range_table = &range_digital;
282: s->insn_bits = parport_intr_insn_bits;
283: s->len_chanlist = 1;
284: s->do_cmdtest = parport_intr_cmdtest;
285: s->do_cmd = parport_intr_cmd;
286: s->cancel = parport_intr_cancel;
287: }
288:
289: outb(0, dev->iobase + PARPORT_DATA_REG);
290: outb(0, dev->iobase + PARPORT_CTRL_REG);
291:
292: return 0;
293: }
SourceFile:drivers/comedi/drivers/comedi_parport.c]
|
| 8/2 |
2026/05/19 06:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/comedi/comedi_fops.c IncludeLines:true Name:do_devconfig_ioctl]
Results: map[SourceCode: 871: /*
872: * COMEDI_DEVCONFIG ioctl
873: * attaches (and configures) or detaches a legacy device
874: *
875: * arg:
876: * pointer to comedi_devconfig structure (NULL if detaching)
877: *
878: * reads:
879: * comedi_devconfig structure (if attaching)
880: *
881: * writes:
882: * nothing
883: */
884: static int do_devconfig_ioctl(struct comedi_device *dev,
885: struct comedi_devconfig __user *arg)
886: {
887: struct comedi_devconfig it;
888:
889: lockdep_assert_held(&dev->mutex);
890: if (!capable(CAP_SYS_ADMIN))
891: return -EPERM;
892:
893: if (!arg) {
894: int rc = 0;
895:
896: if (dev->attached) {
897: down_write(&dev->attach_lock);
898: if (is_device_busy(dev)) {
899: rc = -EBUSY;
900: } else {
901: struct module *driver_module =
902: dev->driver->module;
903:
904: comedi_device_detach_locked(dev);
905: module_put(driver_module);
906: }
907: up_write(&dev->attach_lock);
908: }
909: return rc;
910: }
911:
912: if (copy_from_user(&it, arg, sizeof(it)))
913: return -EFAULT;
914:
915: it.board_name[COMEDI_NAMELEN - 1] = 0;
916:
917: if (it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
918: dev_warn(dev->class_dev,
919: "comedi_config --init_data is deprecated\n");
920: return -EINVAL;
921: }
922:
923: if (dev->minor >= comedi_num_legacy_minors)
924: /* don't re-use dynamically allocated comedi devices */
925: return -EBUSY;
926:
927: /* This increments the driver module count on success. */
928: return comedi_device_attach(dev, &it);
929: }
SourceFile:drivers/comedi/comedi_fops.c]
|
| 9/2 |
2026/05/19 06:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/comedi/drivers.c IncludeLines:true Name:comedi_device_attach]
Results: map[SourceCode:1023: int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
1024: {
1025: struct comedi_driver *driv;
1026: int ret;
1027:
1028: lockdep_assert_held(&dev->mutex);
1029: if (dev->attached)
1030: return -EBUSY;
1031:
1032: mutex_lock(&comedi_drivers_list_lock);
1033: for (driv = comedi_drivers; driv; driv = driv->next) {
1034: if (!try_module_get(driv->module))
1035: continue;
1036: if (driv->num_names) {
1037: dev->board_ptr = comedi_recognize(driv, it->board_name);
1038: if (dev->board_ptr)
1039: break;
1040: } else if (strcmp(driv->driver_name, it->board_name) == 0) {
1041: break;
1042: }
1043: module_put(driv->module);
1044: }
1045: if (!driv) {
1046: /* recognize has failed if we get here */
1047: /* report valid board names before returning error */
1048: for (driv = comedi_drivers; driv; driv = driv->next) {
1049: if (!try_module_get(driv->module))
1050: continue;
1051: comedi_report_boards(driv);
1052: module_put(driv->module);
1053: }
1054: ret = -EIO;
1055: goto out;
1056: }
1057: if (!driv->attach) {
1058: /* driver does not support manual configuration */
1059: dev_warn(dev->class_dev,
1060: "driver '%s' does not support attach using comedi_config\n",
1061: driv->driver_name);
1062: module_put(driv->module);
1063: ret = -EIO;
1064: goto out;
1065: }
1066: dev->driver = driv;
1067: dev->board_name = dev->board_ptr ? *(const char **)dev->board_ptr
1068: : dev->driver->driver_name;
1069: ret = driv->attach(dev, it);
1070: if (ret >= 0)
1071: ret = comedi_device_postconfig(dev);
1072: if (ret < 0) {
1073: comedi_device_detach(dev);
1074: module_put(driv->module);
1075: }
1076: /* On success, the driver module count has been incremented. */
1077: out:
1078: mutex_unlock(&comedi_drivers_list_lock);
1079: return ret;
1080: }
SourceFile:drivers/comedi/drivers.c]
|
| 10/2 |
2026/05/19 06:01 |
llm |
expert |
0m
Model:
Tokens:
input: 9702
output: 43
thoughts: 108
Thoughts: Here's my analysis of the situation:
**Investigating a Kernel Interrupt Handling Warning**
Okay, so I've got a problem. It seems like the kernel is spitting out a WARNING during the `request_threaded_irq` call. The message is pretty explicit: `WARNING: [irq_settings_is_per_cpu_devid(desc)] kernel/irq/manage.c:2150`. That's pointing directly to a potential issue within the interrupt management code, specifically within `kernel/irq/manage.c` at line 2150. I'm going to want to take a close look at that line in the source code; the `irq_settings_is_per_cpu_devid` function likely contains some important information, and the fact that it's generating a warning means something's not right with how the interrupt is being configured or requested. I'm especially interested in what the `desc` variable passed to that function contains, as that's probably the key to diagnosing the interrupt setup.
|
| 11/2 |
2026/05/19 06:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:kernel/irq/manage.c IncludeLines:true Name:request_threaded_irq]
Results: map[SourceCode:2075: /**
2076: * request_threaded_irq - allocate an interrupt line
2077: * @irq: Interrupt line to allocate
2078: * @handler: Function to be called when the IRQ occurs.
2079: * Primary handler for threaded interrupts.
2080: * If handler is NULL and thread_fn != NULL
2081: * the default primary handler is installed.
2082: * @thread_fn: Function called from the irq handler thread
2083: * If NULL, no irq thread is created
2084: * @irqflags: Interrupt type flags
2085: * @devname: An ascii name for the claiming device
2086: * @dev_id: A cookie passed back to the handler function
2087: *
2088: * This call allocates interrupt resources and enables the interrupt line
2089: * and IRQ handling. From the point this call is made your handler function
2090: * may be invoked. Since your handler function must clear any interrupt the
2091: * board raises, you must take care both to initialise your hardware and to
2092: * set up the interrupt handler in the right order.
2093: *
2094: * If you want to set up a threaded irq handler for your device then you
2095: * need to supply @handler and @thread_fn. @handler is still called in hard
2096: * interrupt context and has to check whether the interrupt originates from
2097: * the device. If yes it needs to disable the interrupt on the device and
2098: * return IRQ_WAKE_THREAD which will wake up the handler thread and run
2099: * @thread_fn. This split handler design is necessary to support shared
2100: * interrupts.
2101: *
2102: * @dev_id must be globally unique. Normally the address of the device data
2103: * structure is used as the cookie. Since the handler receives this value
2104: * it makes sense to use it.
2105: *
2106: * If your interrupt is shared you must pass a non NULL dev_id as this is
2107: * required when freeing the interrupt.
2108: *
2109: * Flags:
2110: *
2111: * IRQF_SHARED Interrupt is shared
2112: * IRQF_TRIGGER_* Specify active edge(s) or level
2113: * IRQF_ONESHOT Run thread_fn with interrupt line masked
2114: */
2115: int request_threaded_irq(unsigned int irq, irq_handler_t handler,
2116: irq_handler_t thread_fn, unsigned long irqflags,
2117: const char *devname, void *dev_id)
2118: {
2119: struct irqaction *action;
2120: struct irq_desc *desc;
2121: int retval;
2122:
2123: if (irq == IRQ_NOTCONNECTED)
2124: return -ENOTCONN;
2125:
2126: /*
2127: * Sanity-check: shared interrupts must pass in a real dev-ID,
2128: * otherwise we'll have trouble later trying to figure out
2129: * which interrupt is which (messes up the interrupt freeing
2130: * logic etc).
2131: *
2132: * Also shared interrupts do not go well with disabling auto enable.
2133: * The sharing interrupt might request it while it's still disabled
2134: * and then wait for interrupts forever.
2135: *
2136: * Also IRQF_COND_SUSPEND only makes sense for shared interrupts and
2137: * it cannot be set along with IRQF_NO_SUSPEND.
2138: */
2139: if (((irqflags & IRQF_SHARED) && !dev_id) ||
2140: ((irqflags & IRQF_SHARED) && (irqflags & IRQF_NO_AUTOEN)) ||
2141: (!(irqflags & IRQF_SHARED) && (irqflags & IRQF_COND_SUSPEND)) ||
2142: ((irqflags & IRQF_NO_SUSPEND) && (irqflags & IRQF_COND_SUSPEND)))
2143: return -EINVAL;
2144:
2145: desc = irq_to_desc(irq);
2146: if (!desc)
2147: return -EINVAL;
2148:
2149: if (!irq_settings_can_request(desc) ||
2150: WARN_ON(irq_settings_is_per_cpu_devid(desc)))
2151: return -EINVAL;
2152:
2153: if (!handler) {
2154: if (!thread_fn)
2155: return -EINVAL;
2156: handler = irq_default_primary_handler;
2157: }
2158:
2159: action = kzalloc_obj(struct irqaction);
2160: if (!action)
2161: return -ENOMEM;
2162:
2163: action->handler = handler;
2164: action->thread_fn = thread_fn;
2165: action->flags = irqflags;
2166: action->name = devname;
2167: action->dev_id = dev_id;
2168:
2169: retval = irq_chip_pm_get(&desc->irq_data);
2170: if (retval < 0) {
2171: kfree(action);
2172: return retval;
2173: }
2174:
2175: retval = __setup_irq(irq, desc, action);
2176:
2177: if (retval) {
2178: irq_chip_pm_put(&desc->irq_data);
2179: kfree(action->secondary);
2180: kfree(action);
2181: }
2182:
2183: #ifdef CONFIG_DEBUG_SHIRQ_FIXME
2184: if (!retval && (irqflags & IRQF_SHARED)) {
2185: /*
2186: * It's a shared IRQ -- the driver ought to be prepared for it
2187: * to happen immediately, so let's make sure....
2188: * We disable the irq to make sure that a 'real' IRQ doesn't
2189: * run in parallel with our fake.
2190: */
2191: unsigned long flags;
2192:
2193: disable_irq(irq);
2194: local_irq_save(flags);
2195:
2196: handler(irq, dev_id);
2197:
2198: local_irq_restore(flags);
2199: enable_irq(irq);
2200: }
2201: #endif
2202: return retval;
2203: }
SourceFile:kernel/irq/manage.c]
|
| 12/2 |
2026/05/19 06:01 |
llm |
expert |
0m
Model:
Tokens:
input: 12105
output: 47
thoughts: 279
Thoughts: **Analyzing a Potential Per-CPU DevID IRQ Issue in Comedi's Parport Driver**
Okay, so I'm looking at this `WARN_ON(irq_settings_is_per_cpu_devid(desc))` warning. It's triggered when `request_irq` is called with an IRQ that's flagged as a per-CPU devID. The code snippet shows this happens during the `parport_attach` function within the Comedi driver, specifically in `drivers/comedi/drivers/comedi_parport.c`. `request_irq` is invoked if `it->options[1]` is non-zero. Ah, right, `it->options[1]` is user-controllable; itβs being set through the `do_devconfig_ioctl` call chain. That leads through `comedi_device_attach` and ultimately calls `driv->attach(dev, it)`.
But hold on...there's a capability check in `do_devconfig_ioctl`. That code includes a `!capable(CAP_SYS_ADMIN)` check, returning an error if the calling process isn't sufficiently privileged. Let me double check this `do_devconfig_ioctl` routine in `drivers/comedi/comedi_fops.c`. So, the user controls *which* IRQ is used, but only if they have root access.
So, potentially, a root user could trigger this warning by assigning a per-CPU devID IRQ in a way that the Comedi driver doesn't expect or handle properly. I need to investigate further whether this is actually a problem or just a benign warning due to configuration. Could this be a real issue, or is it a false positive due to how the user configures the hardware?
|
| 13/2 |
2026/05/19 06:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/comedi/comedi_fops.c IncludeLines:true Name:do_devconfig_ioctl]
Results: map[SourceCode: 871: /*
872: * COMEDI_DEVCONFIG ioctl
873: * attaches (and configures) or detaches a legacy device
874: *
875: * arg:
876: * pointer to comedi_devconfig structure (NULL if detaching)
877: *
878: * reads:
879: * comedi_devconfig structure (if attaching)
880: *
881: * writes:
882: * nothing
883: */
884: static int do_devconfig_ioctl(struct comedi_device *dev,
885: struct comedi_devconfig __user *arg)
886: {
887: struct comedi_devconfig it;
888:
889: lockdep_assert_held(&dev->mutex);
890: if (!capable(CAP_SYS_ADMIN))
891: return -EPERM;
892:
893: if (!arg) {
894: int rc = 0;
895:
896: if (dev->attached) {
897: down_write(&dev->attach_lock);
898: if (is_device_busy(dev)) {
899: rc = -EBUSY;
900: } else {
901: struct module *driver_module =
902: dev->driver->module;
903:
904: comedi_device_detach_locked(dev);
905: module_put(driver_module);
906: }
907: up_write(&dev->attach_lock);
908: }
909: return rc;
910: }
911:
912: if (copy_from_user(&it, arg, sizeof(it)))
913: return -EFAULT;
914:
915: it.board_name[COMEDI_NAMELEN - 1] = 0;
916:
917: if (it.options[COMEDI_DEVCONF_AUX_DATA_LENGTH]) {
918: dev_warn(dev->class_dev,
919: "comedi_config --init_data is deprecated\n");
920: return -EINVAL;
921: }
922:
923: if (dev->minor >= comedi_num_legacy_minors)
924: /* don't re-use dynamically allocated comedi devices */
925: return -EBUSY;
926:
927: /* This increments the driver module count on success. */
928: return comedi_device_attach(dev, &it);
929: }
SourceFile:drivers/comedi/comedi_fops.c]
|
| 14/2 |
2026/05/19 06:01 |
llm |
expert |
0m
Model:
Tokens:
input: 13646
output: 51
thoughts: 38
Thoughts: Okay, here's my interpretation of those thoughts, summarized from my perspective as a seasoned expert:
**Investigating Security in Comedi Driver Configuration**
Right, so `do_devconfig_ioctl` clearly enforces that `CAP_SYS_ADMIN` capability check. That's a standard safeguard, a good starting point. But the real question, as always, is whether that's the *only* entry point to `comedi_device_attach`. We know this is a critical function, essentially hooking up our Comedi device to the system. My focus now is to hunt for any potential bypasses or alternative routes. Are there any other paths, any other system calls, device driver interactions, or internal mechanisms that could potentially lead to the execution of `comedi_device_attach` *without* triggering that `CAP_SYS_ADMIN` check? Thatβs what I need to dig into. We need to think like an attacker here.
|
| 15/2 |
2026/05/19 06:02 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/comedi/drivers.c IncludeSnippetLines:0 Name:comedi_device_attach SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:do_devconfig_ioctl SourceFile:drivers/comedi/comedi_fops.c SourceLine:928]] TruncatedOutput:false]
|
| 16/2 |
2026/05/19 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 14008
output: 37
thoughts: 94
Thoughts: **Analyzing the comedi_device_attach Call**
Okay, so it looks like `comedi_device_attach` is only called by `do_devconfig_ioctl`. That's a key observation. Now, I see that `do_devconfig_ioctl` performs a check β `capable(CAP_SYS_ADMIN)`. Wait a secondβ¦ is that `ns_capable` or just the regular `capable` check? I need to clarify that. Ah, it's explicitly `capable(CAP_SYS_ADMIN)`, meaning it's validating the capability within the *initial* user namespace, not a namespaced one. Time to double-check the definition of `capable` to make sure I'm not missing any nuances here. It's important to be precise when it comes to capability checks and namespace interactions.
|
| 17/2 |
2026/05/19 06:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/linux/capability.h IncludeLines:true Name:capable]
Results: map[SourceCode: 404: /**
405: * capable - Determine if the current task has a superior capability in effect
406: * @cap: The capability to be tested for
407: *
408: * Return true if the current task has the given superior capability currently
409: * available for use, false if not.
410: *
411: * This sets PF_SUPERPRIV on the task if the capability is available on the
412: * assumption that it's about to be used.
413: */
414: bool capable(int cap)
415: {
416: return ns_capable(&init_user_ns, cap);
417: }
SourceFile:kernel/capability.c]
|
| 18/2 |
2026/05/19 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 14539
output: 55
thoughts: 278
Thoughts: **Analyzing the Non-Exploitable IRQ Warning**
Okay, so `capable(CAP_SYS_ADMIN)` relies on `ns_capable(&init_user_ns, CAP_SYS_ADMIN)`. That means it's pretty locked down. Unprivileged processes are out, and user namespaces are also generally out, unless we're talking about the initial user namespace, which is essentially root on the host. Good security practice.
Now, let's get down to brass tacks: is this bug exploitable? It's a WARNING in `request_threaded_irq`, specifically the check:
```c
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
```
If `irq_settings_is_per_cpu_devid(desc)` is true, it triggers a warning and, crucially, returns `-EINVAL`. No memory corruption, no panics, no juicy kernel bugs to leverage. Just an error code. From a security standpoint, that's not ideal, but it's not a vulnerability. It's not exploitable, plain and simple.
And it's not a denial of service either. A return of `-EINVAL` means the function gracefully exits, the program continues and the worst you get is a log message spamming. That log spamming would be heavily restricted to `CAP_SYS_ADMIN`, so even that is heavily mitigated.
Okay, so this appears to be a red herring. Let's see if there is another way to trigger `parport_attach` without `comedi_device_attach`.
|
| 19/2 |
2026/05/19 06:02 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/comedi/drivers/comedi_parport.c IncludeSnippetLines:0 Name:parport_attach SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 20/2 |
2026/05/19 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 15255
output: 55
thoughts: 45
Thoughts: Here's my analysis of the provided information, rendered in the first person, assuming an expert-level understanding of the field:
**Internal Analysis of `comedi_parport.c` and its `parport_attach` Function**
Okay, let's break this down. My understanding here is that we're talking about the Comedi framework, specifically the parallel port driver. I see that `parport_attach` is defined as a static function within `drivers/comedi/drivers/comedi_parport.c`. That's standard practice, a static function restricts its scope, only accessible within that specific C file. This function's purpose would be crucial for this driver, likely involved in the attachment or initialization phase, specific to parallel port hardware. The critical piece is that this `parport_attach` function is assigned to the `attach` field of a `struct comedi_driver`. This points directly to how Comedi dynamically registers and manages its drivers. The `attach` field being populated with this function pointer means that when Comedi tries to attach a parallel port device, it'll call `parport_attach`. This is probably where the hardware detection, resource allocation, and device initialization takes place. We're effectively connecting this driver's functionality into the Comedi core. I'll need to remember to check for any mutexes or locking around this region in the code. I am also going to be thinking about what potential errors could happen during attach and how robust the error handling is implemented. Knowing Comedi, there is probably a lot of device detection within this `parport_attach` function.
|
| 21/2 |
2026/05/19 06:02 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/comedi/drivers/comedi_parport.c IncludeSnippetLines:5 Name:parport_attach SourceTreePrefix:]
Results: map[References:<nil> TruncatedOutput:false]
|
| 22/2 |
2026/05/19 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 15712
output: 29
thoughts: 34
Thoughts: Okay, let's dive into this. Here's how I'd approach this task, as if these were my internal thoughts:
**Investigating Parport Attachment in Comedi**
Right, so I need to understand how the `parport_attach` function within the Comedi framework is utilized. My immediate thought is to check the source code itself. Let's go straight to the file, `drivers/comedi/drivers/comedi_parport.c`. That seems like the logical place to start β it's named pretty specifically. I'm going to scan that file and pinpoint exactly where `parport_attach` is actually *called*. Finding the usage will give me the contextual information I need to really understand its role and integration within this particular Comedi driver. This is a common debugging and understanding technique - find the core piece of code and see where it is used. That gives me the "what" and the "how".
|
| 23/2 |
2026/05/19 06:02 |
tool |
read-file |
0m
Args: map[File:drivers/comedi/drivers/comedi_parport.c]
Results: map[Contents:// SPDX-License-Identifier: GPL-2.0+
/*
* comedi_parport.c
* Comedi driver for standard parallel port
*
* For more information see:
* http://retired.beyondlogic.org/spp/parallel.htm
*
* COMEDI - Linux Control and Measurement Device Interface
* Copyright (C) 1998,2001 David A. Schleef <ds@schleef.org>
*/
/*
* Driver: comedi_parport
* Description: Standard PC parallel port
* Author: ds
* Status: works in immediate mode
* Devices: [standard] parallel port (comedi_parport)
* Updated: Tue, 30 Apr 2002 21:11:45 -0700
*
* A cheap and easy way to get a few more digital I/O lines. Steal
* additional parallel ports from old computers or your neighbors'
* computers.
*
* Option list:
* 0: I/O port base for the parallel port.
* 1: IRQ (optional)
*
* Parallel Port Lines:
*
* pin subdev chan type name
* ----- ------ ---- ---- --------------
* 1 2 0 DO strobe
* 2 0 0 DIO data 0
* 3 0 1 DIO data 1
* 4 0 2 DIO data 2
* 5 0 3 DIO data 3
* 6 0 4 DIO data 4
* 7 0 5 DIO data 5
* 8 0 6 DIO data 6
* 9 0 7 DIO data 7
* 10 1 3 DI ack
* 11 1 4 DI busy
* 12 1 2 DI paper out
* 13 1 1 DI select in
* 14 2 1 DO auto LF
* 15 1 0 DI error
* 16 2 2 DO init
* 17 2 3 DO select printer
* 18-25 ground
*
* When an IRQ is configured subdevice 3 pretends to be a digital
* input subdevice, but it always returns 0 when read. However, if
* you run a command with scan_begin_src=TRIG_EXT, it uses pin 10
* as a external trigger, which can be used to wake up tasks.
*/
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/comedi/comedidev.h>
/*
* Register map
*/
#define PARPORT_DATA_REG 0x00
#define PARPORT_STATUS_REG 0x01
#define PARPORT_CTRL_REG 0x02
#define PARPORT_CTRL_IRQ_ENA BIT(4)
#define PARPORT_CTRL_BIDIR_ENA BIT(5)
static int parport_data_reg_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
if (comedi_dio_update_state(s, data))
outb(s->state, dev->iobase + PARPORT_DATA_REG);
data[1] = inb(dev->iobase + PARPORT_DATA_REG);
return insn->n;
}
static int parport_data_reg_insn_config(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
unsigned int ctrl;
int ret;
ret = comedi_dio_insn_config(dev, s, insn, data, 0xff);
if (ret)
return ret;
ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
if (s->io_bits)
ctrl &= ~PARPORT_CTRL_BIDIR_ENA;
else
ctrl |= PARPORT_CTRL_BIDIR_ENA;
outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
return insn->n;
}
static int parport_status_reg_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
data[1] = inb(dev->iobase + PARPORT_STATUS_REG) >> 3;
return insn->n;
}
static int parport_ctrl_reg_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
unsigned int ctrl;
if (comedi_dio_update_state(s, data)) {
ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
ctrl &= (PARPORT_CTRL_IRQ_ENA | PARPORT_CTRL_BIDIR_ENA);
ctrl |= s->state;
outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
}
data[1] = s->state;
return insn->n;
}
static int parport_intr_insn_bits(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_insn *insn,
unsigned int *data)
{
data[1] = 0;
return insn->n;
}
static int parport_intr_cmdtest(struct comedi_device *dev,
struct comedi_subdevice *s,
struct comedi_cmd *cmd)
{
int err = 0;
/* Step 1 : check if triggers are trivially valid */
err |= comedi_check_trigger_src(&cmd->start_src, TRIG_NOW);
err |= comedi_check_trigger_src(&cmd->scan_begin_src, TRIG_EXT);
err |= comedi_check_trigger_src(&cmd->convert_src, TRIG_FOLLOW);
err |= comedi_check_trigger_src(&cmd->scan_end_src, TRIG_COUNT);
err |= comedi_check_trigger_src(&cmd->stop_src, TRIG_NONE);
if (err)
return 1;
/* Step 2a : make sure trigger sources are unique */
/* Step 2b : and mutually compatible */
/* Step 3: check if arguments are trivially valid */
err |= comedi_check_trigger_arg_is(&cmd->start_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->scan_begin_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->convert_arg, 0);
err |= comedi_check_trigger_arg_is(&cmd->scan_end_arg,
cmd->chanlist_len);
err |= comedi_check_trigger_arg_is(&cmd->stop_arg, 0);
if (err)
return 3;
/* Step 4: fix up any arguments */
/* Step 5: check channel list if it exists */
return 0;
}
static int parport_intr_cmd(struct comedi_device *dev,
struct comedi_subdevice *s)
{
unsigned int ctrl;
ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
ctrl |= PARPORT_CTRL_IRQ_ENA;
outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
return 0;
}
static int parport_intr_cancel(struct comedi_device *dev,
struct comedi_subdevice *s)
{
unsigned int ctrl;
ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
ctrl &= ~PARPORT_CTRL_IRQ_ENA;
outb(ctrl, dev->iobase + PARPORT_CTRL_REG);
return 0;
}
static irqreturn_t parport_interrupt(int irq, void *d)
{
struct comedi_device *dev = d;
struct comedi_subdevice *s = dev->read_subdev;
unsigned int ctrl;
unsigned short val = 0;
ctrl = inb(dev->iobase + PARPORT_CTRL_REG);
if (!(ctrl & PARPORT_CTRL_IRQ_ENA))
return IRQ_NONE;
comedi_buf_write_samples(s, &val, 1);
comedi_handle_events(dev, s);
return IRQ_HANDLED;
}
static int parport_attach(struct comedi_device *dev,
struct comedi_devconfig *it)
{
struct comedi_subdevice *s;
int ret;
ret = comedi_request_region(dev, it->options[0], 0x03);
if (ret)
return ret;
if (it->options[1]) {
ret = request_irq(it->options[1], parport_interrupt, 0,
dev->board_name, dev);
if (ret == 0)
dev->irq = it->options[1];
}
ret = comedi_alloc_subdevices(dev, dev->irq ? 4 : 3);
if (ret)
return ret;
/* Digial I/O subdevice - Parallel port DATA register */
s = &dev->subdevices[0];
s->type = COMEDI_SUBD_DIO;
s->subdev_flags = SDF_READABLE | SDF_WRITABLE;
s->n_chan = 8;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = parport_data_reg_insn_bits;
s->insn_config = parport_data_reg_insn_config;
/* Digial Input subdevice - Parallel port STATUS register */
s = &dev->subdevices[1];
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE;
s->n_chan = 5;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = parport_status_reg_insn_bits;
/* Digial Output subdevice - Parallel port CONTROL register */
s = &dev->subdevices[2];
s->type = COMEDI_SUBD_DO;
s->subdev_flags = SDF_WRITABLE;
s->n_chan = 4;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = parport_ctrl_reg_insn_bits;
if (dev->irq) {
/* Digial Input subdevice - Interrupt support */
s = &dev->subdevices[3];
dev->read_subdev = s;
s->type = COMEDI_SUBD_DI;
s->subdev_flags = SDF_READABLE | SDF_CMD_READ;
s->n_chan = 1;
s->maxdata = 1;
s->range_table = &range_digital;
s->insn_bits = parport_intr_insn_bits;
s->len_chanlist = 1;
s->do_cmdtest = parport_intr_cmdtest;
s->do_cmd = parport_intr_cmd;
s->cancel = parport_intr_cancel;
}
outb(0, dev->iobase + PARPORT_DATA_REG);
outb(0, dev->iobase + PARPORT_CTRL_REG);
return 0;
}
static struct comedi_driver parport_driver = {
.driver_name = "comedi_parport",
.module = THIS_MODULE,
.attach = parport_attach,
.detach = comedi_legacy_detach,
};
module_comedi_driver(parport_driver);
MODULE_AUTHOR("Comedi https://www.comedi.org");
MODULE_DESCRIPTION("Comedi: Standard parallel port driver");
MODULE_LICENSE("GPL");
]
|
| 24/2 |
2026/05/19 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 19081
output: 48
thoughts: 275
Thoughts: **Analyzing the Comedi Driver Vulnerability**
Okay, so the initial assessment is that `parport_attach` is *only* reachable through `comedi_device_attach`, which in turn is only called through `do_devconfig_ioctl`. And *that* requires `CAP_SYS_ADMIN` within the initial user namespace. That's good; it's a first layer of protection.
Now, my gut tells me to dig deeper. I need to make *absolutely* sure there aren't any sneaky backdoors to `request_irq` with an invalid IRQ number that an unprivileged user *could* exploit, perhaps involving per-cpu device IDs or something similar. The bug report focuses specifically on `parport_attach` and the user-controlled `it->options[1]` value passed to `request_irq` through the `COMEDI_DEVCONFIG` ioctl.
But... the ioctl requires `CAP_SYS_ADMIN` in the initial namespace. That's critical. So, it's not going to be accessible from unprivileged processes or user namespaces. It simply isn't an attack vector. And even if someone somehow managed to trigger it, it's just going to return an `-EINVAL` after a `WARN_ON`. No denial-of-service, nothing impactful. This doesn't seem exploitable, from what I can see.
Let's do a final check, though, to ensure `do_devconfig_ioctl` isn't called from anywhere else that might present a different avenue. I'll double-check the call graph and confirm it's exclusively called from `comedi_unlocked_ioctl` within `drivers/comedi/comedi_fops.c`.
|
| 25/2 |
2026/05/19 06:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/comedi/comedi_fops.c IncludeLines:true Name:comedi_unlocked_ioctl]
Results: map[SourceCode:2273: static long comedi_unlocked_ioctl(struct file *file, unsigned int cmd,
2274: unsigned long arg)
2275: {
2276: unsigned int minor = iminor(file_inode(file));
2277: struct comedi_file *cfp = file->private_data;
2278: struct comedi_device *dev = cfp->dev;
2279: int rc;
2280:
2281: /* Handle COMEDI_BUFINFO without locking the mutex first. */
2282: if (cmd == COMEDI_BUFINFO) {
2283: return do_bufinfo_ioctl(dev,
2284: (struct comedi_bufinfo __user *)arg,
2285: file);
2286: }
2287:
2288: mutex_lock(&dev->mutex);
2289:
2290: /*
2291: * Device config is special, because it must work on
2292: * an unconfigured device.
2293: */
2294: if (cmd == COMEDI_DEVCONFIG) {
2295: if (minor >= COMEDI_NUM_BOARD_MINORS) {
2296: /* Device config not appropriate on non-board minors. */
2297: rc = -ENOTTY;
2298: goto done;
2299: }
2300: rc = do_devconfig_ioctl(dev,
2301: (struct comedi_devconfig __user *)arg);
2302: if (rc == 0) {
2303: if (arg == 0 &&
2304: dev->minor >= comedi_num_legacy_minors) {
2305: /*
2306: * Successfully unconfigured a dynamically
2307: * allocated device. Try and remove it.
2308: */
2309: if (comedi_clear_board_dev(dev)) {
2310: mutex_unlock(&dev->mutex);
2311: comedi_free_board_dev(dev);
2312: return rc;
2313: }
2314: }
2315: }
2316: goto done;
2317: }
2318:
2319: if (!dev->attached) {
2320: dev_dbg(dev->class_dev, "no driver attached\n");
2321: rc = -ENODEV;
2322: goto done;
2323: }
2324:
2325: switch (cmd) {
2326: case COMEDI_BUFCONFIG:
2327: rc = do_bufconfig_ioctl(dev,
2328: (struct comedi_bufconfig __user *)arg);
2329: break;
2330: case COMEDI_DEVINFO:
2331: rc = do_devinfo_ioctl(dev, (struct comedi_devinfo __user *)arg,
2332: file);
2333: break;
2334: case COMEDI_SUBDINFO:
2335: rc = do_subdinfo_ioctl(dev,
2336: (struct comedi_subdinfo __user *)arg,
2337: file);
2338: break;
2339: case COMEDI_CHANINFO: {
2340: struct comedi_chaninfo it;
2341:
2342: if (copy_from_user(&it, (void __user *)arg, sizeof(it)))
2343: rc = -EFAULT;
2344: else
2345: rc = do_chaninfo_ioctl(dev, &it);
2346: break;
2347: }
2348: case COMEDI_RANGEINFO: {
2349: struct comedi_rangeinfo it;
2350:
2351: if (copy_from_user(&it, (void __user *)arg, sizeof(it)))
2352: rc = -EFAULT;
2353: else
2354: rc = do_rangeinfo_ioctl(dev, &it);
2355: break;
2356: }
2357: case COMEDI_LOCK:
2358: rc = do_lock_ioctl(dev, arg, file);
2359: break;
2360: case COMEDI_UNLOCK:
2361: rc = do_unlock_ioctl(dev, arg, file);
2362: break;
2363: case COMEDI_CANCEL:
2364: rc = do_cancel_ioctl(dev, arg, file);
2365: break;
2366: case COMEDI_CMD: {
2367: struct comedi_cmd cmd;
2368: bool copy = false;
2369:
2370: if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) {
2371: rc = -EFAULT;
2372: break;
2373: }
2374: rc = do_cmd_ioctl(dev, &cmd, ©, file);
2375: if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd)))
2376: rc = -EFAULT;
2377: break;
2378: }
2379: case COMEDI_CMDTEST: {
2380: struct comedi_cmd cmd;
2381: bool copy = false;
2382:
2383: if (copy_from_user(&cmd, (void __user *)arg, sizeof(cmd))) {
2384: rc = -EFAULT;
2385: break;
2386: }
2387: rc = do_cmdtest_ioctl(dev, &cmd, ©, file);
2388: if (copy && copy_to_user((void __user *)arg, &cmd, sizeof(cmd)))
2389: rc = -EFAULT;
2390: break;
2391: }
2392: case COMEDI_INSNLIST: {
2393: struct comedi_insnlist insnlist;
2394: struct comedi_insn *insns = NULL;
2395:
2396: if (copy_from_user(&insnlist, (void __user *)arg,
2397: sizeof(insnlist))) {
2398: rc = -EFAULT;
2399: break;
2400: }
2401: rc = check_insnlist_len(dev, insnlist.n_insns);
2402: if (rc)
2403: break;
2404: insns = memdup_array_user(insnlist.insns, insnlist.n_insns,
2405: sizeof(*insns));
2406: if (IS_ERR(insns)) {
2407: rc = PTR_ERR(insns);
2408: break;
2409: }
2410: rc = do_insnlist_ioctl(dev, insns, insnlist.n_insns, file);
2411: kfree(insns);
2412: break;
2413: }
2414: case COMEDI_INSN: {
2415: struct comedi_insn insn;
2416:
2417: if (copy_from_user(&insn, (void __user *)arg, sizeof(insn)))
2418: rc = -EFAULT;
2419: else
2420: rc = do_insn_ioctl(dev, &insn, file);
2421: break;
2422: }
2423: case COMEDI_POLL:
2424: rc = do_poll_ioctl(dev, arg, file);
2425: break;
2426: case COMEDI_SETRSUBD:
2427: rc = do_setrsubd_ioctl(dev, arg, file);
2428: break;
2429: case COMEDI_SETWSUBD:
2430: rc = do_setwsubd_ioctl(dev, arg, file);
2431: break;
2432: default:
2433: rc = -ENOTTY;
2434: break;
2435: }
2436:
2437: done:
2438: mutex_unlock(&dev->mutex);
2439: return rc;
2440: }
SourceFile:drivers/comedi/comedi_fops.c]
|
| 26/2 |
2026/05/19 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 22187
output: 66
thoughts: 610
Thoughts: **Analysis of the Comedi `parport_attach` Vulnerability**
Okay, so let's walk through this. I can see that `do_devconfig_ioctl` is indeed the function that handles the `COMEDI_DEVCONFIG` ioctl, and it's called from `comedi_unlocked_ioctl`. Crucially, it has a check for `capable(CAP_SYS_ADMIN)`, meaning only privileged processes or those with elevated capabilities within a user namespace can even *reach* this code path. Right, so, in the context of this bug report, specifically concerning `comedi_parport.c` and the `parport_attach` function, this isn't accessible to unprivileged users.
Now, the syzkaller reproducer is using `COMEDI_DEVCONFIG` on `/dev/comedi0` or similar, which makes sense. Since syzkaller defaults to running as root, or with significant capabilities in its initial user namespace, that explains why it can trigger the issue. But for a normal user, no dice. I have to confirm that no other method might trigger `request_irq` with a user-controlled IRQ number. Checking `comedi_parport.c`, I see that `parport_attach` is the only place it's done.
The next question: is it exploitable? I'm looking at the warning. It seems the code just returns `-EINVAL` if the request is invalid. No memory corruption, no kernel panic. It's not a denial of service either; it's simply an error return.
Alright, so let's start filling in the results. Exploitable? No. Denial of Service? No. Unprivileged? No. UserNamespace? No. VMGuestTrigger? No. VMHostTrigger? No. NetworkTrigger? No. RemoteTrigger? No. PeripheralTrigger? No. FilesystemTrigger? No.
Wait, could it be triggered by a peripheral? No, it's triggered by an ioctl on a comedi device. A VM guest? Still no, it's an ioctl on the host.
Let me just double-check, though, on whether we can bypass `capable(CAP_SYS_ADMIN)`. No, `capable` clearly depends on `init_user_ns`. I will also double-check whether `request_irq` is called anywhere else in the Comedi subsystem without the CAP check. Even if there are other Comedi drivers, the reported bug relates to the `parport` driver. These are also attached via the `COMEDI_DEVCONFIG` ioctl which still requires CAP_SYS_ADMIN. Other drivers might be PCI/USB drivers that obtain their IRQ from the bus subsystem, not directly from user space.
So, I think this analysis is solid. We're safe.
|
| 27/2 |
2026/05/19 06:02 |
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]
|
| 28/2 |
2026/05/19 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 23527
output: 503
thoughts: 234
Thoughts: **My Assessment of the Reported Bug**
Okay, the results are in, and now I need to solidify my conclusions in the final response. My analysis indicates this isn't a significant issue. The bug manifests as a WARNING in `request_threaded_irq` when trying to request a per-CPU devid IRQ β specifically, the system throws an `-EINVAL` error and gracefully fails. That's a textbook example of a non-exploitable condition; no memory corruption, no crash, no DoS risk. Just a warning and a clean exit.
The pathway to trigger it involves the `COMEDI_DEVCONFIG` ioctl on a comedi device node, which then leads to `do_devconfig_ioctl`. Thankfully, this critical point has a check for `CAP_SYS_ADMIN` using `capable(CAP_SYS_ADMIN)` within the *initial* user namespace. That means an unprivileged user, even with a new namespace, is blocked from even getting close.
The fact that root privileges, specifically that `CAP_SYS_ADMIN` capability in the init namespace, are needed to even attempt this, combined with the lack of security boundary crossing or any system instability, leads me to conclude that this bug has absolutely no security impact. It's a non-issue, security-wise.
|
| 29/1 |
2026/05/19 06:02 |
action |
format-explanation |
0m
Results: map[Explanation:Based on the analysis of the provided kernel bug report and source code, here is
the assessment of the bug's security impact:
**1. Not Accessible to Unprivileged Users or User Namespaces:**
The bug is triggered via the `COMEDI_DEVCONFIG` ioctl on a comedi device node
(e.g., `/dev/comedi0`), which is handled by `do_devconfig_ioctl()` in
`drivers/comedi/comedi_fops.c`.
Looking at the source code for `do_devconfig_ioctl()`:
```c
static int do_devconfig_ioctl(struct comedi_device *dev,
struct comedi_devconfig __user *arg)
{
struct comedi_devconfig it;
lockdep_assert_held(&dev->mutex);
if (!capable(CAP_SYS_ADMIN))
return -EPERM;
// ...
```
The function explicitly checks for `CAP_SYS_ADMIN` using `capable()`, which
checks the capability against the initial user namespace (`init_user_ns`).
Therefore, an unprivileged user cannot trigger this bug, even if they create a
new user namespace and gain capabilities within it.
**2. Not Exploitable and No Denial of Service:**
The warning is triggered in `request_threaded_irq()` in `kernel/irq/manage.c`
when the user-provided IRQ number corresponds to a per-CPU devid IRQ:
```c
if (!irq_settings_can_request(desc) ||
WARN_ON(irq_settings_is_per_cpu_devid(desc)))
return -EINVAL;
```
When the `WARN_ON` condition is met, the kernel simply prints a warning stack
trace to the kernel log and returns `-EINVAL`. It does not cause a kernel panic,
memory corruption, deadlocks, or any other exploitable condition. The system
continues to operate normally, meaning there is no Denial of Service (DoS) or
privilege escalation risk.
**Conclusion:**
This bug is a harmless warning that can only be triggered by a highly privileged
user (root / `CAP_SYS_ADMIN` in the initial namespace) providing an invalid IRQ
number to a legacy comedi driver configuration ioctl. It has no security impact.]
|