mirror of
https://github.com/crosstool-ng/crosstool-ng.git
synced 2024-12-24 23:16:42 +00:00
256 lines
8.0 KiB
Diff
256 lines
8.0 KiB
Diff
|
From bf3eeaa0182a92987570d9c787bd45079eebf528 Mon Sep 17 00:00:00 2001
|
||
|
From: Richard Sandiford <richard.sandiford@arm.com>
|
||
|
Date: Thu, 15 Jun 2023 19:16:52 +0100
|
||
|
Subject: [PATCH 30/30] aarch64: Make stack smash canary protect saved
|
||
|
registers
|
||
|
|
||
|
AArch64 normally puts the saved registers near the bottom of the frame,
|
||
|
immediately above any dynamic allocations. But this means that a
|
||
|
stack-smash attack on those dynamic allocations could overwrite the
|
||
|
saved registers without needing to reach as far as the stack smash
|
||
|
canary.
|
||
|
|
||
|
The same thing could also happen for variable-sized arguments that are
|
||
|
passed by value, since those are allocated before a call and popped on
|
||
|
return.
|
||
|
|
||
|
This patch avoids that by putting the locals (and thus the canary) below
|
||
|
the saved registers when stack smash protection is active.
|
||
|
|
||
|
The patch fixes CVE-2023-4039.
|
||
|
|
||
|
gcc/
|
||
|
* config/aarch64/aarch64.c (aarch64_save_regs_above_locals_p):
|
||
|
New function.
|
||
|
(aarch64_layout_frame): Use it to decide whether locals should
|
||
|
go above or below the saved registers.
|
||
|
(aarch64_expand_prologue): Update stack layout comment.
|
||
|
Emit a stack tie after the final adjustment.
|
||
|
|
||
|
gcc/testsuite/
|
||
|
* gcc.target/aarch64/stack-protector-8.c: New test.
|
||
|
* gcc.target/aarch64/stack-protector-9.c: Likewise.
|
||
|
---
|
||
|
gcc/config/aarch64/aarch64.c | 46 +++++++++++++--
|
||
|
.../gcc.target/aarch64/stack-protector-8.c | 58 +++++++++++++++++++
|
||
|
.../gcc.target/aarch64/stack-protector-9.c | 33 +++++++++++
|
||
|
3 files changed, 133 insertions(+), 4 deletions(-)
|
||
|
create mode 100644 gcc/testsuite/gcc.target/aarch64/stack-protector-8.c
|
||
|
create mode 100644 gcc/testsuite/gcc.target/aarch64/stack-protector-9.c
|
||
|
|
||
|
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
|
||
|
index 705f719a2eaa..3d094214fac2 100644
|
||
|
--- a/gcc/config/aarch64/aarch64.c
|
||
|
+++ b/gcc/config/aarch64/aarch64.c
|
||
|
@@ -4622,6 +4622,20 @@ aarch64_needs_frame_chain (void)
|
||
|
return aarch64_use_frame_pointer;
|
||
|
}
|
||
|
|
||
|
+/* Return true if the current function should save registers above
|
||
|
+ the locals area, rather than below it. */
|
||
|
+
|
||
|
+static bool
|
||
|
+aarch64_save_regs_above_locals_p ()
|
||
|
+{
|
||
|
+ /* When using stack smash protection, make sure that the canary slot
|
||
|
+ comes between the locals and the saved registers. Otherwise,
|
||
|
+ it would be possible for a carefully sized smash attack to change
|
||
|
+ the saved registers (particularly LR and FP) without reaching the
|
||
|
+ canary. */
|
||
|
+ return crtl->stack_protect_guard;
|
||
|
+}
|
||
|
+
|
||
|
/* Mark the registers that need to be saved by the callee and calculate
|
||
|
the size of the callee-saved registers area and frame record (both FP
|
||
|
and LR may be omitted). */
|
||
|
@@ -4686,6 +4700,16 @@ aarch64_layout_frame (void)
|
||
|
|
||
|
cfun->machine->frame.bytes_below_hard_fp = crtl->outgoing_args_size;
|
||
|
|
||
|
+ bool regs_at_top_p = aarch64_save_regs_above_locals_p ();
|
||
|
+
|
||
|
+ if (regs_at_top_p)
|
||
|
+ {
|
||
|
+ cfun->machine->frame.bytes_below_hard_fp += get_frame_size ();
|
||
|
+ cfun->machine->frame.bytes_below_hard_fp
|
||
|
+ = aligned_upper_bound (cfun->machine->frame.bytes_below_hard_fp,
|
||
|
+ STACK_BOUNDARY / BITS_PER_UNIT);
|
||
|
+ }
|
||
|
+
|
||
|
#define ALLOCATE_GPR_SLOT(REGNO) \
|
||
|
do \
|
||
|
{ \
|
||
|
@@ -4758,9 +4782,11 @@ aarch64_layout_frame (void)
|
||
|
HOST_WIDE_INT varargs_and_saved_regs_size
|
||
|
= offset + cfun->machine->frame.saved_varargs_size;
|
||
|
|
||
|
+ cfun->machine->frame.bytes_above_hard_fp = varargs_and_saved_regs_size;
|
||
|
+ if (!regs_at_top_p)
|
||
|
+ cfun->machine->frame.bytes_above_hard_fp += get_frame_size ();
|
||
|
cfun->machine->frame.bytes_above_hard_fp
|
||
|
- = aligned_upper_bound (varargs_and_saved_regs_size
|
||
|
- + get_frame_size (),
|
||
|
+ = aligned_upper_bound (cfun->machine->frame.bytes_above_hard_fp,
|
||
|
STACK_BOUNDARY / BITS_PER_UNIT);
|
||
|
|
||
|
/* Both these values are already aligned. */
|
||
|
@@ -4772,6 +4798,9 @@ aarch64_layout_frame (void)
|
||
|
|
||
|
cfun->machine->frame.bytes_above_locals
|
||
|
= cfun->machine->frame.saved_varargs_size;
|
||
|
+ if (regs_at_top_p)
|
||
|
+ cfun->machine->frame.bytes_above_locals
|
||
|
+ += cfun->machine->frame.saved_regs_size;
|
||
|
|
||
|
cfun->machine->frame.initial_adjust = 0;
|
||
|
cfun->machine->frame.final_adjust = 0;
|
||
|
@@ -5764,10 +5793,10 @@ aarch64_add_cfa_expression (rtx_insn *insn, unsigned int reg,
|
||
|
| for register varargs |
|
||
|
| |
|
||
|
+-------------------------------+
|
||
|
- | local variables | <-- frame_pointer_rtx
|
||
|
+ | local variables (1) | <-- frame_pointer_rtx
|
||
|
| |
|
||
|
+-------------------------------+
|
||
|
- | padding | \
|
||
|
+ | padding (1) | \
|
||
|
+-------------------------------+ |
|
||
|
| callee-saved registers | | frame.saved_regs_size
|
||
|
+-------------------------------+ |
|
||
|
@@ -5775,6 +5804,10 @@ aarch64_add_cfa_expression (rtx_insn *insn, unsigned int reg,
|
||
|
+-------------------------------+ |
|
||
|
| FP' | / <- hard_frame_pointer_rtx (aligned)
|
||
|
+-------------------------------+
|
||
|
+ | local variables (2) |
|
||
|
+ +-------------------------------+
|
||
|
+ | padding (2) |
|
||
|
+ +-------------------------------+
|
||
|
| dynamic allocation |
|
||
|
+-------------------------------+
|
||
|
| padding |
|
||
|
@@ -5784,6 +5817,9 @@ aarch64_add_cfa_expression (rtx_insn *insn, unsigned int reg,
|
||
|
+-------------------------------+
|
||
|
| | <-- stack_pointer_rtx (aligned)
|
||
|
|
||
|
+ The regions marked (1) and (2) are mutually exclusive. (2) is used
|
||
|
+ when aarch64_save_regs_above_locals_p is true.
|
||
|
+
|
||
|
Dynamic stack allocations via alloca() decrease stack_pointer_rtx
|
||
|
but leave frame_pointer_rtx and hard_frame_pointer_rtx
|
||
|
unchanged.
|
||
|
@@ -5937,6 +5973,8 @@ aarch64_expand_prologue (void)
|
||
|
that is assumed by the called. */
|
||
|
aarch64_allocate_and_probe_stack_space (tmp1_rtx, tmp0_rtx, final_adjust,
|
||
|
!frame_pointer_needed, true);
|
||
|
+ if (emit_frame_chain && maybe_ne (final_adjust, 0))
|
||
|
+ emit_insn (gen_stack_tie (stack_pointer_rtx, hard_frame_pointer_rtx));
|
||
|
}
|
||
|
|
||
|
/* Return TRUE if we can use a simple_return insn.
|
||
|
diff --git a/gcc/testsuite/gcc.target/aarch64/stack-protector-8.c b/gcc/testsuite/gcc.target/aarch64/stack-protector-8.c
|
||
|
new file mode 100644
|
||
|
index 000000000000..c5e7deef6c10
|
||
|
--- /dev/null
|
||
|
+++ b/gcc/testsuite/gcc.target/aarch64/stack-protector-8.c
|
||
|
@@ -0,0 +1,58 @@
|
||
|
+/* { dg-options " -O -fstack-protector-strong -mstack-protector-guard=sysreg -mstack-protector-guard-reg=tpidr2_el0 -mstack-protector-guard-offset=16" } */
|
||
|
+/* { dg-final { check-function-bodies "**" "" } } */
|
||
|
+
|
||
|
+void g(void *);
|
||
|
+
|
||
|
+/*
|
||
|
+** test1:
|
||
|
+** sub sp, sp, #288
|
||
|
+** stp x29, x30, \[sp, #?272\]
|
||
|
+** add x29, sp, #?272
|
||
|
+** mrs (x[0-9]+), tpidr2_el0
|
||
|
+** ldr (x[0-9]+), \[\1, #?16\]
|
||
|
+** str \2, \[sp, #?264\]
|
||
|
+** mov \2, *0
|
||
|
+** add x0, sp, #?8
|
||
|
+** bl g
|
||
|
+** ...
|
||
|
+** mrs .*
|
||
|
+** ...
|
||
|
+** bne .*
|
||
|
+** ...
|
||
|
+** ldp x29, x30, \[sp, #?272\]
|
||
|
+** add sp, sp, #?288
|
||
|
+** ret
|
||
|
+** bl __stack_chk_fail
|
||
|
+*/
|
||
|
+int test1() {
|
||
|
+ int y[0x40];
|
||
|
+ g(y);
|
||
|
+ return 1;
|
||
|
+}
|
||
|
+
|
||
|
+/*
|
||
|
+** test2:
|
||
|
+** stp x29, x30, \[sp, #?-16\]!
|
||
|
+** mov x29, sp
|
||
|
+** sub sp, sp, #1040
|
||
|
+** mrs (x[0-9]+), tpidr2_el0
|
||
|
+** ldr (x[0-9]+), \[\1, #?16\]
|
||
|
+** str \2, \[sp, #?1032\]
|
||
|
+** mov \2, *0
|
||
|
+** add x0, sp, #?8
|
||
|
+** bl g
|
||
|
+** ...
|
||
|
+** mrs .*
|
||
|
+** ...
|
||
|
+** bne .*
|
||
|
+** ...
|
||
|
+** add sp, sp, #?1040
|
||
|
+** ldp x29, x30, \[sp\], #?16
|
||
|
+** ret
|
||
|
+** bl __stack_chk_fail
|
||
|
+*/
|
||
|
+int test2() {
|
||
|
+ int y[0x100];
|
||
|
+ g(y);
|
||
|
+ return 1;
|
||
|
+}
|
||
|
diff --git a/gcc/testsuite/gcc.target/aarch64/stack-protector-9.c b/gcc/testsuite/gcc.target/aarch64/stack-protector-9.c
|
||
|
new file mode 100644
|
||
|
index 000000000000..58f322aa480a
|
||
|
--- /dev/null
|
||
|
+++ b/gcc/testsuite/gcc.target/aarch64/stack-protector-9.c
|
||
|
@@ -0,0 +1,33 @@
|
||
|
+/* { dg-options "-O2 -mcpu=neoverse-v1 -fstack-protector-all" } */
|
||
|
+/* { dg-final { check-function-bodies "**" "" } } */
|
||
|
+
|
||
|
+/*
|
||
|
+** main:
|
||
|
+** ...
|
||
|
+** stp x29, x30, \[sp, #?-[0-9]+\]!
|
||
|
+** ...
|
||
|
+** sub sp, sp, #[0-9]+
|
||
|
+** ...
|
||
|
+** str x[0-9]+, \[x29, #?-8\]
|
||
|
+** ...
|
||
|
+*/
|
||
|
+int f(const char *);
|
||
|
+void g(void *);
|
||
|
+int main(int argc, char* argv[])
|
||
|
+{
|
||
|
+ int a;
|
||
|
+ int b;
|
||
|
+ char c[2+f(argv[1])];
|
||
|
+ int d[0x100];
|
||
|
+ char y;
|
||
|
+
|
||
|
+ y=42; a=4; b=10;
|
||
|
+ c[0] = 'h'; c[1] = '\0';
|
||
|
+
|
||
|
+ c[f(argv[2])] = '\0';
|
||
|
+
|
||
|
+ __builtin_printf("%d %d\n%s\n", a, b, c);
|
||
|
+ g(d);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
--
|
||
|
2.42.0
|
||
|
|