]> granicus.if.org Git - clang/commitdiff
Protection against stack-based memory corruption errors using SafeStack: Clang comman...
authorPeter Collingbourne <peter@pcc.me.uk>
Mon, 15 Jun 2015 21:08:13 +0000 (21:08 +0000)
committerPeter Collingbourne <peter@pcc.me.uk>
Mon, 15 Jun 2015 21:08:13 +0000 (21:08 +0000)
This patch adds the -fsanitize=safe-stack command line argument for clang,
which enables the Safe Stack protection (see http://reviews.llvm.org/D6094
for the detailed description of the Safe Stack).

This patch is our implementation of the safe stack on top of Clang. The
patches make the following changes:

- Add -fsanitize=safe-stack and -fno-sanitize=safe-stack options to clang
  to control safe stack usage (the safe stack is disabled by default).

- Add __attribute__((no_sanitize("safe-stack"))) attribute to clang that can be
  used to disable the safe stack for individual functions even when enabled
  globally.

Original patch by Volodymyr Kuznetsov and others at the Dependable Systems
Lab at EPFL; updates and upstreaming by myself.

Differential Revision: http://reviews.llvm.org/D6095

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@239762 91177308-0d34-0410-b5e6-96231b3b80d8

14 files changed:
docs/LanguageExtensions.rst
docs/SafeStack.rst [new file with mode: 0644]
docs/UsersManual.rst
docs/index.rst
include/clang/Basic/Builtins.def
include/clang/Basic/Sanitizers.def
include/clang/Driver/SanitizerArgs.h
lib/CodeGen/CGDeclCXX.cpp
lib/CodeGen/CodeGenFunction.cpp
lib/Driver/Tools.cpp
lib/Lex/PPMacroExpansion.cpp
test/CodeGen/safestack-attr.cpp [new file with mode: 0644]
test/CodeGen/stack-protector.c
test/Driver/fsanitize.c

index bd5992e3d6e505effa652bb7f047d20bd878649c..0b4775ba6ad4f2aad615ace193082bfbbacc082b 100644 (file)
@@ -1845,6 +1845,9 @@ with :doc:`ThreadSanitizer`.
 Use ``__has_feature(memory_sanitizer)`` to check if the code is being built
 with :doc:`MemorySanitizer`.
 
+Use ``__has_feature(safe_stack)`` to check if the code is being built
+with :doc:`SafeStack`.
+
 
 Extensions for selectively disabling optimization
 =================================================
