3 ms·
But the only failure case there is a DoS. You cannot get memory corruption from overcommit the way you can from a stack clash, and therefore you cannot get arbi
by geofft 8y ago
But the only failure case there is a DoS. You cannot get memory corruption from overcommit the way you can from a stack clash, and therefore you cannot get arbitrary code execution. That seems like a huge reason to prefer overcommit.
- kazinator 8y agoStack clashes are protected with guard pages. Guard pages can be skipped, but not by code like strjoina which fills in all of the bytes. https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt https://www.qualys.com/2017/06/19/stack-clash/stack-clash.tx... The reporters do claim to have a root exploit that they will publish later, which will be interesting (if it still applies on systems with robust guard pages). The strings are copied into the alloca area from the lower address to the higher, so initially, the stpcpy calls might be corrupting memory belonging to an adjacent mapping and not to the stack. Eventually the loop has to cross back into the stack, and it cannot do so without writing into a guard page. Situations where that is still exploitable can be contrived. For instance, the program sets up a SIGSEGV handler to execute on an alternate stack using sigaltstack. When the stack guard page is hit, that handler executes successfully, the program continues somehow (e.g. by a recovering longjmp that rewinds the stack) and then falls victim to the corruption. One thing we can do is after calling alloca, walk over the memory in reverse order (higher address to lower) in strides of 4096 bytes, and touch a byte at every increment. This way we hit the guard page (on any system that has a page size of at least 4096).
- geofft 8y agoThere are a bunch of other cases where guard pages don't help you (as long as you're filling in the bytes from the far end of the stack first, as you probably are on a stacks-grow-down architecture). If you start overwriting an area of memory that's being used by another thread, even if you eventually segfault, you've still corrupted memory. That can be a problem if the process has multiple threads and you can corrupt another thread's memory, or if you overwrite mmaped data and so the writes get committed to disk even if the program proceeds to crash and restart. In any case, those + your SEGV handler case are ones where stack clash attacks are exploitable to corrupt memory or hijack control flow. The OOM killer never corrupts memory or permits hijacking control flow; it only kills the process if it's in an irrecoverable situation. So overcommit is much safer. The technique you describe is sometimes called "stack probing" (or confusingly "stack checking") and is what GCC's -fstack-clash-protection implements, which is why this isn't exploitable on OSes which compile systemd with that option.
- kazinator 8y agoThis is true; if it's under threads and we hop a guard page, we are screwed, due to the race condition; by the time we hit that guard page, the other thread has used the corrupt stack. I have seen this sort of thing happen! Third party routing stack used in system with thread stacks reduced to 64K. Third party stack turned out to have debugging macros producing "{ char msg[8192]; ...}" declarations for sprintf. Twice the size of a page; hopped right into another thread stack. (Don't tell me systemd has threads in it, ouch!)
- loeg 8y ago> Stack clashes are protected with guard pages. Guard pages are necessary but not sufficient, as I think you would agree with based on the rest of your comment. I just object to leading the otherwise thoughtful comment with the absolutism about guard pages (trivial to understand and implement) that ignores the necessity of getting the fiddly bit (stack probing) correct.