PING [Patch][Middle-end]Add -fzero-call-used-regs=[skip|used-gpr|all-gpr|used|all]

Qing Zhao QING.ZHAO@ORACLE.COM
Wed Aug 19 20:05:36 GMT 2020


Hi,

Based on all the previous discussion and more extensive study on ROP and its mitigation techniques these days, I came up with the following
High-level proposal as requested, please take a look and let me know what I should change in this high-level design:

> On Aug 6, 2020, at 6:37 PM, Segher Boessenkool <segher@kernel.crashing.org> wrote:
> 
> Anyway.  This all needs a good description in the user manual (is there?
> I couldn't find any), explaining what exactly it does (user-visible),
> and when you would want to use it, etc.  We need that before we can
> review anything else in this patch sanely.
> 
> 
> Segher

zeroing call-used registers for security purpose

8/19/2020
Qing Zhao
=========================================

**Motivation:
There are two purposes of this patch:

1. ROP mitigation:

ROP (Return-oriented programming, https://en.wikipedia.org/wiki/Return-oriented_programming) is 
one of the most popular code reuse attack technique, which executes gadget chains to perform malicious tasks.
A gadget is a carefully chosen machine instruction sequence that is already present in the machines' memory. 
Each gadget typically ends in a return instruction and is located in a subroutine within the existing program 
and/or shared library code.

There are two variations that use gadgets that end with indirect call (COP, Call Oriented Programming )
 and jump instruction (JOP, Jump-Oriented Programming). However, performing ROP without return 
instructions in reality is difficult because the gadgets of COP and JOP that can form a completed chain 
are almost nonexistent. 

As a result, gadgets based on return instructions remain the most popular.

One important feature of ROP attack is (Clean the Scratch Registers:A Way to Mitigate Return-Oriented
Programming Attacks https://ieeexplore.ieee.org/document/8445132):
the destination of using gadget chains usually call system functions to perform malicious behaviour,
on many of the mordern architectures, the registers would be used to pass parameters for those 
system functions.

So, cleaning the scratch registers that are used to pass parameters at return instructions should 
effectively mitigate ROP attack. 

2. Register Erasure:

In the SECURE project and GCC (https://gcc.gnu.org/wiki/cauldron2018#secure)

One of the well known security techniques is stack and register erasure. 
Ensuring that on return from a function, no data is left lying on the stack or in registers.

As mentioned in the slides (https://gmarkall.files.wordpress.com/2018/09/secure_and_gcc.pdf), 
there is a seperate project that tried to resolve the stack erasure problem. and the patch for
 stack erasure had been ready to submit. That specific patch does not handle register erasure problem. 

So, we will also address the register erasure problem with this patch along with the ROP mitigation. 

** Questions and Answers:

Q1. Which registers should be set to zeros at the return of the function?
A. the caller-saved, i.e, call-used, or call-clobbered registers.
   For ROP mitigation purpose, only the call-used registers that pass
parameters need to be zeroed. 
   For register erasure purpose, all the call-used registers might need to
be zeroed. we can provide multiple levels to user for controling the runtime
overhead. 

Q2. Why zeroing the registers other than randomalize them?
A. (From Kees Cook)
    In the performance analysis I looked at a while ago, doing the
register-self-xor is extremely fast to run (IIRC the cycle counts on x86
were absolutely tiny), and it's smaller for code size which minimized
the overall image footprint.
    a fixed value is a significantly better defensive position to take
for ROP. And specifically zero _tends_ to be the safest choice as it's
less "useful" to be used as a size, index, or pointer. And, no, it is
not perfect, but nothing can be if we're dealing with trying to defend
against arbitrary ROP gadget finding (or uninitialized stack contents,
where the same argument for "zero is best" also holds[1]).

-Kees
([1]https://lists.llvm.org/pipermail/cfe-dev/2020-April/065221.html)

    So, from both run-time performance and code-size aspects, setting the
registers to zero is a better approach. 

** Proposal:

We will provide a new feature into GCC for the above security purposes. 

Add -fzero-call-used-regs=[skip|rop-mitigation|used-gpr|all-gpr|used|all] command-line option
and 
zero_call_used_regs("skip|used-arg-gpr|used-arg|arg|used-gpr|all-gpr|used|all") function attribues:

    1. -mzero-call-used-regs=skip and zero_call_used_regs("skip")

    Don't zero call-used registers upon function return. This is the default behavior.

    2. -mzero-call-used-regs=used-arg-gpr and zero_call_used_regs("used-arg-gpr")

    Zero used call-used general purpose registers that are used to pass parameters upon function return.
    
    3. -mzero-call-used-regs=used-arg and zero_call_used_regs("used-arg")

    Zero used call-used registers that are used to pass parameters upon function return.

    4. -mzero-call-used-regs=arg and zero_call_used_regs("arg")

    Zero all call-used registers that are used to pass parameters upon function return.

    5. -mzero-call-used-regs=used-gpr and zero_call_used_regs("used-gpr")

    Zero used call-used general purpose registers upon function return.

    6. -mzero-call-used-regs=all-gpr and zero_call_used_regs("all-gpr")

    Zero all call-used general purpose registers upon function return.

    7. -mzero-call-used-regs=used and zero_call_used_regs("used")

    Zero used call-used registers upon function return.

    8. -mzero-call-used-regs=all and zero_call_used_regs("all")

    Zero all call-used registers upon function return.


Zero call-used registers at function return to increase the program
security by either mitigating Return-Oriented Programming (ROP) or 
preventing information leak through registers.  

@samp{skip}, which is the default, doesn't zero call-used registers. 

@samp{used-arg-gpr} zeros used call-used general purpose registers that 
pass parameters. @samp{used-arg} zeros used call-used registers that 
pass parameters. @samp{arg} zeros all call-used registers that pass
parameters. These 3 choices are used for ROP mitigation. 

@samp{used-gpr} zeros call-used general purpose registers 
which are used in function.  @samp{all-gpr} zeros all
call-used registers.  @samp{used} zeros call-used registers which
are used in function.  @samp{all} zeros all call-used registers. 
These 4 choices are used for preventing information leak through 
registers. 

You can control this behavior for a specific function by using the function
attribute @code{zero_call_used_regs}.  @xref{Function Attributes}.




More information about the Gcc-patches mailing list