I've found a testcase where the stack protector code generated through
`-fstack-protector-all` doesn't actually protect anything.

#+name stack-reorder.c
#+begin_src c
#include <string.h>
#include <stdio.h>
int foo (int a, int b, int c) {
     char buf[64];
     buf[a] = 1;
     buf[b] = c;

     // Just add something so that the assignments above have some
     // observable behaviour.
     int retval = 0;
     for (size_t i = 0; i < 32; i++)
     {
         retval += buf[i];
     }
     return retval;
}
#+end_src

When compiling on aarch64 with
~gcc -fstack-protector-all -g -S stack-reorder.c  -o test.s -O3
--save-temps -fdump-rtl-all~
(with ~gcc (GCC) 9.0.0 20181214 (experimental)~)

We get an RTL dump on the final pass that has the snippet
#+begin_example
(insn 8 21 130 (parallel [
             (set (mem/v/f/c:DI (plus:DI (reg/f:DI 31 sp)
                         (const_int 88 [0x58])) [1 D.4227+0 S8 A64])
                 (unspec:DI [
                         (mem/v/f/c:DI (reg/f:DI 0 x0 [116]) [1
__stack_chk_guard+0 S8 A64])
                     ] UNSPEC_SP_SET))
             (set (reg:DI 1 x1 [141])
                 (const_int 0 [0]))
         ]) "stack-reorder.c":3:31 1046 {stack_protect_set_di}
      (expr_list:REG_UNUSED (reg:DI 1 x1 [141])
         (nil)))
(note 130 8 117 (var_location b (entry_value:SI (reg:SI 1 x1 [ b ])))
NOTE_INSN_VAR_LOCATION)
(note 117 130 118 stack-reorder.c:4 NOTE_INSN_BEGIN_STMT)
(note 118 117 119 stack-reorder.c:5 NOTE_INSN_BEGIN_STMT)
(note 119 118 120 stack-reorder.c:6 NOTE_INSN_BEGIN_STMT)
(note 120 119 131 stack-reorder.c:10 NOTE_INSN_BEGIN_STMT)
(note 131 120 121 (var_location retval (const_int 0 [0]))
NOTE_INSN_VAR_LOCATION)
(note 121 131 144 stack-reorder.c:11 NOTE_INSN_BEGIN_STMT)
(note 144 121 122 0xffffb76fd960 NOTE_INSN_BLOCK_BEG)
(note 122 144 132 stack-reorder.c:11 NOTE_INSN_BEGIN_STMT)
(note 132 122 123 (var_location retval (nil)) NOTE_INSN_VAR_LOCATION)
(note 123 132 145 stack-reorder.c:13 NOTE_INSN_BEGIN_STMT)
(note 145 123 75 0xffffb76fd960 NOTE_INSN_BLOCK_END)
(insn:TI 75 145 133 (parallel [
             (set (reg:DI 1 x1 [137])
                 (unspec:DI [
                         (mem/v/f/c:DI (plus:DI (reg/f:DI 31 sp)
                                 (const_int 88 [0x58])) [1 D.4227+0 S8 A64])
                         (mem/v/f/c:DI (reg/f:DI 0 x0 [116]) [1
__stack_chk_guard+0 S8 A64])
                     ] UNSPEC_SP_TEST))
             (clobber (reg:DI 2 x2 [142]))
         ]) "stack-reorder.c":16:1 1048 {stack_protect_test_di}
      (expr_list:REG_DEAD (reg/f:DI 0 x0 [116])
         (expr_list:REG_UNUSED (reg:DI 2 x2 [142])
             (nil))))
#+end_example

In this snippet the stack protect set and test patterns are right next to each
other, causing the stack protector to essentially do nothing.
The RTL insns to set the two elements in `buf[]` are left after this snippet.

The stack_protect_set and stack_protect_test patterns are put together in the
sched1 pass (as seen by the change in the RTL between the previous dump and
that one).

I would like to know what is supposed to stop RTL from the stack_protect_set
pattern from being reordered around the code it protects like this?

I recognise this is an unlikely pattern of code and that it doesn't present as
much of a security risk as things like calling memcpy or setting memory through
some sort of loop.
Could the reasoning be that for those patterns likely to cause a security risk
the rescheduling is stopped by jumps/labels/calls?


Reply via email to