linux
local_bh_enable() is called with interrupts disabled when HIGHMEM
Function udp_poll() acquires a spin lock via spin_lock_irq() before operating on the socket buffer. When a datagram is received, the checksum computation will call local_bh_enable() if HIGHMEM is enabled. But local_bh_enable() should be called with IRQs enabled, and thus the WARN(). Note that spin_lock_irq() takes a lock and disables interrupts.
Bug fixed by commit 208d89843b7
| Type | WeakAssertionViolation |
| Config | "HIGHMEM" (1st degree) |
| Fix-in | code |
| Location | kernel/ |
#include <stdlib.h>
#define local_irq_enable()
#define local_irq_disable()
#define local_bh_enable()
#define local_bh_disable()
static inline void spin_lock_irq()
{
local_irq_disable();
}
static inline void spin_unlock_irq()
{
local_irq_enable();
}
static inline void spin_lock_bh()
{
local_bh_disable();
}
static inline void spin_unlock_bh()
{
local_bh_enable();
}
static inline void kunmap_skb_frag()
{
#ifdef CONFIG_HIGHMEM
local_bh_enable(); // ERROR
#endif
}
unsigned int skb_checksum()
{
unsigned int csum = 0;
while (rand() % 2) {
if (rand() % 2) {
kunmap_skb_frag();
}
}
return csum;
}
static int udp_checksum_complete()
{
return skb_checksum();
}
unsigned int udp_poll()
{
unsigned int mask = 0;
spin_lock_irq();
while (rand() % 2) {
udp_checksum_complete();
}
spin_unlock_irq();
return mask;
}
int main(int argc, char** argv)
{
udp_poll();
return 0;
}
diff --git a/simple/208d898.c b/simple/208d898.c
--- a/simple/208d898.c
+++ b/simple/208d898.c
@@ -55,11 +55,11 @@
{
unsigned int mask = 0;
- spin_lock_irq();
+ spin_lock_bh();
while (rand() % 2) {
udp_checksum_complete();
}
- spin_unlock_irq();
+ spin_unlock_bh();
return mask;
}
#include <stdlib.h>
#define local_irq_enable()
#define local_irq_disable()
#define local_bh_enable()
#define local_bh_disable()
int main(int argc, char** argv)
{
// udp_poll();
unsigned int mask = 0;
local_bh_disable();
while (rand() % 2) {
unsigned int csum = 0;
while (rand() % 2) {
if (rand() % 2) {
#ifdef CONFIG_HIGHMEM
local_bh_enable(); // ERROR
#endif
}
}
}
local_bh_enable();
return 0;
}
. call net/ipv4/udp.c:1325: udp_poll()
. 1337: spin_lock_irq(&rcvq->lock);
// spin_lock_irq implies local_irq_disable()
. 1339: if (udp_checksum_complete(skb)) {
.. 766: udp_checksum_complete()
.. 769: __udp_checksum_complete(skb);
... call net/ipv4/udp.c:761:__udp_checksum_complete();
... 763: return (unsigned short)csum_fold(skb_checksum(skb, 0, skb->len, skb->csum));
.... call net/core/skbuff.c:1076: skb_checksum()
.... 1110: kunmap_skb_frag(vaddr);
..... call include/linux/skbuff.h:1155:kunmap_skb_frag()
..... [HIGHMEM] 138: local_bh_enable();
...... call kernel/softirq.c:138:local_bh_enable()
...... ERROR 140: WARN_ON(irqs_disabled())