| 0/0 |
2026/08/09 16:33 |
flow |
assessment-security |
5m
Results: map[DenialOfService:true Explanation:**Risk & Request Analysis**
The bug report indicates a Undefined Behavior Sanitizer (UBSAN)
shift-out-of-bounds warning in the `e100_eeprom_load` function, which is part of
the Intel PRO/100 Ethernet driver (`e100`). The warning triggers a kernel panic
because the kernel is configured with `panic_on_warn`.
Let's analyze the root cause by looking at the source code:
```c
764: u16 addr, addr_len = 8, checksum = 0;
765:
766: /* Try reading with an 8-bit addr len to discover actual addr
len */
767: e100_eeprom_read(nic, &addr_len, 0);
768: nic->eeprom_wc = 1 << addr_len;
```
The `e100_eeprom_read` function communicates with the hardware via MMIO to read
the EEPROM. During this process, it attempts to auto-detect the EEPROM address
length:
```c
743: /* Eeprom drives a dummy zero to EEDO after receiving
744: * complete address. Use this to adjust addr_len. */
745: ctrl = ioread8(&nic->csr->eeprom_ctrl_lo);
746: if (!(ctrl & eedo) && i > 16) {
747: *addr_len -= (i - 16);
748: i = 17;
749: }
```
If a malicious or malfunctioning PCI device returns a value where the `eedo` bit
is 0 early in the loop (e.g., when `i = 31`), `addr_len` (which starts at 8)
will underflow. For instance, `8 - (31 - 16) = -7`, which becomes `65529` since
`addr_len` is a `u16`.
Back in `e100_eeprom_load`, the code executes `1 << addr_len`. Shifting by a
value greater than or equal to the width of the type (32 bits for an integer
literal `1`) is undefined behavior, which is exactly what UBSAN catches.
If UBSAN is disabled (as in typical production builds), the CPU will mask the
shift count. On x86, `65529 & 31 = 25`, so the expression evaluates to `1 << 25`
(`33554432`). This value is then assigned to `nic->eeprom_wc`, which is a `u16`.
The truncation results in `nic->eeprom_wc = 0`.
This leads to a secondary bug. The loop that reads the EEPROM will be skipped
because `addr < 0` is false:
```c
770: for (addr = 0; addr < nic->eeprom_wc; addr++) { ... }
```
Then, the driver attempts to verify the checksum:
```c
778: if (cpu_to_le16(0xBABA - checksum) != nic->eeprom[nic->eeprom_wc
- 1]) {
```
Since `nic->eeprom_wc` is `0`, `nic->eeprom_wc - 1` underflows to `65535`. The
driver will perform an out-of-bounds read at `nic->eeprom[65535]`, which is
roughly 131KB past the end of the `nic` structure. This will likely hit an
unmapped memory page and cause a fatal page fault (kernel panic).
**Security Impact:**
* **Exploitable:** No. The bug results in an out-of-bounds read of 2 bytes, but
the read value is only used in a local comparison to validate a checksum. It is
never written to memory, used as a pointer, or leaked to userspace. Therefore,
it cannot be used for privilege escalation or information disclosure.
* **Denial of Service:** Yes. The out-of-bounds read will likely cause a page
fault, crashing the kernel. Even without the OOB read, the UBSAN panic causes a
DoS on kernels with `panic_on_warn` enabled.
* **Unprivileged / User Namespace:** No. The bug occurs in the PCI device probe
path (`e100_probe`). Triggering a PCI probe requires either physical access to
plug in a device or root privileges to bind a driver via sysfs. It is not
accessible to unprivileged users or within user namespaces.
* **Peripheral Trigger:** Yes. A malicious physical PCI/CardBus device plugged
into the system can return crafted MMIO responses to trigger this bug.
* **VM Guest Trigger:** Yes. A malicious hypervisor or a virtualized PCI device
(e.g., in QEMU) can trigger this in the guest kernel. Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:true VMHostTrigger:false]
|
| 1/1 |
2026/08/09 16:33 |
action |
syz-repro-to-c-repro |
0m
Results:
|
| 2/1 |
2026/08/09 16:33 |
action |
kernel-checkouter |
0m
Results: map[KernelSrc:/app/workdir/cache/src/0bd482cc9fd6a5c3cdf3e7f636a3906397462859]
|
| 3/1 |
2026/08/09 16:33 |
action |
kernel-builder |
0m
Results: map[KernelObj:/app/workdir/cache/build/c6c7b2d9e4d1d7e5a1cffa7566d7d92fb1f9026a]
|
| 4/1 |
2026/08/09 16:33 |
action |
codesearch-prepare |
0m
Results: map[Index:codesearch-index]
|
| 5/1 |
2026/08/09 16:33 |
agent |
expert |
5m
Model:
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:true VMHostTrigger:false]
Instruction: You are an experienced Linux kernel security engineer. Your task is to analyze given kernel bug report
and determine its security impact based on the following dimensions.
Use the provided tools to examine the source code, check for capability checks (e.g., capable(), ns_capable()),
and understand the nature of the bug. Analyze the given kernel build and configuration.
You can check the kernel config by grepping ".config" file; you can check kernel cmdline by grepping
".config" file for "CONFIG_CMDLINE=". Assume sysctl parameters have default values.
But analyze for the corresponding production build w/o debugging tools enabled (like KASAN, KMSAN, UBSAN).
Try different strategies when analyzing the bug:
- think of ways in which the vulnerable code is unreachable
- or the other way around: try to come up with different ideas of how an unprivileged user can reach the bug
If still unsure err on the side of the bug being non-exploitable/not-accessible.
In the final reply, provide a reasoning for your assessment.
Analysis dimensions:
* Exploitable:
Determine if the bug can result in memory corruption, elevated privileges, or an information leak.
Memory safety issues are almost always exploitable (KASAN or UBSAN reports for use-after-free, out-of-bounds;
refcounting issues, corrupted lists, etc). When kernel is crashing on a completely wild pointer access
(e.g. user-space address, or non-canonical address, but not on NULL or address corresponding to KASAN shadow
for NULL address), including both data accesses and control transfers, that also usually implies possibility
of exploitation. Such reports usually say "unable to handle kernel paging request".
Uses of uninitialized values detected by KMSAN may be exploitable b/c attacker frequently can affect uninit
values with spraying techniques. However, for these exploitability depends on how exactly the uninit value
is used in the code, and what it affects.
Information leaks are exploitable on their own and should be classified as such. A bug that copies kernel
memory contents to userspace (e.g. an out-of-bounds read whose result is returned to the caller, or
uninitialized stack/heap bytes written to a user buffer) is exploitable: it can reveal kernel pointer
values and defeat KASLR, expose sensitive data such as cryptographic keys or other processes' memory, and
serves as a necessary building block in most modern kernel privilege-escalation exploit chains. Do not classify
an information leak as non-exploitable solely because it does not directly cause a memory write or control-flow
hijack; the leak itself is the exploit primitive.
Think of what happens after the bug is triggered. Some bugs cause kernel panic and halt execution,
they are harder to exploit. For example, BUG reports halts the kernel. However, WARNING reports don't halt
execution in production builds. Debug bug detection tools (like KASAN, KMSAN, KCSAN, UBSAN) are also not enabled
in production builds, so attacker can freely exploit these bugs w/o being detected by these tools.
If you see an integer overflow, think how the overflowed value used later (if it's used as allocation size,
or an array index). If you see an out-of-bounds read, think if it's followed by an out-of-bounds write as well.
Some KCSAN data-races may be exploitable by skilled attackers as well. Think what data structures got corrupted
as the result of data races and how. However, note that kernel has lots of "benign" data races that don't lead
to any runtime misbehavior at all.
* Denial Of Service:
Determine if the bug can result in denial-of-service. Most bugs can, since they cause system crash,
hangs, deadlocks, or resource leaks. This is mostly applicable to WARNING bugs that won't cause system crash
in production. For these think what will be consequences of the violation of the kernel assumptions flagged
by the WARNING. In some cases the unexpected condition is also properly handled by the normal control flow
(e.g. with "if (WARN_ON(...))"), these won't cause denial-of-service. If the condition is not handled,
then it may or may not cause denial-of-service.
* Accessible From Unprivileged Processes:
Determine if the bug can be reached from a typical (non-root) user process that does NOT have any special capabilities
(like CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON) or access to device nodes restricted to root.
Assume that unprivileged_bpf_disabled=1, that is eBPF loading is not accessible. However, cBPF (classical BPF)
is still accessible to non-root processes.
Assume that user namespaces are not accessible, that is, the process cannot get the mentioned capabilities even
within a new user namespace (checked by ns_capable() function in the kernel sources).
* Accessible From User Namespaces:
Determine if the bug can be reached within a user-namespace where the process has all capabilities
(including CAP_SYS_ADMIN, CAP_NET_ADMIN, CAP_NET_RAW, CAP_PERFMON). Such capabilities are checked with ns_capable()
function in the kernel sources.
* VM Guest Trigger:
Determine if the bug can be triggered from the context of a typical KVM guest (e.g., set up by a QEMU VMM).
Consider accesses to standard Linux host paravirtualized features (virtio-blk, virtio-net, etc.),
and handling of VM exits in the KVM code.
* VM Host Trigger in The Confidential Computing Context:
Determine if the bug can be triggered in a confidential computing guest kernel from the context of a KVM host.
Consider access to standard Linux guest paravirtualized features (virtio-blk, virtio-net, etc.).
* Ethernet Network Trigger:
Determine if the bug can be triggered by processing ingress network Ethernet traffic, either directly (network stack)
or via drivers exposed to network data.
* Other Remote Trigger:
Determine if the bug can be triggered by processing remote traffic other than Ethernet (Wifi, Bluetooth, NFC, etc).
* Peripheral Trigger:
Determine if the bug can be triggered via an untrusted peripheral device that can be physically plugged
into a system, such as a USB device or a niche hardware driver handling external hardware inputs.
This is particularly important for mobile and desktop environments where users can plug in unknown devices.
* Malicious Filesystem Trigger:
Determine if the bug can be triggered by the kernel mounting and parsing a malicious filesystem image.
This is highly critical for Desktop and Mobile environments where external media or downloaded images
might be auto-mounted.
Don't make assumptions about the kernel source code (it may be different from what you assume it is).
Extensively use the provided code access tools (codesearch-*, git-*, grepper, etc)
to examine the actual source code, and confirm any assumptions.
Prefer calling several tools at the same time to save round-trips.
Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.
Prompt:
The kernel bug report is:
Kernel panic - not syncing: UBSAN: panic_on_warn set ...
CPU: 3 UID: 0 PID: 14356 Comm: syz.5.433 Not tainted syzkaller #0 PREEMPT(full)
Hardware name: QEMU Standard PC (Q35 + ICH9, 2009), BIOS 1.16.3-debian-1.16.3-2 04/01/2014
Call Trace:
<TASK>
__dump_stack lib/dump_stack.c:94 [inline]
dump_stack_lvl+0x100/0x190 lib/dump_stack.c:120
vpanic+0x552/0x970 kernel/panic.c:651
panic+0xd1/0xe0 kernel/panic.c:788
check_panic_on_warn kernel/panic.c:525 [inline]
check_panic_on_warn.cold+0x19/0x34 kernel/panic.c:520
__ubsan_handle_shift_out_of_bounds+0x279/0x2a0 lib/ubsan.c:494
e100_eeprom_load.cold+0x55/0x9e drivers/net/ethernet/intel/e100.c:768
e100_probe+0xd07/0x1ab0 drivers/net/ethernet/intel/e100.c:2921
local_pci_probe+0xdd/0x1b0 drivers/pci/pci-driver.c:332
pci_call_probe+0x176/0x800 drivers/pci/pci-driver.c:394
__pci_device_probe drivers/pci/pci-driver.c:455 [inline]
pci_device_probe+0x1cd/0x370 drivers/pci/pci-driver.c:489
call_driver_probe drivers/base/dd.c:628 [inline]
really_probe+0x241/0xa60 drivers/base/dd.c:706
__driver_probe_device+0x20e/0x450 drivers/base/dd.c:868
device_driver_attach+0xd1/0x220 drivers/base/dd.c:1203
bind_store+0xf4/0x190 drivers/base/bus.c:267
drv_attr_store+0x74/0xb0 drivers/base/bus.c:125
sysfs_kf_write+0xf2/0x150 fs/sysfs/file.c:145
kernfs_fop_write_iter+0x3e0/0x5f0 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x6ac/0x1050 fs/read_write.c:687
ksys_write+0x12a/0x250 fs/read_write.c:739
do_syscall_x64 arch/x86/entry/syscall_64.c:63 [inline]
do_syscall_64+0x115/0x870 arch/x86/entry/syscall_64.c:94
entry_SYSCALL_64_after_hwframe+0x77/0x7f
RIP: 0033:0x7f53bab9e019
Code: ff c3 66 2e 0f 1f 84 00 00 00 00 00 0f 1f 44 00 00 48 89 f8 48 89 f7 48 89 d6 48 89 ca 4d 89 c2 4d 89 c8 4c 8b 4c 24 08 0f 05 <48> 3d 01 f0 ff ff 73 01 c3 48 c7 c1 e8 ff ff ff f7 d8 64 89 01 48
RSP: 002b:00007f53bba7f028 EFLAGS: 00000246 ORIG_RAX: 0000000000000001
RAX: ffffffffffffffda RBX: 00007f53bae26090 RCX: 00007f53bab9e019
RDX: 000000000000000c RSI: 0000200000000280 RDI: 0000000000000006
RBP: 00007f53bac3500c R08: 0000000000000000 R09: 0000000000000000
R10: 0000000000000000 R11: 0000000000000246 R12: 0000000000000000
R13: 00007f53bae26128 R14: 00007f53bae26090 R15: 00007fffaac44238
</TASK>
Kernel Offset: disabled
Rebooting in 86400 seconds..
Reply: **Risk & Request Analysis**
The bug report indicates a Undefined Behavior Sanitizer (UBSAN) shift-out-of-bounds warning in the `e100_eeprom_load` function, which is part of the Intel PRO/100 Ethernet driver (`e100`). The warning triggers a kernel panic because the kernel is configured with `panic_on_warn`.
Let's analyze the root cause by looking at the source code:
```c
764: u16 addr, addr_len = 8, checksum = 0;
765:
766: /* Try reading with an 8-bit addr len to discover actual addr len */
767: e100_eeprom_read(nic, &addr_len, 0);
768: nic->eeprom_wc = 1 << addr_len;
```
The `e100_eeprom_read` function communicates with the hardware via MMIO to read the EEPROM. During this process, it attempts to auto-detect the EEPROM address length:
```c
743: /* Eeprom drives a dummy zero to EEDO after receiving
744: * complete address. Use this to adjust addr_len. */
745: ctrl = ioread8(&nic->csr->eeprom_ctrl_lo);
746: if (!(ctrl & eedo) && i > 16) {
747: *addr_len -= (i - 16);
748: i = 17;
749: }
```
If a malicious or malfunctioning PCI device returns a value where the `eedo` bit is 0 early in the loop (e.g., when `i = 31`), `addr_len` (which starts at 8) will underflow. For instance, `8 - (31 - 16) = -7`, which becomes `65529` since `addr_len` is a `u16`.
Back in `e100_eeprom_load`, the code executes `1 << addr_len`. Shifting by a value greater than or equal to the width of the type (32 bits for an integer literal `1`) is undefined behavior, which is exactly what UBSAN catches.
If UBSAN is disabled (as in typical production builds), the CPU will mask the shift count. On x86, `65529 & 31 = 25`, so the expression evaluates to `1 << 25` (`33554432`). This value is then assigned to `nic->eeprom_wc`, which is a `u16`. The truncation results in `nic->eeprom_wc = 0`.
This leads to a secondary bug. The loop that reads the EEPROM will be skipped because `addr < 0` is false:
```c
770: for (addr = 0; addr < nic->eeprom_wc; addr++) { ... }
```
Then, the driver attempts to verify the checksum:
```c
778: if (cpu_to_le16(0xBABA - checksum) != nic->eeprom[nic->eeprom_wc - 1]) {
```
Since `nic->eeprom_wc` is `0`, `nic->eeprom_wc - 1` underflows to `65535`. The driver will perform an out-of-bounds read at `nic->eeprom[65535]`, which is roughly 131KB past the end of the `nic` structure. This will likely hit an unmapped memory page and cause a fatal page fault (kernel panic).
**Security Impact:**
* **Exploitable:** No. The bug results in an out-of-bounds read of 2 bytes, but the read value is only used in a local comparison to validate a checksum. It is never written to memory, used as a pointer, or leaked to userspace. Therefore, it cannot be used for privilege escalation or information disclosure.
* **Denial of Service:** Yes. The out-of-bounds read will likely cause a page fault, crashing the kernel. Even without the OOB read, the UBSAN panic causes a DoS on kernels with `panic_on_warn` enabled.
* **Unprivileged / User Namespace:** No. The bug occurs in the PCI device probe path (`e100_probe`). Triggering a PCI probe requires either physical access to plug in a device or root privileges to bind a driver via sysfs. It is not accessible to unprivileged users or within user namespaces.
* **Peripheral Trigger:** Yes. A malicious physical PCI/CardBus device plugged into the system can return crafted MMIO responses to trigger this bug.
* **VM Guest Trigger:** Yes. A malicious hypervisor or a virtualized PCI device (e.g., in QEMU) can trigger this in the guest kernel.
|
| 6/2 |
2026/08/09 16:33 |
llm |
expert |
0m
Model:
Tokens:
input: 7166
output: 48
thoughts: 62
Thoughts: **Analyzing the e100_eeprom_load Bug**
Okay, so I've got a bug report indicating a shift-out-of-bounds error specifically within the `e100_eeprom_load` function. That's a pretty targeted area, so at least we have a good starting point. My initial thought is to dive right into the source code for `drivers/net/ethernet/intel/e100.c`, because that's where the function lives. Let's see what's happening in there and how the EEPROM loading process is structured, and pinpoint where the shift operation could be going sideways. With my knowledge of network device drivers, I suspect this has to do with reading and interpreting the EEPROM data and how the shift operation is used to extract the configuration parameters from it. I need to understand the data structures and the bit-level operations involved to get to the bottom of this.
|
| 7/2 |
2026/08/09 16:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/ethernet/intel/e100.c Name:e100_eeprom_load]
Results: map[SourceCode: 761: /* Load entire EEPROM image into driver cache and validate checksum */
762: static int e100_eeprom_load(struct nic *nic)
763: {
764: u16 addr, addr_len = 8, checksum = 0;
765:
766: /* Try reading with an 8-bit addr len to discover actual addr len */
767: e100_eeprom_read(nic, &addr_len, 0);
768: nic->eeprom_wc = 1 << addr_len;
769:
770: for (addr = 0; addr < nic->eeprom_wc; addr++) {
771: nic->eeprom[addr] = e100_eeprom_read(nic, &addr_len, addr);
772: if (addr < nic->eeprom_wc - 1)
773: checksum += le16_to_cpu(nic->eeprom[addr]);
774: }
775:
776: /* The checksum, stored in the last word, is calculated such that
777: * the sum of words should be 0xBABA */
778: if (cpu_to_le16(0xBABA - checksum) != nic->eeprom[nic->eeprom_wc - 1]) {
779: netif_err(nic, probe, nic->netdev, "EEPROM corrupted\n");
780: if (!eeprom_bad_csum_allow)
781: return -EAGAIN;
782: }
783:
784: return 0;
785: }
SourceFile:drivers/net/ethernet/intel/e100.c]
|
| 8/2 |
2026/08/09 16:33 |
llm |
expert |
0m
Model:
Tokens:
input: 7906
output: 48
thoughts: 110
Thoughts: **Analyzing a Potential Out-of-Bounds Write in the e100 Driver**
Okay, so I'm digging into this bug report and it looks like we have a shift-out-of-bounds situation within the `e100_eeprom_load` function, specifically at line 768 in `drivers/net/ethernet/intel/e100.c`. The problem seems to revolve around this line: `nic->eeprom_wc = 1 << addr_len;`. The code is taking a bitwise left shift operation using `addr_len` as the shift count, which is then used to initialize a register.
Now, the interesting part is where `addr_len` gets its value: it's modified by a call to `e100_eeprom_read(nic, &addr_len, 0)`. Clearly, the value returned by that `e100_eeprom_read` function is critical. Any unexpected value for `addr_len` there is going to cause a problem and potential vulnerability in the right-shift operation. Thatβs where the vulnerability resides. I need to understand how `e100_eeprom_read` handles the value of `addr_len` to determine the maximum value and edge cases to identify the root cause of this vulnerability.
|
| 9/2 |
2026/08/09 16:33 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/ethernet/intel/e100.c Name:e100_eeprom_read]
Results: map[SourceCode: 720: /* General technique stolen from the eepro100 driver - very clever */
721: static __le16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr)
722: {
723: u32 cmd_addr_data;
724: u16 data = 0;
725: u8 ctrl;
726: int i;
727:
728: cmd_addr_data = ((op_read << *addr_len) | addr) << 16;
729:
730: /* Chip select */
731: iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo);
732: e100_write_flush(nic); udelay(4);
733:
734: /* Bit-bang to read word from eeprom */
735: for (i = 31; i >= 0; i--) {
736: ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs;
737: iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo);
738: e100_write_flush(nic); udelay(4);
739:
740: iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo);
741: e100_write_flush(nic); udelay(4);
742:
743: /* Eeprom drives a dummy zero to EEDO after receiving
744: * complete address. Use this to adjust addr_len. */
745: ctrl = ioread8(&nic->csr->eeprom_ctrl_lo);
746: if (!(ctrl & eedo) && i > 16) {
747: *addr_len -= (i - 16);
748: i = 17;
749: }
750:
751: data = (data << 1) | (ctrl & eedo ? 1 : 0);
752: }
753:
754: /* Chip deselect */
755: iowrite8(0, &nic->csr->eeprom_ctrl_lo);
756: e100_write_flush(nic); udelay(4);
757:
758: return cpu_to_le16(data);
759: };
SourceFile:drivers/net/ethernet/intel/e100.c]
|
| 10/2 |
2026/08/09 16:33 |
llm |
expert |
0m
Model:
Tokens:
input: 8978
output: 40
thoughts: 1447
Thoughts: **Analyzing the e100 Driver Shift-Out-of-Bounds Vulnerability**
Okay, so I've got a handle on this bug in the `e100` driver. It's a classic shift-out-of-bounds issue within `e100_eeprom_load` at `drivers/net/ethernet/intel/e100.c:768`. The line is: `nic->eeprom_wc = 1 << addr_len;`. The culprit is that `addr_len`, which starts at 8, gets a bit twisted during an EEPROM read.
It seems the `e100_eeprom_read` function, specifically this section:
```c
/* Eeprom drives a dummy zero to EEDO after receiving
* complete address. Use this to adjust addr_len. */
ctrl = ioread8(&nic->csr->eeprom_ctrl_lo);
if (!(ctrl & eedo) && i > 16) {
*addr_len -= (i - 16);
i = 17;
}
```
Is where the underflow is happening. When `i` hits 31, and `!(ctrl & eedo)` is true, `addr_len` undergoes a substantial subtraction: `*addr_len -= (31 - 16)`, leading to `8 - 15 = -7`. This `addr_len` is a `u16`, so the negative value wraps around to 65529. Then, the resulting `1 << 65529` causes the shift-out-of-bounds.
I traced this through the `e100_probe` function, which is called when the driver is bound to a PCI device:
```
e100_eeprom_load.cold+0x55/0x9e drivers/net/ethernet/intel/e100.c:768
e100_probe+0xd07/0x1ab0 drivers/net/ethernet/intel/e100.c:2921
local_pci_probe+0xdd/0x1b0 drivers/pci/pci-driver.c:332
pci_call_probe+0x176/0x800 drivers/pci/pci-driver.c:394
__pci_device_probe drivers/pci/pci-driver.c:455 [inline]
pci_device_probe+0x1cd/0x370 drivers/pci/pci-driver.c:489
call_driver_probe drivers/base/dd.c:628 [inline]
really_probe+0x241/0xa60 drivers/base/dd.c:706
__driver_probe_device+0x20e/0x450 drivers/base/dd.c:868
device_driver_attach+0xd1/0x220 drivers/base/dd.c:1203
bind_store+0xf4/0x190 drivers/base/bus.c:267
drv_attr_store+0x74/0xb0 drivers/base/bus.c:125
sysfs_kf_write+0xf2/0x150 fs/sysfs/file.c:145
kernfs_fop_write_iter+0x3e0/0x5f0 fs/kernfs/file.c:345
new_sync_write fs/read_write.c:595 [inline]
vfs_write+0x6ac/0x1050 fs/read_write.c:687
ksys_write+0x12a/0x250 fs/read_write.c:739
```
The stack shows this is triggered by writing to a sysfs `bind` file. However, my understanding is this requires root privileges, so it's not a straightforward unprivileged attack. The `bind` files get created via driver registration, with the mode `0200`, meaning write-only for the owner (root).
But, as this is a PCI driver (`e100`), the more significant angle here is a malicious peripheral. The driver reads the EEPROM data via MMIO (`ioread8`), and the underflow hinges on the device returning a specific value during
|
| 11/2 |
2026/08/09 16:34 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/ethernet/intel/e100.c Name:nic]
Results: map[SourceCode: 539: struct nic {
540: /* Begin: frequently used values: keep adjacent for cache effect */
541: u32 msg_enable ____cacheline_aligned;
542: struct net_device *netdev;
543: struct pci_dev *pdev;
544: u16 (*mdio_ctrl)(struct nic *nic, u32 addr, u32 dir, u32 reg, u16 data);
545:
546: struct rx *rxs ____cacheline_aligned;
547: struct rx *rx_to_use;
548: struct rx *rx_to_clean;
549: struct rfd blank_rfd;
550: enum ru_state ru_running;
551:
552: spinlock_t cb_lock ____cacheline_aligned;
553: spinlock_t cmd_lock;
554: struct csr __iomem *csr;
555: enum scb_cmd_lo cuc_cmd;
556: unsigned int cbs_avail;
557: struct napi_struct napi;
558: struct cb *cbs;
559: struct cb *cb_to_use;
560: struct cb *cb_to_send;
561: struct cb *cb_to_clean;
562: __le16 tx_command;
563: /* End: frequently used values: keep adjacent for cache effect */
564:
565: enum {
566: ich = (1 << 0),
567: promiscuous = (1 << 1),
568: multicast_all = (1 << 2),
569: wol_magic = (1 << 3),
570: ich_10h_workaround = (1 << 4),
571: } flags ____cacheline_aligned;
572:
573: enum mac mac;
574: enum phy phy;
575: struct params params;
576: struct timer_list watchdog;
577: struct mii_if_info mii;
578: struct work_struct tx_timeout_task;
579: enum loopback loopback;
580:
581: struct mem *mem;
582: dma_addr_t dma_addr;
583:
584: struct dma_pool *cbs_pool;
585: dma_addr_t cbs_dma_addr;
586: u8 adaptive_ifs;
587: u8 tx_threshold;
588: u32 tx_frames;
589: u32 tx_collisions;
590: u32 tx_deferred;
591: u32 tx_single_collisions;
592: u32 tx_multiple_collisions;
593: u32 tx_fc_pause;
594: u32 tx_tco_frames;
595:
596: u32 rx_fc_pause;
597: u32 rx_fc_unsupported;
598: u32 rx_tco_frames;
599: u32 rx_short_frame_errors;
600: u32 rx_over_length_errors;
601:
602: u16 eeprom_wc;
603: __le16 eeprom[256];
604: spinlock_t mdio_lock;
605: const struct firmware *fw;
606: };
SourceFile:drivers/net/ethernet/intel/e100.c]
|
| 12/2 |
2026/08/09 16:34 |
llm |
expert |
0m
Model:
Tokens:
input: 11102
output: 66
thoughts: 4747
Thoughts: **Analysis of the e100 Driver EEPROM Handling**
Okay, so I've been digging into this `e100_eeprom_load` function, and it seems like there's a nasty bug lurking in how it determines the EEPROM's address length. The crux of the issue boils down to a potential shift-out-of-bounds situation. Here's how I see it:
The code initializes `addr_len` to 8 and then attempts to read from the EEPROM to figure out the *actual* address length. This `e100_eeprom_read` function is where things get interesting. It looks at some control register bits to deduce the address length, and it *decrements* `addr_len` based on the outcome of a loop. The problem arises when this decrementing happens excessively because the condition is `i > 16`, but it can still affect the address length.
My immediate thought was, if `addr_len` gets decremented enough, it can become a large number, like 65529 (or even larger). Then, the line `nic->eeprom_wc = 1 << addr_len` is executed. The problem is, since `addr_len` is a `u16`, a shift of 65529 bits is going to shift out of bounds. The C standard declares this undefined behavior. On x86, it's equivalent to shifting by `65529 & 31 = 25`, but the resulting value is then truncated to a `u16`. In this case, I'd expect that the shift count will be masked by 31, and `1 << 25` gives you 33554432 (0x2000000). But, the `u16` will become 0, which is `0x0000`.
Then it reads a large amount out of bounds: The loop then tries to read `nic->eeprom[addr]` up to `nic->eeprom_wc`, but because it's a `u16`, if `addr_len` becomes negative, then `nic->eeprom_wc` becomes zero.
The code's structure here is problematic:
1. `addr_len` can only decrement from 8.
2. `addr_len` is a `u16`, it can "wrap around" to large numbers.
3. This is a shift-out-of-bounds, a potential kernel panic.
4. However, it looks like `nic->eeprom_wc` being 0 prevents any out of bounds writes on the heap, but does allow an OOB read.
I'm thinking about the implications. This bug requires a malicious PCI device (or QEMU emulated device) to trigger the faulty EEPROM behavior. It needs to return a 0 for `ctrl & eedo` at `i = 31`. The vulnerability can't be triggered by an unprivileged user writing to a sysfs file, unless they can bind/unbind drivers, which requires root (CAP_SYS_ADMIN). So it is unlikely. But, a malicious PCI device that provides a specific `ctrl` value could trigger this on boot or hotplug.
What is the exploitability?
- The shift-out-of-bounds itself is undefined behavior. UBSAN will catch this and cause a kernel panic.
- A value of 0 is written to `nic->eeprom_wc`, an integer overflow due to the initial incorrect value of `addr_len`.
- This causes an out-of-bounds read of `nic->eeprom[65535]` with offset `131070`.
- The out-of-bounds read leads to a comparison, but no controlled value is leaked to userspace, and no crash will occur.
- However, if the out-of-bounds read accesses an unmapped page, a kernel panic (Denial of Service) occurs.
- On the x86 platform, the right operand is always masked by 31.
- This bug is therefore only a Denial of Service (DoS) and is NOT exploitable for privilege escalation or information leakage.
|
| 13/2 |
2026/08/09 16:34 |
tool |
set-results |
0m
Args: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:true VMHostTrigger:false]
Results: map[DenialOfService:true Exploitable:false FilesystemTrigger:false NetworkTrigger:false PeripheralTrigger:true RemoteTrigger:false Unprivileged:false UserNamespace:false VMGuestTrigger:true VMHostTrigger:false]
|
| 14/2 |
2026/08/09 16:34 |
llm |
expert |
0m
Model:
Tokens:
input: 12147
output: 40
thoughts: 3859
Thoughts: **Bug Analysis: e100_eeprom_load Shift-Out-of-Bounds**
Okay, here's the breakdown of this e100 NIC driver bug. It stems from a shift-out-of-bounds vulnerability within the `e100_eeprom_load` function, specifically related to how the `addr_len` variable is handled during EEPROM read operations. The crux of the issue lies in the line `nic->eeprom_wc = 1 << addr_len;`. The `addr_len` variable, which starts at 8, can be modified within the `e100_eeprom_read` function due to a check involving the EEDO signal from the EEPROM.
The EEPROM protocol provides a dummy zero to EEDO after reading a full address, and if the value of `i > 16` and the EEDO is zero, `addr_len` gets decremented by `i-16` (capped at most to -15). The `addr_len` variable is of type `u16` (unsigned 16-bit integer), thus underflow causes the `addr_len` value to become large. This, in turn, can lead to a shift operation that exceeds the maximum bit width. A `1 << 65529` shift, on a 32-bit architecture, is effectively a `1 << 25`, but the crucial part is that the result is subsequently cast to a `u16`. In most cases on x86, this will result in a value of `0` assigned to `nic->eeprom_wc`, because of the modulo 32 effect of bit shifts on the x86 architecture.
This bug can be triggered during the PCI probe phase (`e100_probe`) when the device is initialized. It means a malicious PCI device, be it a VM guest, or with a PCI hotplug device, could exploit this. Because `addr_len` can only be lowered by a maximum of 15, the variable will never result in `0`. In the worst case, the value will be 0. So, `nic->eeprom_wc` becomes 0, which means the loop `for (addr = 0; addr < nic->eeprom_wc; addr++)` never executes. Later code attempts to read from `nic->eeprom[nic->eeprom_wc - 1]`, which becomes `nic->eeprom[-1]`. Critically, this access is *not* a true out-of-bounds read as it seems. Instead, the `nic->eeprom_wc` variable sits directly *before* the `eeprom` array in memory, so `nic->eeprom[-1]` is actually accessing `nic->eeprom_wc` itself.
The vulnerability's exploitation is limited. The out-of-bounds read of `eeprom_wc` occurs, but the code checks if the value matches a hardcoded checksum value. If the values don't match, it returns `-EAGAIN`. There's no memory corruption, no information leak, and no ability to influence execution flow beyond this return code.
The vulnerability's trigger mechanism is potentially through the virtual PCI device provided by a hypervisor. The analysis of the stack trace of a syzkaller report shows the `sysfs_kf_write` -> `drv_attr_store` -> `bind_store` calls, indicating the device driver is being bound via sysfs. This requires root privilege, making direct unprivileged user exploitation difficult unless the user has control over the EEPROM values.
Crucially, the read of the `eeprom_wc` value (the size of `nic->eeprom`) is used in a condition. If the values match, then the function succeeds. If the values do not match, the code will return `-EAGAIN`. In normal production builds (with UBSAN disabled), the undefined behavior due to the shift will likely cause `nic->eeprom_wc` to be zero, which in turn leads to the check in the eeprom routine failing, which returns a benign -EAGAIN error. Therefore, it is a non-exploitable bug.
Regarding Denial of Service (DoS): while the shift-out-of-bounds will be flagged by UBSAN, this is an undefined behavior that is properly handled in the normal control flow of production builds, so there is no denial of service. Thus the e100 driver will return a standard failure if the EEPROM checksum doesn't match and will continue in its execution with no additional side effects.
|
| 15/2 |
2026/08/09 16:35 |
tool |
codesearch-struct-layout |
0m
Args: map[ContextFile:drivers/net/ethernet/intel/e100.c Name:nic]
Results: map[Fields:[map[Name:msg_enable OffsetBits:0 SizeBits:32] map[Name:netdev OffsetBits:64 SizeBits:64] map[Name:pdev OffsetBits:128 SizeBits:64] map[Name:mdio_ctrl OffsetBits:192 SizeBits:64] map[Name:rxs OffsetBits:512 SizeBits:64] map[Name:rx_to_use OffsetBits:576 SizeBits:64] map[Name:rx_to_clean OffsetBits:640 SizeBits:64] map[Name:blank_rfd OffsetBits:704 SizeBits:128] map[Name:ru_running OffsetBits:832 SizeBits:32] map[Name:cb_lock OffsetBits:1024 SizeBits:512] map[Name:cmd_lock OffsetBits:1536 SizeBits:512] map[Name:csr OffsetBits:2048 SizeBits:64] map[Name:cuc_cmd OffsetBits:2112 SizeBits:32] map[Name:cbs_avail OffsetBits:2144 SizeBits:32] map[Name:napi OffsetBits:2176 SizeBits:4416] map[Name:cbs OffsetBits:6592 SizeBits:64] map[Name:cb_to_use OffsetBits:6656 SizeBits:64] map[Name:cb_to_send OffsetBits:6720 SizeBits:64] map[Name:cb_to_clean OffsetBits:6784 SizeBits:64] map[Name:tx_command OffsetBits:6848 SizeBits:16] map[Name:flags OffsetBits:7168 SizeBits:32] map[Name:mac OffsetBits:7200 SizeBits:32] map[Name:phy OffsetBits:7232 SizeBits:32] map[Name:params OffsetBits:7264 SizeBits:192] map[Name:watchdog OffsetBits:7488 SizeBits:640] map[Name:mii OffsetBits:8128 SizeBits:384] map[Name:tx_timeout_task OffsetBits:8512 SizeBits:576] map[Name:loopback OffsetBits:9088 SizeBits:32] map[Name:mem OffsetBits:9152 SizeBits:64] map[Name:dma_addr OffsetBits:9216 SizeBits:64] map[Name:cbs_pool OffsetBits:9280 SizeBits:64] map[Name:cbs_dma_addr OffsetBits:9344 SizeBits:64] map[Name:adaptive_ifs OffsetBits:9408 SizeBits:8] map[Name:tx_threshold OffsetBits:9416 SizeBits:8] map[Name:tx_frames OffsetBits:9440 SizeBits:32] map[Name:tx_collisions OffsetBits:9472 SizeBits:32] map[Name:tx_deferred OffsetBits:9504 SizeBits:32] map[Name:tx_single_collisions OffsetBits:9536 SizeBits:32] map[Name:tx_multiple_collisions OffsetBits:9568 SizeBits:32] map[Name:tx_fc_pause OffsetBits:9600 SizeBits:32] map[Name:tx_tco_frames OffsetBits:9632 SizeBits:32] map[Name:rx_fc_pause OffsetBits:9664 SizeBits:32] map[Name:rx_fc_unsupported OffsetBits:9696 SizeBits:32] map[Name:rx_tco_frames OffsetBits:9728 SizeBits:32] map[Name:rx_short_frame_errors OffsetBits:9760 SizeBits:32] map[Name:rx_over_length_errors OffsetBits:9792 SizeBits:32] map[Name:eeprom_wc OffsetBits:9824 SizeBits:16] map[Name:eeprom OffsetBits:9840 SizeBits:4096] map[Name:mdio_lock OffsetBits:13952 SizeBits:512] map[Name:fw OffsetBits:14464 SizeBits:64]]]
|
| 16/2 |
2026/08/09 16:35 |
llm |
expert |
0m
Model:
Tokens:
input: 14309
output: 42
thoughts: 3547
Thoughts: **Analysis of the e100 EEPROM Read Vulnerability**
Okay, so I've been digging into this shift-out-of-bounds bug in the `e100_eeprom_read` function within the Intel e100 network driver. It looks like the issue stems from how the `addr_len` variable, which dictates the EEPROM address length, is handled and can potentially be modified in an unexpected manner. Specifically, the underflow of `addr_len` within `e100_eeprom_read` leading to a shift-out-of-bounds operation in `cmd_addr_data` calculation is concerning, as well as the out-of-bounds shift that sets `nic->eeprom_wc`'s value.
Here's the breakdown: `e100_eeprom_load` first tries to determine the correct EEPROM address length. The key lies in the `e100_eeprom_read` function: it uses a bit of hardware interaction via `ioread8` on `nic->csr->eeprom_ctrl_lo`. The value read into `ctrl` can, under specific conditions (if `ctrl & eedo` is 0 and `i > 16`), cause `*addr_len` to be decremented. Since `addr_len` is initialized to 8, it is highly likely it can be decremented to a negative value when viewed as an integer. This is the crux, because, when cast to a `u16`, this `addr_len` becomes a large positive number like 65529. This is where the UB begins. The calculation `op_read << *addr_len` then results in a shift-out-of-bounds (UB). This, in turn, affects `nic->eeprom_wc = 1 << addr_len;`. The code assumes the compiler will mask, or take `addr_len` modulo 31 and shift, but this behavior is undefined. It also creates a potential for out-of-bounds writes because `nic->eeprom_wc` drives a loop that may write out of bounds on `nic->eeprom`.
If, for instance, `addr_len` becomes -7, when cast to `u16` it becomes 65529. The `1 << addr_len` calculation, even if it is modulo 31 (yielding 25), will truncate when cast to `u16`.
My hypothesis is that the hardware, or more realistically a malicious or emulated PCI device (since it is a PCI driver), can be crafted to return specific values in `ioread8` to trigger this `addr_len` underflow. It's a classic case of failing to sanitize input from a peripheral.
The good news, or bad depending on the perspective, is that the initial shift-out-of-bounds itself doesn't directly lead to immediate memory corruption from what I can see. It's an undefined behavior that could cause weird things to happen, but it does not appear exploitable in a predictable way. But given that `panic_on_warn` is set, a UBSAN warning here leads to a kernel panic, a denial of service. The value of `nic->eeprom_wc` does appear to be able to influence the size of an array access with `nic->eeprom`. If that were greater than 256, it would cause out of bounds memory corruption. However, with `addr_len` only being able to be decreased, it is highly likely `nic->eeprom_wc` will never exceed 128.
This bug is not directly exploitable by an unprivileged user because it requires the device to be malicious.
Essentially, this is a denial-of-service vulnerability triggered by a shift-out-of-bounds condition stemming from an underflow of an EEPROM address length variable, based on a read from a hardware register.
|
| 17/2 |
2026/08/09 16:36 |
tool |
codesearch-definition-source |
0m
Error:
Args: map[ContextFile:drivers/net/ethernet/intel/e100.c Name:op_read]
Results: map[SourceCode: SourceFile:]
|
| 18/2 |
2026/08/09 16:36 |
llm |
expert |
0m
Model:
Tokens:
input: 15217
output: 38
thoughts: 0
|
| 19/2 |
2026/08/09 16:36 |
tool |
grepper |
0m
Args: map[Expression:op_read PathPrefix:drivers/net/ethernet/intel/e100.c]
Results: map[Output:drivers/net/ethernet/intel/e100.c=344=enum eeprom_op {
drivers/net/ethernet/intel/e100.c-345- op_write = 0x05,
drivers/net/ethernet/intel/e100.c:346: op_read = 0x06,
drivers/net/ethernet/intel/e100.c-347- op_ewds = 0x10,
--
drivers/net/ethernet/intel/e100.c=721=static __le16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr)
--
drivers/net/ethernet/intel/e100.c-727-
drivers/net/ethernet/intel/e100.c:728: cmd_addr_data = ((op_read << *addr_len) | addr) << 16;
drivers/net/ethernet/intel/e100.c-729-
]
|
| 20/2 |
2026/08/09 16:36 |
llm |
expert |
0m
Model:
Tokens:
input: 29312
output: 61
thoughts: 1671
Thoughts: **Analysis of the e100 EEPROM Loading Vulnerability**
Okay, so I've been digging into this potential issue, and here's what I've found. The `op_read` value of `0x06` is a starting point. The line of code `cmd_addr_data = ((op_read << *addr_len) | addr) << 16;` is the crux of the problem. If `*addr_len` somehow becomes 65529, then the shift operation `op_read << 65529` results in a shift out of bounds which triggers a UBSAN warning. This is confirmed by the stack trace pointing to `e100_eeprom_load.cold+0x55/0x9e drivers/net/ethernet/intel/e100.c:768`, specifically, the line `nic->eeprom_wc = 1 << addr_len;`.
Now, the warning is specifically for `1 << addr_len`. Since `addr_len` is indeed 65529, this confirms my initial analysis. So what are the actual consequences?
If `nic->eeprom_wc` is zero, we get an invalid read from `nic->eeprom[-1]`. I confirmed that the physical layout is such that `eeprom[-1]` is actually `eeprom_wc` itself, meaning we are effectively reading back the zero value of `eeprom_wc`. Then it checks for `cpu_to_le16(0xBABA - 0) != 0`, which is true, and prints an "EEPROM corrupted" message before returning `-EAGAIN`. This whole operation, in essence, is safe. There's no memory corruption because we're reading from the structure itself, and there's no real out-of-bounds access that could cause a crash (unless `panic_on_warn` is set).
Therefore, the only real impact is the UBSAN warning, which will cause a panic if `panic_on_warn` is set, leading to a denial of service (DoS). In production, it would just fail to probe the device, leading to `-EAGAIN`. So a DoS is possible, but this isn't exploitable in a malicious sense.
Can `addr_len` be modified? I was thinking about cases where `ctrl & eedo` is 0 during the loop. If this happens for `i = 17`, then `*addr_len -= 1`, making `addr_len` become 7. The code will set `nic->eeprom_wc = 1 << 7 = 128`. This is completely valid, as it's below 256. What if `ctrl & eedo` is 0 for `i = 16`? The condition `i > 16` is false, so it won't be modified. `addr_len` changes only if `i` is greater than 16. That means `i-16` can have a value from 1 to 15. The amount of the decrement would be at most 15. The starting value is 8, so it can go down to -7. With the decrement of `addr_len` down to 0, or even -7, we are always working with safe values, 1 to 128, with `1 << addr_len`. We may see UB with `-1` to `-7`, but on x86, it would become zero after the cast. That means `nic->eeprom_wc` will always be <= 128 or 0. This confirms there will never be out-of-bounds writes.
I also considered other architectures. On ARM32 and ARM64, shifting by >= 32 could result in 0. Even if it results in something else, it is unlikely to be > 256. The compiler could optimize `1 << addr_len` assuming `addr_len` is between 0 and 31. Even if `addr_len` is 65529, the compiler would likely emit a shift instruction that masks the shift amount. Is there any architecture where `1 << 65529` results in a value > 256 when cast to `u16`? I did the math: 65529 % 32 = 25, so `1 << 25 = 33554432`. After casting to `u16` it will be 0. 65529 % 64 = 57, and `1 << 57` is already a very large number,
|
| 21/2 |
2026/08/09 16:36 |
tool |
codesearch-find-references |
0m
Args: map[ContextFile:drivers/net/ethernet/intel/e100.c IncludeSnippetLines:0 Name:e100_eeprom_load SourceTreePrefix:]
Results: map[References:[map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:e100_diag_test SourceFile:drivers/net/ethernet/intel/e100.c SourceLine:2616] map[ReferenceKind:calls ReferencingEntityKind:function ReferencingEntityName:e100_probe SourceFile:drivers/net/ethernet/intel/e100.c SourceLine:2921]] TruncatedOutput:false]
|
| 22/2 |
2026/08/09 16:36 |
llm |
expert |
0m
Model:
Tokens:
input: 16766
output: 45
thoughts: 5354
Thoughts: **Analysis of the e100 Driver EEPROM Read Vulnerability**
Okay, here's what I've got. The core issue is a shift-out-of-bounds in the `e100_eeprom_read` function, which is called from `e100_eeprom_load` during device probe. `e100_eeprom_read` takes a pointer to `addr_len`, a local variable initialized to 8, which determines how many bits to use for the EEPROM read address. The initial shift `op_read << addr_len` is fine because we start with 8. However, `e100_eeprom_read` *modifies* `addr_len` based on the hardware response.
The critical bit is the loop that reads from the EEPROM. If the hardware (or, potentially, a malicious actor manipulating the hardware state) returns a specific pattern, the code within the EEPROM read function can cause `addr_len` to be decremented by a substantial amount in `e100_eeprom_read`. Specifically, if `!(ctrl & eedo)` when `i > 16`, we get `*addr_len -= (i - 16)`. If that happens when `i` is 31, then `addr_len` becomes negative when it starts at 8. Since `addr_len` is an `u16`, this underflow results in a large positive value (65529).
This value for `addr_len` is then used in `nic->eeprom_wc = 1 << addr_len;`. This is where the shift-out-of-bounds vulnerability is triggered. Shifting 1 by 65529 bits results in an undefined behavior in C. The compiler will emit a left shift that is only performed against the remainder of the modulus operation with 32 if it is a 32-bit integer, and 64 if it is a 64-bit integer, which can result in a value of 0. This undefined behavior is caught by UBSAN, resulting in `__ubsan_handle_shift_out_of_bounds`.
Even though the loop later in `e100_eeprom_load` is not likely to write out of bounds (because `nic->eeprom_wc` being 0), the critical issue is that `nic->eeprom[nic->eeprom_wc - 1]` is accessed, when `nic->eeprom_wc` is 0. This becomes `nic->eeprom[-1]`, or `nic->eeprom[65535]`, which is an enormous out-of-bounds read past the end of the `nic` structure. The `nic->eeprom` array has a size of 256 `__le16`, so the array index of 65535 will cause a serious out-of-bounds read and can expose significant memory past the allocated struct.
The vulnerability stems from the fact that `addr_len` is *modified* in `e100_eeprom_read` and then used in a subsequent calculation and a crucial access to the `nic->eeprom` array. A malicious or misconfigured device could exploit this. Even though the shift out-of-bounds is just UB, and might be silently handled on most architectures, the resultant out-of-bounds read is very dangerous! The out-of-bounds write is the most serious consequence.
|
| 23/2 |
2026/08/09 16:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/ethernet/intel/e100.c Name:e100_probe]
Results: map[SourceCode:2830: static int e100_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
2831: {
2832: struct net_device *netdev;
2833: struct nic *nic;
2834: int err;
2835:
2836: if (!(netdev = alloc_etherdev(sizeof(struct nic))))
2837: return -ENOMEM;
2838:
2839: netdev->hw_features |= NETIF_F_RXFCS;
2840: netdev->priv_flags |= IFF_SUPP_NOFCS;
2841: netdev->hw_features |= NETIF_F_RXALL;
2842:
2843: netdev->netdev_ops = &e100_netdev_ops;
2844: netdev->ethtool_ops = &e100_ethtool_ops;
2845: netdev->watchdog_timeo = E100_WATCHDOG_PERIOD;
2846: strscpy(netdev->name, pci_name(pdev), sizeof(netdev->name));
2847:
2848: nic = netdev_priv(netdev);
2849: netif_napi_add_weight(netdev, &nic->napi, e100_poll, E100_NAPI_WEIGHT);
2850: nic->netdev = netdev;
2851: nic->pdev = pdev;
2852: nic->msg_enable = (1 << debug) - 1;
2853: nic->mdio_ctrl = mdio_ctrl_hw;
2854: pci_set_drvdata(pdev, netdev);
2855:
2856: if ((err = pci_enable_device(pdev))) {
2857: netif_err(nic, probe, nic->netdev, "Cannot enable PCI device, aborting\n");
2858: goto err_out_free_dev;
2859: }
2860:
2861: if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
2862: netif_err(nic, probe, nic->netdev, "Cannot find proper PCI device base address, aborting\n");
2863: err = -ENODEV;
2864: goto err_out_disable_pdev;
2865: }
2866:
2867: if ((err = pci_request_regions(pdev, DRV_NAME))) {
2868: netif_err(nic, probe, nic->netdev, "Cannot obtain PCI resources, aborting\n");
2869: goto err_out_disable_pdev;
2870: }
2871:
2872: if ((err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32)))) {
2873: netif_err(nic, probe, nic->netdev, "No usable DMA configuration, aborting\n");
2874: goto err_out_free_res;
2875: }
2876:
2877: SET_NETDEV_DEV(netdev, &pdev->dev);
2878:
2879: if (use_io)
2880: netif_info(nic, probe, nic->netdev, "using i/o access mode\n");
2881:
2882: nic->csr = pci_iomap(pdev, (use_io ? 1 : 0), sizeof(struct csr));
2883: if (!nic->csr) {
2884: netif_err(nic, probe, nic->netdev, "Cannot map device registers, aborting\n");
2885: err = -ENOMEM;
2886: goto err_out_free_res;
2887: }
2888:
2889: if (ent->driver_data)
2890: nic->flags |= ich;
2891: else
2892: nic->flags &= ~ich;
2893:
2894: e100_get_defaults(nic);
2895:
2896: /* D100 MAC doesn't allow rx of vlan packets with normal MTU */
2897: if (nic->mac < mac_82558_D101_A4)
2898: netdev->features |= NETIF_F_VLAN_CHALLENGED;
2899:
2900: /* locks must be initialized before calling hw_reset */
2901: spin_lock_init(&nic->cb_lock);
2902: spin_lock_init(&nic->cmd_lock);
2903: spin_lock_init(&nic->mdio_lock);
2904:
2905: /* Reset the device before pci_set_master() in case device is in some
2906: * funky state and has an interrupt pending - hint: we don't have the
2907: * interrupt handler registered yet. */
2908: e100_hw_reset(nic);
2909:
2910: pci_set_master(pdev);
2911:
2912: timer_setup(&nic->watchdog, e100_watchdog, 0);
2913:
2914: INIT_WORK(&nic->tx_timeout_task, e100_tx_timeout_task);
2915:
2916: if ((err = e100_alloc(nic))) {
2917: netif_err(nic, probe, nic->netdev, "Cannot alloc driver memory, aborting\n");
2918: goto err_out_iounmap;
2919: }
2920:
2921: if ((err = e100_eeprom_load(nic)))
2922: goto err_out_free;
2923:
2924: e100_phy_init(nic);
2925:
2926: eth_hw_addr_set(netdev, (u8 *)nic->eeprom);
2927: if (!is_valid_ether_addr(netdev->dev_addr)) {
2928: if (!eeprom_bad_csum_allow) {
2929: netif_err(nic, probe, nic->netdev, "Invalid MAC address from EEPROM, aborting\n");
2930: err = -EAGAIN;
2931: goto err_out_free;
2932: } else {
2933: netif_err(nic, probe, nic->netdev, "Invalid MAC address from EEPROM, you MUST configure one.\n");
2934: }
2935: }
2936:
2937: /* Wol magic packet can be enabled from eeprom */
2938: if ((nic->mac >= mac_82558_D101_A4) &&
2939: (le16_to_cpu(nic->eeprom[eeprom_id]) & eeprom_id_wol)) {
2940: nic->flags |= wol_magic;
2941: device_set_wakeup_enable(&pdev->dev, true);
2942: }
2943:
2944: /* ack any pending wake events, disable PME */
2945: pci_pme_active(pdev, false);
2946:
2947: strcpy(netdev->name, "eth%d");
2948: if ((err = register_netdev(netdev))) {
2949: netif_err(nic, probe, nic->netdev, "Cannot register net device, aborting\n");
2950: goto err_out_free;
2951: }
2952: nic->cbs_pool = dma_pool_create(netdev->name,
2953: &nic->pdev->dev,
2954: nic->params.cbs.max * sizeof(struct cb),
2955: sizeof(u32),
2956: 0);
2957: if (!nic->cbs_pool) {
2958: netif_err(nic, probe, nic->netdev, "Cannot create DMA pool, aborting\n");
2959: err = -ENOMEM;
2960: goto err_out_pool;
2961: }
2962: netif_info(nic, probe, nic->netdev,
2963: "addr 0x%llx, irq %d, MAC addr %pM\n",
2964: (unsigned long long)pci_resource_start(pdev, use_io ? 1 : 0),
2965: pdev->irq, netdev->dev_addr);
2966:
2967: return 0;
2968:
2969: err_out_pool:
2970: unregister_netdev(netdev);
2971: err_out_free:
2972: e100_free(nic);
2973: err_out_iounmap:
2974: pci_iounmap(pdev, nic->csr);
2975: err_out_free_res:
2976: pci_release_regions(pdev);
2977: err_out_disable_pdev:
2978: pci_disable_device(pdev);
2979: err_out_free_dev:
2980: free_netdev(netdev);
2981: return err;
2982: }
SourceFile:drivers/net/ethernet/intel/e100.c]
|
| 24/2 |
2026/08/09 16:37 |
llm |
expert |
0m
Model:
Tokens:
input: 20267
output: 48
thoughts: 0
|
| 25/2 |
2026/08/09 16:37 |
tool |
codesearch-definition-source |
0m
Args: map[ContextFile:drivers/net/ethernet/intel/e100.c Name:e100_eeprom_read]
Results: map[SourceCode: 720: /* General technique stolen from the eepro100 driver - very clever */
721: static __le16 e100_eeprom_read(struct nic *nic, u16 *addr_len, u16 addr)
722: {
723: u32 cmd_addr_data;
724: u16 data = 0;
725: u8 ctrl;
726: int i;
727:
728: cmd_addr_data = ((op_read << *addr_len) | addr) << 16;
729:
730: /* Chip select */
731: iowrite8(eecs | eesk, &nic->csr->eeprom_ctrl_lo);
732: e100_write_flush(nic); udelay(4);
733:
734: /* Bit-bang to read word from eeprom */
735: for (i = 31; i >= 0; i--) {
736: ctrl = (cmd_addr_data & (1 << i)) ? eecs | eedi : eecs;
737: iowrite8(ctrl, &nic->csr->eeprom_ctrl_lo);
738: e100_write_flush(nic); udelay(4);
739:
740: iowrite8(ctrl | eesk, &nic->csr->eeprom_ctrl_lo);
741: e100_write_flush(nic); udelay(4);
742:
743: /* Eeprom drives a dummy zero to EEDO after receiving
744: * complete address. Use this to adjust addr_len. */
745: ctrl = ioread8(&nic->csr->eeprom_ctrl_lo);
746: if (!(ctrl & eedo) && i > 16) {
747: *addr_len -= (i - 16);
748: i = 17;
749: }
750:
751: data = (data << 1) | (ctrl & eedo ? 1 : 0);
752: }
753:
754: /* Chip deselect */
755: iowrite8(0, &nic->csr->eeprom_ctrl_lo);
756: e100_write_flush(nic); udelay(4);
757:
758: return cpu_to_le16(data);
759: };
SourceFile:drivers/net/ethernet/intel/e100.c]
|
| 26/2 |
2026/08/09 16:37 |
llm |
expert |
1m
Model:
Tokens:
input: 21053
output: 1119
thoughts: 6934
Thoughts: **Analysis of the e100 Driver EEPROM Load Vulnerability**
Okay, here's the deal: I've been digging into this UBSAN shift-out-of-bounds report within the `e100_eeprom_load` function, called during the e100 network driver's probe. The core issue boils down to a potentially malicious or faulty PCI device manipulating the EEPROM read process. Here's what's happening and my assessment:
The `e100_eeprom_load` function attempts to determine the length of the EEPROM data by reading a value. It initializes `addr_len` to 8 bits. It then calls `e100_eeprom_read` to read the EEPROM. In the `e100_eeprom_read` function, the EEPROM read involves bit-banging a command to the device and receiving data. Crucially, the code checks a flag (`eedo`) from the device. If the device, for some reason, asserts the wrong values for the `eedo` flag, the code reduces the `addr_len`. The problem is that a malicious device can make `addr_len` go negative (underflow) by returning a zero value too many times. Due to the code using a `u16` to hold `addr_len` the large negative value wraps to a very large positive number when it's used as the shift count in `1 << addr_len`.
This large value is then used in a shift operation, which results in undefined behavior. The immediate consequence is that the kernel enters undefined behavior, and UBSAN catches it, resulting in a panic. Let's look further.
The shifted value is ultimately truncated to a `u16` and assigned to `nic->eeprom_wc`, which determines how many words the driver should load from EEPROM. Since `nic->eeprom_wc` is a 16-bit value, it gets truncated which can lead to a `nic->eeprom_wc` value of 0. This results in the code attempting to access `nic->eeprom[-1]` which results in a crash, due to the read from `nic->eeprom[65535]` being an out-of-bounds read, leading to a kernel crash (page fault, a DoS).
Here's the problem in more detail. The code then attempts to validate a checksum on the EEPROM data. If `nic->eeprom_wc` is zero (because of the underflowed `addr_len`), it accesses an element at `nic->eeprom[65535]`. This is an out-of-bounds read, leading to a kernel panic. The value read isn't returned to userspace, but it's compared to a hardcoded checksum, potentially causing a crash if it doesn't match.
**Exploitation Potential and Attack Vectors:**
* **Denial of Service (DoS):** The primary consequence is a kernel crash (DoS). The out-of-bounds read can cause a page fault, leading to a kernel panic. This is not exploitable to escalate privileges or leak information. It can only be used to crash the system.
* **Privilege Escalation/Info Leak:** This vulnerability is *not* exploitable for privilege escalation or information leakage. The read value is only used in a comparison. There is no controlled memory write.
* **Attack Vectors:**
* **Peripheral Trigger:** A malicious PCI device, or a faulty device, could be the trigger.
* **VM Guest Trigger:** If the guest VM has a virtual e100 device, a malicious hypervisor or guest could send data causing an out-of-bounds read and kernel crash.
* **User/Network Trigger:** *Not* triggered by unprivileged users and is *not* a network-facing issue. It's confined to the PCI probe.
**Summary:**
In essence, a carefully crafted EEPROM response from a compromised or malfunctioning PCI device can cause a shift-out-of-bounds condition. This can lead to an out-of-bounds read, culminating in a kernel crash (Denial of Service). I've confirmed that this vulnerability poses no risk for privilege escalation or information leakage.
|
| 27/1 |
2026/08/09 16:38 |
action |
format-explanation |
0m
Results: map[Explanation:**Risk & Request Analysis**
The bug report indicates a Undefined Behavior Sanitizer (UBSAN)
shift-out-of-bounds warning in the `e100_eeprom_load` function, which is part of
the Intel PRO/100 Ethernet driver (`e100`). The warning triggers a kernel panic
because the kernel is configured with `panic_on_warn`.
Let's analyze the root cause by looking at the source code:
```c
764: u16 addr, addr_len = 8, checksum = 0;
765:
766: /* Try reading with an 8-bit addr len to discover actual addr
len */
767: e100_eeprom_read(nic, &addr_len, 0);
768: nic->eeprom_wc = 1 << addr_len;
```
The `e100_eeprom_read` function communicates with the hardware via MMIO to read
the EEPROM. During this process, it attempts to auto-detect the EEPROM address
length:
```c
743: /* Eeprom drives a dummy zero to EEDO after receiving
744: * complete address. Use this to adjust addr_len. */
745: ctrl = ioread8(&nic->csr->eeprom_ctrl_lo);
746: if (!(ctrl & eedo) && i > 16) {
747: *addr_len -= (i - 16);
748: i = 17;
749: }
```
If a malicious or malfunctioning PCI device returns a value where the `eedo` bit
is 0 early in the loop (e.g., when `i = 31`), `addr_len` (which starts at 8)
will underflow. For instance, `8 - (31 - 16) = -7`, which becomes `65529` since
`addr_len` is a `u16`.
Back in `e100_eeprom_load`, the code executes `1 << addr_len`. Shifting by a
value greater than or equal to the width of the type (32 bits for an integer
literal `1`) is undefined behavior, which is exactly what UBSAN catches.
If UBSAN is disabled (as in typical production builds), the CPU will mask the
shift count. On x86, `65529 & 31 = 25`, so the expression evaluates to `1 << 25`
(`33554432`). This value is then assigned to `nic->eeprom_wc`, which is a `u16`.
The truncation results in `nic->eeprom_wc = 0`.
This leads to a secondary bug. The loop that reads the EEPROM will be skipped
because `addr < 0` is false:
```c
770: for (addr = 0; addr < nic->eeprom_wc; addr++) { ... }
```
Then, the driver attempts to verify the checksum:
```c
778: if (cpu_to_le16(0xBABA - checksum) != nic->eeprom[nic->eeprom_wc
- 1]) {
```
Since `nic->eeprom_wc` is `0`, `nic->eeprom_wc - 1` underflows to `65535`. The
driver will perform an out-of-bounds read at `nic->eeprom[65535]`, which is
roughly 131KB past the end of the `nic` structure. This will likely hit an
unmapped memory page and cause a fatal page fault (kernel panic).
**Security Impact:**
* **Exploitable:** No. The bug results in an out-of-bounds read of 2 bytes, but
the read value is only used in a local comparison to validate a checksum. It is
never written to memory, used as a pointer, or leaked to userspace. Therefore,
it cannot be used for privilege escalation or information disclosure.
* **Denial of Service:** Yes. The out-of-bounds read will likely cause a page
fault, crashing the kernel. Even without the OOB read, the UBSAN panic causes a
DoS on kernels with `panic_on_warn` enabled.
* **Unprivileged / User Namespace:** No. The bug occurs in the PCI device probe
path (`e100_probe`). Triggering a PCI probe requires either physical access to
plug in a device or root privileges to bind a driver via sysfs. It is not
accessible to unprivileged users or within user namespaces.
* **Peripheral Trigger:** Yes. A malicious physical PCI/CardBus device plugged
into the system can return crafted MMIO responses to trigger this bug.
* **VM Guest Trigger:** Yes. A malicious hypervisor or a virtualized PCI device
(e.g., in QEMU) can trigger this in the guest kernel.]
|