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' _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits