Refresh

http://reviews.llvm.org/D6095

Files:
  docs/AttributeReference.rst
  docs/SafeStack.rst
  include/clang/Basic/Attr.td
  include/clang/Basic/AttrDocs.td
  include/clang/Basic/Builtins.def
  include/clang/Basic/LangOptions.def
  include/clang/Basic/LangOptions.h
  include/clang/Driver/Options.td
  lib/CodeGen/CodeGenModule.cpp
  lib/Driver/ToolChains.cpp
  lib/Driver/Tools.cpp
  lib/Frontend/CompilerInvocation.cpp
  lib/Frontend/InitPreprocessor.cpp
  lib/Sema/SemaDeclAttr.cpp
  test/CodeGen/safestack-attr.cpp
  test/CodeGen/stack-protector.c
  test/Driver/safestack.c
  test/SemaCXX/attr-no-safestack.cpp

EMAIL PREFERENCES
  http://reviews.llvm.org/settings/panel/emailpreferences/
Index: docs/AttributeReference.rst
===================================================================
--- docs/AttributeReference.rst
+++ docs/AttributeReference.rst
@@ -483,6 +483,21 @@
 not be inserted by ThreadSanitizer. The function is still instrumented by the
 tool to avoid false positives and provide meaningful stack traces.
 
+no_safe_stack
+-------------
+
+.. csv-table:: Supported Syntaxes
+   :header: "GNU", "C++11", "__declspec", "Keyword"
+
+   "X","","",""
+
+.. _langext-safe_stack:
+
+Use ``__attribute__((no_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 -fsafe-stack flag). This attribute may be
+required for functions that make assumptions about the exact layout of their
+stack frames.
 
 no_split_stack (gnu::no_split_stack)
 ------------------------------------
Index: docs/SafeStack.rst
===================================================================
--- /dev/null
+++ docs/SafeStack.rst
@@ -0,0 +1,127 @@
+=========
+SafeStack
+=========
+
+.. contents::
+   :local:
+
+Introduction
+============
+
+SafeStack is an instrumentation pass that protects programs against attacks
+based on stack-based 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.
+
+Compatibility
+-------------
+
+Most programs, static or shared 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. On some platforms, the runtime
+is integrated directly into libc, which enables certain low-level performance
+optimizations.
+
+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_safe_stack))`` below).
+
+Programs that use APIs from ``ucontext.h`` are not supported yet.
+
+Usage
+=====
+
+To enable SafeStack, just pass ``-fsafe-stack`` flag to both compile and link
+command lines.
+
+Supported Platforms
+-------------------
+
+SafeStack was tested on Linux, FreeBSD and MacOSX.
+
+Low-level API
+-------------
+
+``__SAFESTACK__``
+~~~~~~~~~~~~~~~~~
+
+In some rare cases one may need to execute different code depending on whether
+SafeStack is enabled. The define ``__SAFESTACK__`` can be used for this
+purpose.
+
+.. code-block:: c
+
+    #ifdef __SAFESTACK__
+    // code that builds only under SafeStack
+    #endif
+
+``__attribute__((no_safe_stack))``
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Use ``__attribute__((no_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 -fsafe-stack flag). This attribute may be
+required for functions that make assumptions about the exact layout of their
+stack frames.
+
+``__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: include/clang/Basic/Attr.td
===================================================================
--- include/clang/Basic/Attr.td
+++ include/clang/Basic/Attr.td
@@ -1386,6 +1386,13 @@
   let Documentation = [Undocumented];
 }
 
+// Attribute to disable SafeStack (or equivalent) instrumentation.
+def NoSafeStack : InheritableAttr {
+  let Spellings = [GCC<"no_safe_stack">];
+  let Subjects = SubjectList<[Function], ErrorDiag>;
+  let Documentation = [NoSafeStackDocs];
+}
+
 // Attribute to disable AddressSanitizer (or equivalent) checks.
 def NoSanitizeAddress : InheritableAttr {
   let Spellings = [GCC<"no_address_safety_analysis">,
Index: include/clang/Basic/AttrDocs.td
===================================================================
--- include/clang/Basic/AttrDocs.td
+++ include/clang/Basic/AttrDocs.td
@@ -920,6 +920,17 @@
   }];
 }
 