diff --git a/docs/SafeStack.rst b/docs/SafeStack.rst
new file mode 100644 (file)
index 0000000..5115d95
--- /dev/null
@@ -0,0 +1,163 @@
+=========
+SafeStack
+=========
+
+.. contents::
+   :local:
+
+Introduction
+============
+
+SafeStack is an instrumentation pass that protects programs against attacks
+based on stack buffer overflows, without introducing any measurable performance
+overhead. It works by separating the program stack into two distinct regions:
+the safe stack and the unsafe stack. The safe stack stores return addresses,
+register spills, and local variables that are always accessed in a safe way,
+while the unsafe stack stores everything else. This separation ensures that
+buffer overflows on the unsafe stack cannot be used to overwrite anything
+on the safe stack, which includes return addresses.
+
+Performance
+-----------
+
+The performance overhead of the SafeStack instrumentation is less than 0.1% on
+average across a variety of benchmarks (see the `Code-Pointer Integrity
+<http://dslab.epfl.ch/pubs/cpi.pdf>`_ paper for details). This is mainly
+because most small functions do not have any variables that require the unsafe
+stack and, hence, do not need unsafe stack frames to be created. The cost of
+creating unsafe stack frames for large functions is amortized by the cost of
+executing the function.
+
+In some cases, SafeStack actually improves the performance. Objects that end up
+being moved to the unsafe stack are usually large arrays or variables that are
+used through multiple stack frames. Moving such objects away from the safe
+stack increases the locality of frequently accessed values on the stack, such
+as register spills, return addresses, and small local variables.
+
+Limitations
+-----------
+
+SafeStack has not been subjected to a comprehensive security review, and there
+exist known weaknesses, including but not limited to the following.
+
+In its current state, the separation of local variables provides protection
+against stack buffer overflows, but the safe stack itself is not protected
+from being corrupted through a pointer dereference. The Code-Pointer
+Integrity paper describes two ways in which we may protect the safe stack:
+hardware segmentation on the 32-bit x86 architecture or information hiding
+on other architectures.
+
+Even with information hiding, the safe stack would merely be hidden
+from attackers by being somewhere in the address space. Depending on the
+application, the address could be predictable even on 64-bit address spaces
+because not all the bits are addressable, multiple threads each have their
+stack, the application could leak the safe stack address to memory via
+``__builtin_frame_address``, bugs in the low-level runtime support etc.
+Safe stack leaks could be mitigated by writing and deploying a static binary
+analysis or a dynamic binary instrumentation based tool to find leaks.
+
+This approach doesn't prevent an attacker from "imbalancing" the safe
+stack by say having just one call, and doing two rets (thereby returning
+to an address that wasn't meant as a return address). This can be at least
+partially mitigated by deploying SafeStack alongside a forward control-flow
+integrity mechanism to ensure that calls are made using the correct calling
+convention. Clang does not currently implement a comprehensive forward
+control-flow integrity protection scheme; there exists one that protects
+:doc:`virtual calls <ControlFlowIntegrity>` but not non-virtual indirect calls.
+
+Compatibility
+-------------
+
+Most programs, static libraries, or individual files can be compiled
+with SafeStack as is. SafeStack requires basic runtime support, which, on most
+platforms, is implemented as a compiler-rt library that is automatically linked
+in when the program is compiled with SafeStack.
+
+Linking a DSO with SafeStack is not currently supported.
+
+Known compatibility limitations
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Certain code that relies on low-level stack manipulations requires adaption to
+work with SafeStack. One example is mark-and-sweep garbage collection
+implementations for C/C++ (e.g., Oilpan in chromium/blink), which must be
+changed to look for the live pointers on both safe and unsafe stacks.
+
+SafeStack supports linking together modules that are compiled with and without
+SafeStack, both statically and dynamically. One corner case that is not
+supported is using ``dlopen()`` to load a dynamic library that uses SafeStack into
+a program that is not compiled with SafeStack but uses threads.
+
+Signal handlers that use ``sigaltstack()`` must not use the unsafe stack (see
+``__attribute__((no_sanitize("safe-stack")))`` below).
+
+Programs that use APIs from ``ucontext.h`` are not supported yet.
+
+Usage
+=====
+
+To enable SafeStack, just pass ``-fsanitize=safe-stack`` flag to both compile and link
+command lines.
+
+Supported Platforms
+-------------------
+
+SafeStack was tested on Linux, FreeBSD and MacOSX.
+
+Low-level API
+-------------
+
+``__has_feature(safe_stack)``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+In some rare cases one may need to execute different code depending on
+whether SafeStack is enabled. The macro ``__has_feature(safe_stack)`` can
+be used for this purpose.
+
+.. code-block:: c
+
+    #if __has_feature(safe_stack)
+    // code that builds only under SafeStack
+    #endif
+
+``__attribute__((no_sanitize("safe-stack")))``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Use ``__attribute__((no_sanitize("safe-stack")))`` on a function declaration
+to specify that the safe stack instrumentation should not be applied to that
+function, even if enabled globally (see ``-fsanitize=safe-stack`` flag). This
+attribute may be required for functions that make assumptions about the
+exact layout of their stack frames.
+
+Care should be taken when using this attribute. The return address is not
+protected against stack buffer overflows, and it is easier to leak the
+address of the safe stack to memory by taking the address of a local variable.
+
+
+``__builtin___get_unsafe_stack_ptr()``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This builtin function returns current unsafe stack pointer of the current
+thread.
+
+``__builtin___get_unsafe_stack_start()``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+This builtin function returns a pointer to the start of the unsafe stack of the
+current thread.
+
+Design
+======
+
+Please refer to
+`http://dslab.epfl.ch/proj/cpi/ <http://dslab.epfl.ch/proj/cpi/>`_ for more
+information about the design of the SafeStack and its related technologies.
+
+
+Publications
+------------
+
+`Code-Pointer Integrity <http://dslab.epfl.ch/pubs/cpi.pdf>`_.
+Volodymyr Kuznetsov, Laszlo Szekeres, Mathias Payer, George Candea, R. Sekar, Dawn Song.
+USENIX Symposium on Operating Systems Design and Implementation
+(`OSDI <https://www.usenix.org/conference/osdi14>`_), Broomfield, CO, October 2014
index 2ddf0822dd6e9e86d4b111aff73ce3adf6d490aa..fdc52f8fb178994aab1333be058976ceea18ab17 100644 (file)
@@ -980,6 +980,8 @@ are listed below.
       flow analysis.
    -  ``-fsanitize=cfi``: :doc:`control flow integrity <ControlFlowIntegrity>`
       checks. Implies ``-flto``.
+   -  ``-fsanitize=safe-stack``: :doc:`safe stack <SafeStack>`
+      protection against stack-based memory corruption errors.
 
    The following more fine-grained checks are also available:
 
index a3c8ffb8dc69b289d07a3a417d01ef4db9b993c5..dec2bc828c1c68e82c70b4e24f09303e609a6f30 100644 (file)
@@ -29,6 +29,7 @@ Using Clang as a Compiler
    SanitizerCoverage
    SanitizerSpecialCaseList
    ControlFlowIntegrity
+   SafeStack
    Modules
    MSVCCompatibility
    FAQ
index 192790749be89b09e5f82a276cf97b0456260de1..bf65b5fa2ea80303f83b3fff5b996fcacec2ded1 100644 (file)
@@ -1240,6 +1240,10 @@ BUILTIN(__builtin_addressof, "v*v&", "nct")
 BUILTIN(__builtin_operator_new, "v*z", "c")
 BUILTIN(__builtin_operator_delete, "vv*", "n")
 
+// Safestack builtins
+BUILTIN(__builtin___get_unsafe_stack_start, "v*", "Fn")
+BUILTIN(__builtin___get_unsafe_stack_ptr, "v*", "Fn")
+
 #undef BUILTIN
 #undef LIBBUILTIN
 #undef LANGBUILTIN
index 65ababd5ac8679f264aba5cc01475038a43273d9..c50f3e14f51e6a570b09362cb2248da64c14daaa 100644 (file)
@@ -87,6 +87,9 @@ SANITIZER("cfi-vcall", CFIVCall)
 SANITIZER_GROUP("cfi", CFI,
                 CFIDerivedCast | CFIUnrelatedCast | CFINVCall | CFIVCall)
 
+// Safe Stack
+SANITIZER("safe-stack", SafeStack)
+
 // -fsanitize=undefined-trap includes sanitizers from -fsanitize=undefined
 // that can be used without runtime support, generally by providing extra
 // -fsanitize-undefined-trap-on-error flag.
index bfa63e7734fbf4c06d694b2f8654efa0cf0874fe..5edd23030b41d6de5de9f178578379052466f48d 100644 (file)
@@ -47,6 +47,9 @@ class SanitizerArgs {
   }
   bool needsUbsanRt() const;
   bool needsDfsanRt() const { return Sanitizers.has(SanitizerKind::DataFlow); }
+  bool needsSafeStackRt() const {
+    return Sanitizers.has(SanitizerKind::SafeStack);
+  }
 
   bool requiresPIE() const;
   bool needsUnwindTables() const;
index 06d157bd82e7691d729c9d4c0879a18e836e8208..50a499607f88f8d710549db2e90dfe23b89c2755 100644 (file)
@@ -273,6 +273,8 @@ llvm::Function *CodeGenModule::CreateGlobalInitOrDestructFunction(
       Fn->addFnAttr(llvm::Attribute::SanitizeThread);
     if (getLangOpts().Sanitize.has(SanitizerKind::Memory))
       Fn->addFnAttr(llvm::Attribute::SanitizeMemory);
+    if (getLangOpts().Sanitize.has(SanitizerKind::SafeStack))
+      Fn->addFnAttr(llvm::Attribute::SafeStack);
   }
 
   return Fn;
index 01da75005610583deeeb4b51374d40e650fab96c..f370ac27676fd1c826662a710133f6882948add5 100644 (file)
@@ -621,6 +621,8 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
     Fn->addFnAttr(llvm::Attribute::SanitizeThread);
   if (SanOpts.has(SanitizerKind::Memory))
     Fn->addFnAttr(llvm::Attribute::SanitizeMemory);
+  if (SanOpts.has(SanitizerKind::SafeStack))
+    Fn->addFnAttr(llvm::Attribute::SafeStack);
 
   // Pass inline keyword to optimizer if it appears explicitly on any
   // declaration. Also, in the case of -fno-inline attach NoInline
index ed9df1207df56197e89669e6f813905cd14a14f3..800053cc9316a8dedbf729dc01619afeefdf2d15 100644 (file)
@@ -2433,6 +2433,8 @@ collectSanitizerRuntimes(const ToolChain &TC, const ArgList &Args,
     if (SanArgs.linkCXXRuntimes())
       StaticRuntimes.push_back("ubsan_standalone_cxx");
   }
+  if (SanArgs.needsSafeStackRt())
+    StaticRuntimes.push_back("safestack");
 }
 
 // Should be called before we add system libraries (C++ ABI, libstdc++/libc++,
