Overview
In this bridge lecture to the kernel unit, we use our knowledge of assembly to investigate sanitizers, buffer overflow attacks, and buffer overflow preventions.
Full lecture notes on assembly — Textbook readings
Questions?
Sanitizers
- How do they work?
- Let’s look at some generated assembly!
ubsignedinc.cc
Without sanitization
4010ea: 48 8d 35 13 0f 00 00 lea 0xf13(%rip),%rsi ; "%d > %d - assertion passed\n"
4010f1: bf 01 00 00 00 mov $0x1,%edi
4010f6: 48 89 c1 mov %rax,%rcx
4010f9: 8d 50 01 lea 0x1(%rax),%edx
4010fc: 31 c0 xor %eax,%eax
4010fe: e8 9d ff ff ff callq 4010a0 <__printf_chk@plt>
With undefined behavior sanitization
40126e: 41 89 c4 mov %eax,%r12d
401271: 48 89 c1 mov %rax,%rcx
401274: 41 83 c4 01 add $0x1,%r12d
401278: 70 3b jo 4012b5 <main+0x95>
40127a: 44 39 e1 cmp %r12d,%ecx
40127d: 0f 8d a7 00 00 00 jge 40132a <main+0x10a>
401283: 44 89 e2 mov %r12d,%edx
401286: 48 8d 35 b9 0d 00 00 lea 0xdb9(%rip),%rsi # 402046 <_IO_stdin_used+0x46>
40128d: bf 01 00 00 00 mov $0x1,%edi
401292: 31 c0 xor %eax,%eax
401294: e8 47 fe ff ff callq 4010e0 <__printf_chk@plt>
How the sanitizer works
- Consider operation (signed addition) that can cause undefined behavior
- Sanitizer off
- Compiler generates most efficient instruction (
lea 0x1(%rax),%edx
) - Compiler’s assumptions allow it to remove assertion
- Compiler generates most efficient instruction (
- Sanitizer on
- Compiler checks for overflow (
jo
), jumps to error path - Requires use of an arithmetic instruction (
add
, notlea
)
- Compiler checks for overflow (
What happens when the sanitizer is triggered
4012b5: 48 63 f0 movslq %eax,%rsi
4012b8: ba 01 00 00 00 mov $0x1,%edx
4012bd: 48 89 44 24 08 mov %rax,0x8(%rsp)
4012c2: 48 8d 3d f7 2d 00 00 lea 0x2df7(%rip),%rdi
; 0x2df7(%rip) points to a structure:
; const char* file = "ubsignedinc.cc";
; unsigned line = 10;
; unsigned column = 19;
; type_descriptor* type = &int_type;
; “int_type” is another structure:
; uint16_t type_kind = 0; (Integer)
; uint16_t type_info = 0b1011; (Signed, size 2^5 bits)
; const char name[] = "'int'";
4012c9: e8 b2 fe ff ff callq 401180 <__ubsan_handle_add_overflow@plt>
4012ce: 48 8b 4c 24 08 mov 0x8(%rsp),%rcx
4012d3: eb a5 jmp 40127a <main+0x5a>
Sanitization and optimization
- Can this loop ever cause signed overflow?
unsigned sum = 0;
for (int i = 0; i < x; ++i) {
sum += i;
}
Address sanitization (sidebar)
deref.cc
- Compare with and without sanitization
Shadow memory
- Address sanitization allocates a shadow memory that keeps track of what parts of real memory are allocated
- Before dereferencing a pointer, the sanitizer checks that shadow memory allows it
- “the ASAN allocation [of shadow memory] is completely outlandish…closer to 20 TB” ref