"The Stack Clash is a vulnerability in the memory management of several operating systems. (...) It can be exploited by attackers to corrupt memory and execute arbitrary code."
"If you are using Linux, OpenBSD, NetBSD, FreeBSD, or Solaris, on i386 or amd64, you are affected. Other operating systems and architectures may be vulnerable too, but we have not researched any of them yet: please refer to your vendor’s official statement about the Stack Clash for more information."
There is good news, this looks way harder to attack on 64 bit systems (all the CVE are on 32 bit OS), possibly because the adress space is so huge, and standard OS counter measure like ASLR, stack gap etc all help protecting against the attack.
The other way around. The kernel has already mapped pages for the stack, and would never hand such pages to malloc unless there's a serious bug in the vm subsystem.
However, code that uses the stack has no idea where the stack ends. And reaching out of the stack is only detected via page faults. If that access happens to land on a mapped page with the right permissions, there is no fault and the code can effectively grow the stack into heap region.
In theory, you can always decrement the stack pointer for your variables. If you find an unallocated page, the kernel will notice that you're right below the stack and give you more stack pages. There's no other to request more stack memory, the way you can use brk() or mmap() to request more heap memory: you're supposed to page-fault and let the kernel come up with more stack.
In slightly less theory, you can decrement the stack pointer, and if you reach more memory than the kernel is willing to give you, the page fault will turn into an actual segfault, because you'll hit a specially-defined guard page that prevents you from infinitely growing the stack.
In practice, you can decrement the stack pointer by any arbitrary amount and now you just have a pointer somewhere and you have to hope it's either within the stack or in the guard page....
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.
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.
Section IV.1.7 ("64-bit exploitation") mentions CVE-2017-1000379.
These are remarkable because they are conceptually straightforward but the power of the exploits was potentially substantial.
edit: original first sentence was "told Red Hat, Red Hat fixed them." Apparently that was wrong and people (appropriately!) want credit attributed to the right parties.