KASAN: Linux Kernel Memory Error Detection Tool
KASAN stands for Kernel Address Sanitizer, a dynamic memory error detection tool primarily used to check for memory out-of-bounds access and use-after-free issues.
1. How to Enable KASAN
Add the following configuration to the kernel defconfig:
CONFIG_SLUB_DEBUG=y
CONFIG_SLUB_DEBUG_ON=y
CONFIG_KASAN=y
CONFIG_KASAN_INLINE=y
Since 1/8 of memory is used for shadow memory, the available memory decreases by 1/8. For example, with 8 GB of memory, after enabling KASAN, MemTotal is approximately 6.72 GB.
$ cat /proc/meminfo | grep MemTotal
MemTotal: 6723358 kB
2. Overview of KASAN Principles
KASAN uses extra memory to mark the state of usable memory. This extra memory is called shadow memory. KASEN reserves 1/8 of the total memory for shadow memory. It fills shadow memory with special magic numbers and checks the corresponding shadow memory on every load/store to determine if the operation is valid. Every 8 bytes of memory (8-byte aligned) are marked by 1 byte of shadow memory. If all 8 bytes are accessible, the shadow memory value is 0. If N consecutive bytes (1 ≤ N ≤ 7) are accessible, the shadow value is N. If all 8 bytes are invalid, the shadow value is negative.
For example:
adrp x0, 0xffffffc08821e810
mov w1, #0x5
bl __asan_store1
strb w1, [x0]
This assembly writes the value 5 to address 0xffffffc08821e810. When KASAN is enabled, the compiler automatically inserts the bl __asan_store1 instruction (shown in red). The __asan_store1 function checks whether the shadow memory for that address allows writing 1 byte. The actual memory access is the blue strb instruction.
3. How to Determine if a Memory Access is Valid Based on Shadow Memory
The kernel implements shadow memory checks mainly through __asan_load##size() and __asan_store##size() functions. How does KASAN determine validity based on the accessed address and its shadow memory value?
__asan_load##size / __asan_store##size
check_memory_region_inline
memory_is_poisoned
memory_is_poisoned_1
return unlikely(last_accessible_byte >= shadow_value)
memory_is_poisoned_2_4_8
memory_is_poisoned_16
- When accessing 8 bytes: if
*shadow_memory == 0, the access is valid; otherwise invalid. - When accessing N bytes (N = 1,2,4): if
(*shadow && *shadow > ((unsigned long)addr & 7) + N), the access is valid; otherwise invalid.
4. Shadow Memory for Buddy System Allocations
(1) Allocating Memory from Buddy System
- Step 1: Suppose allocating 4 pages from buddy system. The system first removes a memory block from the order=2 list.
- Step 2: Find the corresponding shadow memory using the relation:
Shadow_addr = (addr >> 3) + KASAN_SHADOW_OFFSET. Right-shifting by 3 bits because 8 bytes of memory correspond to 1 byte of shadow memory. - Step 3: Fill the shadow memory. For allocated 4 pages (all accessible), fill with 0.
(2) Freeing Memory to Buddy System
- Step 1: Free 4 pages from the order=2 list.
- Step 2: Find the shadow memory using the same relation.
- Step 3: Fill the corresponding shadow memory region (2 KB = 16 KB / 8) with 0xFF (
KASAN_FREE_PAGE).
5. Shadow Memory for SLUB Allocations
(1) Allocating Memory from SLUB Cache
- Step 1: Suppose allocating 20 bytes from kmalloc-32 cache. The remaining 12 bytes are marked as inaccessible by KASAN.
- Step 2: Find the shadow memory using the memory address mapping.
- Step 3: Fill the shadow memory with
00 00 04 FC. The meanings:- First two
00: bytes 0–15 are accessible. 04: bytes 16–23 only the first 4 bytes are accessible.FC: bytes 24–31 areKASAN_KMALLOC_REDZONE, inaccessible.
- First two
- Step 4: Save the allocation call stack.
- Step 5: If the redzone is accessed, KASAN reports an out-of-bounds bug.
(2) Freeing Memory from SLUB Cache
- Step 1: Free 20 bytes from kmalloc-32 cache.
- Step 2: Find the shadow memory address.
- Step 3: Fill the 4 bytes of shadow memory with
FB(KASAN_KMALLOC_FREE). - Step 4: Save the free call stack.
- Step 5: If memory corresponding to
FBis accessed, KASAN reports a use-after-free bug.
6. Shadow Memory for Other Allocation Types
Global variables and stack-allocated memory follow similar principles but with implementation differences.