

LLVM merges SafeStack - theraven
https://github.com/llvm-mirror/llvm/commit/7ffec838a2b72e6841d9fb993b5fe6a45f3b2a90

======
ekr
Does this mean that it will no longer be possible to do things like return-
oriented programming?

LE: indeed, it's quite clear from the mentioned article
([http://dslab.epfl.ch/pubs/cpi.pdf](http://dslab.epfl.ch/pubs/cpi.pdf)). So
this provides great exploit protection.

~~~
pvdebbe
What is return-oriented programming? Recursion?

~~~
garblegarble
It looks like it's an exploit technique where the stack is modified to set up
malicious calls to functions: [https://en.wikipedia.org/wiki/Return-
oriented_programming](https://en.wikipedia.org/wiki/Return-
oriented_programming)

~~~
mafribe
That's not really the essence of ROP because other attack techniques often
also need to manipulate the stack. The key novel idea in ROP is to use data in
unintended ways. This is based on the insight that the memory often contains
short sequences of bytes (e.g. a .jpg image) that can be interpreted as
machine instructions. For example an mp3 file might contain the sequence 99
19933, 16 which translates to

    
    
        increment register 16
        return
    

in the ambient machine language. Call that "dual use data". ROP searches the
memory for sufficient "dual use data" and then builds an ac-hoc compiler with
"dual use data" as target language. Then the attack software compiles to "dual
use data" and then runs the compiled code.

Of course one may ask: can we always find enough "dual use data" to build a
Turing-complete set of instructions as a compilation target. Turns out that
with high probability that is the case.

~~~
Dylan16807
None of that should be marked executable, though. The real risk in ROP is
using little bits of legitimate functions to bypass DEP.

~~~
mafribe
That is true, I should have been more clear about this but you don't use the
"legitimate function"'s intended functionality, you only use the fact that it
can be execute (and the byte-string that is it's code).

I used mp3s and jpgs as extreme examples of data that was never intended to be
executed, but still can be interpreted as code. In ROP, you don't care about
the intended meaning of the bytes that make up "legitimate functions" (or any
other data you may use) for it's unlikely to have the sought functionality.
Instead you use you search for "dual use code" too, and piece together the
functionality you need.

~~~
revelation
Well, but you missed that _data can not be executed_.

Unless you store your MP3s and jpegs in .text, the memory pages all that stuff
is in are marked not executable and will only cause a crash if you jump to it.
Regardless of whether the bytes make useful instructions.

~~~
mafribe
Data can be executed if it is in the executable part of the memory. There is
quite a lot of such data. In particular, code is data!

~~~
mafribe
Why is this post downvoted? This idea of (mis)using code is part of the
essence of ROP! Could somebody please explain where I'm wrong, so I can learn?

~~~
tptacek
I didn't downvote it, but I feel like your comments have been technically
correct but practically misleading.

It's possible to have executable data, but if you do, you generally have
bigger problems: the exploit can simply write a complete first-stage into the
data and execute it directly, and not bother going through return-oriented
contortions.

The reality is that gadget harvesting is about analyzing program text ---
actual binary machine instructions --- not about looking for ways to interpret
JPEGs or MP3s or (I wrote DOCX and then PDF and then thought "huh bad
examples") RTF files as instruction streams.

It's also true that you can exploit insane x86 encoding to synthesize
unintended instructions, but that's (I think) less important than the simpler
idea of taking whole assembled programs, harvesting very small subsequences,
wiring them together with a forged series of stack frames, and achieving
general computation.

------
thisismyhaendel
To be clear: SafeStack does _NOT_ prevent return oriented programming. It
makes the bar much higher, and it should be lauded for that. But please don't
for a second think that this is a solved problem: ROP can occur on the heap,
for instance. CPI as a system also does not completely solve the problem: it
is possible to break, for example
([http://web.mit.edu/ha22286/www/papers/conference/Oakland15.p...](http://web.mit.edu/ha22286/www/papers/conference/Oakland15.pdf)
) and despite the CPI author's conclusions, produces high overheads for
programs with large amounts of code pointers (C++ programs with vtables are
good examples). Also not prevented are attacks that use data pointers (non
control-flow data attacks), an area that has seen little study.

~~~
thisismyhaendel
Also see papers like BlindROP:
[http://www.scs.stanford.edu/~sorbo/brop/bittau-
brop.pdf](http://www.scs.stanford.edu/~sorbo/brop/bittau-brop.pdf) and
Sigreturn oriented programming:
[https://www.cs.vu.nl/~herbertb/papers/srop_sp14.pdf](https://www.cs.vu.nl/~herbertb/papers/srop_sp14.pdf)
to get a little bit more of the idea of how complicated ROP can actually get.

------
VeejayRampay
From the article: "The overhead of our implementation of the safe stack is
very close to zero (0.01% on the Phoronix benchmarks)". That's quite awesome,
congratulations.

~~~
vanderZwan
Especially if you add the follow-up sentence:

"This is lower than the overhead of stack cookies, which are supported by LLVM
and are commonly used today, yet the security guarantees of the safe stack are
strictly stronger than stack cookies."

Win-win for everyone

------
joosters
The performance is impressive, considering that maintaining a second stack
presumably requires another register exclusively dedicated to it. I'm
surprised it makes such little difference. Or are there some cunning
optimisations going on?

~~~
evanpw
I was wondering the same thing. The linked article
([http://dslab.epfl.ch/pubs/cpi.pdf](http://dslab.epfl.ch/pubs/cpi.pdf)) says
that they don't use a dedicated register. The unsafe stack pointer is saved in
the thread control block, accessible through a segment register. From there,
they let LLVM choose a register (not necessarily the same one for each
function). They also say that only 25% of functions need any unsafe stack
access at all, which I guess is why this is faster than using a dedicated
register.

------
extropy
Why don't we have a CPU architecutre with two stacks - one for stack data and
another for return addresses?

~~~
arielby
That's what SafeStack (and IA-64) do.

------
willvarfar
Apple, which has started distributing LLVM bitcode, will be able to apply it
on all the new apps in the App Store transparently.

Google will be able to do likewise for NaCL apps.

~~~
higherpurpose
Apple is making its apps LLVM-based now? Doesn't that mean they could soon
push for ARM CPUs in Mac OS, too?

~~~
crucialfelix
yes. developers will be able to submit apps as LLVM Bitcode which means that
Apple can switch to ARM for some new device and recompile the app without the
developer having to resubmit it.

It also means the end of Universal binaries as Apple can thin the compiled app
for each target device.

the PPC switch over was painful, as was the 32 versus 64 bit era. They want to
avoid that in the future.

~~~
pg314
At the moment LLVM bit code generated by Clang is still architecture dependent
(e.g. sizeof() still generates code that depends on the architecture).

~~~
willvarfar
But modern CPUs are all converging on 64-bits, for example.

Use of SIMD intrinsics are a tougher nut, but I've actually been playing with
them at an IR level for hobby stuff and I declare its not intractable.

~~~
mappu
Converging on 64-bit CPUs doesn't mean converging on standard `sizeof()`
values (c.f. windows x86_64 ABI is LLP64 but linux x86_64 ABI is LP64)

~~~
willvarfar
It's easy to loose sight of the fact that we are talking about 64bit OS X
here... The diff between ir that targeted x86-64 and arm64 and so on is
something Apple has some control over.

I know, I play with a hobby llvm backend that retargets.

------
hughw
It never occurred to me before to ask, but aren't Emscripten asm.js programs
vulnerable to the same exploits C programs are? e.g. I could exploit a buffer
overflow in some trusted js code to get some sensitive information from the
site. If that's the case, would emcc with SafeStack mitigate that?

------
comex
Interesting. I haven't fully digested the paper, but a few notes for context:

\- Most real-world exploits these days are based on use-after-frees, heap
buffer overflows, and other heap-related weirdness, rather than stack buffer
overflows. It's nice that SafeStack mitigates that attack vector though (but
if you disable stack canaries in favor of it, you actually reopen the door to
exploit certain types of vulnerabilities...)

\- A (the most?) common method to proceed from memory corruption to return-
oriented programming is to redirect a virtual method call or other indirect
jump to a stack pivot instruction. SafeStack _alone_ does nothing to prevent
this, so it doesn't prevent ROP.

\- However, the general code-pointer indirection mechanisms described in the
paper, of which SafeStack is an important component, could make ROP
significantly harder, because you would only be able to jump to the starts of
functions. This guarantee is similar to Windows's CFG (although the
implementation is different), but SafeStack makes it harder to bypass by
finding a pointer into the stack (either on the heap or via gadget).

\- In practice, interoperation with unprotected OS libraries is likely to
seriously compromise the security benefits of the combined scheme, because
they will store pointers into the real stack, jump directly to code pointers
on the heap, etc. JIT compilers are also likely to be problematic.

\- In addition, there are more direct ways for an attacker to work around the
protection, such as using as gadgets starts of functions that do some small
operation and then proceed to a virtual call on an argument. The larger the
application, the more possibilities for bypass there are.

\- Still, "harder" is pretty good.

Edit: By the way, the point about function start gadgets makes questionable
the paper's claim that "CPI guarantees the impossibility of any control-flow
hijack attack based on memory corruptions." Also, if you want to guarantee rsp
isn't leaked, it isn't enough to keep all pointers near it out of regular
memory: they also have to be kept out of the stack itself, because functions
with many (or variable) arguments will read them from the stack - at least, I
don't see a claim in the paper about moving them - so subverting an indirect
call to go to a function that takes more arguments than actually provided (or
just changing a printf format string to have a lot of arguments) will cause
whatever data's on the stack to be treated as arguments. Ditto registers that
either can be used for arguments or are callee-saved. That means frame
pointers have to be disabled or munged, and any cases where LLVM automatically
generates temporary pointers for stack stores - which I've seen it do before -
have to be addressed.

If you do move non-register arguments to the safe stack then the situation is
improved, but you still have to watch out for temporaries left in argument
registers.

~~~
arielby
SafeStack is essentially an IA-64/SPARC-style two-stack model - there are no
pointers to the %rsp stack.

~~~
comex
The issue is preventing pointers to the real stack _on_ the real stack. I'm
pretty sure you can't do that reliably at the LLVM IR level, since as I said
such pointers can be introduced during code generation. In fact, I just looked
at the source to the merged pass, and it doesn't even try - it only checks if
stack pointers are passed to calls, but e.g.

    
    
        int *p = cond ? &a : &b;
    
        ...later enough that this isn't trivially optimized into two stores...
    
        *p = 1; 
    

will probably not be flagged (it depends on what optimization passes have run
before the SafeStack pass), but will put the pointer in the stack or a
register that may be saved.

------
wang_li
Now if we can get the stack to grow upwards instead of downwards my life will
be complete and I can die.

------
arielby
IA-64 had this since it was created - nice to see it coming to x86.

