r329236 - AArch64: Implement support for the shadowcallstack attribute.
Peter Collingbourne via cfe-commits
cfe-commits at lists.llvm.org
Wed Apr 4 14:55:44 PDT 2018
Author: pcc
Date: Wed Apr 4 14:55:44 2018
New Revision: 329236
URL: http://llvm.org/viewvc/llvm-project?rev=329236&view=rev
Log:
AArch64: Implement support for the shadowcallstack attribute.
The implementation of shadow call stack on aarch64 is quite different to
the implementation on x86_64. Instead of reserving a segment register for
the shadow call stack, we reserve the platform register, x18. Any function
that spills lr to sp also spills it to the shadow call stack, a pointer to
which is stored in x18.
Differential Revision: https://reviews.llvm.org/D45239
Modified:
cfe/trunk/docs/ShadowCallStack.rst
cfe/trunk/lib/Driver/SanitizerArgs.cpp
cfe/trunk/lib/Driver/ToolChain.cpp
cfe/trunk/test/Driver/sanitizer-ld.c
Modified: cfe/trunk/docs/ShadowCallStack.rst
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ShadowCallStack.rst?rev=329236&r1=329235&r2=329236&view=diff
==============================================================================
--- cfe/trunk/docs/ShadowCallStack.rst (original)
+++ cfe/trunk/docs/ShadowCallStack.rst Wed Apr 4 14:55:44 2018
@@ -9,11 +9,11 @@ Introduction
============
ShadowCallStack is an **experimental** instrumentation pass, currently only
-implemented for x86_64, that protects programs against return address
-overwrites (e.g. stack buffer overflows.) It works by saving a function's return
-address to a separately allocated 'shadow call stack' in the function prolog and
-checking the return address on the stack against the shadow call stack in the
-function epilog.
+implemented for x86_64 and aarch64, that protects programs against return
+address overwrites (e.g. stack buffer overflows.) It works by saving a
+function's return address to a separately allocated 'shadow call stack'
+in the function prolog and checking the return address on the stack against
+the shadow call stack in the function epilog.
Comparison
----------
@@ -37,8 +37,16 @@ support.
Compatibility
-------------
-ShadowCallStack currently only supports x86_64. A runtime is not currently
-provided in compiler-rt so one must be provided by the compiled application.
+ShadowCallStack currently only supports x86_64 and aarch64. A runtime is not
+currently provided in compiler-rt so one must be provided by the compiled
+application.
+
+On aarch64, the instrumentation makes use of the platform register ``x18``.
+On some platforms, ``x18`` is reserved, and on others, it is designated as
+a scratch register. This generally means that any code that may run on the
+same thread as code compiled with ShadowCallStack must either target one
+of the platforms whose ABI reserves ``x18`` (currently Darwin, Fuchsia and
+Windows) or be compiled with the flag ``-ffixed-x18``.
Security
========
@@ -56,28 +64,37 @@ has been checked and before it has been
semantics to fix this on x86_64 would incur an unacceptable performance overhead
due to return branch prediction.
-The instrumentation makes use of the ``gs`` segment register to reference the
-shadow call stack meaning that references to the shadow call stack do not have
-to be stored in memory. This makes it possible to implement a runtime that
-avoids exposing the address of the shadow call stack to attackers that can read
-arbitrary memory. However, attackers could still try to exploit side channels
-exposed by the operating system `[1]`_ `[2]`_ or processor `[3]`_ to discover
-the address of the shadow call stack.
+The instrumentation makes use of the ``gs`` segment register on x86_64,
+or the ``x18`` register on aarch64, to reference the shadow call stack
+meaning that references to the shadow call stack do not have to be stored in
+memory. This makes it possible to implement a runtime that avoids exposing
+the address of the shadow call stack to attackers that can read arbitrary
+memory. However, attackers could still try to exploit side channels exposed
+by the operating system `[1]`_ `[2]`_ or processor `[3]`_ to discover the
+address of the shadow call stack.
.. _`[1]`: https://eyalitkin.wordpress.com/2017/09/01/cartography-lighting-up-the-shadows/
.. _`[2]`: https://www.blackhat.com/docs/eu-16/materials/eu-16-Goktas-Bypassing-Clangs-SafeStack.pdf
.. _`[3]`: https://www.vusec.net/projects/anc/
-Leaf functions are optimized to store the return address in a free register
-and avoid writing to the shadow call stack if a register is available. Very
-short leaf functions are uninstrumented if their execution is judged to be
-shorter than the race condition window intrinsic to the instrumentation.
+On x86_64, leaf functions are optimized to store the return address in a
+free register and avoid writing to the shadow call stack if a register is
+available. Very short leaf functions are uninstrumented if their execution
+is judged to be shorter than the race condition window intrinsic to the
+instrumentation.
+
+On aarch64, the architecture's call and return instructions (``bl`` and
+``ret``) operate on a register rather than the stack, which means that
+leaf functions are generally protected from return address overwrites even
+without ShadowCallStack. It also means that ShadowCallStack on aarch64 is not
+vulnerable to the same types of time-of-check-to-time-of-use races as x86_64.
Usage
=====
-To enable ShadowCallStack, just pass the ``-fsanitize=shadow-call-stack`` flag
-to both compile and link command lines.
+To enable ShadowCallStack, just pass the ``-fsanitize=shadow-call-stack``
+flag to both compile and link command lines. On aarch64, you also need to pass
+``-ffixed-x18`` unless your target already reserves ``x18``.
Low-level API
-------------
@@ -125,7 +142,20 @@ Generates the following x86_64 assembly
pop %rcx
retq
-Adding ``-fsanitize=shadow-call-stack`` would output the following:
+or the following aarch64 assembly:
+
+.. code-block:: none
+
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl bar
+ add w0, w0, #1
+ ldp x29, x30, [sp], #16
+ ret
+
+
+Adding ``-fsanitize=shadow-call-stack`` would output the following x86_64
+assembly:
.. code-block:: gas
@@ -148,3 +178,16 @@ Adding ``-fsanitize=shadow-call-stack``
trap:
ud2
+
+or the following aarch64 assembly:
+
+.. code-block:: none
+
+ str x30, [x18], #8
+ stp x29, x30, [sp, #-16]!
+ mov x29, sp
+ bl bar
+ add w0, w0, #1
+ ldp x29, x30, [sp], #16
+ ldr x30, [x18, #-8]!
+ ret
Modified: cfe/trunk/lib/Driver/SanitizerArgs.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/SanitizerArgs.cpp?rev=329236&r1=329235&r2=329236&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/SanitizerArgs.cpp (original)
+++ cfe/trunk/lib/Driver/SanitizerArgs.cpp Wed Apr 4 14:55:44 2018
@@ -18,6 +18,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/SpecialCaseList.h"
+#include "llvm/Support/TargetParser.h"
#include <memory>
using namespace clang;
@@ -375,6 +376,15 @@ SanitizerArgs::SanitizerArgs(const ToolC
<< lastArgumentForMask(D, Args, Kinds & NeedsLTO) << "-flto";
}
+ if ((Kinds & ShadowCallStack) &&
+ TC.getTriple().getArch() == llvm::Triple::aarch64 &&
+ !llvm::AArch64::isX18ReservedByDefault(TC.getTriple()) &&
+ !Args.hasArg(options::OPT_ffixed_x18)) {
+ D.Diag(diag::err_drv_argument_only_allowed_with)
+ << lastArgumentForMask(D, Args, Kinds & ShadowCallStack)
+ << "-ffixed-x18";
+ }
+
// Report error if there are non-trapping sanitizers that require
// c++abi-specific parts of UBSan runtime, and they are not provided by the
// toolchain. We don't have a good way to check the latter, so we just
Modified: cfe/trunk/lib/Driver/ToolChain.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/ToolChain.cpp?rev=329236&r1=329235&r2=329236&view=diff
==============================================================================
--- cfe/trunk/lib/Driver/ToolChain.cpp (original)
+++ cfe/trunk/lib/Driver/ToolChain.cpp Wed Apr 4 14:55:44 2018
@@ -814,7 +814,8 @@ SanitizerMask ToolChain::getSupportedSan
getTriple().getArch() == llvm::Triple::wasm32 ||
getTriple().getArch() == llvm::Triple::wasm64)
Res |= CFIICall;
- if (getTriple().getArch() == llvm::Triple::x86_64)
+ if (getTriple().getArch() == llvm::Triple::x86_64 ||
+ getTriple().getArch() == llvm::Triple::aarch64)
Res |= ShadowCallStack;
return Res;
}
Modified: cfe/trunk/test/Driver/sanitizer-ld.c
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/sanitizer-ld.c?rev=329236&r1=329235&r2=329236&view=diff
==============================================================================
--- cfe/trunk/test/Driver/sanitizer-ld.c (original)
+++ cfe/trunk/test/Driver/sanitizer-ld.c Wed Apr 4 14:55:44 2018
@@ -563,6 +563,19 @@
// CHECK-SHADOWCALLSTACK-LINUX-X86-64-NOT: error:
// RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \
+// RUN: -target aarch64-unknown-linux -fuse-ld=ld \
+// RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-LINUX-AARCH64 %s
+// CHECK-SHADOWCALLSTACK-LINUX-AARCH64: '-fsanitize=shadow-call-stack' only allowed with '-ffixed-x18'
+
+// RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \
+// RUN: -target aarch64-unknown-linux -fuse-ld=ld -ffixed-x18 \
+// RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-LINUX-AARCH64-X18 %s
+// RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \
+// RUN: -target arm64-unknown-ios -fuse-ld=ld \
+// RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-LINUX-AARCH64-X18 %s
+// CHECK-SHADOWCALLSTACK-LINUX-AARCH64-X18-NOT: error:
+
+// RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \
// RUN: -target x86-unknown-linux -fuse-ld=ld \
// RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-LINUX-X86 %s
// CHECK-SHADOWCALLSTACK-LINUX-X86: error: unsupported option '-fsanitize=shadow-call-stack' for target 'x86-unknown-linux'
More information about the cfe-commits
mailing list