SMAP bypass
Team Summary
Official summary from PlayStation
SMAP is a security feature on x86 CPUs, that forbids ring0 from reading/writing to ring3 pages, making it harder to exploit entire classes of vulnerabilities. There is a vulnerability in FreeBSD 12 that allows SMAP to be bypassed by userland. There is a very high probability that it affects the PS5 but I was unable to access a PS5 firmware to confirm it. This vuln downgrades the security properties of the OS, and is a building block for exploitation chains. ## Details With SMAP enabled, when `%RFLAGS.AC` is cleared the kernel will page-fault if it tries to access a page marked as "user page". When `%RFLAGS.AC` is set the kernel can access the user pages as if SMAP was not enabled. In the FreeBSD kernel, a few functions exist that temporarily set `%RFLAGS.AC` in order to access user pages: the `copyin()` and `copyout()` functions. These functions are used in all syscalls and are the only ways the kernel can copy data from/to userland. These functions handle faults gracefully, that is, if userland passes an unmapped address and the kernel tries to copy data from it, the functions will simply return an error without kernel panic. There is a bug in the fault handling of these functions. Typically `copyin()` is implemented as follows: ```asm .macro COPYIN smap erms /* ... */ movq $copy_fault,PCB_ONFAULT(%r11) /* ... */ stac // set %RFLAGS.AC, to allow access to user pages do_the_copyin clac // clear %RFLAGS.AC, to forbid access to user pages /* ... */ copy_fault: movq $0,PCB_ONFAULT(%r11) movl $EFAULT,%eax POP_FRAME_POINTER ret ``` ```c void trap(struct trapframe *frame) { /* ... */ if (curpcb->pcb_onfault != NULL) { frame->tf_rip = (long)curpcb->pcb_onfault; return; } /* ... */ } ``` The fault handler `copy_fault` is registered in `pcb_onfault` at the beginning, then `%RFLAGS.AC` is set, the copy is made, and `%RFLAGS.AC` is cleared back. If the copy faults for whatever reason, an exception is raised, the `trap()` handler sees that `pcb_onfault` has a pointer registered, and simply `IRET`s back to the pointer that was registered. The problem is, the fault handler `copy_fault` does not clear `%RFLAGS.AC`, meaning that it remains set after `copyin()`/`copyout()` returns. The rest of the syscall will therefore execute with SMAP effectively disabled, until the kernel returns to userland where the SMAP state gets reset back to normal. This SMAP disablement survives context switches, so a user that disables SMAP during one of his syscalls can also disable SMAP in other user/kernel threads if a rescheduling happens after/during the syscall (taking a mutex for example). ## Fix Add a `clac` instruction in `copy_fault` to clear `%RFLAGS.AC`: ```diff copy_fault: + clac movq $0,PCB_ONFAULT(%r11) movl $EFAULT,%eax POP_FRAME_POINTER ret ``` ## Impact Userland can open lage windows where the kernel executes with SMAP disabled. Lack of SMAP makes exploitation of common vulnerabilities easy/trivial.
Report Details
Additional information and metadata
State
Closed
Substate
Resolved