Fuzzing in the old days: Excel example Randomly Run Excel Generate/mutate an Excel file 24
Fuzzing in the old days: Excel example Randomly Crash! Run Excel Generate/mutate an Excel file 24
Fuzzing in the old days: Excel example Randomly Crash! Run Excel Generate/mutate an Excel file Finding a crashing input gets harder and harder 24
Fuzzing: Desired Features • Fuzzing attempts to • Detect explicit violation • While exploring all different program states in a given implementation • Desired features • Good violation detection capability • Exploring all different program states 25
Fuzzing: Good Violation Detection Capability • Memory bug • Violation conditions are clear • Per-pointer capability w.r.t. accessing memory space • Concurrency bug • Violation conditions are clear • If race occurs, it is undefined behavior • Challenges: False positive issues in concurrency bug detection • Undefined behavior can be implementation-defined behavior • Developers may intentionally introduce races • Difficult to differentiate benign VS harmful races • Semantic bug • Challenges: Violation conditions are unclear • We don’t even know violation conditions • Only assertions (manually inserted by developers) may help • Only a few research have been done 26
Fuzzing: Exploring all different program states • State exploration and bug finding • More code coverage More complete testing More bugs • Most fuzzing techniques take coverage-oriented genetic algorithm (proposed by AFL) • Coverage-oriented genetic algorithm for fuzzing • Design an object function f to evaluate goodness of input’s coverage • Algorithm • Initialize a corpus C • Corpus: a set of inputs to be mutated • Generate an input i • Generation: Randomly generate i from scratch • Mutation: Pick c from C, and then mutate c • If f(i) >= f(c) for all c in C • Add input i to the corpus C 27
Fuzzing: Exploring all different program states • Covering complete context sensitivity is challenging • Concurrency bugs are much more complex • Scheduler mechanisms can be seen as non-deterministic • For fuzzing, input definition is complicated • Should consider thread interleaving • Related to hardness of race bug reproduction 28
Challenges in Fuzzing: Violation Detection input output 29
Challenges in Fuzzing: Violation Detection input output This crash may not be observable in practice. It may simply overwrite some other variables, and it won't involve critically bad behaviors. 29
Memory error bugs • Spatial safety violation • When accessing beyond allocated memory regions • e.g., stack overflow, heap overflow • Temporal safety violation • When accessing deallocated memory regions • e.g., use-after-free, double-free 30
Memory Error: Detection Approaches • Pointer-based detection • Keep track of each pointer’s capability w.r.t. memory accesses • Software: SoftBound [PLDI 09], CETS [ISMM 10] • Hardware: Watch Dog [ISCA 12], Intel Memory Protection Extension (MPX) • Redzone-based detection • Keep track of global memory space accessibility • Software: AddressSanitizer [ATC 12] • Hardware: REST [ISCA 18] 31
SoftBound [PLDI 09] • Maintain per-pointer metadata • (base, bound) per pointer • Runtime safety enforcement • All memory access should be within the range (base, base+bound) • Metadata propagation • Per-pointer metadata is propagated when performing pointer arithmetic operations • Disjoint metadata scheme • For better compatiblity , metadata space is isolated from original program’s memory space 32
Intel MPX • Hardware-assisted version of SoftBound • Hardware-assisted metadata management • Metadata management: Bound registers + Bound table • Bound registers • (base, bound) information is now stored in CPU registers • Fast bound check: a dedicated instruction is supported by MPX • Bound table • Used if bound registers are not available • Limitations • Compatibility issues (arbitrary type casting, external libraries, multi-threading) • More details: Intel MPX Explained [SIGMETRICS 18] 33
AddressSanitizer [ATC 12] • Buffer overflow (spatial memory errors) ptrX Shadow memory: a bitmap to validate all addresses objX Check before access Shadow memory Accessible 34
AddressSanitizer [ATC 12] • Buffer overflow (spatial memory errors) ptrX Shadow memory: a bitmap to validate all addresses objX Check before Redzone: access inaccessible region between objects Shadow memory Accessible Inaccessible (redzone) 34
AddressSanitizer [ATC 12] • Buffer overflow (spatial memory errors) ptrX Shadow memory: a bitmap to validate all addresses objX Redzone: Error! inaccessible region between objects Shadow memory Accessible Inaccessible (redzone) 34
AddressSanitizer [ATC 12] • Use-after-free (Temporal memory errors) ptrX objX Accessible Inaccessible Shadow memory 35
AddressSanitizer [ATC 12] • Use-after-free (Temporal memory errors) ptrX ptrX Quarant- free(ptrX) objX Region is invalidated ined and quarantined, but not deallocated Accessible Inaccessible Shadow memory 35
AddressSanitizer [ATC 12] • Use-after-free (Temporal memory errors) ptrX ptrX Hold the Quarant- free(ptrX) objX region until ined quarantine zone is full (FIFO) Accessible Inaccessible Shadow memory 35
AddressSanitizer [ATC 12] • Use-after-free (Temporal memory errors) ptrX ptrY ptrX ptrY = Quarant- malloc() free(ptrX) objX objY The region is actually ined deallocated, and can be allocated for a new object Accessible Inaccessible Shadow memory 35
REST [ISCA 18] • Hardware-assisted version of AddressSanitizer • A fixed token value in redzone • A pre-determined random value (in 64-byte space) is reserved for representing an invalid memory region • No need to maintain shadow memory • Validity checks on memory read/write can be very efficient • Simply check if the token bit (in cache) is set 36
Race condition • Race conditions • If following three conditions meet • Two instructions access the same memory location • At least one of two is a write instruction • These two are executed concurrently • If a race condition occurs, the computational results may vary depending on the execution order. 37
Race condition: A simple example a = 1; a = 0; If (a) { b = 0; } else { b = 1; } Thread 1 Thread 2 38
Race condition: A simple example a = 1; a = 0; If (a) { b = 0; } else { b = 1; } Thread 1 Thread 2 b will be 1 38
Race condition: A simple example a = 0; a = 1; a = 1; a = 0; If (a) { If (a) { b = 0; b = 0; } else { } else { b = 1; b = 1; } } Thread 1 Thread 2 Thread 1 Thread 2 b will be 1 38
Race condition: A simple example a = 0; a = 1; a = 1; a = 0; If (a) { If (a) { b = 0; b = 0; } else { } else { b = 1; b = 1; } } Thread 1 Thread 2 Thread 1 Thread 2 b will be 1 b will be 0 38
Real-world int fd = open(”/dev/ ptmx ”); race example: write(fd , “……”); ioctl(fd, TCFLSH); User CVE-2017-2636 ioctl(fd, TCXONC); User thread A User thread B close(fd); (Linux Kernel) 431: if (n_hdlc->tbuf) { 432: push_back(free_list, n_hdlc->tbuf); 216: tbuf = n_hdlc->tbuf; 440: n_hdlc->tbuf = NULL; 217: if (tbuf) Kernel 441: } 218: push_back(free_list, tbuf); Kernel thread B Kernel thread A 266: if (n_hdlc->flag & TCXONC) 267: while (list_empty(free_list)) { 268: buf = pop_front(free_list); 269: kfree(buf); 270: } 39
Real-world int fd = open(”/dev/ ptmx ”); race example: write(fd , “……”); ioctl(fd, TCFLSH); User CVE-2017-2636 ioctl(fd, TCXONC); User thread A User thread B close(fd); (Linux Kernel) 431: if (n_hdlc->tbuf) { 432: push_back(free_list, n_hdlc->tbuf); 216: tbuf = n_hdlc->tbuf; 440: n_hdlc->tbuf = NULL; 217: if (tbuf) Kernel 441: } 218: push_back(free_list, tbuf); Kernel thread B Kernel thread A 266: if (n_hdlc->flag & TCXONC) This will turn into “double - free”, 267: while (list_empty(free_list)) { 268: buf = pop_front(free_list); which in turn allows a privilege escalation 269: kfree(buf); 270: } 39
Race condition and fuzzing • Finding race conditions through fuzzing is challenging • How to execution racing pair instructions concurrently ? • All the execution interleaving (scheduling) is “randomly” determined • Case study: Previous techniques • Syzkaller: a system call fuzzer for the Linux kernel • SKI: an academic prototype to find a race [OSDI 14] 40
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … … … cmp %r14,%r12 mov -0x20(%rdi), %r8 je ffff …..734 test %r8, %r8 … … Guest ` Kernel … … movq $0, 0x20(%rbx) mov $0x2400,%esi add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … … … cmp %r14,%r12 mov -0x20(%rdi), %r8 je ffff …..734 test %r8, %r8 … … Guest ` Kernel … … movq $0, 0x20(%rbx) mov $0x2400,%esi add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B ⬤ : Syzkaller 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … u u … … cmp %r14,%r12 mov -0x20(%rdi), %r8 je ffff …..734 test %r8, %r8 … … Guest ` Kernel … … movq $0, 0x20(%rbx) mov $0x2400,%esi add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B ⬤ : Syzkaller 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … u u … … cmp %r14,%r12 mov -0x20(%rdi), %r8 je ffff …..734 test %r8, %r8 … … Guest ` v v Kernel … … movq $0, 0x20(%rbx) mov $0x2400,%esi add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B ⬤ : Syzkaller 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … u u … … cmp %r14,%r12 mov -0x20(%rdi), %r8 je ffff …..734 test %r8, %r8 … … Guest ` v v Kernel … … movq $0, 0x20(%rbx) mov $0x2400,%esi add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B ⬤ : Syzkaller ⃝ : SKI 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … u ① u … … cmp %r14,%r12 mov -0x20(%rdi), %r8 je ffff …..734 test %r8, %r8 … … Guest ` v v Kernel … … movq $0, 0x20(%rbx) mov $0x2400,%esi add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B ⬤ : Syzkaller ⃝ : SKI 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … u ① u ② … … cmp %r14,%r12 mov -0x20(%rdi), %r8 je ffff …..734 test %r8, %r8 … … Guest ` v v Kernel … … movq $0, 0x20(%rbx) mov $0x2400,%esi add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B ⬤ : Syzkaller ⃝ : SKI 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … u ① u ② … … cmp %r14,%r12 mov -0x20(%rdi), %r8 ③ je ffff …..734 test %r8, %r8 … … Guest ` v v Kernel … … movq $0, 0x20(%rbx) mov $0x2400,%esi add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B ⬤ : Syzkaller ⃝ : SKI 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … u ① u ② … … cmp %r14,%r12 mov -0x20(%rdi), %r8 ③ je ffff …..734 test %r8, %r8 … … Guest ` v ④ v Kernel … … movq $0, 0x20(%rbx) mov $0x2400,%esi add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B ⬤ : Syzkaller ⃝ : SKI 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … u ① u ② … … cmp %r14,%r12 mov -0x20(%rdi), %r8 ③ je ffff …..734 test %r8, %r8 ⑤ … … Guest ` v ④ v Kernel … … movq $0, 0x20(%rbx) ⑥ mov $0x2400,%esi ⑦ add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B ⬤ : Syzkaller ⃝ : SKI 41
… … syscall j (y, …) syscall i – 1 (w, …) Guest syscall j + 1 (z, …) syscall I (x, …) User … … User Thread A User Thread B … … u ① u ② … … cmp %r14,%r12 mov -0x20(%rdi), %r8 ③ je ffff …..734 test %r8, %r8 ⑤ … … Guest ` v ④ v Kernel … … movq $0, 0x20(%rbx) ⑥ mov $0x2400,%esi ⑦ add $0x10,%rdi callq 0xffff……cbf0 … … … … Kernel Thread A Kernel Thread B Very unlikely to be executed concurrently ⬤ : Syzkaller Very unlikely to trigger a race! ⃝ : SKI 41
Finding Kernel Races through Fuzzing • Razzer [S&P 19] • #1. Static analysis to find potential race spots • Points-to analysis over an entire kernel • #2. For each potential race spots, setup per-core breakpoint • Run the kernel on modified hypervisor (QEMU/KVM) • #3. Check if a race truly occurs • Prioritize a truly racing pair to be further fuzzed later 42
Guest write(fd , “…”) ioctl(fd,TCFLSH) … sshd init user User thread A User thread B mov 0x20(%rdi), %r8 movq $0, 0x20(%rbx) Guest test %r8, %r8 callq 0xffff……cbf0 Kernel Kernel thread B Kernel thread A Hypervisor vCPU0 vCPU1 43
Guest write(fd , “…”) ioctl(fd,TCFLSH) … sshd init user User thread A User thread B mov 0x20(%rdi), %r8 movq $0, 0x20(%rbx) Breakpoint Guest test %r8, %r8 callq 0xffff……cbf0 Kernel Kernel thread B Kernel thread A Hypervisor vCPU0 vCPU1 43
Guest write(fd , “…”) ioctl(fd,TCFLSH) … sshd init user User thread A User thread B u Execute u Execute mov 0x20(%rdi), %r8 movq $0, 0x20(%rbx) Breakpoint Guest test %r8, %r8 callq 0xffff……cbf0 Kernel Kernel thread B Kernel thread A Hypervisor vCPU0 vCPU1 43
Guest write(fd , “…”) ioctl(fd,TCFLSH) … sshd init user User thread A User thread B u Execute u Execute mov 0x20(%rdi), %r8 movq $0, 0x20(%rbx) v v Breakpoint Guest test %r8, %r8 callq 0xffff……cbf0 Kernel Kernel thread B Kernel thread A Hypervisor vCPU0 vCPU1 43
Recommend
More recommend