[llvm-dev] How to prevent registers from spilling?

David Chisnall via llvm-dev
Tue, 03 Nov 2015 09:48:32 -0000

Hi Stephen,

On 2 Nov 2015, at 23:24, Stephen Crane via llvm-dev <llvm-dev at
lists.llvm.org> wrote:
>
> I've been trying to figure out if there is a feasible way to prevent
values from ever spilling from registers to the stack. I've looked for code
or documentation on how to do this but haven't found anything, apologies if
this has already been done.
>
> Recent security research has shown that protection schemes such as CFI
(that might otherwise be secure) are undermined by sensitive values spilling to
the stack. When security-critical values spill from registers to the stack, they
can be read and overwritten by an attacker with arbitrary memory read or write
capabilities. See "Losing Control" from CCS 2015 for more details on
this sort of attack: https://www.ics.uci.edu/~perl/ccs15_stackdefiler.pdf
>
> I think it would be great if we could allow values or at least virtual regs
to be tagged as "security-sensitive" and disallow spilling of these
values across their lifetime. I expect that the best way to do this would be to
start at the virtual register level and push support up to IR values as well if
and only if machine IR turns out to be insufficient.
>
> Is this a good idea? Does something to support register pinning already
exist? I'm unfortunately not familiar enough with the register allocators to
know how to best support this, although I'm willing to give it a try if
people can point me in the right direction.

I implemented something like this for MIPS a couple of years ago.  A few things:

- Marking variables doesn’t make sense.  You don’t know what temporaries will
exist that are derived from that variable and can allow an attacker to
materialise it.  You really want to mark functions as sensitive.

- Preventing spills does not actually buy you anything.  A lot of recent attacks
exploit signal handlers.  If you can deliver a signal in the middle of the
sensitive code then all of its registers are spilled in the ucontext.  You need
to also modify the kernel to zero this region of the stack after signal
delivery, at the very least, and also ideally use separate signal stacks with
different page table mappings.  I seem to recall that this was a possible attack
vector for your Oakland paper too, as it will allow unmasking the PC.

- Preventing spills won’t work if you’re not in a leaf function, as you don’t
know if the callee will will spill callee-save registers that you’ve put your
temporaries in.

The approach that I used made the compiler zero all spill slots in the return
path and added a warning if you called a non-sensitive function from a sensitive
function and stored zero in all temporary registers and unused argument
registers.  This is something that you can do entirely in the back end, as long
as you have a list of sensitive functions.  Other projects got in the way and I
never had time to do a proper security evaluation, but the approach seemed sane
to the cryptographers that I discussed it with.

David