+def NoSafeStackDocs : Documentation {
+  let Category = DocCatFunction;
+  let Content = [{
+Use ``__attribute__((no_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 -fsafe-stack flag). This attribute may be
+required for functions that make assumptions about the exact layout of their
+stack frames.
+  }];
+}
+
 def NoSanitizeAddressDocs : Documentation {
   let Category = DocCatFunction;
   // This function has multiple distinct spellings, and so it requires a custom
Index: include/clang/Basic/Builtins.def
===================================================================
--- include/clang/Basic/Builtins.def
+++ include/clang/Basic/Builtins.def
@@ -1240,6 +1240,10 @@
 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: include/clang/Basic/LangOptions.def
===================================================================
--- include/clang/Basic/LangOptions.def
+++ include/clang/Basic/LangOptions.def
@@ -198,7 +198,7 @@
              "value symbol visibility")
 ENUM_LANGOPT(TypeVisibilityMode, Visibility, 3, DefaultVisibility,
              "type symbol visibility")
-ENUM_LANGOPT(StackProtector, StackProtectorMode, 2, SSPOff,
+ENUM_LANGOPT(StackProtector, StackProtectorMode, 3, SSPOff,
              "stack protector mode")
 ENUM_LANGOPT(SignedOverflowBehavior, SignedOverflowBehaviorTy, 2, SOB_Undefined,
              "signed integer overflow handling")
Index: include/clang/Basic/LangOptions.h
===================================================================
--- include/clang/Basic/LangOptions.h
+++ include/clang/Basic/LangOptions.h
@@ -50,7 +50,7 @@
   typedef clang::Visibility Visibility;
   
   enum GCMode { NonGC, GCOnly, HybridGC };
-  enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq };
+  enum StackProtectorMode { SSPOff, SSPOn, SSPStrong, SSPReq, SSPSafeStack };
   
   enum SignedOverflowBehaviorTy {
     SOB_Undefined,  // Default C standard behavior.
Index: include/clang/Driver/Options.td
===================================================================
--- include/clang/Driver/Options.td
+++ include/clang/Driver/Options.td
@@ -922,6 +922,10 @@
   HelpText<"Use a strong heuristic to apply stack protectors to functions">;
 def fstack_protector : Flag<["-"], "fstack-protector">, Group<f_Group>,
   HelpText<"Enable stack protectors for functions potentially vulnerable to stack smashing">;
+def fsafe_stack : Flag<["-"], "fsafe-stack">, Group<f_Group>,
+  HelpText<"Enable safe stack protection against stack-based memory corruption errors">;
+def fno_safe_stack : Flag<["-"], "fno-safe-stack">, Group<f_Group>,
+  HelpText<"Disable safe stack protection against stack-based memory corruption errors">;
 def fstandalone_debug : Flag<["-"], "fstandalone-debug">, Group<f_Group>, Flags<[CC1Option]>,
   HelpText<"Emit full debug info for all types used by the program">;
 def fno_standalone_debug : Flag<["-"], "fno-standalone-debug">, Group<f_Group>, Flags<[CC1Option]>,
Index: lib/CodeGen/CodeGenModule.cpp
===================================================================
--- lib/CodeGen/CodeGenModule.cpp
+++ lib/CodeGen/CodeGenModule.cpp
@@ -744,6 +744,9 @@
     B.addAttribute(llvm::Attribute::StackProtectStrong);
   else if (LangOpts.getStackProtector() == LangOptions::SSPReq)
     B.addAttribute(llvm::Attribute::StackProtectReq);
+  else if (LangOpts.getStackProtector() == LangOptions::SSPSafeStack)
+    if (!D->hasAttr<NoSafeStackAttr>())
+      B.addAttribute(llvm::Attribute::SafeStack);
 
   // Add sanitizer attributes if function is not blacklisted.
   if (!isInSanitizerBlacklist(F, D->getLocation())) {
Index: lib/Driver/ToolChains.cpp
===================================================================
--- lib/Driver/ToolChains.cpp
+++ lib/Driver/ToolChains.cpp
@@ -10,6 +10,7 @@
 #include "ToolChains.h"
 #include "clang/Basic/ObjCRuntime.h"
 #include "clang/Basic/Version.h"
+#include "clang/Basic/LangOptions.h"
 #include "clang/Config/config.h" // for GCC_INSTALL_PREFIX
 #include "clang/Driver/Compilation.h"
 #include "clang/Driver/Driver.h"
Index: lib/Driver/Tools.cpp
===================================================================
--- lib/Driver/Tools.cpp
+++ lib/Driver/Tools.cpp
@@ -2269,6 +2269,25 @@
     CmdArgs.push_back("-no-whole-archive");
 }
 
+static void addSafeStackRT(
+    const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) {
+  if (!Args.hasFlag(options::OPT_fsafe_stack,
+                    options::OPT_fno_safe_stack, false))
+    return;
+
+  if (Args.hasArg(options::OPT_shared)) {
+    // This is a temporary limitation caused by linking issues.
+    TC.getDriver().Diag(diag::err_drv_argument_not_allowed_with)
+      << "-fsafe-stack" << "-shared";
+  }
+
+  addSanitizerRuntime(TC, Args, CmdArgs, "safestack", false);
+
+  // Safestack runtime requires dl on Linux
+  if (TC.getTriple().isOSLinux())
+    CmdArgs.push_back("-ldl");
+}
+
 // Tries to use a file with the list of dynamic symbols that need to be exported
 // from the runtime library. Returns true if the file was found.
 static bool addSanitizerDynamicList(const ToolChain &TC, const ArgList &Args,
@@ -3833,7 +3852,14 @@
 
   // -stack-protector=0 is default.
   unsigned StackProtectorLevel = 0;
-  if (Arg *A = Args.getLastArg(options::OPT_fno_stack_protector,
+  if (Args.hasFlag(options::OPT_fsafe_stack,
+                   options::OPT_fno_safe_stack, false)) {
+    StackProtectorLevel = LangOptions::SSPSafeStack;
+    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)) {
@@ -6209,6 +6235,21 @@
       !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 (Args.hasFlag(options::OPT_fsafe_stack,
+                   options::OPT_fno_safe_stack, false)) {
+    getMachOToolChain().AddLinkRuntimeLib(Args, CmdArgs,
+                                          "libclang_rt.safestack_osx.a");
+
+    // We need to ensure that the safe stack init function from the safestack
+    // runtime library is linked in, even though it might not be referenced by
+    // any code in the module before LTO optimizations are applied.
+    CmdArgs.push_back("-u");
+    CmdArgs.push_back("___safestack_init");
+  }
+
   Args.AddAllArgs(CmdArgs, options::OPT_L);
 
   LibOpenMP UsedOpenMPLib = LibUnknown;
@@ -6475,6 +6516,8 @@
 
   CmdArgs.push_back(Args.MakeArgString("-L" + GCCLibPath));
 
+  addSafeStackRT(getToolChain(), Args, CmdArgs);
+
   Args.AddAllArgs(CmdArgs, options::OPT_L);
   Args.AddAllArgs(CmdArgs, options::OPT_T_Group);
   Args.AddAllArgs(CmdArgs, options::OPT_e);
@@ -7024,6 +7067,8 @@
     CmdArgs.push_back(Args.MakeArgString(ToolChain.GetFilePath(crtbegin)));
   }
 
+  addSafeStackRT(getToolChain(), Args, CmdArgs);
+
   Args.AddAllArgs(CmdArgs, options::OPT_L);
   const ToolChain::path_list &Paths = ToolChain.getFilePaths();
   for (const auto &Path : Paths)
@@ -7320,6 +7365,8 @@
     }
   }
 
+  addSafeStackRT(getToolChain(), Args, CmdArgs);
+
   Args.AddAllArgs(CmdArgs, options::OPT_L);
   Args.AddAllArgs(CmdArgs, options::OPT_T_Group);
   Args.AddAllArgs(CmdArgs, options::OPT_e);
@@ -7883,6 +7930,8 @@
     ToolChain.AddFastMathRuntimeIfAvailable(Args, CmdArgs);
   }
 
+  addSafeStackRT(getToolChain(), Args, CmdArgs);
+
   Args.AddAllArgs(CmdArgs, options::OPT_L);
   Args.AddAllArgs(CmdArgs, options::OPT_u);
 
@@ -8194,6 +8243,8 @@
       CmdArgs.push_back(Args.MakeArgString(getToolChain().GetFilePath("crtn.o")));
   }
 
