Log in

View Full Version : When heap and stack collide: Compilers still forgo stack probing


LigH
21st June 2017, 09:18
It's groundhog day again: A computer security auditor (and "conspiracy" blogger) reports about discussions (https://blog.fefe.de/?ts=a7b7880f) regarding risks of heap/stack collisions and the lack of several compilers checking for this less probable yet possible issue. Microsoft Visual Studio does it. GCC usually does not (at least not in C/C++, reported in 2007 (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=34118), although it would be supported (https://gcc.gnu.org/onlinedocs/gccint/Stack-Checking.html)), and CLang rather only under Windows (https://twitter.com/CopperheadOS/status/876835207701200896). Not even Rust (the language praised for security efforts) cared much about it (https://github.com/rust-lang/rust/issues/16012#issuecomment-309656710).

Qualys (famous for e.g. their SSL security check of webservers) published a text regarding the "stack clash" (https://www.qualys.com/2017/06/19/stack-clash/stack-clash.txt), describing possible vulnerabilities by allocating stack variables larger than the "guard page", thus skipping the MMU internal protection.

I don't know how much current video tools use the stack to exchange large data structures; I would bet they will prefer only pointers to them. Still, project authors may keep in mind to care about this risk and evaluate how much this extra check would cost performance, if elaborate use of the stack can't be avoided.

No reason for panic, though. :cool: Protection against maliciously prepared software would possibly be a matter of the OS, instead?! :rolleyes:

LoRd_MuldeR
22nd June 2017, 18:52
I'm not sure I get the point.

Of course you can make the stack grow until it overflows. Every programmer has had his program crash due to stack overflow, e.g. because of an infinite recursion. Also it is not that surprising that - with some trickery - you can make the stack pointer "jump" across the protection page. Thus the stack overflow won't be detected by the OS, and thus your program won't crash, and thus your stack grows into whatever comes next in your memory space – e.g. the heap space.

But: It is fundamental to any modern operating system that each process runs in its own separate/isolated address space. So, the "worst" thing that can happen is that you grow your own stack into your own heap space. You certainly can not "reach" another process' heap, as it lives in a completely separate address space. What you might be able to do is "abusing" your stack to "steal" (read) some data from your own heap, or to "destroy" (overwrite) some data on your own heap – only that doing so is rather pointless! That's because you could have simply read or overwritten that data by accessing your heap directly. It's your own heap, living in your own address space, after all.

Surely, if an attack was able to modify your code to make the stack grow into the heap without your consent, then "bad" things may happen. But at the point where the attacker can modify your code you have much more serious problems!

LigH
22nd June 2017, 21:28
I am not the expert here :o - not as much as Qualys. But I guess it is (not probable, but) possible to provoke unwanted behaviour of an application by accessing private data via a public interface... maybe. Only theory so far. Until proven, it seems to me to be mainly a possible way to provoke a crash, which might already be annoying on its own (DoS).

shekh
22nd June 2017, 21:36
It is the same
https://en.wikipedia.org/wiki/Buffer_overflow
The idea is that malicious user would break a program by supplying some sort of invalid data to it, not modify its code.
The program must be interesting for attack, e.g. have elevated permissions. Otherwise there is little point for attacker.

LigH
22nd June 2017, 21:44
I believe the topic of these reports is mainly: Can it be detected and avoided before it happens? How much of that protection is the responsibility of the CPU / OS / compiler? Obviously, the problem is known for more than a decade already. But well ... in the scope of a video board, it seems to be merely a curious topic.

feisty2
24th June 2017, 16:31
I don't know how much current video tools use the stack to exchange large data structures; I would bet they will prefer only pointers to them. Still, project authors may keep in mind to care about this risk and evaluate how much this extra check would cost performance, if elaborate use of the stack can't be avoided.

I use "alloca" to dynamically allocate from the stack all the time for some temporary VLAs in my plugins (damn, C++ doesn't have VLA on stack at the language level like C99, I just have to use the non-standard "alloca", it's widely supported by pretty much every compiler out there tho..)

nevcairiel
24th June 2017, 23:12
damn, C++ doesn't have VLA on stack at the language level like C99

Just for the record, VLAs have also been downgraded to an optional feature in C11 and beyond. Excessive Stack use can have problems, especially in plugins that have no information about the stack size the program that calls your plugin might give you.

feisty2
25th June 2017, 04:45
Just for the record, VLAs have also been downgraded to an optional feature in C11 and beyond. Excessive Stack use can have problems, especially in plugins that have no information about the stack size the program that calls your plugin might give you.

but stack allocation is FAST, alloca will typically get inlined by compilers, so it is just

sub rsp, size (size should be mod16)
mov [some register that has the address of the target pointer, probably rax], rsp

at the assembly level, while malloc and new are a lot more complicated than that

nevcairiel
25th June 2017, 10:10
but stack allocation is FAST

Unless you do allocations within a tight loop (which you probably really shouldn't), the speed is often irrelevant. First part of optimization is to know which optimizations actually make a real difference. :)

LoRd_MuldeR
25th June 2017, 11:48
but stack allocation is FASTUnless you do allocations within a tight loop (which you probably really shouldn't), the speed is often irrelevant. First part of optimization is to know which optimizations actually make a real difference. :)

Yeah, if you allocate (and free) a lot of short-lived buffers in the "hot" execution path of your program or plugin, then you probably should re-consider your design to allocate all the required buffers once at the beginning of your program or when your plugin is initialized - and free them all once when the program terminates or when the plugin is unloaded. Moving the malloc's and free's out of the "hot" execution part makes their speed negligible...