back
4 comments
Slightly easier to read: https://blog.qualys.com/securitylabs/2017/06/19/the-stack-cl...

"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."

If you're looking for a background on the attack itself, I did a writeup of the basic attack some time ago at https://ldpreload.com/blog/stack-smashes-you . The new thing is that Qualys has developed this into a real class of exploits against lots of software; I think we all thought that it was relatively rare to run into this vulnerability.
On Linux and most Unices (at least on ix86), the heap starts at the bottom of the address space, and the upper bound grows up as you allocate more memory, while the stack starts at the top of the memory space and grows downward as you allocate more stack (stack allocation : mostly function calls and the odd alloca()). malloc() who manages the heap as no idea of where the stack currently ends, and thus can allocate memory at an adress that is also claimed by the stack. When writing to this address, this permit to smash the stack without needing an actual buffer overflow in the attacked code.

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.

> malloc() who manages the heap as no idea of where the stack currently ends, and thus can allocate memory at an adress that is also claimed by the stack

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.

Right. The malloc heap has defined starting and ending points. (I say "points" because it's common to use mmap to get a bunch of new pages, which might be discontiguous from the existing heap, but you still know exactly where those pages are.) The stack, on the other hand, is just a pointer.

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....

Slightly more specifically, if the stack is growing into space that the heap already owns, you don't get a page fault. So the OS assumes that the stack already owns that memory, since there was no page fault. Now if you write to the heap, you can corrupt the stack.
> 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.

From 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.

This 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...
Is there an efficient way on x64 to guard the stack pointer or program pointer? Maybe using hardware breakpoints? freeRTOS has such an option.
Yeah, it's basically inherently unsafe to allocate large buffers on the stack. grsecurity/PaX have apparently mitigated this somewhat by increasing the size of buffer required to exploit this, but there's no way for the kernel to fix the issue.
> all the CVE are on 32 bit OS

Section IV.1.7 ("64-bit exploitation") mentions CVE-2017-1000379.

Qualys found some exploits, told parties responsible for fixing the problems, those parties fixed the problems.

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.

Actually no, Qualys told Red Hat and SUSE initially (I asked for early access to confirm how bad it was, Red Hat and SUSE are capable of handling very sensitive embargoed material as we have enough engineers internally to do Kernel/glibc/etc stuff in house) and then we (Red Hat and SUSE) agreed that 1) this was as bad as Qualys said and 2) we need to get the entire community involved ASAP (via the distros list and CC's for people not on it like the Kernel people and so on).