3 ms·
> Slightly more specifically, if the stack is growing into space that the heap already owns, you don't get a page fault. Exactly. But the stack is only growin
by clarry 9y ago
> Slightly more specifically, if the stack is growing into space that the heap already owns, you don't get a page fault.
Exactly. But the stack is only growing in the program's view of the world.
> So the OS assumes that the stack already owns that memory
Nah, the OS is blissfully unaware that the program has moved its stack pointer to point off the stack. If anything, the OS wrongly assumes the program is still operating within the stack space explicitly and rightly reserved for it.
And the program in turn wrongly assumes this memory newly referenced via the stack pointer has been reserved for its stack, because it wasn't killed for accessing it.
> Now if you write to the heap, you can corrupt the stack.
That is right. You will corrupt what the program believes to be stack.
- AnimalMuppet 9y agoFrom TFA: The user-space stack of a process is automatically expanded by the kernel: - if the stack-pointer (the esp register, on i386) reaches the start of the stack and the unmapped memory pages below (the stack grows down, on i386), - then a "page-fault" exception is raised and caught by the kernel, - and the page-fault handler transparently expands the user-space stack of the process (it decreases the start address of the stack), - or it terminates the process with a SIGSEGV if the stack expansion fails (for example, if the RLIMIT_STACK is reached). Unfortunately, this stack expansion mechanism is implicit and fragile: it relies on page-fault exceptions, but if another memory region is mapped directly below the stack, then the stack-pointer can move from the stack into the other memory region without raising a page-fault, and: - the kernel cannot tell that the process needed more stack memory; - the process cannot tell that its stack-pointer moved from the stack into another memory region.
- MrBuddyCasino 9y agoIs there an efficient way on x64 to guard the stack pointer or program pointer? Maybe using hardware breakpoints? freeRTOS has such an option.
- spc476 9y agoI doubt it. The stack pointer is modified upon function entry, exit, and per function call (parameters pushed onto the stack, popped off afterwards). You could try to add code to every function to check of the stack pointer is between two addresses and fault if not, but then threading becomes more difficult ...
- e12e 9y agoThis is crazy. I remember thinking when I first heard about stack and heap growing towards each other: uh-oh. But the problem was so blindingly obvious that I just assumed any system written for anything beyond co-operative multitasking had a fix - because if not there was obviously no actual memory safety...
- tptacek 9y agoWhat would the fix be? The fact that you can point the stack pointer at arbitrary memory and the CPU will treat that memory as the stack is a feature, and an important one, of the ISA. The real issue here is that writing programs in memory-unsafe languages is inherently difficult and risky, and fewer programs should be written that way.
- geofft 9y agoI'd hesitate to pin the blame on "memory-unsafe languages", because Rust is currently vulnerable to the same attack: https://github.com/rust-lang/rust/issues/16012 https://github.com/rust-lang/rust/issues/16012 It looks like the fix is going to involve adding some code to LLVM to probe each stack page when you make a large stack allocation, but once that happens, it's straightforward for clang to implement -fstack-check for C and C++ programs, too. This is a weird emergent problem from the fact that stack and heap memory are part of the same address space and that the stack is designed to implicitly grow as needed. It's not clear that it's a language or compiler's fault for relying on the stack doing that, nor that it's the platform or kernel's fault for making that approach possible. I'm not totally sure how you would design a language so that you don't have this problem. I guess you could forbid a function from using more than some small amount of stack, and make sure your stack guard area is at least that big, but that seems like an unfortunate restriction. Maybe something like the Stackless Python approach, where all variables are heap-allocated, would work? Also, all that said, note that Windows gets this right: MSVC inserts calls to _chkstk() to do stack probing. I believe this entire class of vulnerability doesn't exist on Windows, and probably some people at MS who spend their entire lives in memory-unsafe languages are feeling very smug today.
- 9y ago