+  addSafeStackRT(getToolChain(), Args, CmdArgs);
+
   Args.AddAllArgs(CmdArgs, options::OPT_L);
   Args.AddAllArgs(CmdArgs, options::OPT_T_Group);
   Args.AddAllArgs(CmdArgs, options::OPT_e);
@@ -8320,6 +8371,8 @@
                               getToolChain().GetFilePath("crtbegin.o")));
   }
 
+  addSafeStackRT(getToolChain(), Args, CmdArgs);
+
   Args.AddAllArgs(CmdArgs, options::OPT_L);
   Args.AddAllArgs(CmdArgs, options::OPT_T_Group);
   Args.AddAllArgs(CmdArgs, options::OPT_e);
Index: lib/Frontend/CompilerInvocation.cpp
===================================================================
--- lib/Frontend/CompilerInvocation.cpp
+++ lib/Frontend/CompilerInvocation.cpp
@@ -1662,6 +1662,7 @@
   case 1: Opts.setStackProtector(LangOptions::SSPOn);  break;
   case 2: Opts.setStackProtector(LangOptions::SSPStrong); break;
   case 3: Opts.setStackProtector(LangOptions::SSPReq); break;
+  case 4: Opts.setStackProtector(LangOptions::SSPSafeStack); break;
   }
 
   // Parse -fsanitize= arguments.
