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