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())