Author: vlad.tsyrklevich Date: Tue Apr 3 15:33:53 2018 New Revision: 329122
URL: http://llvm.org/viewvc/llvm-project?rev=329122&view=rev Log: Add the -fsanitize=shadow-call-stack flag Summary: Add support for the -fsanitize=shadow-call-stack flag which causes clang to add ShadowCallStack attribute to functions compiled with that flag enabled. Reviewers: pcc, kcc Reviewed By: pcc, kcc Subscribers: cryptoad, cfe-commits, kcc Differential Revision: https://reviews.llvm.org/D44801 Added: cfe/trunk/docs/ShadowCallStack.rst cfe/trunk/test/CodeGen/shadowcallstack-attr.c Modified: cfe/trunk/docs/index.rst cfe/trunk/include/clang/Basic/Sanitizers.def cfe/trunk/lib/CodeGen/CGDeclCXX.cpp cfe/trunk/lib/CodeGen/CodeGenFunction.cpp cfe/trunk/lib/Driver/SanitizerArgs.cpp cfe/trunk/lib/Driver/ToolChain.cpp cfe/trunk/lib/Lex/PPMacroExpansion.cpp cfe/trunk/test/Driver/sanitizer-ld.c Added: cfe/trunk/docs/ShadowCallStack.rst URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/ShadowCallStack.rst?rev=329122&view=auto ============================================================================== --- cfe/trunk/docs/ShadowCallStack.rst (added) +++ cfe/trunk/docs/ShadowCallStack.rst Tue Apr 3 15:33:53 2018 @@ -0,0 +1,150 @@ +=============== +ShadowCallStack +=============== + +.. contents:: + :local: + +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. + +Comparison +---------- + +To optimize for memory consumption and cache locality, the shadow call stack +stores an index followed by an array of return addresses. This is in contrast +to other schemes, like :doc:`SafeStack`, that mirror the entire stack and +trade-off consuming more memory for shorter function prologs and epilogs with +fewer memory accesses. Similarly, `Return Flow Guard`_ consumes more memory with +shorter function prologs and epilogs than ShadowCallStack but suffers from the +same race conditions (see `Security`_). Intel `Control-flow Enforcement Technology`_ +(CET) is a proposed hardware extension that would add native support to +use a shadow stack to store/check return addresses at call/return time. It +would not suffer from race conditions at calls and returns and not incur the +overhead of function instrumentation, but it does require operating system +support. + +.. _`Return Flow Guard`: https://xlab.tencent.com/en/2016/11/02/return-flow-guard/ +.. _`Control-flow Enforcement Technology`: https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf + +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. + +Security +======== + +ShadowCallStack is intended to be a stronger alternative to +``-fstack-protector``. It protects from non-linear overflows and arbitrary +memory writes to the return address slot; however, similarly to +``-fstack-protector`` this protection suffers from race conditions because of +the call-return semantics on x86_64. There is a short race between the call +instruction and the first instruction in the function that reads the return +address where an attacker could overwrite the return address and bypass +ShadowCallStack. Similarly, there is a time-of-check-to-time-of-use race in the +function epilog where an attacker could overwrite the return address after it +has been checked and before it has been returned to. Modifying the call-return +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. + +.. _`[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. + +Usage +===== + +To enable ShadowCallStack, just pass the ``-fsanitize=shadow-call-stack`` flag +to both compile and link command lines. + +Low-level API +------------- + +``__has_feature(shadow_call_stack)`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In some cases one may need to execute different code depending on whether +ShadowCallStack is enabled. The macro ``__has_feature(shadow_call_stack)`` can +be used for this purpose. + +.. code-block:: c + + #if defined(__has_feature) + # if __has_feature(shadow_call_stack) + // code that builds only under ShadowCallStack + # endif + #endif + +``__attribute__((no_sanitize("shadow-call-stack")))`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Use ``__attribute__((no_sanitize("shadow-call-stack")))`` on a function +declaration to specify that the shadow call stack instrumentation should not be +applied to that function, even if enabled globally. + +Example +======= + +The following example code: + +.. code-block:: c++ + + int foo() { + return bar() + 1; + } + +Generates the following x86_64 assembly when compiled with ``-O2``: + +.. code-block:: gas + + push %rax + callq foo + add $0x1,%eax + pop %rcx + retq + +Adding ``-fsanitize=shadow-call-stack`` would output the following: + +.. code-block:: gas + + mov (%rsp),%r10 + xor %r11,%r11 + addq $0x8,%gs:(%r11) + mov %gs:(%r11),%r11 + mov %r10,%gs:(%r11) + push %rax + callq foo + add $0x1,%eax + pop %rcx + xor %r11,%r11 + mov %gs:(%r11),%r10 + mov %gs:(%r10),%r10 + subq $0x8,%gs:(%r11) + cmp %r10,(%rsp) + jne trap + retq + + trap: + ud2 Modified: cfe/trunk/docs/index.rst URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/docs/index.rst?rev=329122&r1=329121&r2=329122&view=diff ============================================================================== --- cfe/trunk/docs/index.rst (original) +++ cfe/trunk/docs/index.rst Tue Apr 3 15:33:53 2018 @@ -36,6 +36,7 @@ Using Clang as a Compiler ControlFlowIntegrity LTOVisibility SafeStack + ShadowCallStack SourceBasedCodeCoverage Modules MSVCCompatibility Modified: cfe/trunk/include/clang/Basic/Sanitizers.def URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Basic/Sanitizers.def?rev=329122&r1=329121&r2=329122&view=diff ============================================================================== --- cfe/trunk/include/clang/Basic/Sanitizers.def (original) +++ cfe/trunk/include/clang/Basic/Sanitizers.def Tue Apr 3 15:33:53 2018 @@ -110,6 +110,9 @@ SANITIZER_GROUP("cfi", CFI, // Safe Stack SANITIZER("safe-stack", SafeStack) +// Shadow Call Stack +SANITIZER("shadow-call-stack", ShadowCallStack) + // -fsanitize=undefined includes all the sanitizers which have low overhead, no // ABI or address space layout implications, and only catch undefined behavior. SANITIZER_GROUP("undefined", Undefined, Modified: cfe/trunk/lib/CodeGen/CGDeclCXX.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDeclCXX.cpp?rev=329122&r1=329121&r2=329122&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CGDeclCXX.cpp (original) +++ cfe/trunk/lib/CodeGen/CGDeclCXX.cpp Tue Apr 3 15:33:53 2018 @@ -343,6 +343,10 @@ llvm::Function *CodeGenModule::CreateGlo !isInSanitizerBlacklist(SanitizerKind::SafeStack, Fn, Loc)) Fn->addFnAttr(llvm::Attribute::SafeStack); + if (getLangOpts().Sanitize.has(SanitizerKind::ShadowCallStack) && + !isInSanitizerBlacklist(SanitizerKind::ShadowCallStack, Fn, Loc)) + Fn->addFnAttr(llvm::Attribute::ShadowCallStack); + return Fn; } Modified: cfe/trunk/lib/CodeGen/CodeGenFunction.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CodeGenFunction.cpp?rev=329122&r1=329121&r2=329122&view=diff ============================================================================== --- cfe/trunk/lib/CodeGen/CodeGenFunction.cpp (original) +++ cfe/trunk/lib/CodeGen/CodeGenFunction.cpp Tue Apr 3 15:33:53 2018 @@ -861,6 +861,8 @@ void CodeGenFunction::StartFunction(Glob Fn->addFnAttr(llvm::Attribute::SanitizeMemory); if (SanOpts.has(SanitizerKind::SafeStack)) Fn->addFnAttr(llvm::Attribute::SafeStack); + if (SanOpts.has(SanitizerKind::ShadowCallStack)) + Fn->addFnAttr(llvm::Attribute::ShadowCallStack); // Apply fuzzing attribute to the function. if (SanOpts.hasOneOf(SanitizerKind::Fuzzer | SanitizerKind::FuzzerNoLink)) Modified: cfe/trunk/lib/Driver/SanitizerArgs.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/SanitizerArgs.cpp?rev=329122&r1=329121&r2=329122&view=diff ============================================================================== --- cfe/trunk/lib/Driver/SanitizerArgs.cpp (original) +++ cfe/trunk/lib/Driver/SanitizerArgs.cpp Tue Apr 3 15:33:53 2018 @@ -343,7 +343,10 @@ SanitizerArgs::SanitizerArgs(const ToolC std::make_pair(Scudo, Address | HWAddress | Leak | Thread | Memory | KernelAddress | Efficiency), std::make_pair(SafeStack, Address | HWAddress | Leak | Thread | Memory | - KernelAddress | Efficiency)}; + KernelAddress | Efficiency), + std::make_pair(ShadowCallStack, Address | HWAddress | Leak | Thread | + Memory | KernelAddress | Efficiency | + SafeStack)}; // Enable toolchain specific default sanitizers if not explicitly disabled. SanitizerMask Default = TC.getDefaultSanitizers() & ~AllRemove; Modified: cfe/trunk/lib/Driver/ToolChain.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Driver/ToolChain.cpp?rev=329122&r1=329121&r2=329122&view=diff ============================================================================== --- cfe/trunk/lib/Driver/ToolChain.cpp (original) +++ cfe/trunk/lib/Driver/ToolChain.cpp Tue Apr 3 15:33:53 2018 @@ -814,6 +814,8 @@ SanitizerMask ToolChain::getSupportedSan getTriple().getArch() == llvm::Triple::wasm32 || getTriple().getArch() == llvm::Triple::wasm64) Res |= CFIICall; + if (getTriple().getArch() == llvm::Triple::x86_64) + Res |= ShadowCallStack; return Res; } Modified: cfe/trunk/lib/Lex/PPMacroExpansion.cpp URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Lex/PPMacroExpansion.cpp?rev=329122&r1=329121&r2=329122&view=diff ============================================================================== --- cfe/trunk/lib/Lex/PPMacroExpansion.cpp (original) +++ cfe/trunk/lib/Lex/PPMacroExpansion.cpp Tue Apr 3 15:33:53 2018 @@ -1275,6 +1275,8 @@ static bool HasFeature(const Preprocesso .Case("is_union", LangOpts.CPlusPlus) .Case("modules", LangOpts.Modules) .Case("safe_stack", LangOpts.Sanitize.has(SanitizerKind::SafeStack)) + .Case("shadow_call_stack", + LangOpts.Sanitize.has(SanitizerKind::ShadowCallStack)) .Case("tls", PP.getTargetInfo().isTLSSupported()) .Case("underlying_type", LangOpts.CPlusPlus) .Default(false); Added: cfe/trunk/test/CodeGen/shadowcallstack-attr.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/CodeGen/shadowcallstack-attr.c?rev=329122&view=auto ============================================================================== --- cfe/trunk/test/CodeGen/shadowcallstack-attr.c (added) +++ cfe/trunk/test/CodeGen/shadowcallstack-attr.c Tue Apr 3 15:33:53 2018 @@ -0,0 +1,16 @@ +// RUN: %clang_cc1 -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=UNBLACKLISTED %s + +// RUN: %clang_cc1 -D ATTR -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLACKLISTED %s + +// RUN: echo -e "[shadow-call-stack]\nfun:foo" > %t +// RUN: %clang_cc1 -fsanitize-blacklist=%t -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=shadow-call-stack | FileCheck -check-prefix=BLACKLISTED %s + +#ifdef ATTR +__attribute__((no_sanitize("shadow-call-stack"))) +#endif +int foo(int *a) { return *a; } + +// CHECK: define i32 @foo(i32* %a) + +// BLACKLISTED-NOT: attributes {{.*}}shadowcallstack{{.*}} +// UNBLACKLISTED: attributes {{.*}}shadowcallstack{{.*}} Modified: cfe/trunk/test/Driver/sanitizer-ld.c URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Driver/sanitizer-ld.c?rev=329122&r1=329121&r2=329122&view=diff ============================================================================== --- cfe/trunk/test/Driver/sanitizer-ld.c (original) +++ cfe/trunk/test/Driver/sanitizer-ld.c Tue Apr 3 15:33:53 2018 @@ -557,6 +557,21 @@ // CHECK-SAFESTACK-LINUX: "-lpthread" // CHECK-SAFESTACK-LINUX: "-ldl" +// RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \ +// RUN: -target x86_64-unknown-linux -fuse-ld=ld \ +// RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-LINUX-X86-64 %s +// CHECK-SHADOWCALLSTACK-LINUX-X86-64-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' + +// RUN: %clang -fsanitize=shadow-call-stack %s -### -o %t.o 2>&1 \ +// RUN: -fsanitize=safe-stack -target x86_64-unknown-linux -fuse-ld=ld \ +// RUN: | FileCheck --check-prefix=CHECK-SHADOWCALLSTACK-SAFESTACK %s +// CHECK-SHADOWCALLSTACK-SAFESTACK: error: invalid argument '-fsanitize=shadow-call-stack' not allowed with '-fsanitize=safe-stack' + // RUN: %clang -fsanitize=cfi -fsanitize-stats %s -### -o %t.o 2>&1 \ // RUN: -target x86_64-unknown-linux -fuse-ld=ld \ // RUN: --sysroot=%S/Inputs/basic_linux_tree \ _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits