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 -fsanitize=safe-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/LanguageExtensions.rst =================================================================== --- docs/LanguageExtensions.rst +++ docs/LanguageExtensions.rst @@ -1845,6 +1845,9 @@ 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 ================================================= Index: docs/SafeStack.rst =================================================================== --- /dev/null +++ docs/SafeStack.rst @@ -0,0 +1,125 @@ +========= +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 +`_ 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. + +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 ``-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_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 -fsanitize=safe-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/ `_ for more +information about the design of the SafeStack and its related technologies. + + +Publications +------------ + +`Code-Pointer Integrity `_. +Volodymyr Kuznetsov, Laszlo Szekeres, Mathias Payer, George Candea, R. Sekar, Dawn Song. +USENIX Symposium on Operating Systems Design and Implementation +(`OSDI `_), Broomfield, CO, October 2014 Index: docs/UsersManual.rst =================================================================== --- docs/UsersManual.rst +++ docs/UsersManual.rst @@ -978,6 +978,8 @@ flow analysis. - ``-fsanitize=cfi``: :doc:`control flow integrity ` checks. Implies ``-flto``. + - ``-fsanitize=safe-stack``: :doc:`safe stack ` + protection against stack-based memory corruption errors. The following more fine-grained checks are also available: 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 -fsanitize=safe-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.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/Basic/LangOptions.def =================================================================== --- include/clang/Basic/LangOptions.def +++ include/clang/Basic/LangOptions.def @@ -199,7 +199,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/Sanitizers.def =================================================================== --- include/clang/Basic/Sanitizers.def +++ include/clang/Basic/Sanitizers.def @@ -87,6 +87,9 @@ 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: include/clang/Driver/SanitizerArgs.h =================================================================== --- include/clang/Driver/SanitizerArgs.h +++ include/clang/Driver/SanitizerArgs.h @@ -47,6 +47,9 @@ } 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: 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()) + 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 @@ -2279,6 +2279,24 @@ CmdArgs.push_back("-no-whole-archive"); } +static void addSafeStackRT( + const ToolChain &TC, const ArgList &Args, ArgStringList &CmdArgs) { + if (!TC.getSanitizerArgs().needsSafeStackRt()) + 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) + << "-fsanitize=safe-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, @@ -3855,7 +3873,13 @@ // -stack-protector=0 is default. unsigned StackProtectorLevel = 0; - if (Arg *A = Args.getLastArg(options::OPT_fno_stack_protector, + if (getToolChain().getSanitizerArgs().needsSafeStackRt()) { + 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)) { @@ -6230,6 +6254,20 @@ !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"); + + // 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; @@ -6496,6 +6534,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); @@ -7045,6 +7085,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) @@ -7341,6 +7383,8 @@ } } + addSafeStackRT(getToolChain(), Args, CmdArgs); + Args.AddAllArgs(CmdArgs, options::OPT_L); Args.AddAllArgs(CmdArgs, options::OPT_T_Group); Args.AddAllArgs(CmdArgs, options::OPT_e); @@ -7904,6 +7948,8 @@ ToolChain.AddFastMathRuntimeIfAvailable(Args, CmdArgs); } + addSafeStackRT(getToolChain(), Args, CmdArgs); + Args.AddAllArgs(CmdArgs, options::OPT_L); Args.AddAllArgs(CmdArgs, options::OPT_u); @@ -8215,6 +8261,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); @@ -8341,6 +8389,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 @@ -1668,6 +1668,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/Lex/PPMacroExpansion.cpp =================================================================== --- lib/Lex/PPMacroExpansion.cpp +++ lib/Lex/PPMacroExpansion.cpp @@ -1186,6 +1186,7 @@ .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); Index: lib/Sema/SemaDeclAttr.cpp =================================================================== --- lib/Sema/SemaDeclAttr.cpp +++ lib/Sema/SemaDeclAttr.cpp @@ -4822,6 +4822,9 @@ case AttributeList::AT_ScopedLockable: handleSimpleAttribute(S, D, Attr); break; + case AttributeList::AT_NoSafeStack: + handleSimpleAttribute(S, D, Attr); + break; case AttributeList::AT_NoSanitizeAddress: handleSimpleAttribute(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-sanitize=safe-stack -### %s 2>&1 | FileCheck %s -check-prefix=NOSP +// NOSP-NOT: "-stack-protector" "4" + +// RUN: %clang -fsanitize=safe-stack -### %s 2>&1 | FileCheck %s -check-prefix=SP +// RUN: %clang -fsanitize=address,safe-stack -### %s 2>&1 | FileCheck %s -check-prefix=SP +// RUN: %clang -fstack-protector -fsanitize=safe-stack -### %s 2>&1 | FileCheck %s -check-prefix=SP +// RUN: %clang -fsanitize=safe-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@-1 {{'no_safe_stack' attribute only applies to functions}} + return x; +} + +int nosp_test_var NO_SAFE_STACK; + // expected-error@-1 {{'no_safe_stack' attribute only applies to functions}} + +class NoSPFoo { + private: + int test_field NO_SAFE_STACK; + // expected-error@-1 {{'no_safe_stack' attribute only applies to functions}} + void test_method() NO_SAFE_STACK; +}; + +class NO_SAFE_STACK NoSPTestClass { + // expected-error@-1 {{'no_safe_stack' attribute only applies to functions}} +}; + +void nosp_fun_params(int lvar NO_SAFE_STACK); + // expected-error@-1 {{'no_safe_stack' attribute only applies to functions}}