# Address Sanitizer (ASan)
## What is "Address Sanitizer"?
Address Sanitizer is a tool for detecting memory errors such as stack overflows, buffer overflows, and memory leaks.
In userspace, we can use ASan to catch these issues.
In kernel space, similar tools are available — ==KASAN== for memory access bugs and ==kmemleak== for tracking memory leaks.
# About ASAN
## How to use it ?
1. set the compiler flag
```
-fsanitize=address
-fno-omit-frame-pointer
-fsanitize-recover=address
```
2. including the LSan, so when you enable ASan, the LSan also be enabled.
:::info
You can use
==ASAN_OPTIONS=detect_leaks=1== to enable it
==ASAN_OPTIONS=detect_leaks=0== to disenable it
:::
Here are others options:
* halt_on_error=0:non-stop when memory happens
* detect_stack_use_after_return=0/1
* malloc_context_size=15
* log_path=/path/to/logfile
* suppressions=$SUPP_FILE
4. running the code
```shell
LD_PRELOAD=/path/to/Asanlib.so ASAN_OPTIONS=<option1>=<value1>:<option2>=<value2> ./test_program
```
or add ==-static-libasan== for static library into the target program
## Example
* stack-use-after-return
```c
int *stack_para_ptr;
int testcall(int in, int in2)
{
int a = in;
int b = in2;
int c = a + b * 10;
stack_para_ptr = &a;
return c;
}
int main() {
testcall(1 ,20);
*stack_para_ptr = 10;
return 0;
}
```
The report will be:
```log
==3522954==ERROR: AddressSanitizer: stack-use-after-return on address 0x715db5100020 at pc 0x64c93ced2650 bp 0x7ffc4b7ffc70 sp 0x7ffc4b7ffc60
WRITE of size 4 at 0x715db5100020 thread T0
#0 0x64c93ced264f in main
... skip ...
This frame has 1 object(s):
[32, 36) 'a' (line 9) <== Memory access at offset 32 is inside this variable
... skip ...
Shadow bytes around the buggy address:
0x715db50fff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x715db5100000: f5 f5 f5 f5[f5]f5 f5 f5 00 00 00 00 00 00 00 00
0x715db5100080: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
```
This line show some information:
:::warning
[32, 36) 'a' (line 9) <== Memory access at offset 32 is inside this variable
:::
and the line 9 is : ```int a = in;```
----
* heap-buffer-overflow
```c
int heap_underflow()
{
char *buff = calloc(10, sizeof(char));
strcpy(buff, "heap_ovf");
printf("buff addr = %p\n", buff);
buff = buff - 10;
buff[0] = 'A';
return 0;
}
```
```log
==3526317==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x502000000006 at pc 0x641cdd9ca550 bp 0x7ffcbce525c0 sp 0x7ffcbce525b0
WRITE of size 1 at 0x502000000006 thread T0
#0 0x641cdd9ca54f in heap_overflow
... skip ...
0x502000000006 is located 10 bytes before 10-byte region [0x502000000010,0x50200000001a)
... skip ...
SUMMARY: AddressSanitizer: heap-buffer-overflow
Shadow bytes around the buggy address:
0x501fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x502000000000:[fa]fa 00 02 fa fa fa fa fa fa fa fa fa fa fa fa
0x502000000080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
```
also some information
:::warning
0x502000000006 is located 10 bytes before 10-byte region [0x502000000010,0x50200000001a)
:::
And we can use the shadow map
:::info
0x502000000000:[fa]fa 00 02 fa fa fa fa fa fa fa fa fa fa fa fa
:::
00 02 -> ```calloc(10, sizeof(char))```, means the space that be allocated.
[fa] fa -> ```ptr = ptr + 10 ```, means you try to access the "redzone", and the ==[fa]== is the position that you accessd.
fa: [means](#Symbol_meaning)
* heap-use-after-free
```c
int use_after_free()
{
char *buff = calloc(10, sizeof(char));
strcpy(buff, "uafree");
printf("buff addr = %p\n", buff);
free(buff);
buff[0] = 'A';
return 0;
}
```
```log
==3526317==ERROR: AddressSanitizer: heap-use-after-free on address 0x502000000030 at pc 0x641cdd9ca5f3 bp 0x7ffcbce525c0 sp 0x7ffcbce525b0
WRITE of size 1 at 0x502000000030 thread T0
#0 0x641cdd9ca5f2 in use_after_free (/home/roy/src_code/00.project/UML/Asan/Asan_test+0x15f2) (BuildId: bdceaface8cedeb2f775388e225102511187a8eb)
... skip ...
0x502000000030 is located 0 bytes inside of 10-byte region [0x502000000030,0x50200000003a)
... skip ...
SUMMARY: AddressSanitizer: heap-use-after-free (/home/roy/src_code/00.project/UML/Asan/Asan_test+0x15e8) (BuildId: 8f9944b2a6af4b51abc0edb95ac5cfcd72f3affe) in use_after_free
Shadow bytes around the buggy address:
0x501fffffff80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x502000000000: fa fa 00 02 fa fa[fd]fd fa fa fa fa fa fa fa fa
0x502000000080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
```
[fd]fd -> the address, which is allocated before, is freed.
Asan_test+0x15e8 -> the program be detected.
159c: 48 89 c7 mov %rax,%rdi
159f: b8 00 00 00 00 mov $0x0,%eax
15a4: e8 07 fc ff ff call 11b0 <printf@plt>
15a9: 48 8b 45 f8 mov -0x8(%rbp),%rax
15ad: 48 89 c7 mov %rax,%rdi
15b0: e8 db fb ff ff call 1190 <free@plt>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
free(buff)
15b5: 48 8b 45 f8 mov -0x8(%rbp),%rax
15b9: 48 89 c2 mov %rax,%rdx
15bc: 48 c1 ea 03 shr $0x3,%rdx
15c0: 48 81 c2 00 80 ff 7f add $0x7fff8000,%rdx
15c7: 0f b6 12 movzbl (%rdx),%edx
15ca: 84 d2 test %dl,%dl
15cc: 0f 95 c1 setne %cl
15cf: 48 89 c6 mov %rax,%rsi
15d2: 83 e6 07 and $0x7,%esi
15d5: 40 38 d6 cmp %dl,%sil
15d8: 0f 9d c2 setge %dl
15db: 21 ca and %ecx,%edx
15dd: 84 d2 test %dl,%dl
15df: 74 08 je 15e9 <use_after_free+0x95>
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
jump to check address is legal or not
15e1: 48 89 c7 mov %rax,%rdi
15e4: e8 87 fb ff ff call 1170 <__asan_report_store1_noabort@plt>
15e9: 48 8b 45 f8 mov -0x8(%rbp),%rax
15ed: c6 00 41 movb $0x41,(%rax)
---
* LeakSanitizer
```c
char *leak_ptr = NULL;
void leak_memory() {
leak_ptr = calloc(100, sizeof(char));
strcpy(leak_ptr, "LeakedMemory");
printf("Allocated but not freed: %s\n", leak_ptr);
leak_ptr = calloc(50, sizeof(char));
strcpy(leak_ptr, "LeakedMemory2");
}
```
let the leak_ptr leak in the memory, and we can see the report shows:
```log
=================================================================
==3522612==ERROR: LeakSanitizer: detected memory leaks
Direct leak of 100 byte(s) in 1 object(s) allocated from:
#0 0x70a6336fd340 in calloc ../../../../src/libsanitizer/asan/asan_malloc_linux.cpp:77
#1 0x5a6f42d6741d in leak_memory (/home/roy/src_code/00.project/UML/Asan/Asan_test+0x141d) (BuildId: c13b89ffe6483ec4c09bf94c7c2f3a5876ffd189)
#2 0x5a6f42d675e8 in main (/home/roy/src_code/00.project/UML/Asan/Asan_test+0x15e8) (BuildId: c13b89ffe6483ec4c09bf94c7c2f3a5876ffd189)
#3 0x70a63322a1c9 in __libc_start_call_main ../sysdeps/nptl/libc_start_call_main.h:58
#4 0x70a63322a28a in __libc_start_main_impl ../csu/libc-start.c:360
#5 0x5a6f42d671e4 in _start (/home/roy/src_code/00.project/UML/Asan/Asan_test+0x11e4) (BuildId: c13b89ffe6483ec4c09bf94c7c2f3a5876ffd189)
SUMMARY: AddressSanitizer: 100 byte(s) leaked in 1 allocation(s).
```
100 byte(s) leaked in 1 allocation(s).
## Symbol_meaning
```
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
```
# About KASAN
KASAN (Kernel Address Sanitizer) is similar to the userspace tool provided by glibc — it helps detect memory errors such as buffer overflows and underflows.
For example, consider the following code:
```c
char *ptr = kmalloc(16, GFP_KERNEL);
ptr = (ptr - 10);
ptr[0] = 'a';
```
This simple snippet demonstrates a pointer underflow, which KASAN can catch at runtime.
## How to use it ?
Enable config
```log
Location: │
│ -> Kernel hacking │
│ -> Memory Debugging │
│ (1) -> KASAN: dynamic memory safety error detector (KASAN [=y])
```
or set the config
```
CONFIG_KASAN=y
CONFIG_KASAN_GENERIC=y
CONFIG_KASAN_VMALLOC=y
```
Build moduole with KASAN
```
-fanitize=kernel-address
```
### Example
```c
static void kasan_heap_underflow(void)
{
char *ptr = kmalloc(16, GFP_KERNEL);
ptr = (ptr - 10);
ptr[0] = 'a';
char *dup = kstrdup(ptr, GFP_KERNEL);
printk(KERN_ALERT "dup str = %s\n", dup);
}
```
```
BUG: KASAN: slab-out-of-bounds in Kasan_init+0x99/0x1be [Kasan]
Write of size 1 at addr 0000000061e3d7d6 by task insmod/26
... skip ...
The buggy address belongs to the object at 0000000061e3d7b0
which belongs to the cache kmalloc-16 of size 16
The buggy address is located 26 bytes to the right of
allocated 12-byte region [0000000061e3d7b0, 0000000061e3d7bc)
The buggy address belongs to the physical page:
page: refcount:0 mapcount:0 mapping:0000000000000000 index:0x0 pfn:0x1e3d
flags: 0x0(zone=0)
page_type: f5(slab)
raw: 0000000000000000 0000000061401500 0000000000000122 0000000000000000
raw: 0000000000000000 0000000080550055 00000000f5000000
page dumped because: kasan: bad access detected
Memory state around the buggy address:
0000000061e3d680: fc fc 00 02 fc fc fc fc 00 04 fc fc fc fc 00 04
0000000061e3d700: fc fc fc fc 00 03 fc fc fc fc 00 03 fc fc fc fc
>0000000061e3d780: 00 04 fc fc fc fc 00 04 fc fc fc fc 00 00 fc fc
^
0000000061e3d800: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
0000000061e3d880: fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc fc
```
src code decode byu asm
```asm
94: 4c 89 e7 mov %r12,%rdi
97: ff d0 call *%rax
char *dup = kstrdup(ptr, GFP_KERNEL);
99: 4c 89 e7 mov %r12,%rdi
9c: be c0 0c 00 00 mov $0xcc0,%esi
ptr[0] = 'a';
a1: 41 c6 45 f6 61 movb $0x61,-0xa(%r13)
```
## How does the (K)ASAN work ?
Frist, Let we check [x86_64 vitrual memory map] (https://elixir.bootlin.com/linux/v6.16.4/source/Documentation/arch/x86/x86_64/mm.rst) (use 47bit)
```
+---0x0000000000000000--+
|======= user space ====|
|---0x00007fffffffffff--|
|======== HOlE =========|
|---0xffff800000000000--|
|===== Kernel space ====| -----\
|- .................. -| |
|-----------------------| |
| direct mapping of all | |
| physical memory | |
|-----------------------| |
| vmalloc/ioremap space | |
| (vmalloc_base) | |
|-----------------------| |
| virtual memory map |
| (vmemmap_base) | KSAN protect area
|---0xffffec0000000000--|
| KASAN shadow memory | |
|---0xfffffbffffffffff--| |
|- .................. -| |
|-----------------------| |
| kernel text mapping | |
|-----------------------| |
| module mapping space | |
+-----------------------+ -----/
```
The KASAN shadow memory in the kernel is located at address 0xffffec0000000000, and on x86_64 each allocation unit covers 8 (>> 3) bytes of real memory. So we know that every 8 bytes can coved by 1 KASAN address space.
Actually mapping will be ```KASAN shadow address = VM >> 3 + KASAN_SHADOW_OFFSET``` and the offset is [KASAN_SHADOW_OFFSET](https://elixir.bootlin.com/linux/v6.16.3/source/arch/x86/Kconfig#L407) : 0xdffffc0000000000
:::info
KASAN shadow offset = KASAN start - Kernal start >> 3
0xdffffc0000000000 = 0xffffec0000000000 - (0xffff800000000000 >> 3)
:::
:::success
[KASAN_SHADOW_START](https://elixir.bootlin.com/linux/v6.16.3/source/arch/x86/include/asm/kasan.h#L15): it is differnet with 56bit or 47bit (5 layers PT or 4 layers PT)
:::
### mechaism
There are 2 things we should know,
1. How does the KASAN know that the memory access is valid or not?
* By the previous explanation, we know that KASNA use 1 shadow byte maps to 8 bytes address, and Each 8-byte block has 8 possible states ==(1–8 bytes valid)==.
For exmaple, if kmalloc(21, [GFP_KERNEL](https://docs.kernel.org/core-api/memory-allocation.html)), 21 bytes are split into (16 + 5),
So (K)ASAN defines:
==the shadow byte is 0 -> all 8 bytes are valid
the shadow byte is 1~7 -> 1~7 byte(s) are valid==
kmalloc(21, GFP_KERNEL) -> SHADOW bytes: 0x 00 00 05
That the reason why you see the ```[fa]fa 00 02 fa fa``` messages from KASAN
2. How does the KASAN know which instruction needs to check?
* This mechanism relies on the compiler. When the compiler parses the code and sees a memory access instruction, it inserts =="__asan_load##size() or __asan_store##size()"==, and let ASAN verify whether the memory access is valid before continuing execution.
### allocate memory
1. Get a page to the SLUB allocator
When allocating a new memory page from the page allocator, the page address is mapped to the KASAN shadow memory, and the entire page is initially poisoned (0x00).
2. From SLUB allocator
When the page is handed over to the SLUB allocator, it calls [Kasan_poison_slab()](https://elixir.bootlin.com/linux/v6.16.4/source/mm/slub.c#L2642) to mark the [SLAB REDZONE](https://elixir.bootlin.com/linux/v6.16.4/source/mm/kasan/common.c#L152).
3. Allocate an object from the SLUB allocator
When an object is allocated from the slab, its objest's shadow bytes are poisoned to KASAN_SLAB_REDZONE. The shadow bytes corresponding to the allocated memory are unpoisoned (0x00)
here is the allocation flow
```
Page Allocator
|
| Allocate new page
|
Page mapped to KASAN shadow memory
|
| Poison entire page (0x00)
|
SLUB Allocator
|
| Dispatch page
|
kasan_poison_slab()
|
| Poison SLAB redzones
|
Allocate object from slab
|
| Poison redzone shadow bytes
| Unpoison allocated object (0x00)
|
Object ready for use with KASAN protection
```
Here is the kmalloc call flow
```
#define kmalloc(...) alloc_hooks(kmalloc_noprof(__VA_ARGS__))
kmalloc_noprof
|
+----------------------------------------+
| |
if (__builtin_constant_p(size) && size) |
__kmalloc_cache_noprof __kmalloc_noprof
| |
| __do_kmalloc_node
| |
+----------------------------------------+
|
slab_alloc_node
|
kasan_kmalloc
```
```
slab_alloc_node
|
(set the KASAN_SLAB_REDZONE to shadow bytes)
obj = __slab_alloc_node
|
(unpoison (set to 0x00) the kalloc size)
slab_post_alloc_hook(.., &obj, init, size)
|
kasan_slab_alloc
|
unpoison_slab_object
```
```
kasan_kmalloc
|
__kasan_kmalloc(s, object, size, flags)
|
(set the redzone)
poison_kmalloc_redzone
```
****
> [kmalloc_noprof](https://elixir.bootlin.com/linux/v6.16.4/source/include/linux/slab.h#L896)
[__kmalloc_cache_noprof](https://elixir.bootlin.com/linux/v6.16.4/source/mm/slub.c#L4357)
****
> [__kmalloc_noprof](https://elixir.bootlin.com/linux/v6.16.4/source/mm/slub.c#L4343)
[__do_kmalloc_node](https://elixir.bootlin.com/linux/v6.16.4/source/mm/slub.c#L4314)
[__kmalloc_cache_node_noprof](https://elixir.bootlin.com/linux/v6.16.4/source/mm/slub.c#L4369)
****
>[slab_post_alloc_hook](https://elixir.bootlin.com/linux/v6.16.4/source/mm/slub.c#L4108)
[__kasan_slab_alloc](https://elixir.bootlin.com/linux/v6.16.4/source/mm/kasan/common.c#L322)
****
> [kasan_kmalloc](https://elixir.bootlin.com/linux/v6.16.4/source/include/linux/kasan.h#L256)
[__kasan_kmalloc](https://elixir.bootlin.com/linux/v6.16.4/source/mm/kasan/common.c#L381)
[poison_kmalloc_redzone](https://elixir.bootlin.com/linux/v6.16.4/source/mm/kasan/common.c#L394)(return object)
****
> KASAN_SHADOW_SCALE_SHIFT 3
KASAN_GRANULE_SIZE = 1 << KASAN_SHADOW_SCALE_SHIFT
KASAN_GRANULE_MASK = (KASAN_GRANULE_SIZE - 1) = 7
[#define arch_kasan_get_tag(addr) 0](https://elixir.bootlin.com/linux/v6.16.4/source/mm/kasan/kasan.h#L420)
[kasan_unpoison](https://elixir.bootlin.com/linux/v6.16.4/source/mm/kasan/shadow.c#L163)
***
### free memory
1. Ojbect to Slub
The shadow bytes corresponding to the allocated memory are poisoned to KASAN_SLAB_FREE
* Slub to page
When freeing the page, it will be poison to "FF"
### KASAN define
```c
#define KASAN_PAGE_FREE 0xFF /* freed page */
#define KASAN_PAGE_REDZONE 0xFE /* redzone for kmalloc_large allocation */
#define KASAN_SLAB_REDZONE 0xFC /* redzone for slab object */
#define KASAN_SLAB_FREE 0xFB /* freed slab object */
#define KASAN_VMALLOC_INVALID 0xF8 /* inaccessible space in vmap area */
#define KASAN_SLAB_FREE_META 0xFA /* freed slab object with free meta */
#define KASAN_GLOBAL_REDZONE 0xF9 /* redzone for global variable */
/* Stack redzone shadow values. Compiler ABI, do not change. */
#define KASAN_STACK_LEFT 0xF1
#define KASAN_STACK_MID 0xF2
#define KASAN_STACK_RIGHT 0xF3
#define KASAN_STACK_PARTIAL 0xF4
/* alloca redzone shadow values. */
#define KASAN_ALLOCA_LEFT 0xCA
#define KASAN_ALLOCA_RIGHT 0xCB
```
```c
#define DEFINE_ASAN_SET_SHADOW(byte) \
void __asan_set_shadow_##byte(const void *addr, ssize_t size) \
{ \
__memset((void *)addr, 0x##byte, size); \
} \
EXPORT_SYMBOL(__asan_set_shadow_##byte)
DEFINE_ASAN_SET_SHADOW(00);
DEFINE_ASAN_SET_SHADOW(f1);
DEFINE_ASAN_SET_SHADOW(f2);
DEFINE_ASAN_SET_SHADOW(f3);
DEFINE_ASAN_SET_SHADOW(f5);
DEFINE_ASAN_SET_SHADOW(f8);
```
## ref
* [ __init kasan_early_init(void)](https://elixir.bootlin.com/linux/v6.16.4/source/arch/x86/mm/kasan_init_64.c#L287)
* [kaasn.h](https://elixir.bootlin.com/linux/v6.14/source/mm/kasan/kasan.h#L143)
* [Clang Asan](https://clang.llvm.org/docs/AddressSanitizer.html)
* [Address Sanitizer](https://github.com/google/sanitizers/wiki/AddressSanitizer)
* [Kernel KASAN document](https://docs.kernel.org/dev-tools/kasan.html)