diff --git a/clang/docs/LanguageExtensions.rst b/clang/docs/LanguageExtensions.rst index bd5992e3d6e5..0b4775ba6ad4 100644 --- a/clang/docs/LanguageExtensions.rst +++ b/clang/docs/LanguageExtensions.rst @@ -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/clang/docs/SafeStack.rst b/clang/docs/SafeStack.rst new file mode 100644 index 000000000000..5115d959954e --- /dev/null +++ b/clang/docs/SafeStack.rst @@ -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 diff --git a/clang/docs/UsersManual.rst b/clang/docs/UsersManual.rst index 2ddf0822dd6e..fdc52f8fb178 100644 --- a/clang/docs/UsersManual.rst +++ b/clang/docs/UsersManual.rst @@ -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: diff --git a/clang/docs/index.rst b/clang/docs/index.rst index a3c8ffb8dc69..dec2bc828c1c 100644 --- a/clang/docs/index.rst +++ b/clang/docs/index.rst @@ -29,6 +29,7 @@ Using Clang as a Compiler SanitizerCoverage SanitizerSpecialCaseList ControlFlowIntegrity + SafeStack Modules MSVCCompatibility FAQ diff --git a/clang/include/clang/Basic/Builtins.def b/clang/include/clang/Basic/Builtins.def index 192790749be8..bf65b5fa2ea8 100644 --- a/clang/include/clang/Basic/Builtins.def +++ b/clang/include/clang/Basic/Builtins.def @@ -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 diff --git a/clang/include/clang/Basic/Sanitizers.def b/clang/include/clang/Basic/Sanitizers.def index 65ababd5ac86..c50f3e14f51e 100644 --- a/clang/include/clang/Basic/Sanitizers.def +++ b/clang/include/clang/Basic/Sanitizers.def @@ -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. diff --git a/clang/include/clang/Driver/SanitizerArgs.h b/clang/include/clang/Driver/SanitizerArgs.h index bfa63e7734fb..5edd23030b41 100644 --- a/clang/include/clang/Driver/SanitizerArgs.h +++ b/clang/include/clang/Driver/SanitizerArgs.h @@ -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; diff --git a/clang/lib/CodeGen/CGDeclCXX.cpp b/clang/lib/CodeGen/CGDeclCXX.cpp index 06d157bd82e7..50a499607f88 100644 --- a/clang/lib/CodeGen/CGDeclCXX.cpp +++ b/clang/lib/CodeGen/CGDeclCXX.cpp @@ -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; diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index 01da75005610..f370ac27676f 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -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 diff --git a/clang/lib/Driver/Tools.cpp b/clang/lib/Driver/Tools.cpp index ed9df1207df5..800053cc9316 100644 --- a/clang/lib/Driver/Tools.cpp +++ b/clang/lib/Driver/Tools.cpp @@ -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, diff --git a/clang/lib/Lex/PPMacroExpansion.cpp b/clang/lib/Lex/PPMacroExpansion.cpp index 03784e204503..0aaf3dd23c41 100644 --- a/clang/lib/Lex/PPMacroExpansion.cpp +++ b/clang/lib/Lex/PPMacroExpansion.cpp @@ -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/clang/test/CodeGen/safestack-attr.cpp b/clang/test/CodeGen/safestack-attr.cpp new file mode 100644 index 000000000000..9d1ed0d2e493 --- /dev/null +++ b/clang/test/CodeGen/safestack-attr.cpp @@ -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{{.*}} } diff --git a/clang/test/CodeGen/stack-protector.c b/clang/test/CodeGen/stack-protector.c index 2fb9b2cf7e65..8039b6059efd 100644 --- a/clang/test/CodeGen/stack-protector.c +++ b/clang/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 -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{{.*}} } diff --git a/clang/test/Driver/fsanitize.c b/clang/test/Driver/fsanitize.c index fff59a3480ea..964ad2b1d74d 100644 --- a/clang/test/Driver/fsanitize.c +++ b/clang/test/Driver/fsanitize.c @@ -216,3 +216,15 @@ // 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"