r329122 - Add the -fsanitize=shadow-call-stack flag

Vlad Tsyrklevich via cfe-commits cfe-commits at lists.llvm.org
Tue Apr 3 15:33:53 PDT 2018


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 \




More information about the cfe-commits mailing list