drivers/net/ethernet/packetengines/hamachi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
During the hardware initialization phase in hamachi_init_one(), the driver
reads the PCIClkMeas register to calculate the PCI bus frequency.
The current code attempts to prevent a divide-by-zero error using a ternary
operator: `i ? 2000/(i&0x7f) : 0`. However, this check is flawed. The highest
bit of `i` (0x80) acts as a ready flag. If unreliable hardware or a malicious
virtual device returns a value where the ready bit is set but the lower 7 bits
are zero (e.g., 0x80), the condition `i` evaluates to true, but `(i & 0x7f)`
evaluates to 0. This results in a fatal divide-by-zero exception.
This bug was discovered during an automated virtual device fuzzing campaign
testing the hardware-software trust boundary. When the hardware returns 0x80,
it bypassed the readiness while-loop but triggered the divide error. In our
tests, this panic interrupted the module loading process, further triggering
a KASAN slab-out-of-bounds in the module error path, and ultimately leading
to a multi-core soft lockup and RCU stall.
This patch fixes the issue by explicitly checking the divisor `(i & 0x7f)`
instead of the entire register value `i` before performing the division.
Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
---
drivers/net/ethernet/packetengines/hamachi.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c
index b0de7e9f12a5..1d7206dd18fd 100644
--- a/drivers/net/ethernet/packetengines/hamachi.c
+++ b/drivers/net/ethernet/packetengines/hamachi.c
@@ -748,7 +748,7 @@ static int hamachi_init_one(struct pci_dev *pdev,
printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers "
"%2.2x, LPA %4.4x.\n",
dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32,
- i ? 2000/(i&0x7f) : 0, i&0x7f, (int)readb(ioaddr + VirtualJumpers),
+ (i & 0x7f) ? 2000 / (i & 0x7f) : 0, i & 0x7f, (int)readb(ioaddr + VirtualJumpers),
readw(ioaddr + ANLinkPartnerAbility));
if (chip_tbl[hmp->chip_id].flags & CanHaveMII) {
--
2.34.1
On Sat, Apr 18, 2026 at 08:18:04PM +0800, Mingyu Wang wrote: > During the hardware initialization phase in hamachi_init_one(), the driver > reads the PCIClkMeas register to calculate the PCI bus frequency. > > The current code attempts to prevent a divide-by-zero error using a ternary > operator: `i ? 2000/(i&0x7f) : 0`. However, this check is flawed. The highest > bit of `i` (0x80) acts as a ready flag. If unreliable hardware or a malicious > virtual device returns a value where the ready bit is set but the lower 7 bits > are zero (e.g., 0x80), the condition `i` evaluates to true, but `(i & 0x7f)` > evaluates to 0. This results in a fatal divide-by-zero exception. > > This bug was discovered during an automated virtual device fuzzing campaign > testing the hardware-software trust boundary. When the hardware returns 0x80, > it bypassed the readiness while-loop but triggered the divide error. In our > tests, this panic interrupted the module loading process, further triggering > a KASAN slab-out-of-bounds in the module error path, and ultimately leading > to a multi-core soft lockup and RCU stall. Isn't that a good result of somebody trying to use emulated hardware with bad behaviour? The machine grinds to a halt? So it is not exploitable. What happens with your patch in place? How are you reporting the hardware is attacking the machine, and the hardware should not be trusted? Andrew
Hi Andrew,
Regarding the "halt" and exploitability:
While a clean panic on bad hardware is a safe defense, this is an unhandled exception that breaks the module error-handling path. It causes memory corruption rather than a safe halt, which is dangerous for hot-pluggable VMs. The trace shows a KASAN out-of-bounds read and a subsequent soft lockup:
==================================================================
BUG: KASAN: slab-out-of-bounds in idempotent_init_module+0x54a/0x620
Read of size 8 at addr ffff8881191e7e08 by task systemd-udevd/169
...
watchdog: BUG: soft lockup - CPU#2 stuck for 245s! [systemd-udevd:174]
RIP: 0010:queued_spin_lock_slowpath+0x243/0xb90
==================================================================
Regarding reporting the attack:
You are entirely correct that the v1 patch passively masked the math error. This v2 patch actively detects the untrusted hardware state ((i & 0x80) && !(i & 0x7f)), loudly reports it via dev_err(), and safely aborts the probe with -EIO.
Signed-off-by: Mingyu Wang <25181214217@stu.xidian.edu.cn>
---
Changes in v2:
- Addressed feedback from Andrew Lunn regarding trusting bad hardware.
- Added explicit detection for the untrusted hardware state.
- Added dev_err() to report the attack and aborted the probe with -EIO to prevent error-path memory corruption.
drivers/net/ethernet/packetengines/hamachi.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/net/ethernet/packetengines/hamachi.c b/drivers/net/ethernet/packetengines/hamachi.c
index b0de7e9f12a5..2f487db2fb33 100644
--- a/drivers/net/ethernet/packetengines/hamachi.c
+++ b/drivers/net/ethernet/packetengines/hamachi.c
@@ -745,6 +745,14 @@ static int hamachi_init_one(struct pci_dev *pdev,
dev->name, chip_tbl[chip_id].name, readl(ioaddr + ChipRev),
ioaddr, dev->dev_addr, irq);
i = readb(ioaddr + PCIClkMeas);
+
+ if ((i & 0x80) && !(i & 0x7f)) {
+ dev_err(&pdev->dev, "Invalid PCIClkMeas value (0x%02x), hardware untrusted.\n", i);
+ unregister_netdev(dev);
+ ret = -EIO;
+ goto err_out_unmap_rx;
+ }
+
printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers "
"%2.2x, LPA %4.4x.\n",
dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32,
--
2.34.1
2026-04-18 23:34:26 "Andrew Lunn" <andrew@lunn.ch> 写道:
> On Sat, Apr 18, 2026 at 08:18:04PM +0800, Mingyu Wang wrote:
> > During the hardware initialization phase in hamachi_init_one(), the driver
> > reads the PCIClkMeas register to calculate the PCI bus frequency.
> >
> > The current code attempts to prevent a divide-by-zero error using a ternary
> > operator: `i ? 2000/(i&0x7f) : 0`. However, this check is flawed. The highest
> > bit of `i` (0x80) acts as a ready flag. If unreliable hardware or a malicious
> > virtual device returns a value where the ready bit is set but the lower 7 bits
> > are zero (e.g., 0x80), the condition `i` evaluates to true, but `(i & 0x7f)`
> > evaluates to 0. This results in a fatal divide-by-zero exception.
> >
> > This bug was discovered during an automated virtual device fuzzing campaign
> > testing the hardware-software trust boundary. When the hardware returns 0x80,
> > it bypassed the readiness while-loop but triggered the divide error. In our
> > tests, this panic interrupted the module loading process, further triggering
> > a KASAN slab-out-of-bounds in the module error path, and ultimately leading
> > to a multi-core soft lockup and RCU stall.
>
> Isn't that a good result of somebody trying to use emulated hardware
> with bad behaviour? The machine grinds to a halt? So it is not
> exploitable.
>
> What happens with your patch in place? How are you reporting the
> hardware is attacking the machine, and the hardware should not be
> trusted?
>
> Andrew
So what is your security model here? How did i get the hacked card
into the machine? If i have physical access, isn't it already too
late?
As far as i can see, this devices does not load firmware. Even if it
did load firmware from /lib/firmware, i need root to put a hacked
version there.
The PacketEngine Hamachi was in use around year 2000. It needs a PCI
slot. Not PCIe but PCI. Do they even exist any more? Both the card and
a machine with a PCI slot?
Maybe, rather than fix this /0, you should look around and see if you
can find any indication this hardware is still in use, and if not,
submit a patch removing the whole driver?
> +++ b/drivers/net/ethernet/packetengines/hamachi.c
> @@ -745,6 +745,14 @@ static int hamachi_init_one(struct pci_dev *pdev,
> dev->name, chip_tbl[chip_id].name, readl(ioaddr + ChipRev),
> ioaddr, dev->dev_addr, irq);
> i = readb(ioaddr + PCIClkMeas);
> +
> + if ((i & 0x80) && !(i & 0x7f)) {
> + dev_err(&pdev->dev, "Invalid PCIClkMeas value (0x%02x), hardware untrusted.\n", i);
> + unregister_netdev(dev);
> + ret = -EIO;
> + goto err_out_unmap_rx;
> + }
> +
> printk(KERN_INFO "%s: %d-bit %d Mhz PCI bus (%d), Virtual Jumpers "
> "%2.2x, LPA %4.4x.\n",
> dev->name, readw(ioaddr + MiscStatus) & 1 ? 64 : 32,
If you do decide it is worth keeping the driver, please add another
label at the end of the function, and do the unregister_netdev there.
Could i also suggest you consider more likely scenarios. It is much
easier to produce a hacked USB dongle than a PCI card. Users plug in
USB dongles without thinking, where as few users plug in a PCI
card. You are more likely to fix security problems which affect real
systems if you look at USB devices. I would also suggest that / 0 is
not that important. But can you trigger a buffer overrun?
Andrew
---
pw-bot: cr
© 2016 - 2026 Red Hat, Inc.