GFP CONTEXT BUG EXERCISE
Axiomatic Derivation | Linux Kernel 6.14.0-37-generic | x86_64
MACHINE DATA (AXIOMS)
Kernel: 6.14.0-37-generic
PAGE_SIZE: 4096
GFP_KERNEL: 0xCC0
GFP_ATOMIC: 0x0 (simplified, actually has other bits)
PART 1: PREEMPT_COUNT BITS (DO BY HAND)
- AXIOM: preempt_count() returns 32-bit integer.
- AXIOM: Bits 0-7 = preemption depth. Range: 0-255.
- AXIOM: Bits 8-15 = softirq count.
- AXIOM: Bits 16-19 = hardirq count.
- AXIOM: Bit 20 = NMI.
- CALCULATE: Softirq mask = bits 8-15 = 0x___ (fill hex).
- CALCULATE: Hardirq mask = bits 16-19 = 0x___ (fill hex).
-
| CALCULATE: Interrupt mask = softirq |
hardirq |
NMI = 0x___ (fill hex). |
- FORMULA: in_interrupt() = (preempt_count() & interrupt_mask) != 0.
PART 2: CONTEXT DETERMINATION (DO BY HAND)
- SCENARIO A: preempt_count() = 0x00000000.
- CALCULATE: 0x00000000 & 0x1FFF00 = ___.
- RESULT: in_interrupt() = ___ (true/false).
- CONTEXT: ___ context (process/interrupt).
-
GFP: Can use GFP_KERNEL? ___ (yes/no).
- SCENARIO B: preempt_count() = 0x00000100.
- CALCULATE: 0x00000100 & 0x1FFF00 = ___.
- RESULT: in_interrupt() = ___ (true/false).
- CONTEXT: ___ context (process/interrupt/softirq).
-
GFP: Can use GFP_KERNEL? ___ (yes/no).
- SCENARIO C: preempt_count() = 0x00010000.
- CALCULATE: 0x00010000 & 0x1FFF00 = ___.
- RESULT: in_interrupt() = ___ (true/false).
- CONTEXT: ___ context (hardirq).
- GFP: Can use GFP_KERNEL? ___ (yes/no).
PART 3: GFP FLAGS BITS (DO BY HAND)
- AXIOM: ___GFP_IO_BIT = 6.
- AXIOM: ___GFP_FS_BIT = 7.
- AXIOM: ___GFP_DIRECT_RECLAIM_BIT = 10.
- AXIOM: ___GFP_KSWAPD_RECLAIM_BIT = 11.
- CALCULATE: _GFP_IO = 1 « 6 = __.
- CALCULATE: _GFP_FS = 1 « 7 = __.
- CALCULATE: _GFP_DIRECT_RECLAIM = 1 « 10 = __.
- CALCULATE: _GFP_KSWAPD_RECLAIM = 1 « 11 = __.
-
| CALCULATE: __GFP_RECLAIM = __GFP_DIRECT_RECLAIM |
_GFP_KSWAPD_RECLAIM = __ |
__ = __. |
-
| CALCULATE: GFP_KERNEL = __GFP_RECLAIM |
__GFP_IO |
_GFP_FS = __ |
___ |
__ = __. |
- VERIFY: GFP_KERNEL = 0xCC0? ___ (✓/✗).
PART 4: WHY GFP_KERNEL FAILS IN INTERRUPT
- PROBLEM: alloc_page(GFP_KERNEL) in softirq.
- GFP_KERNEL contains _GFP_DIRECT_RECLAIM = bit 10 = __.
- __GFP_DIRECT_RECLAIM means: kernel MAY call schedule() to wait for pages.
- schedule() checks: in_atomic()?
- If in_atomic() = true AND schedule() called → BUG_ON(“scheduling while atomic”).
- Timer callback runs in softirq → preempt_count() has bits 8-15 set.
- in_interrupt() = true → atomic context.
- schedule() called → BUG.
PART 5: TRACE TIMER FLOW
- module_init() runs in process context.
- preempt_count() = 0x00000000 → in_interrupt() = false.
- GFP_KERNEL safe here.
- timer_setup() arms timer for 100ms later.
- 100ms passes…
- Timer fires → timer_callback() runs.
- Now in softirq context → preempt_count() = 0x00000100 (example).
- alloc_page(GFP_KERNEL) → checks if can reclaim.
- __GFP_DIRECT_RECLAIM set → tries to sleep.
- schedule() → BUG_ON(in_atomic()).
PART 6: VERIFICATION COMMANDS
- BEFORE: sudo insmod gfp_context_bug.ko
- WAIT: 100ms for timer to fire.
-
| CHECK: sudo dmesg |
grep GFP_CONTEXT |
- EXPECTED OUTPUT:
- init: in_interrupt()=0, preempt_count()=0x0
- timer_callback: in_interrupt()=1, preempt_count()=0x??? (non-zero)
- alloc_SUCCESS or alloc_FAILED (depending on memory pressure)
- IF BUG LINE UNCOMMENTED: dmesg shows “BUG: scheduling while atomic” or similar.
- sudo rmmod gfp_context_bug
PART 7: FAILURE PREDICTIONS
F1. User uses GFP_KERNEL everywhere → works in init, crashes in timer.
F2. User confuses preempt_count bits → wrong mask calculation.
F3. User forgets timer runs async → expects sequential execution.
F4. User doesn’t check alloc return → NULL dereference in interrupt = hard crash.
F5. User uses GFP_ATOMIC in process context → works but wastes emergency reserves.
ANSWERS (FILL AFTER TRYING)
06. 0xFF00 (bits 8-15)
07. 0xF0000 (bits 16-19)
08. 0x1FFF00 (all interrupt bits)
11. 0x00000000
12. false
13. process
14. yes
16. 0x00000100
17. true
18. softirq
19. no
21. 0x00010000
22. true
23. hardirq
24. no
29. 64 = 0x40
30. 128 = 0x80
31. 1024 = 0x400
32. 2048 = 0x800
33. 0x400 | 0x800 = 0xC00
34. 0xC00 | 0x40 | 0x80 = 0xCC0
35. ✓
37. 0x400