In the Linux kernel, the following vulnerability has been resolved:
net: ks8851: Handle softirqs at the end of IRQ thread to fix hang
The ks8851irq() thread may call ks8851rxpkts() in case there are any packets in the MAC FIFO, which calls netifrx(). This netifrx() implementation is guarded by localbhdisable() and localbhenable(). The localbhenable() may call dosoftirq() to run softirqs in case any are pending. One of the softirqs is netrxaction, which ultimately reaches the driver .start_xmit callback. If that happens, the system hangs. The entire call chain is below:
ks8851startxmitpar from netdevstartxmit netdevstartxmit from devhardstartxmit devhardstartxmit from schdirectxmit schdirectxmit from _devqueuexmit _devqueuexmit from _neighupdate _neighupdate from neighupdate neighupdate from arpprocess.constprop.0 arpprocess.constprop.0 from _netifreceiveskbonecore _netifreceiveskbonecore from processbacklog processbacklog from _napipoll.constprop.0 _napipoll.constprop.0 from netrxaction netrxaction from _dosoftirq _dosoftirq from callwithstack callwithstack from dosoftirq dosoftirq from _localbhenableip _localbhenableip from netifrx netifrx from ks8851irq ks8851irq from irqthreadfn irqthreadfn from irqthread irqthread from kthread kthread from retfrom_fork
The hang happens because ks8851irq() first locks a spinlock in ks8851par.c ks8851lockpar() spinlockirqsave(&ksp->lock, ...) and with that spinlock locked, calls netifrx(). Once the execution reaches ks8851startxmitpar(), it calls ks8851lockpar() again which attempts to claim the already locked spinlock again, and the hang happens.
Move the dosoftirq() call outside of the spinlock protected section of ks8851irq() by disabling BHs around the entire spinlock protected section of ks8851irq() handler. Place localbhenable() outside of the spinlock protected section, so that it can trigger dosoftirq() without the ks8851par.c ks8851lockpar() spinlock being held, and safely call ks8851startxmitpar() without attempting to lock the already locked spinlock.
Since ks8851irq() is protected by localbhdisable()/localbhenable() now, replace netifrx() with _netifrx() which is not duplicating the localbhdisable()/localbhenable() calls.