@@ -4001,7 +4003,12 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
 
   // -stack-protector=0 is default.
   unsigned StackProtectorLevel = 0;
-  if (Arg *A = Args.getLastArg(options::OPT_fno_stack_protector,
+  if (getToolChain().getSanitizerArgs().needsSafeStackRt()) {
+    Args.ClaimAllArgs(options::OPT_fno_stack_protector);
+    Args.ClaimAllArgs(options::OPT_fstack_protector_all);
+    Args.ClaimAllArgs(options::OPT_fstack_protector_strong);
+    Args.ClaimAllArgs(options::OPT_fstack_protector);
+  } else if (Arg *A = Args.getLastArg(options::OPT_fno_stack_protector,
                                options::OPT_fstack_protector_all,
                                options::OPT_fstack_protector_strong,
                                options::OPT_fstack_protector)) {
@@ -6364,6 +6371,15 @@ void darwin::Link::ConstructJob(Compilation &C, const JobAction &JA,
       !Args.hasArg(options::OPT_nostartfiles))
     getMachOToolChain().addStartObjectFileArgs(Args, CmdArgs);
 
+  // SafeStack requires its own runtime libraries
+  // These libraries should be linked first, to make sure the
+  // __safestack_init constructor executes before everything else
+  if (getToolChain().getSanitizerArgs().needsSafeStackRt()) {
+    getMachOToolChain().AddLinkRuntimeLib(Args, CmdArgs,
+                                          "libclang_rt.safestack_osx.a",
+                                          /*AlwaysLink=*/true);
+  }
+
   Args.AddAllArgs(CmdArgs, options::OPT_L);
 
   if (Args.hasFlag(options::OPT_fopenmp, options::OPT_fopenmp_EQ,
index 03784e204503ad81f76d78b1bbec3e7ff36129be..0aaf3dd23c413b3f55a4d251604c62d8a228c719 100644 (file)
@@ -1190,6 +1190,7 @@ static bool HasFeature(const Preprocessor &PP, const IdentifierInfo *II) {
       .Case("is_trivially_copyable", LangOpts.CPlusPlus)
       .Case("is_union", LangOpts.CPlusPlus)
       .Case("modules", LangOpts.Modules)
+      .Case("safe_stack", LangOpts.Sanitize.has(SanitizerKind::SafeStack))
       .Case("tls", PP.getTargetInfo().isTLSSupported())
       .Case("underlying_type", LangOpts.CPlusPlus)
       .Default(false);
diff --git a/test/CodeGen/safestack-attr.cpp b/test/CodeGen/safestack-attr.cpp
new file mode 100644 (file)
index 0000000..9d1ed0d
--- /dev/null
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -triple x86_64-linux-unknown -emit-llvm -o - %s -fsanitize=safe-stack | FileCheck -check-prefix=SP %s
+
+__attribute__((no_sanitize("safe-stack")))
+int foo(int *a) {  return *a; }
+
+// SP-NOT: attributes #{{.*}} = { {{.*}}safestack{{.*}} }
index 2fb9b2cf7e6540548874306f25ac31b7a7fa38bc..8039b6059efd42995a64c5748348c928dc662342 100644 (file)
@@ -6,6 +6,8 @@
 // SSPSTRONG: define void @test1(i8* %msg) #0 {
 // RUN: %clang_cc1 -emit-llvm -o - %s -stack-protector 3 | FileCheck -check-prefix=SSPREQ %s
 // SSPREQ: define void @test1(i8* %msg) #0 {
+// RUN: %clang_cc1 -emit-llvm -o - %s -fsanitize=safe-stack | FileCheck -check-prefix=SAFESTACK %s
+// SAFESTACK: define void @test1(i8* %msg) #0 {
 
 typedef __SIZE_TYPE__ size_t;
 
@@ -26,3 +28,5 @@ void test1(const char *msg) {
 // SSPSTRONG: attributes #{{.*}} = { nounwind sspstrong{{.*}} }
 
 // SSPREQ: attributes #{{.*}} = { nounwind sspreq{{.*}} }
+
+// SAFESTACK: attributes #{{.*}} = { nounwind safestack{{.*}} }
index fff59a3480eae20ced33de33634199b1e84a0fae..964ad2b1d74dfd56bd4bf5888bb49fe4217baa8e 100644 (file)
 // RUN: %clang_cl -fsanitize=address -c -MDd -MD -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-RELEASERTL
 // RUN: %clang_cl -fsanitize=address -c -LDd -LD -### -- %s 2>&1 | FileCheck %s -check-prefix=CHECK-ASAN-RELEASERTL
 // CHECK-ASAN-RELEASERTL-NOT: error: invalid argument
+
+// RUN: %clang -fno-sanitize=safe-stack -### %s 2>&1 | FileCheck %s -check-prefix=NOSP
+// NOSP-NOT: "-fsanitize=safe-stack"
+
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=safe-stack -### %s 2>&1 | FileCheck %s -check-prefix=SP
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=address,safe-stack -### %s 2>&1 | FileCheck %s -check-prefix=SP-ASAN
+// RUN: %clang -target x86_64-linux-gnu -fstack-protector -fsanitize=safe-stack -### %s 2>&1 | FileCheck %s -check-prefix=SP
+// RUN: %clang -target x86_64-linux-gnu -fsanitize=safe-stack -fstack-protector-all -### %s 2>&1 | FileCheck %s -check-prefix=SP
+// SP-NOT: stack-protector
+// SP: "-fsanitize=safe-stack"
+// SP-ASAN-NOT: stack-protector
+// SP-ASAN: "-fsanitize=address,safe-stack"