Index: lib/Frontend/InitPreprocessor.cpp
===================================================================
--- lib/Frontend/InitPreprocessor.cpp
+++ lib/Frontend/InitPreprocessor.cpp
@@ -845,6 +845,8 @@
     Builder.defineMacro("__SSP_STRONG__", "2");
   else if (LangOpts.getStackProtector() == LangOptions::SSPReq)
     Builder.defineMacro("__SSP_ALL__", "3");
+  else if (LangOpts.getStackProtector() == LangOptions::SSPSafeStack)
+    Builder.defineMacro("__SAFESTACK__", "4");
 
   if (FEOpts.ProgramAction == frontend::RewriteObjC)
     Builder.defineMacro("__weak", "__attribute__((objc_gc(weak)))");
Index: lib/Sema/SemaDeclAttr.cpp
===================================================================
--- lib/Sema/SemaDeclAttr.cpp
+++ lib/Sema/SemaDeclAttr.cpp
@@ -4822,6 +4822,9 @@
   case AttributeList::AT_ScopedLockable:
     handleSimpleAttribute<ScopedLockableAttr>(S, D, Attr);
     break;
+  case AttributeList::AT_NoSafeStack:
+    handleSimpleAttribute<NoSafeStackAttr>(S, D, Attr);
+    break;
   case AttributeList::AT_NoSanitizeAddress:
     handleSimpleAttribute<NoSanitizeAddressAttr>(S, D, Attr);
     break;
