| 0/0 |
2026/08/09 05:25 |
flow |
assessment-security |
42m
Results: map[DenialOfService:true Explanation:The bug report describes a page fault in `snd_hdac_bus_parse_capabilities` when
reading from the `0x1c094` address.
### Root Cause Analysis
The issue occurs during the initialization of an Intel High Definition Audio
(HDA) controller. In `azx_first_init()`, the driver maps the device's Base
Address Register (BAR 0) using `pcim_iomap_region(pci, 0, "ICH HD audio")`. The
driver assumes that BAR 0 is always a Memory-Mapped I/O (MMIO) region, as
specified by the Intel HDA specification. However, it does not verify the
resource flags before mapping.
If a malicious or misconfigured PCI device exposes BAR 0 as an I/O port instead
of an MMIO region, `pcim_iomap_region()` maps it as an I/O port and returns a
PIO cookie (e.g., `0x10000 + port`). Later, `snd_hdac_bus_parse_capabilities()`
attempts to read from this region using `readw()`. On x86, `readw()` directly
dereferences the provided address. Dereferencing a PIO cookie (which falls in
the user-space address range `0x10000` - `0x1FFFF`) causes a page fault if SMAP
(Supervisor Mode Access Prevention) is enabled, or reads from user-space memory
if SMAP is disabled.
Furthermore, even if BAR 0 is correctly exposed as an MMIO region,
`snd_hdac_bus_parse_capabilities()` trusts the capabilities list provided by the
device without bounds checking. The device can provide an `offset` up to
`0xFFFF` (64KB). If the device exposes a small MMIO BAR (e.g., 4KB), the driver
will read from and write to `bus->remap_addr + offset`, which falls outside the
mapped MMIO region and accesses adjacent `vmalloc` memory.
### Security Impact
* **Exploitable:** Yes.
1. **Out-of-bounds vmalloc access:** A malicious device can expose a small
MMIO BAR and provide large offsets in the capabilities list, causing the
driver to read/write adjacent `vmalloc` allocations. This is a powerful
primitive that can lead to arbitrary kernel memory corruption and privilege
escalation.
2. **User-space memory access:** If the device exposes BAR 0 as an I/O port
and SMAP is disabled, the kernel will read the capabilities list from
user-space memory, which could also be manipulated for exploitation.
* **Denial of Service:** Yes. The bug trivially causes a kernel panic (page
fault) or OOM (by providing a massive capabilities list count).
* **Peripheral Trigger:** Yes. An attacker can trigger this bug by physically
plugging in a malicious PCIe device via Thunderbolt or USB4 that spoofs a modern
Intel HDA controller (e.g., Skylake, Alderlake) and exposes a malformed BAR 0 or
capabilities list.
* **VM Host Trigger in Confidential Computing:** Yes. An untrusted KVM host
(VMM) can expose a malicious emulated Intel HDA controller to a Confidential VM
(e.g., AMD SEV, Intel TDX). When the guest kernel probes the device, the host
can exploit the out-of-bounds `vmalloc` access to compromise the guest kernel.
* **Unprivileged / User Namespaces:** No. Triggering the bug requires either
physical access to plug in a PCIe device or the ability to create virtual PCI
devices (which requires root privileges or specific fuzzing setups like
syzkaller). Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:true]
|
| 1/1 |
2026/08/09 05:25 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/08/09 05:25 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/9d4bc8dbb37da7464722132cca9f0756f97b7cb8]
|
| 3/1 |
2026/08/09 05:25 |
action |
kernel-builder |
17m
Results: map[KernelObj:/app/workdir/cache/build/495ebcb3a488089b977eb39ae949e8c92267255e]
|
| 4/1 |
2026/08/09 05:42 |
action |
codesearch-prepare |
19m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/09 06:01 |
agent |
expert |
5m
Model:
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:true]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
BUG: unable to handle page fault for address: 000000000001c094
#PF: supervisor read access in kernel mode
#PF: error_code(0x0000) - not-present page
PGD 3b4a3067 P4D 3b4a3067 PUD 0
Oops: Oops: 0000 [#1] SMP KASAN NOPTI
CPU: 0 UID: 0 PID: 9 Comm: kworker/0:0 Tainted: G L syzkaller #0 PREEMPT(full)
Tainted: [L]=SOFTLOCKUP
Hardware name: Google Google Compute Engine/Google Compute Engine, BIOS Google 07/16/2026
Workqueue: events azx_probe_work
RIP: 0010:readw arch/x86/include/asm/io.h:58 [inline]
RIP: 0010:snd_hdac_reg_readw include/sound/hdaudio.h:458 [inline]
RIP: 0010:snd_hdac_bus_parse_capabilities+0x42/0x6d0 sound/hda/core/controller.c:412
Code: 98 f8 48 8d 45 20 48 89 c2 48 89 44 24 10 48 b8 00 00 00 00 00 fc ff df 48 c1 ea 03 80 3c 02 00 0f 85 f8 05 00 00 48 8b 45 20 <66> 44 8b 68 14 4c 8b 7c 24 10 48 89 e8 45 0f b7 ed 45 31 f6 48 ba
RSP: 0018:ffffc900000e7ab0 EFLAGS: 00010246
RAX: 000000000001c080 RBX: ffff88805268e058 RCX: ffffffff897da916
RDX: 1ffff1100a4d1c0b RSI: ffffffff8971ecda RDI: ffff88805268e038
RBP: ffff88805268e038 R08: 0000000000000005 R09: 0000000000000003
R10: 0000000000000003 R11: 0000000000000000 R12: 0000000000000003
R13: ffff88801e6d4f44 R14: ffff888023aca000 R15: 0000000000000000
FS: 0000000000000000(0000) GS:ffff888123ded000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000000000001c094 CR3: 000000009bf8c000 CR4: 00000000003526f0
Call Trace:
<TASK>
azx_first_init sound/hda/controllers/intel.c:1936 [inline]
azx_probe_continue sound/hda/controllers/intel.c:2365 [inline]
azx_probe_work+0x1d8e/0x2640 sound/hda/controllers/intel.c:1737
process_one_work+0xa23/0x1940 kernel/workqueue.c:3322
process_scheduled_works kernel/workqueue.c:3405 [inline]
worker_thread+0x5ef/0xe50 kernel/workqueue.c:3486
kthread+0x370/0x450 kernel/kthread.c:436
ret_from_fork+0x72b/0xd50 arch/x86/kernel/process.c:158
ret_from_fork_asm+0x1a/0x30 arch/x86/entry/entry_64.S:245
</TASK>
Modules linked in:
CR2: 000000000001c094
---[ end trace 0000000000000000 ]---
RIP: 0010:readw arch/x86/include/asm/io.h:58 [inline]
RIP: 0010:snd_hdac_reg_readw include/sound/hdaudio.h:458 [inline]
RIP: 0010:snd_hdac_bus_parse_capabilities+0x42/0x6d0 sound/hda/core/controller.c:412
Code: 98 f8 48 8d 45 20 48 89 c2 48 89 44 24 10 48 b8 00 00 00 00 00 fc ff df 48 c1 ea 03 80 3c 02 00 0f 85 f8 05 00 00 48 8b 45 20 <66> 44 8b 68 14 4c 8b 7c 24 10 48 89 e8 45 0f b7 ed 45 31 f6 48 ba
RSP: 0018:ffffc900000e7ab0 EFLAGS: 00010246
RAX: 000000000001c080 RBX: ffff88805268e058 RCX: ffffffff897da916
RDX: 1ffff1100a4d1c0b RSI: ffffffff8971ecda RDI: ffff88805268e038
RBP: ffff88805268e038 R08: 0000000000000005 R09: 0000000000000003
R10: 0000000000000003 R11: 0000000000000000 R12: 0000000000000003
R13: ffff88801e6d4f44 R14: ffff888023aca000 R15: 0000000000000000
FS: 0000000000000000(0000) GS:ffff888123ded000(0000) knlGS:0000000000000000
CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033
CR2: 000000000001c094 CR3: 000000009bf8c000 CR4: 00000000003526f0
----------------
Code disassembly (best guess):
0: 98 cwtl
1: f8 clc
2: 48 8d 45 20 lea 0x20(%rbp),%rax
6: 48 89 c2 mov %rax,%rdx
9: 48 89 44 24 10 mov %rax,0x10(%rsp)
e: 48 b8 00 00 00 00 00 movabs $0xdffffc0000000000,%rax
15: fc ff df
18: 48 c1 ea 03 shr $0x3,%rdx
1c: 80 3c 02 00 cmpb $0x0,(%rdx,%rax,1)
20: 0f 85 f8 05 00 00 jne 0x61e
26: 48 8b 45 20 mov 0x20(%rbp),%rax
* 2a: 66 44 8b 68 14 mov 0x14(%rax),%r13w <-- trapping instruction
2f: 4c 8b 7c 24 10 mov 0x10(%rsp),%r15
34: 48 89 e8 mov %rbp,%rax
37: 45 0f b7 ed movzwl %r13w,%r13d
3b: 45 31 f6 xor %r14d,%r14d
3e: 48 rex.W
3f: ba .byte 0xba
Reply: The bug report describes a page fault in `snd_hdac_bus_parse_capabilities` when reading from the `0x1c094` address.
### Root Cause Analysis
The issue occurs during the initialization of an Intel High Definition Audio (HDA) controller. In `azx_first_init()`, the driver maps the device's Base Address Register (BAR 0) using `pcim_iomap_region(pci, 0, "ICH HD audio")`. The driver assumes that BAR 0 is always a Memory-Mapped I/O (MMIO) region, as specified by the Intel HDA specification. However, it does not verify the resource flags before mapping.
If a malicious or misconfigured PCI device exposes BAR 0 as an I/O port instead of an MMIO region, `pcim_iomap_region()` maps it as an I/O port and returns a PIO cookie (e.g., `0x10000 + port`). Later, `snd_hdac_bus_parse_capabilities()` attempts to read from this region using `readw()`. On x86, `readw()` directly dereferences the provided address. Dereferencing a PIO cookie (which falls in the user-space address range `0x10000` - `0x1FFFF`) causes a page fault if SMAP (Supervisor Mode Access Prevention) is enabled, or reads from user-space memory if SMAP is disabled.
Furthermore, even if BAR 0 is correctly exposed as an MMIO region, `snd_hdac_bus_parse_capabilities()` trusts the capabilities list provided by the device without bounds checking. The device can provide an `offset` up to `0xFFFF` (64KB). If the device exposes a small MMIO BAR (e.g., 4KB), the driver will read from and write to `bus->remap_addr + offset`, which falls outside the mapped MMIO region and accesses adjacent `vmalloc` memory.
### Security Impact
* **Exploitable:** Yes.
1. **Out-of-bounds vmalloc access:** A malicious device can expose a small MMIO BAR and provide large offsets in the capabilities list, causing the driver to read/write adjacent `vmalloc` allocations. This is a powerful primitive that can lead to arbitrary kernel memory corruption and privilege escalation.
2. **User-space memory access:** If the device exposes BAR 0 as an I/O port and SMAP is disabled, the kernel will read the capabilities list from user-space memory, which could also be manipulated for exploitation.
* **Denial of Service:** Yes. The bug trivially causes a kernel panic (page fault) or OOM (by providing a massive capabilities list count).
* **Peripheral Trigger:** Yes. An attacker can trigger this bug by physically plugging in a malicious PCIe device via Thunderbolt or USB4 that spoofs a modern Intel HDA controller (e.g., Skylake, Alderlake) and exposes a malformed BAR 0 or capabilities list.
* **VM Host Trigger in Confidential Computing:** Yes. An untrusted KVM host (VMM) can expose a malicious emulated Intel HDA controller to a Confidential VM (e.g., AMD SEV, Intel TDX). When the guest kernel probes the device, the host can exploit the out-of-bounds `vmalloc` access to compromise the guest kernel.
* **Unprivileged / User Namespaces:** No. Triggering the bug requires either physical access to plug in a PCIe device or the ability to create virtual PCI devices (which requires root privileges or specific fuzzing setups like syzkaller).
|
| 6/2 |
2026/08/09 06:01 |
llm |
expert |
0m
Model:
Tokens:
input: 8233
output: 44
thoughts: 193
Thoughts: **Analyzing a Page Fault in the HD-Audio Controller**
Okay, here's what I'm seeing: we've got a page fault, specifically inside `snd_hdac_bus_parse_capabilities`, down in `sound/hda/core/controller.c` at line 412. The faulting address is `000000000001c094`. That instruction, `mov 0x14(%rax),%r13w`, strongly suggests an MMIO read is the culprit. We're trying to fetch a 16-bit word from an offset of 0x14 relative to the address in `%rax`.
The snag is, `%rax` is holding `000000000001c080`. That's a huge red flag. It's not a valid *kernel* virtual address. It looks much more like a physical address or, perhaps, an offset within a region that *should* be mapped but isn't. Something's gone wrong with the virtual-to-physical mapping, or the address is just fundamentally wrong.
Time to delve into `snd_hdac_bus_parse_capabilities`. I need to understand what's supposed to be happening there, what this function is trying to read from, and how the MMIO region is expected to be set up. This is likely an HD-audio controller initialization or capability detection problem. Gotta trace the path of how this physical address gets used. Let's see if we can find some clues in the surrounding code.
|
| 7/2 |
2026/08/09 06:01 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:sound/hda/core/controller.c Name:snd_hdac_bus_parse_capabilities]
Results: map[SourceCode: 400: /**
401: * snd_hdac_bus_parse_capabilities - parse capability structure
402: * @bus: the pointer to bus object
403: *
404: * Returns 0 if successful, or a negative error code.
405: */
406: int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
407: {
408: unsigned int cur_cap;
409: unsigned int offset;
410: unsigned int counter = 0;
411:
412: offset = snd_hdac_chip_readw(bus, LLCH);
413:
414: /* Lets walk the linked capabilities list */
415: do {
416: cur_cap = _snd_hdac_chip_readl(bus, offset);
417:
418: dev_dbg(bus->dev, "Capability version: 0x%x\n",
419: (cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);
420:
421: dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
422: (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
423:
424: if (cur_cap == -1) {
425: dev_dbg(bus->dev, "Invalid capability reg read\n");
426: break;
427: }
428:
429: switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
430: case AZX_ML_CAP_ID:
431: dev_dbg(bus->dev, "Found ML capability\n");
432: bus->mlcap = bus->remap_addr + offset;
433: break;
434:
435: case AZX_GTS_CAP_ID:
436: dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
437: bus->gtscap = bus->remap_addr + offset;
438: break;
439:
440: case AZX_PP_CAP_ID:
441: /* PP capability found, the Audio DSP is present */
442: dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
443: bus->ppcap = bus->remap_addr + offset;
444: break;
445:
446: case AZX_SPB_CAP_ID:
447: /* SPIB capability found, handler function */
448: dev_dbg(bus->dev, "Found SPB capability\n");
449: bus->spbcap = bus->remap_addr + offset;
450: break;
451:
452: case AZX_DRSM_CAP_ID:
453: /* DMA resume capability found, handler function */
454: dev_dbg(bus->dev, "Found DRSM capability\n");
455: bus->drsmcap = bus->remap_addr + offset;
456: break;
457:
458: default:
459: dev_err(bus->dev, "Unknown capability %d\n", cur_cap);
460: cur_cap = 0;
461: break;
462: }
463:
464: counter++;
465:
466: if (counter > HDAC_MAX_CAPS) {
467: dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n");
468: break;
469: }
470:
471: /* read the offset of next capability */
472: offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
473:
474: } while (offset);
475:
476: return 0;
477: }
SourceFile:sound/hda/core/controller.c]
|
| 8/2 |
2026/08/09 06:01 |
llm |
expert |
0m
Model:
Tokens:
input: 10028
output: 43
thoughts: 44
Thoughts: Okay, here's my take on those thoughts, framed as an expert's internal monologue:
**Diving into the HD-audio Driver Code**
Alright, let's get into it. Line 412 is grabbing an `offset` value using `snd_hdac_chip_readw(bus, LLCH)`. Right, so I need to understand what this `snd_hdac_chip_readw` function actually *does*. It's critical to understanding the interaction with the High Definition Audio controller, specifically how it's getting the data. I'm assuming that 'bus' refers to the PCI bus instance but I need to know where the values read by `LLCH` are being stored. I'll need to remember to check the return value, too, in case of an error condition. This `LLCH` must represent a specific register offset within the HD-audio controller... I wonder what 'LLCH' stands for, exactly? Let's check the header files for the structure definition... and see what memory locations are being accessed here.
|
| 9/2 |
2026/08/09 06:01 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:sound/hda/core/controller.c Name:snd_hdac_chip_readw]
Results: map[SourceCode: SourceFile:]
|
| 10/2 |
2026/08/09 06:01 |
llm |
expert |
0m
Model:
Tokens:
input: 10363
output: 25
thoughts: 14
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and written in the first person:
**Initial Assessment: Probable Macro-Based Behavior**
Alright, based on the initial observation (which I'm not privy to, but assume I've encountered some anomalous behavior), my first inclination is that this is likely driven by a macro. My experience has taught me that these types of anomalies frequently point to malicious code or unauthorized automation, and macros are a common vector for that sort of thing. I need to dig deeper.
I'll quickly start with the initial assessment and dive in to try to locate this macro.
|
| 11/2 |
2026/08/09 06:02 |
tool |
grepper |
0m
Args: map[Expression:#define snd_hdac_chip_readw]
Results: map[Output:include/sound/hdaudio.h=455=static inline u16 snd_hdac_reg_readw(struct hdac_bus *bus, void __iomem *addr)
--
include/sound/hdaudio.h-490- _snd_hdac_chip_readl(chip, AZX_REG_ ## reg)
include/sound/hdaudio.h:491:#define snd_hdac_chip_readw(chip, reg) \
include/sound/hdaudio.h-492- _snd_hdac_chip_readw(chip, AZX_REG_ ## reg)
]
|
| 12/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 10702
output: 26
thoughts: 16
Thoughts: Okay, here's my attempt at summarizing the thought process, as requested, in the first person and assuming an expert audience:
**Analyzing the `_snd_hdac_chip_readw` Function**
Alright, let's dive into this `_snd_hdac_chip_readw` function. Given the naming convention, I'm immediately expecting this to be a low-level, hardware-interaction routine related to reading a word (16 bits, typically) from an HD Audio Controller (HDA) chip. The prefix "snd_" strongly indicates this is part of the ALSA sound system framework, so I'm thinking embedded driver code.
My initial questions are: what registers does this function handle? Is this part of a larger read/write API? Are there any associated mutexes or spinlocks involved, and are they properly handled to ensure atomicity and prevent race conditions with other accesses to the same hardware registers?
I need to understand the context. Is this called directly by other driver functions, or is it abstracted behind a higher-level interface? If it's used internally, I'll need to see the calling functions to understand *how* it's being used β are there any potential read-modify-write sequences happening? Are there any inherent risks for stale data if the underlying register is modified elsewhere?
Next, I'll want to trace its usage within the overall HDA driver. Are there any error handling or retry mechanisms implemented? What happens if the read fails? Does it involve specific delays or hardware access timing constraints? If it directly interacts with MMIO, I'll need to check for proper address translation, including potential PCI configuration issues. Finally, I'll search for potential bugs by examining how the return values are used, and whether the function properly validates its input parameters.
|
| 13/2 |
2026/08/09 06:02 |
tool |
grepper |
0m
Args: map[Expression:#define _snd_hdac_chip_readw]
Results: map[Output:include/sound/hdaudio.h=455=static inline u16 snd_hdac_reg_readw(struct hdac_bus *bus, void __iomem *addr)
--
include/sound/hdaudio.h-474- snd_hdac_reg_writew(chip, (chip)->remap_addr + (reg), value)
include/sound/hdaudio.h:475:#define _snd_hdac_chip_readw(chip, reg) \
include/sound/hdaudio.h-476- snd_hdac_reg_readw(chip, (chip)->remap_addr + (reg))
]
|
| 14/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 11295
output: 42
thoughts: 64
Thoughts: Here's my take on the thought process, as requested:
**Deconstructing the Code**
Okay, so I'm looking at this `snd_hdac_chip_readw(bus, LLCH)` call. Right off the bat, I know that expands to `snd_hdac_reg_readw(bus, bus->remap_addr + AZX_REG_LLCH)`. No surprises there. It's a pretty standard macro expansion, handling the register address calculation. Now, the meat of the operation is really within `snd_hdac_reg_readw`. I need to figure out what's going on *inside* that function. Let's delve in there and see how it is handling the bus and address. Hopefully it's not too convoluted. I'm especially curious about how it's interacting with the hardware abstraction layer. Let's see...
|
| 15/2 |
2026/08/09 06:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/sound/hdaudio.h Name:snd_hdac_reg_readw]
Results: map[SourceCode: 455: static inline u16 snd_hdac_reg_readw(struct hdac_bus *bus, void __iomem *addr)
456: {
457: return snd_hdac_aligned_mmio(bus) ?
458: snd_hdac_aligned_read(addr, 0xffff) : readw(addr);
459: }
SourceFile:include/sound/hdaudio.h]
|
| 16/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 11727
output: 23
thoughts: 73
Thoughts: **Analyzing the Memory Read Crash**
Okay, so I've hit a snag. The system's crashing right at `readw(addr)`, and based on the call stack, `addr` is derived from `bus->remap_addr + AZX_REG_LLCH`. My initial thought is to dive into the definition of `AZX_REG_LLCH` right away to understand the context. It looks like the faulting address is `000000000001c094`. I need to figure out how this derived address relates to the faulting address and how `AZX_REG_LLCH` is used to trigger it. Let's see what that register represents and if the value is what I'd expect.
|
| 17/2 |
2026/08/09 06:02 |
tool |
grepper |
0m
Args: map[Expression:#define AZX_REG_LLCH]
Results: map[Output:include/sound/hda_register.h-32-#define AZX_REG_GCAP2 0x12
include/sound/hda_register.h:33:#define AZX_REG_LLCH 0x14
include/sound/hda_register.h-34-#define AZX_REG_OUTSTRMPAY 0x18
--
include/sound/hda_register.h=78=enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
--
include/sound/hda_register.h-96-/* GTS registers */
include/sound/hda_register.h:97:#define AZX_REG_LLCH 0x14
include/sound/hda_register.h-98-
]
|
| 18/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 12197
output: 40
thoughts: 172
Thoughts: **Decoding the Address Remapping**
Okay, so I'm looking at this `AZX_REG_LLCH`, which I see is defined as `0x14`. Right, so this means, based on my understanding of this system, that `bus->remap_addr` is initialized to `0x1c080`. But, hold on a second... *why* is it initialized to *that* specific value? I need to understand the logic behind this assignment.
Let me trace back the initialization. Where does `bus->remap_addr` get its value? The call trace provides the necessary clues: it goes through `azx_first_init` (at line 1936 in intel.c), then `azx_probe_continue` (line 2365), and finally ends up in `azx_probe_work` (line 1737), which looks like where the core logic is situated. I'll need to dig into those functions, in that order, to fully grasp the mapping and the reasoning behind that particular base address. I'm guessing there's some hardware configuration or register read happening somewhere in there that sets this value. Time to dive into the source!
|
| 19/2 |
2026/08/09 06:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:sound/hda/controllers/intel.c Name:azx_first_init]
Results: map[SourceCode:1892: static int azx_first_init(struct azx *chip)
1893: {
1894: int dev = chip->dev_index;
1895: struct pci_dev *pci = chip->pci;
1896: struct snd_card *card = chip->card;
1897: struct hdac_bus *bus = azx_bus(chip);
1898: int err;
1899: unsigned short gcap;
1900: unsigned int dma_bits = 64;
1901:
1902: #if BITS_PER_LONG != 64
1903: /* Fix up base address on ULI M5461 */
1904: if (chip->driver_type == AZX_DRIVER_ULI) {
1905: u16 tmp3;
1906: pci_read_config_word(pci, 0x40, &tmp3);
1907: pci_write_config_word(pci, 0x40, tmp3 | 0x10);
1908: pci_write_config_dword(pci, PCI_BASE_ADDRESS_1, 0);
1909: }
1910: #endif
1911: /*
1912: * Fix response write request not synced to memory when handle
1913: * hdac interrupt on Glenfly Gpus
1914: */
1915: if (chip->driver_type == AZX_DRIVER_GFHDMI)
1916: bus->polling_mode = 1;
1917:
1918: if (chip->driver_type == AZX_DRIVER_LOONGSON) {
1919: bus->polling_mode = 1;
1920: bus->not_use_interrupts = 1;
1921: bus->access_sdnctl_in_dword = 1;
1922: if (!chip->jackpoll_interval)
1923: chip->jackpoll_interval = msecs_to_jiffies(1500);
1924: }
1925:
1926: if (chip->driver_type == AZX_DRIVER_ZHAOXINHDMI)
1927: bus->polling_mode = 1;
1928:
1929: bus->remap_addr = pcim_iomap_region(pci, 0, "ICH HD audio");
1930: if (IS_ERR(bus->remap_addr))
1931: return PTR_ERR(bus->remap_addr);
1932:
1933: bus->addr = pci_resource_start(pci, 0);
1934:
1935: if (chip->driver_type == AZX_DRIVER_SKL)
1936: snd_hdac_bus_parse_capabilities(bus);
1937:
1938: /*
1939: * Some Intel CPUs has always running timer (ART) feature and
1940: * controller may have Global time sync reporting capability, so
1941: * check both of these before declaring synchronized time reporting
1942: * capability SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME
1943: */
1944: chip->gts_present = false;
1945:
1946: #ifdef CONFIG_X86
1947: if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART))
1948: chip->gts_present = true;
1949: #endif
1950:
1951: pci_set_master(pci);
1952:
1953: gcap = azx_readw(chip, GCAP);
1954: dev_dbg(card->dev, "chipset global capabilities = 0x%x\n", gcap);
1955:
1956: /* AMD devices support 40 or 48bit DMA, take the safe one */
1957: if (chip->pci->vendor == PCI_VENDOR_ID_AMD)
1958: dma_bits = 40;
1959:
1960: /* disable SB600 64bit support for safety */
1961: if (chip->pci->vendor == PCI_VENDOR_ID_ATI) {
1962: struct pci_dev *p_smbus;
1963: dma_bits = 40;
1964: p_smbus = pci_get_device(PCI_VENDOR_ID_ATI,
1965: PCI_DEVICE_ID_ATI_SBX00_SMBUS,
1966: NULL);
1967: if (p_smbus) {
1968: if (p_smbus->revision < 0x30)
1969: gcap &= ~AZX_GCAP_64OK;
1970: pci_dev_put(p_smbus);
1971: }
1972: }
1973:
1974: /* NVidia hardware normally only supports up to 40 bits of DMA */
1975: if (chip->pci->vendor == PCI_VENDOR_ID_NVIDIA)
1976: dma_bits = 40;
1977:
1978: /* disable 64bit DMA address on some devices */
1979: if (chip->driver_caps & AZX_DCAPS_NO_64BIT) {
1980: dev_dbg(card->dev, "Disabling 64bit DMA\n");
1981: gcap &= ~AZX_GCAP_64OK;
1982: }
1983:
1984: /* disable buffer size rounding to 128-byte multiples if supported */
1985: if (align_buffer_size >= 0)
1986: chip->align_buffer_size = !!align_buffer_size;
1987: else {
1988: if (chip->driver_caps & AZX_DCAPS_NO_ALIGN_BUFSIZE)
1989: chip->align_buffer_size = 0;
1990: else
1991: chip->align_buffer_size = 1;
1992: }
1993:
1994: /* allow 64bit DMA address if supported by H/W */
1995: if (!(gcap & AZX_GCAP_64OK))
1996: dma_bits = 32;
1997: if (dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(dma_bits)))
1998: dma_set_mask_and_coherent(&pci->dev, DMA_BIT_MASK(32));
1999: dma_set_max_seg_size(&pci->dev, UINT_MAX);
2000:
2001: if (chip->msi && chip->driver_caps & AZX_DCAPS_NO_MSI64) {
2002: dev_dbg(card->dev, "Restricting MSI to %u-bit\n", dma_bits);
2003: pci->msi_addr_mask = DMA_BIT_MASK(dma_bits);
2004: }
2005:
2006: /* read number of streams from GCAP register instead of using
2007: * hardcoded value
2008: */
2009: chip->capture_streams = (gcap >> 8) & 0x0f;
2010: chip->playback_streams = (gcap >> 12) & 0x0f;
2011: if (!chip->playback_streams && !chip->capture_streams) {
2012: /* gcap didn't give any info, switching to old method */
2013:
2014: switch (chip->driver_type) {
2015: case AZX_DRIVER_ULI:
2016: chip->playback_streams = ULI_NUM_PLAYBACK;
2017: chip->capture_streams = ULI_NUM_CAPTURE;
2018: break;
2019: case AZX_DRIVER_ATIHDMI:
2020: case AZX_DRIVER_ATIHDMI_NS:
2021: chip->playback_streams = ATIHDMI_NUM_PLAYBACK;
2022: chip->capture_streams = ATIHDMI_NUM_CAPTURE;
2023: break;
2024: case AZX_DRIVER_GFHDMI:
2025: case AZX_DRIVER_ZHAOXINHDMI:
2026: case AZX_DRIVER_GENERIC:
2027: default:
2028: chip->playback_streams = ICH6_NUM_PLAYBACK;
2029: chip->capture_streams = ICH6_NUM_CAPTURE;
2030: break;
2031: }
2032: }
2033: chip->capture_index_offset = 0;
2034: chip->playback_index_offset = chip->capture_streams;
2035: chip->num_streams = chip->playback_streams + chip->capture_streams;
2036:
2037: /* sanity check for the SDxCTL.STRM field overflow */
2038: if (chip->num_streams > 15 &&
2039: (chip->driver_caps & AZX_DCAPS_SEPARATE_STREAM_TAG) == 0) {
2040: dev_warn(chip->card->dev, "number of I/O streams is %d, "
2041: "forcing separate stream tags", chip->num_streams);
2042: chip->driver_caps |= AZX_DCAPS_SEPARATE_STREAM_TAG;
2043: }
2044:
2045: /* initialize streams */
2046: err = hda_init_streams(chip);
2047: if (err < 0)
2048: return err;
2049:
2050: err = azx_alloc_stream_pages(chip);
2051: if (err < 0)
2052: return err;
2053:
2054: /* initialize chip */
2055: azx_init_pci(chip);
2056:
2057: snd_hdac_i915_set_bclk(bus);
2058:
2059: hda_intel_init_chip(chip, (probe_only[dev] & 2) == 0);
2060:
2061: /* codec detection */
2062: if (!azx_bus(chip)->codec_mask) {
2063: dev_err(card->dev, "no codecs found!\n");
2064: /* keep running the rest for the runtime PM */
2065: }
2066:
2067: if (azx_acquire_irq(chip, 0) < 0)
2068: return -EBUSY;
2069:
2070: strscpy(card->driver, "HDA-Intel");
2071: strscpy(card->shortname, driver_short_names[chip->driver_type],
2072: sizeof(card->shortname));
2073: snprintf(card->longname, sizeof(card->longname),
2074: "%s at 0x%lx irq %i",
2075: card->shortname, bus->addr, bus->irq);
2076:
2077: return 0;
2078: }
SourceFile:sound/hda/controllers/intel.c]
|
| 20/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 15867
output: 42
thoughts: 206
Thoughts: **Decoding the IOMAP Address Remapping**
Okay, so I'm looking at this code snippet: `bus->remap_addr = pcim_iomap_region(pci, 0, "ICH HD audio");` It's mapping a region, probably for some audio hardware. Right, the expectation is that `pcim_iomap_region` returns a memory-mapped address. But the crucial thing here is that if it *fails*, it returns an error *pointer*. Not just any error code, but an actual pointer-like value that signifies failure.
My first instinct is to check if it failed with `IS_ERR(bus->remap_addr)`. That's the correct approach to see if itβs an error pointer, not a valid memory address. But, just to be precise, I need to remind myself how this all works... `pcim_iomap_region` calls `pci_iomap` at some point.
Now, wait a second... what if the returned address *looks* like a valid address, but is actually the error pointer? Say it returns `0x1c080`? Is *that* an error? `0x1c080` is 114816 in decimal, which looks completely reasonable... That's a valid address, it's NOT -ENOMEM. So, let me just double check how `pcim_iomap_region` and its underlying functions handle error scenarios. It has to be something that marks this address as invalid. I need to refresh my memory on what that looks like and how the error-checking logic works, because it's easy to get confused with memory addresses when dealing with drivers. Okay, let me go look at `pcim_iomap_region` to be sure.
|
| 21/2 |
2026/08/09 06:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:sound/hda/controllers/intel.c Name:pcim_iomap_region]
Results: map[SourceCode: 593: /**
594: * pcim_iomap_region - Request and iomap a PCI BAR
595: * @pdev: PCI device to map IO resources for
596: * @bar: Index of a BAR to map
597: * @name: Name of the driver requesting the resource
598: *
599: * Returns: __iomem pointer on success, an IOMEM_ERR_PTR on failure.
600: *
601: * Mapping and region will get automatically released on driver detach. If
602: * desired, release manually only with pcim_iounmap_region().
603: */
604: void __iomem *pcim_iomap_region(struct pci_dev *pdev, int bar,
605: const char *name)
606: {
607: int ret;
608: struct pcim_addr_devres *res;
609:
610: if (!pci_bar_index_is_valid(bar))
611: return IOMEM_ERR_PTR(-EINVAL);
612:
613: res = pcim_addr_devres_alloc(pdev);
614: if (!res)
615: return IOMEM_ERR_PTR(-ENOMEM);
616:
617: res->type = PCIM_ADDR_DEVRES_TYPE_REGION_MAPPING;
618: res->bar = bar;
619:
620: ret = pci_request_region(pdev, bar, name);
621: if (ret != 0)
622: goto err_region;
623:
624: res->baseaddr = pci_iomap(pdev, bar, 0);
625: if (!res->baseaddr) {
626: ret = -EINVAL;
627: goto err_iomap;
628: }
629:
630: devres_add(&pdev->dev, res);
631: return res->baseaddr;
632:
633: err_iomap:
634: pci_release_region(pdev, bar);
635: err_region:
636: pcim_addr_devres_free(res);
637:
638: return IOMEM_ERR_PTR(ret);
639: }
SourceFile:drivers/pci/devres.c]
|
| 22/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 17242
output: 39
thoughts: 166
Thoughts: **Decoding the `pci_iomap` Mystery (Again)**
Okay, so `pci_iomap` returns a `void __iomem *`. Standard. But if I get something like `0x1c080` back, my initial thought is, "Aha! I/O port mapping!" It's that subtle difference between memory and port space that always gets a little murky.
Right, I need to remember β and confirm β that `pci_iomap` *can* indeed handle I/O ports. I have a feeling that if the Base Address Register (BAR) describes an I/O port, the returned pointer is actually something along the lines of `(void __iomem *)(port + PIO_OFFSET)`, or maybe `PCI_IOBASE` is involved. My mental model is a bit fuzzy on the exact offset calculation.
The x86 architecture is where this gets particularly interesting and potentially tricky. Is it simply the I/O port number, offset by `PCI_IOBASE`, or is there a more complex translation happening? I should probably check the source code for `pci_iomap` on x86 specifically to confirm my understanding of this. Time to dig in and clarify that little detail once and for all.
|
| 23/2 |
2026/08/09 06:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/pci/devres.c Name:pci_iomap]
Results: map[SourceCode: 109: /**
110: * pci_iomap - create a virtual mapping cookie for a PCI BAR
111: * @dev: PCI device that owns the BAR
112: * @bar: BAR number
113: * @maxlen: length of the memory to map
114: *
115: * Using this function you will get a __iomem address to your device BAR.
116: * You can access it using ioread*() and iowrite*(). These functions hide
117: * the details if this is a MMIO or PIO address space and will just do what
118: * you expect from them in the correct way.
119: *
120: * @maxlen specifies the maximum length to map. If you want to get access to
121: * the complete BAR without checking for its length first, pass %0 here.
122: * */
123: void __iomem *pci_iomap(struct pci_dev *dev, int bar, unsigned long maxlen)
124: {
125: return pci_iomap_range(dev, bar, 0, maxlen);
126: }
SourceFile:drivers/pci/iomap.c]
|
| 24/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 18079
output: 42
thoughts: 14
Thoughts: Okay, here's my attempt at summarizing that thought process, keeping in mind I'm speaking to a fellow expert:
**Investigating `pci_iomap_range`**
Right, let's dive into this. The initial thought, of course, is to check `pci_iomap_range`. We're likely dealing with PCI I/O mapping, and this function name is a pretty strong hint. I'm immediately thinking about the underlying hardware and the potential for memory-mapped I/O, or possibly even direct memory access. I'll need to refresh my memory on the specifics of how this function interacts with the PCI configuration space, and how it handles potentially complex resource allocations. I'll be thinking about possible issues such as address space exhaustion, memory aliasing, and the various access permissions that might be in play. Ultimately, the goal is to understand how this function facilitates the interaction between the kernel and the PCI device's registers. I will examine its behavior within the context of the larger architecture. I will need to consider its implications for driver stability and performance.
|
| 25/2 |
2026/08/09 06:02 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/pci/iomap.c Name:pci_iomap_range]
Results: map[SourceCode: 14: /**
15: * pci_iomap_range - create a virtual mapping cookie for a PCI BAR
16: * @dev: PCI device that owns the BAR
17: * @bar: BAR number
18: * @offset: map memory at the given offset in BAR
19: * @maxlen: max length of the memory to map
20: *
21: * Using this function you will get a __iomem address to your device BAR.
22: * You can access it using ioread*() and iowrite*(). These functions hide
23: * the details if this is a MMIO or PIO address space and will just do what
24: * you expect from them in the correct way.
25: *
26: * @maxlen specifies the maximum length to map. If you want to get access to
27: * the complete BAR from offset to the end, pass %0 here.
28: * */
29: void __iomem *pci_iomap_range(struct pci_dev *dev,
30: int bar,
31: unsigned long offset,
32: unsigned long maxlen)
33: {
34: resource_size_t start, len;
35: unsigned long flags;
36:
37: if (!pci_bar_index_is_valid(bar))
38: return NULL;
39:
40: start = pci_resource_start(dev, bar);
41: len = pci_resource_len(dev, bar);
42: flags = pci_resource_flags(dev, bar);
43:
44: if (len <= offset || !start)
45: return NULL;
46:
47: len -= offset;
48: start += offset;
49: if (maxlen && len > maxlen)
50: len = maxlen;
51: if (flags & IORESOURCE_IO)
52: return __pci_ioport_map(dev, start, len);
53: if (flags & IORESOURCE_MEM)
54: return ioremap(start, len);
55: /* What? */
56: return NULL;
57: }
SourceFile:drivers/pci/iomap.c]
|
| 26/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 19020
output: 42
thoughts: 22
Thoughts: Okay, here's my interpretation of that thought process, tailored for an expert audience, and presented as if it were my own internal monologue:
**Mapping I/O Ports in the PCI Context**
Right, so I'm looking at how we're handling PCI configuration space access, specifically focusing on I/O ports. My immediate trigger here is the identification of an I/O port. If the hardware registers a location as an I/O port, then there is only one function that is capable to handle the transaction: `__pci_ioport_map`. This is the core function for mapping those I/O port regions. It does all the necessary low-level work, taking into account the nuances of the PCI bus architecture. It's the building block, essentially. I don't need to overthink it; the function call is clear and direct.
|
| 27/2 |
2026/08/09 06:02 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/pci/iomap.c Name:__pci_ioport_map]
Results: map[SourceCode: SourceFile:]
|
| 28/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 19287
output: 22
thoughts: 15
Thoughts: Okay, here's my summary, presented as a stream of thought:
**Diving into the Kernel's PCI I/O Mapping**
Alright, time to get to work. I need to understand how this system handles PCI I/O port mapping. Where do I even start? Hmm, let's try the direct approach. I'll search the codebase for the function `__pci_ioport_map`. That should be a good starting point. This name *suggests* it's core to the process. Hopefully, this function is central, or at least a good indicator of the code I'm looking for. Let's see what the search turns up. Once I have the results, I can start to map out the function's dependencies and how it interacts with other parts of the PCI subsystem. This is a common and usually revealing approach to figuring out the kernel's architecture. I expect I'll be sifting through some header files and definitions along the way, but that's standard operating procedure. I am expecting some interesting results.
|
| 29/2 |
2026/08/09 06:02 |
tool |
grepper |
0m
Args: map[Expression:__pci_ioport_map]
Results: map[Output:arch/mips/lib/iomap-pci.c-15-
arch/mips/lib/iomap-pci.c:16:void __iomem *__pci_ioport_map(struct pci_dev *dev,
arch/mips/lib/iomap-pci.c-17- unsigned long port, unsigned int nr)
--
arch/sh/drivers/pci/pci.c=262=void __ref pcibios_report_status(unsigned int status_mask, int warn)
--
arch/sh/drivers/pci/pci.c-276-
arch/sh/drivers/pci/pci.c:277:void __iomem *__pci_ioport_map(struct pci_dev *dev,
arch/sh/drivers/pci/pci.c-278- unsigned long port, unsigned int nr)
--
drivers/pci/iomap.c=29=void __iomem *pci_iomap_range(struct pci_dev *dev,
--
drivers/pci/iomap.c-51- if (flags & IORESOURCE_IO)
drivers/pci/iomap.c:52: return __pci_ioport_map(dev, start, len);
drivers/pci/iomap.c-53- if (flags & IORESOURCE_MEM)
--
include/asm-generic/pci_iomap.h=21=extern void pci_iounmap(struct pci_dev *dev, void __iomem *);
--
include/asm-generic/pci_iomap.h-25-#ifdef CONFIG_NO_GENERIC_PCI_IOPORT_MAP
include/asm-generic/pci_iomap.h:26:extern void __iomem *__pci_ioport_map(struct pci_dev *dev, unsigned long port,
include/asm-generic/pci_iomap.h-27- unsigned int nr);
include/asm-generic/pci_iomap.h-28-#elif !defined(CONFIG_HAS_IOPORT_MAP)
include/asm-generic/pci_iomap.h:29:#define __pci_ioport_map(dev, port, nr) NULL
include/asm-generic/pci_iomap.h-30-#else
include/asm-generic/pci_iomap.h:31:#define __pci_ioport_map(dev, port, nr) ioport_map((port), (nr))
include/asm-generic/pci_iomap.h-32-#endif
]
|
| 30/2 |
2026/08/09 06:02 |
llm |
expert |
0m
Model:
Tokens:
input: 20217
output: 20
thoughts: 22
Thoughts: Here's my summary, as though I'm thinking through this myself, given my expertise:
**x86 PCI I/O Port Mapping: A Familiar Connection**
Okay, so I'm looking at this x86 architecture and, right off the bat, I see a bit of a shortcut. The code states that `__pci_ioport_map` is just a direct alias for `ioport_map`. That's not entirely surprising, really. Given x86's legacy design, the PCI I/O port mapping is inherently handled by the more general `ioport_map` function. There's no separate or specialized function needed here. `ioport_map` is the core mechanism that's been in place forever to deal with this, so the PCI aspects piggyback right off it. Itβs pretty efficient from a code reuse perspective, and keeps things simple within the kernel, given the history of the hardware. Good to see that efficiency implemented here.
|
| 31/2 |
2026/08/09 06:03 |
tool |
grepper |
0m
Args: map[Expression:#define ioport_map]
Results: map[Output:arch/alpha/include/asm/io.h=298=extern inline void ioport_unmap(void __iomem *addr)
--
arch/alpha/include/asm/io.h-301-
arch/alpha/include/asm/io.h:302:#define ioport_map ioport_map
arch/alpha/include/asm/io.h-303-#define ioport_unmap ioport_unmap
--
arch/arm/include/asm/io.h=395=void *arch_memremap_wb(phys_addr_t phys_addr, size_t size, unsigned long flags);
--
arch/arm/include/asm/io.h-407-#ifndef ioport_map
arch/arm/include/asm/io.h:408:#define ioport_map ioport_map
arch/arm/include/asm/io.h-409-extern void __iomem *ioport_map(unsigned long port, unsigned int nr);
--
arch/m68k/include/asm/kmap.h=50=static inline void memcpy_toio(volatile void __iomem *dst, const void *src,
--
arch/m68k/include/asm/kmap.h-57-
arch/m68k/include/asm/kmap.h:58:#define ioport_map ioport_map
arch/m68k/include/asm/kmap.h-59-static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
--
arch/parisc/include/asm/io.h=239=extern void ioport_unmap(void __iomem *);
arch/parisc/include/asm/io.h:240:#define ioport_map ioport_map
arch/parisc/include/asm/io.h-241-#define ioport_unmap ioport_unmap
--
arch/powerpc/include/asm/io.h=750=void __iomem *ioport_map(unsigned long port, unsigned int len);
arch/powerpc/include/asm/io.h:751:#define ioport_map ioport_map
arch/powerpc/include/asm/io.h-752-#endif
--
arch/sh/include/asm/io.h=119=__BUILD_MEMORY_STRING(__raw_, q, u64)
arch/sh/include/asm/io.h-120-
arch/sh/include/asm/io.h:121:#define ioport_map ioport_map
arch/sh/include/asm/io.h-122-#define pci_iounmap pci_iounmap
--
arch/sparc/include/asm/io_64.h=452=void ioport_unmap(void __iomem *);
arch/sparc/include/asm/io_64.h:453:#define ioport_map ioport_map
arch/sparc/include/asm/io_64.h-454-#define ioport_unmap ioport_unmap
--
include/asm-generic/io.h=1196=static inline void __iomem *ioremap_np(phys_addr_t offset, size_t size)
--
include/asm-generic/io.h-1204-#ifndef ioport_map
include/asm-generic/io.h:1205:#define ioport_map ioport_map
include/asm-generic/io.h-1206-static inline void __iomem *ioport_map(unsigned long port, unsigned int nr)
]
|
| 32/2 |
2026/08/09 06:03 |
llm |
expert |
0m
Model:
Tokens:
input: 21365
output: 37
thoughts: 22
Thoughts: Here's my take on those thoughts, tailored for an expert audience:
**Diving into the Low-Level I/O Details**
Okay, so I need to refresh my memory on the kernel's I/O mapping mechanisms. Specifically, I need to understand how the `ioport_map` function works. Let's start by digging into `include/asm-generic/io.h`. I'm guessing this file provides a generic definition, or at least a high-level abstraction, of the hardware I/O port mapping process. I'll need to check if this is hardware-dependent. If so, I'll need to remember where to find the architecture-specific implementations (x86, ARM, etc.). The goal is to quickly see the details of how I/O ports are translated to virtual addresses and what protections are employed, so that I can figure out the context of the calling code.
|
| 33/2 |
2026/08/09 06:03 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/asm-generic/io.h Name:ioport_map]
Results: map[SourceCode: 405: /* Create a virtual mapping cookie for an IO port range */
406: void __iomem *ioport_map(unsigned long port, unsigned int nr)
407: {
408: if (port > PIO_MASK)
409: return NULL;
410: return (void __iomem *) (unsigned long) (port + PIO_OFFSET);
411: }
SourceFile:lib/iomap.c]
|
| 34/2 |
2026/08/09 06:03 |
llm |
expert |
0m
Model:
Tokens:
input: 21761
output: 20
thoughts: 61
Thoughts: **Navigating the I/O Mapping Landscape**
Okay, so I need to find the definition for `ioport_map`. Right, I remember seeing that function. Let me see... yes, `lib/iomap.c`, got it. Now, just a gut feeling here, but I recall some hardware-specific nuances with I/O port offsets, especially on x86 architectures. Wasn't `PIO_OFFSET` something like `0x10000` or thereabouts on x86? Just to be absolutely sure, I should check that `PIO_OFFSET` definition specific to x86. That's the next step. I'll make sure to double check how the offset is defined to make sure nothing is overlooked.
|
| 35/2 |
2026/08/09 06:03 |
tool |
grepper |
0m
Args: map[Expression:#define PIO_OFFSET]
Results: map[Output:arch/openrisc/include/asm/io.h-28-#define PIO_RESERVED 0X0UL
arch/openrisc/include/asm/io.h:29:#define PIO_OFFSET 0
arch/openrisc/include/asm/io.h-30-#define PIO_MASK 0
--
arch/powerpc/include/asm/io.h=838=extern void __iomem *__ioremap_caller(phys_addr_t, unsigned long size,
--
arch/powerpc/include/asm/io.h-847-#define HAVE_ARCH_PIO_SIZE 1
arch/powerpc/include/asm/io.h:848:#define PIO_OFFSET 0x00000000UL
arch/powerpc/include/asm/io.h-849-#define PIO_MASK (FULL_IO_SIZE - 1)
--
lib/iomap.c-34- */
lib/iomap.c:35:#define PIO_OFFSET 0x10000UL
lib/iomap.c-36-#define PIO_MASK 0x0ffffUL
]
|
| 36/2 |
2026/08/09 06:03 |
llm |
expert |
0m
Model:
Tokens:
input: 22300
output: 42
thoughts: 448
Thoughts: **Analyzing the HD-Audio Controller Register Access**
Okay, let's break this down step-by-step. I'm looking at the HD-audio controller access code. I remember `PIO_OFFSET` being defined as `0x10000UL` in `lib/iomap.c`. So, if the base `port` is `0xc080`, `ioport_map` correctly returns `0x1c080`. That looks right, and `bus->remap_addr` is indeed `0x1c080`.
Now, the troublesome part. `snd_hdac_bus_parse_capabilities` then proceeds to do: `offset = snd_hdac_chip_readw(bus, LLCH);`. This expands to `snd_hdac_reg_readw(bus, bus->remap_addr + AZX_REG_LLCH)`. Substituting the values, that becomes `snd_hdac_reg_readw(bus, 0x1c080 + 0x14)`, which simplifies to `snd_hdac_reg_readw(bus, 0x1c094)`.
Now, the critical function, `snd_hdac_reg_readw` uses `readw(addr)`. Ah, here's the problem. On x86, `readw` is just a simple pointer dereference: `*(volatile u16 *)addr`.
If `addr` is `0x1c094`, this means we are attempting to dereference the address `0x1c094`. That will cause a page fault because it is trying to access an invalid memory location!
Wait a minute, `pci_iomap` *returns* an I/O port mapping cookie, which looks something like `0x10000 + port`. To access it, you're supposed to use `ioread16(addr)`. `ioread16` internally checks if `addr < 0x20000`. If so, it uses `inw(addr & 0xffff)` to perform the I/O read. That's how it is *supposed* to work.
But, `snd_hdac_reg_readw` *directly* dereferences the pointer with `readw(addr)`. That's a direct memory access to an I/O port, which is wrong! Let me go back and double check `snd_hdac_reg_readw`. I need to confirm this assumption.
|
| 37/2 |
2026/08/09 06:03 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/sound/hdaudio.h Name:snd_hdac_reg_readw]
Results: map[SourceCode: 455: static inline u16 snd_hdac_reg_readw(struct hdac_bus *bus, void __iomem *addr)
456: {
457: return snd_hdac_aligned_mmio(bus) ?
458: snd_hdac_aligned_read(addr, 0xffff) : readw(addr);
459: }
SourceFile:include/sound/hdaudio.h]
|
| 38/2 |
2026/08/09 06:03 |
llm |
expert |
0m
Model:
Tokens:
input: 23488
output: 39
thoughts: 490
Thoughts: **Analyzing a Potential Kernel Vulnerability**
Okay, so I'm looking at this code and I see it's using `readw(addr)`. My initial thought is, wait, `readw` is generally for Memory-Mapped I/O (MMIO), not for Port I/O (PIO) cookies. This immediately raises a red flag. Why is `azx_first_init` calling `pcim_iomap_region(pci, 0, "ICH HD audio")`? BAR 0 of this HD audio controller *should* be MMIO. The problem lies in this: if a device, malicious or simply misconfigured, presents BAR 0 as an I/O port, `pcim_iomap_region` *will* happily map it as such, returning a PIO cookie, something like `0x1c080`.
And here's where it goes sideways. Then, `snd_hdac_bus_parse_capabilities` proceeds to call `readw(0x1c094)`, leading to an immediate page fault. Okay, so we need to drill down and see if this is exploitable.
Let's check `azx_first_init`. `bus->remap_addr = pcim_iomap_region(pci, 0, "ICH HD audio");`. Right. The key here is, if we're dealing with a virtual device β like something created in a fuzzing environment such as syzkaller using `vhci` or raw `pci` fuzzing β we can potentially control BAR 0 and set it to be an I/O port. Bingo!
Is this exploitable? Let's see. It's a read from a relatively fixed, low address (`0x1c094`). Now, I recall that `mmap_min_addr` is *usually* set to `65536` (`0x10000`). If that's the case, then `0x1c094` which is `114836` is *above* `mmap_min_addr`, which is great! This means...wait, let me confirm. `mmap_min_addr` is `65536`. `0x1c094` is indeed `114836`. So, a user process *can* potentially map memory at `0x1c000`! If a user process manages to map memory at `0x1c000`, then the `readw(0x1c094)` call will... *read from user memory*! This is great!
The only question that now remains is that `readw` on x86 *might* not be able to read from user memory if SMAP (Supervisor Mode Access Prevention) is enabled. So now I need to check the x86 `readw` implementation to confirm that.
|
| 39/2 |
2026/08/09 06:03 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/x86/include/asm/io.h Name:readw]
Results: map[SourceCode: 58: build_mmio_read(readw, "w", unsigned short, "=r", :"memory")
SourceFile:arch/x86/include/asm/io.h]
|
| 40/2 |
2026/08/09 06:03 |
llm |
expert |
0m
Model:
Tokens:
input: 24718
output: 43
thoughts: 12
Thoughts: Okay, let's dive into this. Here's my summary, framed from my expert perspective:
**Analyzing `build_mmio_read`**
Alright, time to critically assess this `build_mmio_read` function. Based on the name, I'm expecting this to be the crucial piece responsible for constructing the logic that reads from memory-mapped I/O (MMIO) devices. My immediate mental checklist is ticking off key considerations: potential for race conditions, memory ordering guarantees, volatile accesses, alignment requirements... all the usual suspects when dealing with direct memory access. I need to understand how it's generating the read operation. Is it emitting assembly, or relying on some higher-level abstraction? If the latter, I'll be scrutinizing that abstraction's potential overhead and its ability to handle platform-specific nuances. I'm also looking for any opportunities for optimization. Can it be inlined? Does it generate code that's amenable to compiler optimizations? Are there redundant memory barriers or unnecessary code generation? I'm particularly interested in error handling. What mechanisms are in place to deal with unexpected hardware behaviors like MMIO read failures? I'm going to want to verify that the generated code respects the device's access sizes and any necessary address shifting or masking. And finally, I need to confirm that this doesn't accidentally have any data dependency bugs. Time to dig in and see how well this thing performs. Let's start with a function level assessment.
|
| 41/2 |
2026/08/09 06:03 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:arch/x86/include/asm/io.h Name:build_mmio_read]
Results: map[SourceCode: SourceFile:]
|
| 42/2 |
2026/08/09 06:03 |
llm |
expert |
0m
Model:
Tokens:
input: 25103
output: 22
thoughts: 13
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Analyzing the `build_mmio_read` Context**
Okay, so I'm thinking about `build_mmio_read`. That function name rings a bell; it screams "memory-mapped I/O read construction" to me. My immediate instinct is to figure out where it's being used and how. Time to dust off the `grep` skills. I need to quickly trace the call sites, understand the arguments being passed, and see what the function returns. That'll give me the context I need to figure out its purpose. I'll focus on potential issues like how it handles different data widths, any potential atomicity concerns if it's operating on multi-byte registers, and how it deals with endianness. Depending on the architecture, any register access requires special attention, so I'll be looking for nuances in address calculation or memory barrier usage, if present. I'm also curious about any potential optimizations the compiler might have made in this function. I'll start by making sure I have the latest version of the code and then start grepping for `build_mmio_read`.
|
| 43/2 |
2026/08/09 06:03 |
tool |
grepper |
0m
Args: map[Expression:#define build_mmio_read]
Results: map[Output:arch/x86/include/asm/io.h-46-
arch/x86/include/asm/io.h:47:#define build_mmio_read(name, size, type, reg, barrier) \
arch/x86/include/asm/io.h-48-static inline type name(const volatile void __iomem *addr) \
--
tools/arch/x86/include/asm/io.h-8-
tools/arch/x86/include/asm/io.h:9:#define build_mmio_read(name, size, type, reg, barrier) \
tools/arch/x86/include/asm/io.h-10-static inline type name(const volatile void __iomem *addr) \
]
|
| 44/2 |
2026/08/09 06:03 |
llm |
expert |
0m
Model:
Tokens:
input: 25585
output: 43
thoughts: 28
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience:
**Diving into the MMIO Read Implementation**
Okay, let's get down to brass tacks. I need to understand precisely how memory-mapped I/O reads are handled within the x86 architecture, specifically within the kernel. My starting point is the `build_mmio_read` macro, and I know I can find it in `arch/x86/include/asm/io.h`. This is where the core of the low-level read operation is likely defined, or at least a significant portion of the mechanism. I'm expecting to see either inline assembly or calls to other platform-specific functions that handle the underlying hardware interaction. I'll be looking to understand the interplay between the macro's arguments, any implicit memory barriers used, and how it deals with potential issues like cache coherency or unaligned accesses. My goal is to determine the optimal way to write MMIO read instructions as part of a current problem.
|
| 45/2 |
2026/08/09 06:03 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:arch/x86/include/asm/io.h Name:build_mmio_read]
Results: map[SourceCode: SourceFile:]
|
| 46/2 |
2026/08/09 06:03 |
llm |
expert |
0m
Model:
Tokens:
input: 25893
output: 40
thoughts: 9
Thoughts: Okay, here's the summary, formatted as you requested, assuming the thoughts are my own:
**My Approach to File Processing**
Alright, so I need to get this done efficiently. My mind immediately goes to leveraging the `read-file` function. I recognize this as a core utility for data access within the system I'm working with. This function will give me the raw data I need to work with. There is no other logical solution, the problem asks that the process begin with a `read-file` call. I need to ensure it's properly configured and handles potential errors gracefully, but the core of my plan revolves around that. It's the starting point.
|
| 47/2 |
2026/08/09 06:04 |
tool |
read-file |
0m
Args: map[File:arch/x86/include/asm/io.h FirstLine:45 LineCount:20]
Results: map[Contents: 45: #include <asm/special_insns.h>
46:
47: #define build_mmio_read(name, size, type, reg, barrier) \
48: static inline type name(const volatile void __iomem *addr) \
49: { type ret; asm volatile("mov" size " %1,%0":reg (ret) \
50: :"m" (*(volatile type __force *)addr) barrier); return ret; }
51:
52: #define build_mmio_write(name, size, type, reg, barrier) \
53: static inline void name(type val, volatile void __iomem *addr) \
54: { asm volatile("mov" size " %0,%1": :reg (val), \
55: "m" (*(volatile type __force *)addr) barrier); }
56:
57: build_mmio_read(readb, "b", unsigned char, "=q", :"memory")
58: build_mmio_read(readw, "w", unsigned short, "=r", :"memory")
59: build_mmio_read(readl, "l", unsigned int, "=r", :"memory")
60:
61: build_mmio_read(__readb, "b", unsigned char, "=q", )
62: build_mmio_read(__readw, "w", unsigned short, "=r", )
63: build_mmio_read(__readl, "l", unsigned int, "=r", )
64:
]
|
| 48/2 |
2026/08/09 06:04 |
llm |
expert |
0m
Model:
Tokens:
input: 26503
output: 38
thoughts: 971
Thoughts: **Vulnerability Analysis: Kernel Driver - HD Audio Controller**
Okay, so I'm looking at this code, and it's doing a simple `mov` instruction, nothing inherently alarming at first glance. However, it's immediately clear that if Supervisor Mode Access Prevention (SMAP) is enabled, we'd expect a page fault because the kernel is accessing user memory without the necessary `stac()` call. But, if SMAP is disabled, or the kernel is compiled without SMAP support, it will happily read from user memory. Thatβs concerning, but letβs dig deeper. Even if it reads from user memory, what does it *do* with the read value?
Specifically, I'm focusing on `snd_hdac_bus_parse_capabilities`. I'm seeing that `offset` comes from `snd_hdac_chip_readw(bus, LLCH)`. The code then enters a loop, reading `cur_cap` using an offset derived from a value controlled by user memory. If `offset` is controlled by user memory, `cur_cap` gets read from `bus->remap_addr + offset`. Since `bus->remap_addr` is fixed at `0x1c080`, and `offset` can range up to `0xffff`, `cur_cap` could be read from any address between `0x1c080` and `0x2c07f`, which is also in user space. This is where things get interesting. Subsequently, pointers like `bus->mlcap`, `bus->gtscap`, etc., are set to `bus->remap_addr + offset`. These are pointers to capability structures. Later on, the driver will use these pointers to read/write these capabilities. This is where the vulnerability lies: these pointers reference user memory, meaning the driver will be reading *and writing* user memory.
Now, if SMAP is enabled, it should crash when trying to access user memory, which would result in a denial of service. However, if SMAP is disabled, this is a massive security hole β potential for privilege escalation, data corruption, and code execution. The critical question is, how can an unprivileged user trigger this vulnerability?
It looks like the bug resides in `azx_probe_work`, which is called during device probe. To make this happen, a PCI device has to be probed. Can an unprivileged user just plug in a malicious PCI device? No. Okay, what about USB? No, USB devices expose USB interfaces. Thunderbolt and USB4, however... yes! These interfaces can expose external PCIe devices! So, a peripheral trigger is possible. A malicious Thunderbolt device could masquerade as an Intel HDA audio controller, but with BAR 0 configured as an I/O port instead of an MMIO region.
Wait a second. Does the PCI subsystem even *allow* BAR 0 to be an I/O port if the driver expects MMIO? The driver calls `pcim_iomap_region(pci, 0, "ICH HD audio")`. This eventually calls `pci_iomap(pdev, 0, 0)`. `pci_iomap` *does* check the resource flags. If it sees `IORESOURCE_IO`, it calls `ioport_map(...)`. So, yes! If the device exposes BAR 0 as I/O, `pcim_iomap_region` will happily map it as I/O and return a PIO cookie. Then, the driver uses `readw` on the PIO cookie, which is the bug!
Okay, let's confirm the driver's behavior. Does it check the resource flags before mapping? I need to review `azx_first_init`. Looking at that function, I see the call to `pcim_iomap_region(pci, 0, "ICH HD audio")`. The code *doesn't* check if BAR 0 is `IORESOURCE_MEM`. I'm not seeing any checks in `azx_probe` either. This is a clear vulnerability; no validation, a direct path to an exploit.
|
| 49/2 |
2026/08/09 06:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:sound/hda/controllers/intel.c Name:azx_probe]
Results: map[SourceCode:2150: static int azx_probe(struct pci_dev *pci,
2151: const struct pci_device_id *pci_id)
2152: {
2153: const struct dmi_system_id *dmi;
2154: struct snd_card *card;
2155: struct hda_intel *hda;
2156: struct azx *chip;
2157: int dev;
2158: int err;
2159:
2160: if (pci_match_id(driver_denylist, pci)) {
2161: dev_info(&pci->dev, "Skipping the device on the denylist\n");
2162: return -ENODEV;
2163: }
2164:
2165: dmi = dmi_first_match(driver_denylist_dmi);
2166: if (dmi && pci_match_id(dmi->driver_data, pci)) {
2167: dev_info(&pci->dev, "Skipping the device on the DMI denylist\n");
2168: return -ENODEV;
2169: }
2170:
2171: dev = find_first_zero_bit(probed_devs, SNDRV_CARDS);
2172: if (dev >= SNDRV_CARDS)
2173: return -ENODEV;
2174: if (!enable[dev]) {
2175: set_bit(dev, probed_devs);
2176: return -ENOENT;
2177: }
2178:
2179: /*
2180: * stop probe if another Intel's DSP driver should be activated
2181: */
2182: if (dmic_detect) {
2183: err = snd_intel_dsp_driver_probe(pci);
2184: if (err != SND_INTEL_DSP_DRIVER_ANY && err != SND_INTEL_DSP_DRIVER_LEGACY) {
2185: dev_dbg(&pci->dev, "HDAudio driver not selected, aborting probe\n");
2186: return -ENODEV;
2187: }
2188: } else {
2189: dev_warn(&pci->dev, "dmic_detect option is deprecated, pass snd-intel-dspcfg.dsp_driver=1 option instead\n");
2190: }
2191:
2192: err = snd_card_new(&pci->dev, index[dev], id[dev], THIS_MODULE,
2193: 0, &card);
2194: if (err < 0) {
2195: dev_err(&pci->dev, "Error creating card!\n");
2196: return err;
2197: }
2198:
2199: err = azx_create(card, pci, dev, pci_id->driver_data, &chip);
2200: if (err < 0)
2201: goto out_free;
2202: card->private_data = chip;
2203: hda = container_of(chip, struct hda_intel, chip);
2204:
2205: pci_set_drvdata(pci, card);
2206:
2207: #ifdef CONFIG_SND_HDA_I915
2208: /* bind with i915 if needed */
2209: if (chip->driver_caps & AZX_DCAPS_I915_COMPONENT) {
2210: err = snd_hdac_i915_init(azx_bus(chip));
2211: if (err < 0) {
2212: if (err == -EPROBE_DEFER)
2213: goto out_free;
2214:
2215: /* if the controller is bound only with HDMI/DP
2216: * (for HSW and BDW), we need to abort the probe;
2217: * for other chips, still continue probing as other
2218: * codecs can be on the same link.
2219: */
2220: if (HDA_CONTROLLER_IN_GPU(pci)) {
2221: dev_err_probe(card->dev, err,
2222: "HSW/BDW HD-audio HDMI/DP requires binding with gfx driver\n");
2223:
2224: goto out_free;
2225: } else {
2226: /* don't bother any longer */
2227: chip->driver_caps &= ~AZX_DCAPS_I915_COMPONENT;
2228: }
2229: }
2230:
2231: /* HSW/BDW controllers need this power */
2232: if (HDA_CONTROLLER_IN_GPU(pci))
2233: hda->need_i915_power = true;
2234: }
2235: #else
2236: if (HDA_CONTROLLER_IN_GPU(pci))
2237: dev_err(card->dev, "Haswell/Broadwell HDMI/DP must build in CONFIG_SND_HDA_I915\n");
2238: #endif
2239:
2240: err = register_vga_switcheroo(chip);
2241: if (err < 0) {
2242: dev_err(card->dev, "Error registering vga_switcheroo client\n");
2243: goto out_free;
2244: }
2245:
2246: if (check_hdmi_disabled(pci)) {
2247: dev_info(card->dev, "VGA controller is disabled\n");
2248: dev_info(card->dev, "Delaying initialization\n");
2249: chip->disabled = true;
2250: }
2251:
2252: if (!chip->disabled)
2253: schedule_delayed_work(&hda->probe_work, 0);
2254:
2255: set_bit(dev, probed_devs);
2256: if (chip->disabled)
2257: complete_all(&hda->probe_wait);
2258: return 0;
2259:
2260: out_free:
2261: pci_set_drvdata(pci, NULL);
2262: snd_card_free(card);
2263: return err;
2264: }
SourceFile:sound/hda/controllers/intel.c]
|
| 50/2 |
2026/08/09 06:04 |
llm |
expert |
0m
Model:
Tokens:
input: 30274
output: 39
thoughts: 676
Thoughts: **Vulnerability Analysis: User-Controlled I/O Port Access in HD-audio Driver**
Okay, so I'm digging into this HD-audio driver and I've found something potentially nasty. It looks like there's no check in `azx_probe` to prevent a malicious PCI device from being exploited. If a rogue device plugs in, and its BAR 0 happens to be an I/O port, then `pcim_iomap_region` happily maps it as an I/O port, returning a PIO cookie. Now, the `snd_hdac_bus_parse_capabilities` function then tries to use `readw` on this PIO cookie.
The crux of the issue is how the PIO cookie is calculated: it's `0x10000 + port`, where `port` is the I/O port number, which can go up to `0xffff`. This means our cookie ends up being somewhere between `0x10000` and `0x1ffff`. And here's the kicker β that's a *user-space* address!
On x86 systems with SMAP enabled (which is pretty much the default on modern machines), this is going to result in a kernel crash because of a page fault (supervisor read access in kernel mode). So, at best, it's a denial-of-service, a kernel panic. If SMAP is disabled, however, things get *really* interesting. Then, the kernel ends up reading from user memory!
Wait a second⦠if it reads from user memory, can the user then control what's read? Yes! The user can map memory at the address the kernel will try to read, controlling the capabilities list that the driver will read.
The driver grabs `cur_cap` from user memory. Then, it sets `bus->mlcap`, `bus->gtscap`, `bus->ppcap`, `bus->spbcap`, and `bus->drsmcap` to `bus->remap_addr + offset`. Since `offset` is user-controlled (up to `0xffff`), these pointers *also* point to user memory, up to `0x2ffff`. Later on, the driver uses these pointers to read and write capabilities β for instance, `snd_hdac_ext_bus_get_ml_capabilities` reads from `bus->mlcap`. So, essentially, it's reading and writing user memory.
This has the potential to lead to arbitrary kernel memory read/write if the driver uses these capabilities to calculate offsets or sizes for DMA, or similar, that are then used by other parts of the system. I would have to confirm if this is the case.
But wait, this requires SMAP to be disabled! If SMAP is enabled, it's just a crash. I need to know if this crash can be leveraged or not.
I started to think, "What about non-x86 systems?" On ARM, `ioport_map` returns a *virtual* address in the vmalloc area, such as `PCI_IOBASE + port`. `PCI_IOBASE` itself is a kernel virtual address. This is the difference: `readw` on the PIO cookie *actually* performs an I/O port read. On ARM, I/O ports are memory-mapped at `PCI_IOBASE`. So `readw` on `PCI_IOBASE + port` is equivalent to a memory-mapped read from the I/O port space, which is exactly what `ioread16` would do on ARM! I should check the implementation of `ioread16` to confirm.
|
| 51/2 |
2026/08/09 06:04 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:arch/arm/include/asm/io.h Name:ioread16]
Results: map[SourceCode: 85: unsigned int ioread16(const void __iomem *addr)
86: {
87: IO_COND(addr, return inw(port), return readw(addr));
88: return 0xffff;
89: }
SourceFile:lib/iomap.c]
|
| 52/2 |
2026/08/09 06:04 |
llm |
expert |
0m
Model:
Tokens:
input: 31879
output: 21
thoughts: 66
Thoughts: **Investigating `ioread16` on ARM**
Okay, hold on a second. I'm pretty sure I remember seeing `ioread16` defined within `lib/iomap.c`, but only if `CONFIG_GENERIC_IOMAP` is enabled. That's a key detail. Now, my immediate instinct is telling me that on most ARM configurations, `CONFIG_GENERIC_IOMAP` isn't typically enabled by default. I need to confirm this assumption. The next logical step is to dive deeper into the ARM-specific implementations and see how `ioread16` is actually handled in that architecture. Let's see what's what.
|
| 53/2 |
2026/08/09 06:04 |
tool |
grepper |
0m
Args: map[Expression:#define ioread16]
Results: map[Output:arch/alpha/include/asm/io.h=379=extern inline void outw(u16 b, unsigned long port)
--
arch/alpha/include/asm/io.h-385-#define ioread8 ioread8
arch/alpha/include/asm/io.h:386:#define ioread16 ioread16
arch/alpha/include/asm/io.h-387-#define iowrite8 iowrite8
--
arch/alpha/include/asm/io.h=534=extern inline void writeq(u64 b, volatile void __iomem *addr)
--
arch/alpha/include/asm/io.h-540-
arch/alpha/include/asm/io.h:541:#define ioread16be(p) swab16(ioread16(p))
arch/alpha/include/asm/io.h-542-#define ioread32be(p) swab32(ioread32(p))
--
arch/alpha/include/asm/io.h=627=extern void outsl (unsigned long port, const void *src, unsigned long count);
--
arch/alpha/include/asm/io.h-641-#define ioread8_rep ioread8_rep
arch/alpha/include/asm/io.h:642:#define ioread16_rep ioread16_rep
arch/alpha/include/asm/io.h-643-#define ioread32_rep ioread32_rep
--
arch/arc/include/asm/io.h=32=static inline void ioport_unmap(void __iomem *addr)
--
arch/arc/include/asm/io.h-38- */
arch/arc/include/asm/io.h:39:#define ioread16be(p) ({ u16 __v = be16_to_cpu((__force __be16)__raw_readw(p)); __iormb(); __v; })
arch/arc/include/asm/io.h-40-#define ioread32be(p) ({ u32 __v = be32_to_cpu((__force __be32)__raw_readl(p)); __iormb(); __v; })
--
arch/arm/include/asm/io.h=395=void *arch_memremap_wb(phys_addr_t phys_addr, size_t size, unsigned long flags);
--
arch/arm/include/asm/io.h-400- */
arch/arm/include/asm/io.h:401:#define ioread16be(p) ({ __u16 __v = be16_to_cpu((__force __be16)__raw_readw(p)); __iormb(); __v; })
arch/arm/include/asm/io.h-402-#define ioread32be(p) ({ __u32 __v = be32_to_cpu((__force __be32)__raw_readl(p)); __iormb(); __v; })
--
arch/arm64/include/asm/io.h=269=static inline void __iomem *ioremap_prot(phys_addr_t phys, size_t size,
--
arch/arm64/include/asm/io.h-297- */
arch/arm64/include/asm/io.h:298:#define ioread16be(p) ({ __u16 __v = be16_to_cpu((__force __be16)__raw_readw(p)); __iormb(__v); __v; })
arch/arm64/include/asm/io.h-299-#define ioread32be(p) ({ __u32 __v = be32_to_cpu((__force __be32)__raw_readl(p)); __iormb(__v); __v; })
--
arch/parisc/include/asm/io.h=247=extern unsigned int ioread32be(const void __iomem *);
arch/parisc/include/asm/io.h-248-#define ioread8 ioread8
arch/parisc/include/asm/io.h:249:#define ioread16 ioread16
arch/parisc/include/asm/io.h-250-#define ioread32 ioread32
arch/parisc/include/asm/io.h:251:#define ioread16be ioread16be
arch/parisc/include/asm/io.h-252-#define ioread32be ioread32be
--
arch/parisc/include/asm/io.h=267=extern void ioread32_rep(const void __iomem *port, void *buf, unsigned long count);
arch/parisc/include/asm/io.h-268-#define ioread8_rep ioread8_rep
arch/parisc/include/asm/io.h:269:#define ioread16_rep ioread16_rep
arch/parisc/include/asm/io.h-270-#define ioread32_rep ioread32_rep
--
arch/powerpc/include/asm/io.h=707=static inline unsigned int ioread16be(const void __iomem *addr)
--
arch/powerpc/include/asm/io.h-710-}
arch/powerpc/include/asm/io.h:711:#define ioread16be ioread16be
arch/powerpc/include/asm/io.h-712-
--
arch/sparc/include/asm/io_64.h=283=static inline void writesl(void __iomem *port, const void *buf, unsigned long count)
--
arch/sparc/include/asm/io_64.h-289-#define ioread8_rep(p,d,l) readsb(p,d,l)
arch/sparc/include/asm/io_64.h:290:#define ioread16_rep(p,d,l) readsw(p,d,l)
arch/sparc/include/asm/io_64.h-291-#define ioread32_rep(p,d,l) readsl(p,d,l)
--
arch/sparc/include/asm/io_64.h=435=static inline void iounmap(volatile void __iomem *addr)
--
arch/sparc/include/asm/io_64.h-439-#define ioread8 readb
arch/sparc/include/asm/io_64.h:440:#define ioread16 readw
arch/sparc/include/asm/io_64.h:441:#define ioread16be __raw_readw
arch/sparc/include/asm/io_64.h-442-#define ioread32 readl
--
drivers/gpu/drm/nouveau/include/nvif/os.h-38-#ifdef __BIG_ENDIAN
drivers/gpu/drm/nouveau/include/nvif/os.h:39:#define ioread16_native ioread16be
drivers/gpu/drm/nouveau/include/nvif/os.h-40-#define iowrite16_native iowrite16be
--
drivers/gpu/drm/nouveau/include/nvif/os.h-43-#else
drivers/gpu/drm/nouveau/include/nvif/os.h:44:#define ioread16_native ioread16
drivers/gpu/drm/nouveau/include/nvif/os.h-45-#define iowrite16_native iowrite16
--
include/asm-generic/io.h=888=static inline u8 ioread8(const volatile void __iomem *addr)
--
include/asm-generic/io.h-894-#ifndef ioread16
include/asm-generic/io.h:895:#define ioread16 ioread16
include/asm-generic/io.h-896-static inline u16 ioread16(const volatile void __iomem *addr)
--
include/asm-generic/io.h=947=static inline void iowrite64(u64 value, volatile void __iomem *addr)
--
include/asm-generic/io.h-954-#ifndef ioread16be
include/asm-generic/io.h:955:#define ioread16be ioread16be
include/asm-generic/io.h-956-static inline u16 ioread16be(const volatile void __iomem *addr)
--
include/asm-generic/io.h=1008=static inline void ioread8_rep(const volatile void __iomem *addr, void *buffer,
--
include/asm-generic/io.h-1015-#ifndef ioread16_rep
include/asm-generic/io.h:1016:#define ioread16_rep ioread16_rep
include/asm-generic/io.h-1017-static inline void ioread16_rep(const volatile void __iomem *addr,
]
|
| 54/2 |
2026/08/09 06:04 |
llm |
expert |
0m
Model:
Tokens:
input: 34476
output: 37
thoughts: 22
Thoughts: Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:
**Diving into `ioread16`**
Okay, let's take a look at `ioread16`, specifically where it's defined within the `include/asm-generic/io.h` header. I'm expecting to see a level of abstraction here, as `asm-generic` implies a generic implementation, likely leveraging macros to handle the underlying hardware details based on the target architecture's configuration. I'm curious if they've opted for a direct memory access method with explicit casting to a volatile `u16*`, or if there's an intermediate layer involved. I anticipate a fair bit of conditional compilation going on behind the scenes, potentially relying on macros related to memory mapping or I/O ports. I wonder if there's any cleverness in how they handle endianness concerns too β particularly important for 16-bit reads. Right, let's see what they've come up with. Let's see how they implemented it...
|
| 55/2 |
2026/08/09 06:05 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:include/asm-generic/io.h Name:ioread16]
Results: map[SourceCode: 85: unsigned int ioread16(const void __iomem *addr)
86: {
87: IO_COND(addr, return inw(port), return readw(addr));
88: return 0xffff;
89: }
SourceFile:lib/iomap.c]
|
| 56/2 |
2026/08/09 06:05 |
llm |
expert |
0m
Model:
Tokens:
input: 34867
output: 53
thoughts: 1266
Thoughts: **Analysis of a Potential Kernel Vulnerability**
Okay, let me break this down. I see that the `ioread16` function in `lib/iomap.c` is being used for checking `IO_COND`. But wait, that's not the same function that `snd_hdac_reg_readw` uses. It's actually using `readw(addr)`, which on x86 is just a direct dereference: `*(volatile u16 *)addr`. And on ARM, it's the same, it directly dereferences the address using `__raw_readw(addr)`.
So, if `readw` is used on an ARM PIO cookie, it will *also* dereference that cookie directly. Now, on ARM, `ioport_map` returns `PCI_IOBASE + port`, and `PCI_IOBASE` is a virtual address within the vmalloc area. This means `readw` on this calculated address will correctly read from the memory-mapped I/O port space, and that is perfectly valid on ARM. However, the same operation on x86, `ioport_map` returns `0x10000 + port`, which is a user-space address. The direct dereference, `readw`, therefore reads from user memory on x86.
This seems exploitable, definitely. If SMAP (Supervisor Mode Access Prevention) is enabled, it'll crash with a Denial of Service. But, if SMAP is disabled (due to `nosmap` or older CPUs), the code will read from user memory, and that's a privilege escalation. Since SMAP is a security feature that can be disabled, it's exploitable.
Now, could an unprivileged user plug in a malicious PCI device? No, but what about a USB device? Still no, those don't expose PCI BARs (Base Address Registers). Ah, but Thunderbolt or USB4? Bingo. Thunderbolt / USB4 devices *can* expose PCIe devices. So a user could plug in a malicious Thunderbolt device that pretends to be an Intel HDA controller and has BAR 0 set as an I/O port. The kernel probes the device, maps BAR 0, and then calls `snd_hdac_bus_parse_capabilities`. This function will then read from user memory if SMAP is off, or crash if it's on. So, this is a **Peripheral Trigger**.
Let's check for any other attack vectors. Could an unprivileged user trigger this via a virtual device? `vhci` or `uhid` won't work, those are for USB and HID. What about `vfio`? A user with VFIO access can map BARs but can't change the BAR type (I/O vs MMIO) of a physical device. Syzkaller is interesting. It can trigger this using `syz_pci_setup`, which creates a virtual PCI device using the `pci` fuzzing subsystem (if enabled, e.g. `CONFIG_PCI_SYSCALL` or similar, wait, syzkaller uses a custom `pci` fuzzer module or `vhci` for PCI?). It looks like Syzkaller uses a pseudo-syscall that injects virtual PCI devices, but this requires either specific fuzzing support in the kernel or root access to some debugfs/sysfs nodes. In a production environment, an unprivileged user cannot create virtual PCI devices. Therefore, the *only* way to trigger this in a production setting is by physically plugging in a malicious PCIe device, via Thunderbolt or similar.
Now, let me double-check. Can a user namespace trigger this? Nope, user namespaces don't let you plug in or create virtual PCI devices. So, `UserNamespace` is False. And for `Unprivileged`, that's false too, because you need physical access to a Thunderbolt port. But wait, the prompt says the bug needs to be accessible from unprivileged processes, and *without* special capabilities. Since it requires physical access, this isn't accessible from an unprivileged process, therefore it's a hardware event. So, `Unprivileged` is False. `PeripheralTrigger` is definitely True.
Now, is this actually exploitable? If SMAP is disabled, the kernel reads `cur_cap` from user memory. Specifically, the attacker controls `offset` (from `LLCH`) which is then used to read `cur_cap` (from `bus->remap_addr + offset`). This allows setting `bus->mlcap` to a user-space address (`bus->remap_addr + offset`), and then the driver uses `bus->mlcap` to read/write, specifically, it will be used in `snd_hdac_ext_bus_get_ml_capabilities`. This looks like
|
| 57/2 |
2026/08/09 06:05 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:sound/hda/ext/hdac_ext_controller.c Name:snd_hdac_ext_bus_get_ml_capabilities]
Results: map[SourceCode: SourceFile:]
|
| 58/2 |
2026/08/09 06:05 |
llm |
expert |
0m
Model:
Tokens:
input: 37232
output: 17
thoughts: 9
Thoughts: Okay, here's my attempt at summarizing the thought process, tailored for an expert audience, and written in the first person:
**Initial Inquiry: The `mlcap` Investigation**
Right, let's start with a search for `mlcap`. That's the initial trigger, the key term. I need to get a handle on the landscape. Is this a specific project, a concept, an acronym for something I already know, or a broader research area I haven't been tracking closely enough? The goal is to quickly orient myself. Where's the context, and what's the most promising angle of attack? I'm anticipating both precise hits and potentially some noisy results. Gotta be ready to sift through, evaluate relevance, and spot the signal amidst the static.
|
| 59/2 |
2026/08/09 06:05 |
tool |
grepper |
0m
Args:
Results: map[Output:sound/hda/controllers/intel.c=469=static int intel_get_lctl_scf(struct azx *chip)
--
sound/hda/controllers/intel.c-475-
sound/hda/controllers/intel.c:476: val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCAP);
sound/hda/controllers/intel.c-477-
--
sound/hda/controllers/intel.c=488=static int intel_ml_lctl_set_power(struct azx *chip, int state)
--
sound/hda/controllers/intel.c-497- */
sound/hda/controllers/intel.c:498: val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
sound/hda/controllers/intel.c-499- val &= ~AZX_ML_LCTL_SPA;
sound/hda/controllers/intel.c-500- val |= state << AZX_ML_LCTL_SPA_SHIFT;
sound/hda/controllers/intel.c:501: writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
sound/hda/controllers/intel.c-502- /* wait for CPA */
--
sound/hda/controllers/intel.c-504- while (timeout) {
sound/hda/controllers/intel.c:505: if (((readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL)) &
sound/hda/controllers/intel.c-506- AZX_ML_LCTL_CPA) == (state << AZX_ML_LCTL_CPA_SHIFT))
--
sound/hda/controllers/intel.c=515=static void intel_init_lctl(struct azx *chip)
--
sound/hda/controllers/intel.c-521- /* 0. check lctl register value is correct or not */
sound/hda/controllers/intel.c:522: val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
sound/hda/controllers/intel.c-523- /* only perform additional configurations if the SCF is initially based on 6MHz */
--
sound/hda/controllers/intel.c-543- val |= intel_get_lctl_scf(chip);
sound/hda/controllers/intel.c:544: writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
sound/hda/controllers/intel.c-545-
--
sound/hda/controllers/intel.c=552=static void hda_intel_init_chip(struct azx *chip, bool full_reset)
--
sound/hda/controllers/intel.c-576-
sound/hda/controllers/intel.c:577: if (bus->mlcap != NULL)
sound/hda/controllers/intel.c-578- intel_init_lctl(chip);
--
sound/hda/core/controller.c=406=int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
--
sound/hda/core/controller.c-431- dev_dbg(bus->dev, "Found ML capability\n");
sound/hda/core/controller.c:432: bus->mlcap = bus->remap_addr + offset;
sound/hda/core/controller.c-433- break;
--
sound/hda/core/ext/controller.c=80=int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus)
--
sound/hda/core/ext/controller.c-86-
sound/hda/core/ext/controller.c:87: link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
sound/hda/core/ext/controller.c-88-
--
sound/hda/core/ext/controller.c-96- hlink->bus = bus;
sound/hda/core/ext/controller.c:97: hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
sound/hda/core/ext/controller.c-98- (AZX_ML_INTERVAL * idx);
--
sound/soc/intel/avs/core.c=425=static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
--
sound/soc/intel/avs/core.c-475- snd_hdac_bus_parse_capabilities(bus);
sound/soc/intel/avs/core.c:476: if (bus->mlcap)
sound/soc/intel/avs/core.c-477- snd_hdac_ext_bus_get_ml_capabilities(bus);
--
sound/soc/sof/intel/hda-ctrl.c=58=int hda_dsp_ctrl_get_caps(struct snd_sof_dev *sdev)
--
sound/soc/sof/intel/hda-ctrl.c-117- offset);
sound/soc/sof/intel/hda-ctrl.c:118: bus->mlcap = bus->remap_addr + offset;
sound/soc/sof/intel/hda-ctrl.c-119- break;
--
sound/soc/sof/intel/hda-mlink.c=389=static int hda_ml_alloc_h2link(struct hdac_bus *bus, int index)
--
sound/soc/sof/intel/hda-mlink.c-403- hlink->bus = bus;
sound/soc/sof/intel/hda-mlink.c:404: hlink->ml_addr = bus->mlcap + AZX_ML_BASE + (AZX_ML_INTERVAL * index);
sound/soc/sof/intel/hda-mlink.c-405-
--
sound/soc/sof/intel/hda-mlink.c=426=int hda_bus_ml_init(struct hdac_bus *bus)
--
sound/soc/sof/intel/hda-mlink.c-431-
sound/soc/sof/intel/hda-mlink.c:432: if (!bus->mlcap)
sound/soc/sof/intel/hda-mlink.c-433- return 0;
sound/soc/sof/intel/hda-mlink.c-434-
sound/soc/sof/intel/hda-mlink.c:435: link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
sound/soc/sof/intel/hda-mlink.c-436-
--
sound/soc/sof/intel/hda-mlink.c=450=void hda_bus_ml_free(struct hdac_bus *bus)
--
sound/soc/sof/intel/hda-mlink.c-454-
sound/soc/sof/intel/hda-mlink.c:455: if (!bus->mlcap)
sound/soc/sof/intel/hda-mlink.c-456- return;
]
|
| 60/2 |
2026/08/09 06:05 |
llm |
expert |
0m
Model:
Tokens:
input: 39323
output: 50
thoughts: 517
Thoughts: **Analyzing a Potential Privilege Escalation Vulnerability**
Okay, so I'm looking at this `intel_ml_lctl_set_power` function, specifically how it interacts with memory. The core of it seems to be reading from and writing to a memory location determined by `bus->mlcap`. Initially, the code reads a value using `readl`, clears some bits (`AZX_ML_LCTL_SPA`), and then sets a bit based on the `state` variable before writing it back using `writel`. The critical thing is that `bus->mlcap` appears to be a user-space address if SMAP is disabled.
My immediate concern is: does this present a path to privilege escalation? `writel` writing to user memory is not inherently a problem since the user already controls that space. It would only be a problem if the function was writing to kernel memory. So, I am going to investigate if there's any way to trick the code into writing to kernel memory.
The next thing to investigate is, how is `bus->mlcap` determined? I see that it's calculated as `bus->remap_addr + offset`. `bus->remap_addr` comes from `0x10000 + port`. `offset` itself is read from user memory. Specifically, it's derived from `bus->remap_addr + LLCH`, but ultimately masked by `AZX_CAP_HDR_NXT_PTR_MASK` which equals `0xffff`.
So, `offset` is limited to at most `0xffff`. This makes `bus->remap_addr` at most `0x1ffff`, meaning that `bus->mlcap` is capped at `0x2fffe`. This means that `bus->mlcap` will always be in user space. This means it only reads/writes user memory. This significantly reduces the chances of exploitation.
Let's expand the scope. Are there any other similar operations I need to review? What about `bus->ppcap`, `bus->spbcap`, and `bus->drsmcap`? My initial assumption is that these operate similarly as they are computed like `bus->remap_addr + offset`. Are there any possibilities to exploit these locations? Are these values, in turn, used to index into any kernel-space arrays? I need to check `snd_hdac_ext_bus_get_ml_capabilities` to confirm this.
|
| 61/2 |
2026/08/09 06:05 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:sound/hda/core/ext/controller.c Name:snd_hdac_ext_bus_get_ml_capabilities]
Results: map[SourceCode: SourceFile:]
|
| 62/2 |
2026/08/09 06:05 |
llm |
expert |
0m
Model:
Tokens:
input: 40433
output: 50
thoughts: 45
Thoughts: Okay, let's dive into this.
**Navigating the HDA Codebase**
Alright, I need to investigate something related to HDA audio, and my starting point is the grep output. I'm seeing a filename flagged: `sound/hda/ext/hdac_ext_controller.c`. Hmm, wait a minute, the original grep result I ran gave me `sound/hda/core/ext/controller.c`. I need to double-check that this is the correct starting point for investigating the issue at hand. There might have been some confusion in the output itself or how I'm filtering my search. Let me make sure I'm looking at the right file, before I waste time going down the wrong path.
|
| 63/2 |
2026/08/09 06:05 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:sound/hda/core/ext/controller.c Name:snd_hdac_ext_bus_get_ml_capabilities]
Results: map[SourceCode: SourceFile:]
|
| 64/2 |
2026/08/09 06:05 |
llm |
expert |
0m
Model:
Tokens:
input: 40707
output: 42
thoughts: 51
Thoughts: Okay, here's my interpretation of that thought process, from an expert's perspective:
**Reflecting on Code Location and Data Retrieval**
Right, so I'm dealing with the High Definition Audio (HDA) extended controller. Immediately I'm thinking, "hdac_ext_controller.c" β ah, that's the *old* naming convention. I'm used to seeing that specific file. Okay, no problem. I need to examine this code, probably to understand its functionality, a specific issue or perhaps its integration. Let me quickly grab the content of `sound/hda/ext/hdac_ext_controller.c`. Time to use `read-file` to get a look at the data. Let's see what we are dealing with.
|
| 65/2 |
2026/08/09 06:05 |
tool |
read-file |
0m
Error:
Args: map[File:sound/hda/ext/hdac_ext_controller.c FirstLine:70 LineCount:40]
Results:
|
| 66/2 |
2026/08/09 06:05 |
llm |
expert |
0m
Model:
Tokens:
input: 40984
output: 41
thoughts: 135
Thoughts: **Examining the Grep Output**
Okay, let's take another look at that `grep` output. It seems like the function I'm interested in, `snd_hdac_ext_bus_get_ml_capabilities`, is defined in `sound/hda/core/ext/controller.c`. Hmm, I was pretty sure the relevant file was `sound/hda/ext/hdac_ext_controller.c`. That's where my mental model had placed it. But, based on this `grep` output, I was mistaken. Right, clearly I need to read `sound/hda/core/ext/controller.c` now to confirm this.
|
| 67/2 |
2026/08/09 06:05 |
tool |
read-file |
0m
Error:
Args: map[File:sound/hda/ext/hdac_ext_controller.c FirstLine:1 LineCount:10]
Results:
|
| 68/2 |
2026/08/09 06:05 |
llm |
expert |
0m
Model:
Tokens:
input: 41325
output: 28
thoughts: 114
Thoughts: Okay, here's my summary, presented as a stream of consciousness:
**Navigating the HDA Controller Source**
Right, let's see, I need to get at the source code for the HDA external controller, so my first instinct is to try `read-file` on `sound/hda/ext/hdac_ext_controller.c`. Hmm, but wait, let's be thorough. Before I get my hopes up, I should probably check if it actually exists. Let's fire off a quick `grep` for that path. Dammit, the `grep` output says it's not there! Okay, so maybe I typed the path wrong. Let me just try reading the same file using `read-file` again. Nope. The `grep` output I just performed says that the *real* path is `sound/hda/core/ext/controller.c`. That's not what I was expecting. Okay, okay, no need to panic. Iβm an expert, let's keep my cool. Fine, let's just go with my tried and true method and use `grepper` to be absolutely sure I'm finding the right file.
|
| 69/2 |
2026/08/09 06:05 |
tool |
grepper |
0m
Args: map[Expression:snd_hdac_ext_bus_get_ml_capabilities]
Results: map[Output:include/sound/hdaudio_ext.h=22=void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *chip, bool enable);
include/sound/hdaudio_ext.h-23-
include/sound/hdaudio_ext.h:24:int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus);
include/sound/hdaudio_ext.h-25-struct hdac_ext_link *snd_hdac_ext_bus_get_hlink_by_id(struct hdac_bus *bus, u32 id);
--
sound/hda/core/ext/controller.c=65=EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
--
sound/hda/core/ext/controller.c-72-/**
sound/hda/core/ext/controller.c:73: * snd_hdac_ext_bus_get_ml_capabilities - get multilink capability
sound/hda/core/ext/controller.c-74- * @bus: the pointer to HDAC bus object
--
sound/hda/core/ext/controller.c-79- */
sound/hda/core/ext/controller.c:80:int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus)
sound/hda/core/ext/controller.c-81-{
--
sound/hda/core/ext/controller.c-115-}
sound/hda/core/ext/controller.c:116:EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
sound/hda/core/ext/controller.c-117-
--
sound/soc/intel/avs/core.c=425=static int avs_pci_probe(struct pci_dev *pci, const struct pci_device_id *id)
--
sound/soc/intel/avs/core.c-476- if (bus->mlcap)
sound/soc/intel/avs/core.c:477: snd_hdac_ext_bus_get_ml_capabilities(bus);
sound/soc/intel/avs/core.c-478-
]
|
| 70/2 |
2026/08/09 06:05 |
llm |
expert |
0m
Model:
Tokens:
input: 42303
output: 39
thoughts: 25
Thoughts: Okay, let's dive into this. I'm going to start by examining the `sound/hda/core/ext/controller.c` file, specifically from line 80 onward. I need to understand the controller's behavior and the logic implemented there, so I'll be looking for key functions, data structures, and any potentially interesting control flow. My expert eye is scanning for anything that relates to interrupt handling, DMA setup, codec communication, or error management within the HDA controller context. I'll pay close attention to how they've chosen to implement any specific workarounds or optimizations.
|
| 71/2 |
2026/08/09 06:06 |
tool |
read-file |
0m
Args: map[File:sound/hda/core/ext/controller.c FirstLine:80 LineCount:40]
Results: map[Contents: 80: int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_bus *bus)
81: {
82: int idx;
83: u32 link_count;
84: struct hdac_ext_link *hlink;
85: u32 leptr;
86:
87: link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
88:
89: dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
90:
91: for (idx = 0; idx < link_count; idx++) {
92: hlink = kzalloc_obj(*hlink);
93: if (!hlink)
94: return -ENOMEM;
95: hlink->index = idx;
96: hlink->bus = bus;
97: hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
98: (AZX_ML_INTERVAL * idx);
99: hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
100: hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
101: hlink->slcount = FIELD_GET(AZX_ML_HDA_LCAP_SLCOUNT, hlink->lcaps) + 1;
102:
103: if (hdac_ext_link_alt(hlink)) {
104: leptr = readl(hlink->ml_addr + AZX_REG_ML_LEPTR);
105: hlink->id = FIELD_GET(AZX_REG_ML_LEPTR_ID, leptr);
106: }
107:
108: /* since link in On, update the ref */
109: hlink->ref_count = 1;
110:
111: list_add_tail(&hlink->list, &bus->hlink_list);
112: }
113:
114: return 0;
115: }
116: EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_get_ml_capabilities);
117:
118: /**
119: * snd_hdac_ext_link_free_all- free hdac extended link objects
]
|
| 72/2 |
2026/08/09 06:06 |
llm |
expert |
0m
Model:
Tokens:
input: 43195
output: 29
thoughts: 861
Thoughts: **Vulnerability Deep Dive: OOB Read/Write in HD Audio Driver**
Okay, let's break this down. My initial thought was: the `link_count` variable seems suspicious. It's read directly from user memory via `bus->mlcap`, then used to control a loop that allocates a bunch of memory. If a user can set `link_count` to a massive value, like `0xffffffff`, we're looking at a classic denial-of-service vulnerability due to an out-of-memory condition. Easy to spot. But then I saw `hlink->ml_addr`. This is also a user-controlled address. I immediately thought "arbitrary kernel read/write?", but then I quickly realized it's reading and writing user memory. No immediate kernel takeover. So, no joy.
But, that got me thinking about how `bus->mlcap` gets set. I followed the chain and recalled how `bus->mlcap = bus->remap_addr + offset`. `bus->remap_addr` is derived from an I/O port mapping, so it's not a direct route to kernel memory, right? The `offset` is derived from the device's capabilities, so it's also user controlled.
However, the lightbulb came on! What if `bus->remap_addr` *isn't* a standard I/O port mapping? If it's a regular MMIO mapping, then it's mapping kernel memory, specifically the vmalloc area! But then `snd_hdac_bus_parse_capabilities` will actually read from the device memory. Aha! The device is the key. A malicious Thunderbolt device could return arbitrary values for the `cur_cap` register. That would mean the `offset` is controlled by the device. If `offset` is large enough, and the MMIO mapping is small, we've got an out-of-bounds read/write. I had to check that `offset` is indeed a large value.
So, how big is the MMIO mapping? Thatβs where `azx_first_init` comes in. It uses `pcim_iomap_region` to map BAR 0. BAR 0 size? Ah, a malicious device can make BAR 0 *tiny*, say, 4KB. But `offset` can be up to `0xffff`, or 64KB! Bam! That means a 60KB out-of-bounds access in the vmalloc area. Since we're dealing with `ioremap` mappings, we're likely to be reading and writing into other vmalloc allocations. That's a classic out-of-bounds memory access! If we control the offset, we can read from and write to arbitrary, adjacent memory areas. This is definitely exploitable. So, my next check is: what is the actual bitmask on that `offset`? Gotta confirm.
|
| 73/2 |
2026/08/09 06:06 |
tool |
grepper |
0m
Args: map[Expression:#define AZX_CAP_HDR_NXT_PTR_MASK]
Results: map[Output:include/sound/hda_register.h=78=enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
--
include/sound/hda_register.h-196-#define AZX_CAP_HDR_ID_MASK (0xFFF << AZX_CAP_HDR_ID_OFF)
include/sound/hda_register.h:197:#define AZX_CAP_HDR_NXT_PTR_MASK 0xFFFF
include/sound/hda_register.h-198-
]
|
| 74/2 |
2026/08/09 06:06 |
llm |
expert |
0m
Model:
Tokens:
input: 44859
output: 38
thoughts: 3688
Thoughts: **Summary of the Vulnerability Assessment**
Okay, here's my breakdown of this security issue. It looks like we've got a classic out-of-bounds read/write problem in the Linux kernel's HDA driver (`snd_hda_intel`), stemming from a lack of bounds checking on device-provided capabilities data. Let's walk through it.
Essentially, the driver reads a linked list of capabilities from a PCI device's BAR (Base Address Register). The device supplies an `offset` into this BAR, which the driver uses to read the next capability's header. Critically, the driver *doesn't* validate that this `offset` is within the bounds of the BAR. If the device provides an `offset` larger than the BAR size, then we have an out-of-bounds access.
If the BAR is memory-mapped I/O (MMIO), this translates to reading/writing outside the intended MMIO region, potentially within the kernel's vmalloc area. `ioremap` creates a virtual mapping for the MMIO region. Due to guard pages, we can bypass them and hit adjacent kernel memory. The device can thus control reads/writes. This is *highly* exploitable. Even if the BAR *is* configured as an I/O port, we can exploit the issue by providing a malicious `offset`.
Here's a critical point: If the BAR is set as an I/O port, then `pcim_iomap_region` returns a "PIO cookie," which is essentially a pointer into user-space from the perspective of the kernel. The driver then dereferences this *directly*. This leads to a page fault. If SMAP is disabled this might translate to a read of the user space data, and depending on the data, the device might control the adjacent reads.
From a confidentiality perspective, the most dangerous scenario is within a Confidential VM (CVM). In a CVM, the host (VMM) is untrusted. The host could provide the VM with an emulated, *malicious* HDA controller, and the guest kernel will probe it, loading `snd_hda_intel`. The malicious host now controls the capabilities data and can trigger the vulnerability at will. This is a significant CVM attack vector.
Considering the exploitability, a malicious device can provide a crafted `offset` that allows an out-of-bounds read/write into the vmalloc area. Exploitable indeed, and since this can lead to memory corruption it is a security vulnerability. An interesting scenario to consider, is where the attacker supplies an offset which is still within the vmalloc space, but it points to some kernel data structure; by overwriting that memory, the attacker can then pivot to other parts of the kernel. This is exploitable.
I/O port BARs will result in DoS unless SMAP is disabled.
Denial of Service (DoS) is almost certain, the driver will likely crash with a page fault if SMAP is enabled.
Unprivileged user access is not directly possible, it requires a malicious device plugged in (like a Thunderbolt device) or some kind of virtual PCI device creation which requires root privileges or fuzzer configuration.
User namespaces are not involved.
VM guest triggering is true because a host (VMM like QEMU) can emulate an Intel HDA audio controller and feed a malicious configuration to a guest VM.
Ethernet network triggering is not applicable.
Other remote triggers are not applicable.
Peripheral trigger is Yes - specifically, malicious Thunderbolt/USB4 devices are an issue.
Malicious file systems are not involved.
The key take-aways:
* **Vulnerability:** Out-of-bounds read/write due to a missing bounds check on capabilities data read from a PCI device's BAR.
* **Attack Vector:** A malicious PCI device, or, more critically, a malicious *host* in a CVM setting.
* **Exploitability:** High. Attacker can corrupt kernel memory with control of the offset, and write to vmalloc memory. PIO cookie bug is a DoS.
* **Mitigation:** Proper bounds checking on the device-provided `offset` is essential.
I need to confirm if that call `snd_hdac_bus_parse_capabilities` is made to ensure broad applicability of this vulnerability.
|
| 75/2 |
2026/08/09 06:07 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:sound/hda/controllers/intel.c Name:azx_ids]
Results: map[SourceCode:2477: /* PCI IDs */
2478: static const struct pci_device_id azx_ids[] = {
2479: /* CPT */
2480: { PCI_DEVICE_DATA(INTEL, HDA_CPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) },
2481: /* PBG */
2482: { PCI_DEVICE_DATA(INTEL, HDA_PBG, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) },
2483: /* Panther Point */
2484: { PCI_DEVICE_DATA(INTEL, HDA_PPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH_NOPM) },
2485: /* Lynx Point */
2486: { PCI_DEVICE_DATA(INTEL, HDA_LPT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
2487: /* 9 Series */
2488: { PCI_DEVICE_DATA(INTEL, HDA_9_SERIES, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
2489: /* Wellsburg */
2490: { PCI_DEVICE_DATA(INTEL, HDA_WBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
2491: { PCI_DEVICE_DATA(INTEL, HDA_WBG_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
2492: /* Lewisburg */
2493: { PCI_DEVICE_DATA(INTEL, HDA_LBG_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) },
2494: { PCI_DEVICE_DATA(INTEL, HDA_LBG_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE) },
2495: /* Lynx Point-LP */
2496: { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_0, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
2497: /* Lynx Point-LP */
2498: { PCI_DEVICE_DATA(INTEL, HDA_LPT_LP_1, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
2499: /* Wildcat Point-LP */
2500: { PCI_DEVICE_DATA(INTEL, HDA_WPT_LP, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_PCH) },
2501: /* Skylake (Sunrise Point) */
2502: { PCI_DEVICE_DATA(INTEL, HDA_SKL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2503: /* Skylake-LP (Sunrise Point-LP) */
2504: { PCI_DEVICE_DATA(INTEL, HDA_SKL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2505: /* Kabylake */
2506: { PCI_DEVICE_DATA(INTEL, HDA_KBL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2507: /* Kabylake-LP */
2508: { PCI_DEVICE_DATA(INTEL, HDA_KBL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2509: /* Kabylake-H */
2510: { PCI_DEVICE_DATA(INTEL, HDA_KBL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2511: /* Coffelake */
2512: { PCI_DEVICE_DATA(INTEL, HDA_CNL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2513: /* Cannonlake */
2514: { PCI_DEVICE_DATA(INTEL, HDA_CNL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2515: /* CometLake-LP */
2516: { PCI_DEVICE_DATA(INTEL, HDA_CML_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2517: /* CometLake-H */
2518: { PCI_DEVICE_DATA(INTEL, HDA_CML_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2519: { PCI_DEVICE_DATA(INTEL, HDA_RKL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2520: /* CometLake-S */
2521: { PCI_DEVICE_DATA(INTEL, HDA_CML_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2522: /* CometLake-R */
2523: { PCI_DEVICE_DATA(INTEL, HDA_CML_R, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2524: /* Icelake */
2525: { PCI_DEVICE_DATA(INTEL, HDA_ICL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2526: /* Icelake-H */
2527: { PCI_DEVICE_DATA(INTEL, HDA_ICL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2528: /* Jasperlake */
2529: { PCI_DEVICE_DATA(INTEL, HDA_ICL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2530: { PCI_DEVICE_DATA(INTEL, HDA_JSL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2531: /* Tigerlake */
2532: { PCI_DEVICE_DATA(INTEL, HDA_TGL_LP, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2533: /* Tigerlake-H */
2534: { PCI_DEVICE_DATA(INTEL, HDA_TGL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2535: /* DG1 */
2536: { PCI_DEVICE_DATA(INTEL, HDA_DG1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2537: /* DG2 */
2538: { PCI_DEVICE_DATA(INTEL, HDA_DG2_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2539: { PCI_DEVICE_DATA(INTEL, HDA_DG2_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2540: { PCI_DEVICE_DATA(INTEL, HDA_DG2_2, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2541: /* Alderlake-S */
2542: { PCI_DEVICE_DATA(INTEL, HDA_ADL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2543: /* Alderlake-P */
2544: { PCI_DEVICE_DATA(INTEL, HDA_ADL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2545: { PCI_DEVICE_DATA(INTEL, HDA_ADL_PS, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2546: { PCI_DEVICE_DATA(INTEL, HDA_ADL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2547: /* Alderlake-M */
2548: { PCI_DEVICE_DATA(INTEL, HDA_ADL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2549: /* Alderlake-N */
2550: { PCI_DEVICE_DATA(INTEL, HDA_ADL_N, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2551: /* Elkhart Lake */
2552: { PCI_DEVICE_DATA(INTEL, HDA_EHL_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2553: { PCI_DEVICE_DATA(INTEL, HDA_EHL_3, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2554: /* Raptor Lake */
2555: { PCI_DEVICE_DATA(INTEL, HDA_RPL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2556: { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_0, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2557: { PCI_DEVICE_DATA(INTEL, HDA_RPL_P_1, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2558: { PCI_DEVICE_DATA(INTEL, HDA_RPL_M, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2559: { PCI_DEVICE_DATA(INTEL, HDA_RPL_PX, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2560: { PCI_DEVICE_DATA(INTEL, HDA_MTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2561: /* Battlemage */
2562: { PCI_DEVICE_DATA(INTEL, HDA_BMG, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2563: /* Lunarlake-P */
2564: { PCI_DEVICE_DATA(INTEL, HDA_LNL_P, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
2565: /* Arrow Lake-S */
2566: { PCI_DEVICE_DATA(INTEL, HDA_ARL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2567: /* Arrow Lake */
2568: { PCI_DEVICE_DATA(INTEL, HDA_ARL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_SKYLAKE) },
2569: /* Panther Lake */
2570: { PCI_DEVICE_DATA(INTEL, HDA_PTL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
2571: /* Panther Lake-H */
2572: { PCI_DEVICE_DATA(INTEL, HDA_PTL_H, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
2573: /* Wildcat Lake */
2574: { PCI_DEVICE_DATA(INTEL, HDA_WCL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_LNL) },
2575: /* Nova Lake */
2576: { PCI_DEVICE_DATA(INTEL, HDA_NVL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_NVL) },
2577: { PCI_DEVICE_DATA(INTEL, HDA_NVL_S, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_NVL) },
2578: /* Apollolake (Broxton-P) */
2579: { PCI_DEVICE_DATA(INTEL, HDA_APL, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
2580: /* Gemini-Lake */
2581: { PCI_DEVICE_DATA(INTEL, HDA_GLK, AZX_DRIVER_SKL | AZX_DCAPS_INTEL_BROXTON) },
2582: /* Haswell */
2583: { PCI_DEVICE_DATA(INTEL, HDA_HSW_0, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
2584: { PCI_DEVICE_DATA(INTEL, HDA_HSW_2, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
2585: { PCI_DEVICE_DATA(INTEL, HDA_HSW_3, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_HASWELL) },
2586: /* Broadwell */
2587: { PCI_DEVICE_DATA(INTEL, HDA_BDW, AZX_DRIVER_HDMI | AZX_DCAPS_INTEL_BROADWELL) },
2588: /* 5 Series/3400 */
2589: { PCI_DEVICE_DATA(INTEL, HDA_5_3400_SERIES_0, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) },
2590: { PCI_DEVICE_DATA(INTEL, HDA_5_3400_SERIES_1, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_NOPM) },
2591: /* Poulsbo */
2592: { PCI_DEVICE_DATA(INTEL, HDA_POULSBO, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE |
2593: AZX_DCAPS_POSFIX_LPIB) },
2594: /* Oaktrail */
2595: { PCI_DEVICE_DATA(INTEL, HDA_OAKTRAIL, AZX_DRIVER_SCH | AZX_DCAPS_INTEL_PCH_BASE) },
2596: /* BayTrail */
2597: { PCI_DEVICE_DATA(INTEL, HDA_BYT, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BAYTRAIL) },
2598: /* Braswell */
2599: { PCI_DEVICE_DATA(INTEL, HDA_BSW, AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BRASWELL) },
2600: /* ICH6 */
2601: { PCI_DEVICE_DATA(INTEL, HDA_ICH6, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
2602: /* ICH7 */
2603: { PCI_DEVICE_DATA(INTEL, HDA_ICH7, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
2604: /* ESB2 */
2605: { PCI_DEVICE_DATA(INTEL, HDA_ESB2, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
2606: /* ICH8 */
2607: { PCI_DEVICE_DATA(INTEL, HDA_ICH8, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
2608: /* ICH9 */
2609: { PCI_DEVICE_DATA(INTEL, HDA_ICH9_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
2610: /* ICH9 */
2611: { PCI_DEVICE_DATA(INTEL, HDA_ICH9_1, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
2612: /* ICH10 */
2613: { PCI_DEVICE_DATA(INTEL, HDA_ICH10_0, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
2614: /* ICH10 */
2615: { PCI_DEVICE_DATA(INTEL, HDA_ICH10_1, AZX_DRIVER_ICH | AZX_DCAPS_INTEL_ICH) },
2616: /* Generic Intel */
2617: { PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_ANY_ID),
2618: .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
2619: .class_mask = 0xffffff,
2620: .driver_data = AZX_DRIVER_ICH | AZX_DCAPS_NO_ALIGN_BUFSIZE },
2621: /* ATI SB 450/600/700/800/900 */
2622: { PCI_VDEVICE(ATI, 0x437b),
2623: .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
2624: { PCI_VDEVICE(ATI, 0x4383),
2625: .driver_data = AZX_DRIVER_ATI | AZX_DCAPS_PRESET_ATI_SB },
2626: /* AMD Hudson */
2627: { PCI_VDEVICE(AMD, 0x780d),
2628: .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
2629: /* AMD, X370 & co */
2630: { PCI_VDEVICE(AMD, 0x1457),
2631: .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
2632: /* AMD, X570 & co */
2633: { PCI_VDEVICE(AMD, 0x1487),
2634: .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
2635: /* AMD Stoney */
2636: { PCI_VDEVICE(AMD, 0x157a),
2637: .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB |
2638: AZX_DCAPS_PM_RUNTIME },
2639: /* AMD Raven */
2640: { PCI_VDEVICE(AMD, 0x15e3),
2641: .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_AMD_SB },
2642: /* ATI HDMI */
2643: { PCI_VDEVICE(ATI, 0x0002),
2644: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2645: AZX_DCAPS_PM_RUNTIME },
2646: { PCI_VDEVICE(ATI, 0x1308),
2647: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
2648: { PCI_VDEVICE(ATI, 0x157a),
2649: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
2650: { PCI_VDEVICE(ATI, 0x15b3),
2651: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
2652: { PCI_VDEVICE(ATI, 0x793b),
2653: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2654: { PCI_VDEVICE(ATI, 0x7919),
2655: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2656: { PCI_VDEVICE(ATI, 0x960f),
2657: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2658: { PCI_VDEVICE(ATI, 0x970f),
2659: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2660: { PCI_VDEVICE(ATI, 0x9840),
2661: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
2662: { PCI_VDEVICE(ATI, 0xaa00),
2663: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2664: { PCI_VDEVICE(ATI, 0xaa08),
2665: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2666: { PCI_VDEVICE(ATI, 0xaa10),
2667: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2668: { PCI_VDEVICE(ATI, 0xaa18),
2669: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2670: { PCI_VDEVICE(ATI, 0xaa20),
2671: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2672: { PCI_VDEVICE(ATI, 0xaa28),
2673: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2674: { PCI_VDEVICE(ATI, 0xaa30),
2675: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2676: { PCI_VDEVICE(ATI, 0xaa38),
2677: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2678: { PCI_VDEVICE(ATI, 0xaa40),
2679: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2680: { PCI_VDEVICE(ATI, 0xaa48),
2681: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2682: { PCI_VDEVICE(ATI, 0xaa50),
2683: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2684: { PCI_VDEVICE(ATI, 0xaa58),
2685: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2686: { PCI_VDEVICE(ATI, 0xaa60),
2687: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2688: { PCI_VDEVICE(ATI, 0xaa68),
2689: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2690: { PCI_VDEVICE(ATI, 0xaa80),
2691: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2692: { PCI_VDEVICE(ATI, 0xaa88),
2693: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2694: { PCI_VDEVICE(ATI, 0xaa90),
2695: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2696: { PCI_VDEVICE(ATI, 0xaa98),
2697: .driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
2698: { PCI_VDEVICE(ATI, 0x9902),
2699: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
2700: { PCI_VDEVICE(ATI, 0xaaa0),
2701: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
2702: { PCI_VDEVICE(ATI, 0xaaa8),
2703: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
2704: { PCI_VDEVICE(ATI, 0xaab0),
2705: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
2706: { PCI_VDEVICE(ATI, 0xaac0),
2707: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2708: AZX_DCAPS_PM_RUNTIME },
2709: { PCI_VDEVICE(ATI, 0xaac8),
2710: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2711: AZX_DCAPS_PM_RUNTIME },
2712: { PCI_VDEVICE(ATI, 0xaad8),
2713: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2714: AZX_DCAPS_PM_RUNTIME },
2715: { PCI_VDEVICE(ATI, 0xaae0),
2716: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2717: AZX_DCAPS_PM_RUNTIME },
2718: { PCI_VDEVICE(ATI, 0xaae8),
2719: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2720: AZX_DCAPS_PM_RUNTIME },
2721: { PCI_VDEVICE(ATI, 0xaaf0),
2722: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2723: AZX_DCAPS_PM_RUNTIME },
2724: { PCI_VDEVICE(ATI, 0xaaf8),
2725: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2726: AZX_DCAPS_PM_RUNTIME },
2727: { PCI_VDEVICE(ATI, 0xab00),
2728: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2729: AZX_DCAPS_PM_RUNTIME },
2730: { PCI_VDEVICE(ATI, 0xab08),
2731: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2732: AZX_DCAPS_PM_RUNTIME },
2733: { PCI_VDEVICE(ATI, 0xab10),
2734: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2735: AZX_DCAPS_PM_RUNTIME },
2736: { PCI_VDEVICE(ATI, 0xab18),
2737: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2738: AZX_DCAPS_PM_RUNTIME },
2739: { PCI_VDEVICE(ATI, 0xab20),
2740: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2741: AZX_DCAPS_PM_RUNTIME },
2742: { PCI_VDEVICE(ATI, 0xab28),
2743: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2744: AZX_DCAPS_PM_RUNTIME },
2745: { PCI_VDEVICE(ATI, 0xab30),
2746: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2747: AZX_DCAPS_PM_RUNTIME },
2748: { PCI_VDEVICE(ATI, 0xab38),
2749: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2750: AZX_DCAPS_PM_RUNTIME },
2751: { PCI_VDEVICE(ATI, 0xab40),
2752: .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS |
2753: AZX_DCAPS_PM_RUNTIME },
2754: /* GLENFLY */
2755: { PCI_DEVICE(PCI_VENDOR_ID_GLENFLY, PCI_ANY_ID),
2756: .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
2757: .class_mask = 0xffffff,
2758: .driver_data = AZX_DRIVER_GFHDMI | AZX_DCAPS_POSFIX_LPIB |
2759: AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
2760: /* VIA VT8251/VT8237A */
2761: { PCI_VDEVICE(VIA, 0x3288), .driver_data = AZX_DRIVER_VIA },
2762: /* VIA GFX VT7122/VX900 */
2763: { PCI_VDEVICE(VIA, 0x9170), .driver_data = AZX_DRIVER_GENERIC },
2764: /* VIA GFX VT6122/VX11 */
2765: { PCI_VDEVICE(VIA, 0x9140), .driver_data = AZX_DRIVER_GENERIC },
2766: /* SIS966 */
2767: { PCI_VDEVICE(SI, 0x7502), .driver_data = AZX_DRIVER_SIS },
2768: /* ULI M5461 */
2769: { PCI_VDEVICE(AL, 0x5461), .driver_data = AZX_DRIVER_ULI },
2770: /* NVIDIA MCP */
2771: { PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID),
2772: .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
2773: .class_mask = 0xffffff,
2774: .driver_data = AZX_DRIVER_NVIDIA | AZX_DCAPS_PRESET_NVIDIA },
2775: /* Teradici */
2776: { PCI_DEVICE(0x6549, 0x1200),
2777: .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
2778: { PCI_DEVICE(0x6549, 0x2200),
2779: .driver_data = AZX_DRIVER_TERA | AZX_DCAPS_NO_64BIT },
2780: /* Creative X-Fi (CA0110-IBG) */
2781: /* CTHDA chips */
2782: { PCI_VDEVICE(CREATIVE, 0x0010),
2783: .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
2784: { PCI_VDEVICE(CREATIVE, 0x0012),
2785: .driver_data = AZX_DRIVER_CTHDA | AZX_DCAPS_PRESET_CTHDA },
2786: #if !IS_ENABLED(CONFIG_SND_CTXFI)
2787: /* the following entry conflicts with snd-ctxfi driver,
2788: * as ctxfi driver mutates from HD-audio to native mode with
2789: * a special command sequence.
2790: */
2791: { PCI_DEVICE(PCI_VENDOR_ID_CREATIVE, PCI_ANY_ID),
2792: .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
2793: .class_mask = 0xffffff,
2794: .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
2795: AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
2796: #else
2797: /* this entry seems still valid -- i.e. without emu20kx chip */
2798: { PCI_VDEVICE(CREATIVE, 0x0009),
2799: .driver_data = AZX_DRIVER_CTX | AZX_DCAPS_CTX_WORKAROUND |
2800: AZX_DCAPS_NO_64BIT | AZX_DCAPS_POSFIX_LPIB },
2801: #endif
2802: /* CM8888 */
2803: { PCI_VDEVICE(CMEDIA, 0x5011),
2804: .driver_data = AZX_DRIVER_CMEDIA |
2805: AZX_DCAPS_NO_MSI | AZX_DCAPS_POSFIX_LPIB | AZX_DCAPS_SNOOP_OFF },
2806: /* Vortex86MX */
2807: { PCI_VDEVICE(RDC, 0x3010), .driver_data = AZX_DRIVER_GENERIC },
2808: /* VMware HDAudio */
2809: { PCI_VDEVICE(VMWARE, 0x1977), .driver_data = AZX_DRIVER_GENERIC },
2810: /* AMD/ATI Generic, PCI class code and Vendor ID for HD Audio */
2811: { PCI_DEVICE(PCI_VENDOR_ID_ATI, PCI_ANY_ID),
2812: .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
2813: .class_mask = 0xffffff,
2814: .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
2815: { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_ANY_ID),
2816: .class = PCI_CLASS_MULTIMEDIA_HD_AUDIO << 8,
2817: .class_mask = 0xffffff,
2818: .driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_HDMI },
2819: /* Zhaoxin */
2820: { PCI_VDEVICE(ZHAOXIN, 0x3288), .driver_data = AZX_DRIVER_ZHAOXIN },
2821: { PCI_VDEVICE(ZHAOXIN, 0x9141),
2822: .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
2823: AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
2824: { PCI_VDEVICE(ZHAOXIN, 0x9142),
2825: .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
2826: AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
2827: { PCI_VDEVICE(ZHAOXIN, 0x9144),
2828: .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
2829: AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
2830: { PCI_VDEVICE(ZHAOXIN, 0x9145),
2831: .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
2832: AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
2833: { PCI_VDEVICE(ZHAOXIN, 0x9146),
2834: .driver_data = AZX_DRIVER_ZHAOXINHDMI | AZX_DCAPS_POSFIX_LPIB |
2835: AZX_DCAPS_NO_MSI | AZX_DCAPS_NO_64BIT },
2836: /* Loongson HDAudio*/
2837: { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDA),
2838: .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
2839: { PCI_VDEVICE(LOONGSON, PCI_DEVICE_ID_LOONGSON_HDMI),
2840: .driver_data = AZX_DRIVER_LOONGSON | AZX_DCAPS_NO_TCSEL },
2841: { 0, }
2842: };
SourceFile:sound/hda/controllers/intel.c]
|
| 76/2 |
2026/08/09 06:07 |
llm |
expert |
0m
Model:
Tokens:
input: 59847
output: 66
thoughts: 192
Thoughts: **Vulnerability Assessment: AZX_DRIVER_SKL**
Okay, so the `AZX_DRIVER_SKL` driver is clearly a critical point of concern. The fact that it's utilized across such a broad range of Intel architectures β Skylake, Kabylake, Coffelake, Icelake, Tigerlake, Alderlake, Raptorlake, and probably future generations β immediately screams widespread impact. A vulnerability here could potentially compromise almost any modern Intel system, provided a malicious Thunderbolt device can spoof the appropriate PCI IDs. That's the key attack vector to keep in mind.
Now, let's nail down the exploitability. Yes, it's definitely exploitable. The out-of-bounds read/write within the vmalloc area, combined with the potential for user-space memory access (assuming SMAP is disabled), paints a pretty clear picture. We're looking at both a denial-of-service vector and the chance of full system compromise.
Privilege escalation? No, thankfully, it's not unprivileged. However, UserNamespace isn't a factor. This also doesn't rely on virtual machine guests directly. The host, though, can be exploited, with the potential of exposing a fake Intel HDA controller to the Confidential VM, making this a true host vulnerability.
Network and remote triggers are off the table, but the peripheral trigger is real, and it's a significant threat vector. This is a Thunderbolt/USB4 issue. We don't have to worry about the filesystem.
|
| 77/2 |
2026/08/09 06:07 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:true]
Results: map[DenialOfService:true Exploitable:true FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:false VMHostTrigger:true]
|
| 78/2 |
2026/08/09 06:07 |
llm |
expert |
0m
Model:
Tokens:
input: 60488
output: 781
thoughts: 13
Thoughts: Here's your summary:
**Finalizing the Response**
Okay, the analysis is done. All the data, the nuances, the potential pitfalls β I've processed it all. I've considered the implications and the likely counter-arguments. Now, it's time to craft the final response. Itβs time to synthesize everything and present a coherent, informed conclusion. I need to ensure it's accurate, concise, and effectively communicates the core findings. Let's get this right.
|
| 79/1 |
2026/08/09 06:07 |
action |
format-explanation |
0m
Results: map[Explanation:The bug report describes a page fault in `snd_hdac_bus_parse_capabilities` when
reading from the `0x1c094` address.
### Root Cause Analysis
The issue occurs during the initialization of an Intel High Definition Audio
(HDA) controller. In `azx_first_init()`, the driver maps the device's Base
Address Register (BAR 0) using `pcim_iomap_region(pci, 0, "ICH HD audio")`. The
driver assumes that BAR 0 is always a Memory-Mapped I/O (MMIO) region, as
specified by the Intel HDA specification. However, it does not verify the
resource flags before mapping.
If a malicious or misconfigured PCI device exposes BAR 0 as an I/O port instead
of an MMIO region, `pcim_iomap_region()` maps it as an I/O port and returns a
PIO cookie (e.g., `0x10000 + port`). Later, `snd_hdac_bus_parse_capabilities()`
attempts to read from this region using `readw()`. On x86, `readw()` directly
dereferences the provided address. Dereferencing a PIO cookie (which falls in
the user-space address range `0x10000` - `0x1FFFF`) causes a page fault if SMAP
(Supervisor Mode Access Prevention) is enabled, or reads from user-space memory
if SMAP is disabled.
Furthermore, even if BAR 0 is correctly exposed as an MMIO region,
`snd_hdac_bus_parse_capabilities()` trusts the capabilities list provided by the
device without bounds checking. The device can provide an `offset` up to
`0xFFFF` (64KB). If the device exposes a small MMIO BAR (e.g., 4KB), the driver
will read from and write to `bus->remap_addr + offset`, which falls outside the
mapped MMIO region and accesses adjacent `vmalloc` memory.
### Security Impact
* **Exploitable:** Yes.
1. **Out-of-bounds vmalloc access:** A malicious device can expose a small
MMIO BAR and provide large offsets in the capabilities list, causing the
driver to read/write adjacent `vmalloc` allocations. This is a powerful
primitive that can lead to arbitrary kernel memory corruption and privilege
escalation.
2. **User-space memory access:** If the device exposes BAR 0 as an I/O port
and SMAP is disabled, the kernel will read the capabilities list from
user-space memory, which could also be manipulated for exploitation.
* **Denial of Service:** Yes. The bug trivially causes a kernel panic (page
fault) or OOM (by providing a massive capabilities list count).
* **Peripheral Trigger:** Yes. An attacker can trigger this bug by physically
plugging in a malicious PCIe device via Thunderbolt or USB4 that spoofs a modern
Intel HDA controller (e.g., Skylake, Alderlake) and exposes a malformed BAR 0 or
capabilities list.
* **VM Host Trigger in Confidential Computing:** Yes. An untrusted KVM host
(VMM) can expose a malicious emulated Intel HDA controller to a Confidential VM
(e.g., AMD SEV, Intel TDX). When the guest kernel probes the device, the host
can exploit the out-of-bounds `vmalloc` access to compromise the guest kernel.
* **Unprivileged / User Namespaces:** No. Triggering the bug requires either
physical access to plug in a PCIe device or the ability to create virtual PCI
devices (which requires root privileges or specific fuzzing setups like
syzkaller).]
|