Index: test/CodeGen/safestack-attr.cpp
===================================================================
--- /dev/null
+++ test/CodeGen/safestack-attr.cpp
@@ -0,0 +1,6 @@
+// RUN: %clang_cc1 -triple x86_64-linux-unknown -emit-llvm -o - %s -stack-protector 4 | FileCheck -check-prefix=SP %s
+
+__attribute__((no_safe_stack))
+int foo(int *a) {  return *a; }
+
+// SP-NOT: attributes #{{.*}} = { nounwind safestack{{.*}} }
Index: test/CodeGen/stack-protector.c
===================================================================
--- test/CodeGen/stack-protector.c
+++ test/CodeGen/stack-protector.c
@@ -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 -stack-protector 4 | FileCheck -check-prefix=SAFESTACK %s
+// SAFESTACK: define void @test1(i8* %msg) #0 {
 
 typedef __SIZE_TYPE__ size_t;
 
@@ -26,3 +28,5 @@
 // SSPSTRONG: attributes #{{.*}} = { nounwind sspstrong{{.*}} }
 
 // SSPREQ: attributes #{{.*}} = { nounwind sspreq{{.*}} }
+
+// SAFESTACK: attributes #{{.*}} = { nounwind safestack{{.*}} }
Index: test/Driver/safestack.c
===================================================================
--- /dev/null
+++ test/Driver/safestack.c
@@ -0,0 +1,8 @@
+// RUN: %clang -fno-safe-stack -### %s 2>&1 | FileCheck %s -check-prefix=NOSP
+// NOSP-NOT: "-stack-protector" "4"
+
+// RUN: %clang -fsafe-stack -### %s 2>&1 | FileCheck %s -check-prefix=SP
+// RUN: %clang -fsanatizer=address -fsafe-stack -### %s 2>&1 | FileCheck %s -check-prefix=SP
+// RUN: %clang -fstack-protector -fsafe-stack -### %s 2>&1 | FileCheck %s -check-prefix=SP
+// RUN: %clang -fsafe-stack -fstack-protector-all -### %s 2>&1 | FileCheck %s -check-prefix=SP
+// SP: "-stack-protector" "4"
Index: test/SemaCXX/attr-no-safestack.cpp
===================================================================
--- /dev/null
+++ test/SemaCXX/attr-no-safestack.cpp
@@ -0,0 +1,37 @@
+// RUN: %clang_cc1 -fsyntax-only -verify  %s
+
+#define NO_SAFE_STACK __attribute__((no_safe_stack))
+
+#if !__has_attribute(no_safe_stack)
+#error "Should support no_safe_stack"
+#endif
+
+void nosp_fun() NO_SAFE_STACK;
+
+void nosp_fun_args() __attribute__((no_safe_stack(1))); // \
+  // expected-error {{'no_safe_stack' attribute takes no arguments}}
+
+int nosp_testfn(int y) NO_SAFE_STACK;
+
+int nosp_testfn(int y) {
+  int x NO_SAFE_STACK = y; // \
+    // expected-error {{'no_safe_stack' attribute only applies to functions}}
+  return x;
+}
+
+int nosp_test_var NO_SAFE_STACK; // \
+  // expected-error {{'no_safe_stack' attribute only applies to functions}}
+
+class NoSPFoo {
+ private:
+  int test_field NO_SAFE_STACK; // \
+    // expected-error {{'no_safe_stack' attribute only applies to functions}}
+  void test_method() NO_SAFE_STACK;
+};
+
+class NO_SAFE_STACK NoSPTestClass { // \
+  // expected-error {{'no_safe_stack' attribute only applies to functions}}
+};
+
+void nosp_fun_params(int lvar NO_SAFE_STACK); // \
+  // expected-error {{'no_safe_stack' attribute only applies to functions}}
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to