Permalink
Browse files

Protection against stack-based memory corruption errors using SafeStack

This patch adds the safe stack instrumentation pass to LLVM, which separates
the program stack into a safe stack, which stores return addresses, register
spills, and local variables that are statically verified to be accessed
in a safe way, and the unsafe stack, which stores everything else. Such
separation makes it much harder for an attacker to corrupt objects on the
safe stack, including function pointers stored in spilled registers and
return addresses. You can find more information about the safe stack, as
well as other parts of or control-flow hijack protection technique in our
OSDI paper on code-pointer integrity (http://dslab.epfl.ch/pubs/cpi.pdf)
and our project website (http://levee.epfl.ch).

The overhead of our implementation of the safe stack is very close to zero
(0.01% on the Phoronix benchmarks). This is lower than the overhead of
stack cookies, which are supported by LLVM and are commonly used today,
yet the security guarantees of the safe stack are strictly stronger than
stack cookies. In some cases, the safe stack improves performance due to
better cache locality.

Our current implementation of the safe stack is stable and robust, we
used it to recompile multiple projects on Linux including Chromium, and
we also recompiled the entire FreeBSD user-space system and more than 100
packages. We ran unit tests on the FreeBSD system and many of the packages
and observed no errors caused by the safe stack. The safe stack is also fully
binary compatible with non-instrumented code and can be applied to parts of
a program selectively.

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

- Add the safestack function attribute, similar to the ssp, sspstrong and
  sspreq attributes.

- Add the SafeStack instrumentation pass that applies the safe stack to all
  functions that have the safestack attribute. This pass moves all unsafe local
  variables to the unsafe stack with a separate stack pointer, whereas all
  safe variables remain on the regular stack that is managed by LLVM as usual.

- Invoke the pass as the last stage before code generation (at the same time
  the existing cookie-based stack protector pass is invoked).

- Add unit tests for the safe stack.

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/D6094

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@239761 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information...
1 parent 3aef776 commit 7ffec838a2b72e6841d9fb993b5fe6a45f3b2a90 @pcc pcc committed Jun 15, 2015
Showing with 1,417 additions and 4 deletions.
  1. +9 −0 docs/LangRef.rst
  2. +2 −1 include/llvm/Bitcode/LLVMBitCodes.h
  3. +1 −0 include/llvm/IR/Attributes.h
  4. +1 −0 include/llvm/InitializePasses.h
  5. +1 −0 include/llvm/LinkAllPasses.h
  6. +4 −0 include/llvm/Transforms/Instrumentation.h
  7. +1 −0 lib/AsmParser/LLLexer.cpp
  8. +3 −0 lib/AsmParser/LLParser.cpp
  9. +1 −0 lib/AsmParser/LLToken.h
  10. +2 −0 lib/Bitcode/Reader/BitcodeReader.cpp
  11. +2 −0 lib/Bitcode/Writer/BitcodeWriter.cpp
  12. +1 −1 lib/CodeGen/LLVMBuild.txt
  13. +4 −0 lib/CodeGen/Passes.cpp
  14. +3 −0 lib/IR/Attributes.cpp
  15. +1 −0 lib/IR/Verifier.cpp
  16. +1 −0 lib/Target/CppBackend/CPPBackend.cpp
  17. +9 −2 lib/Transforms/IPO/Inliner.cpp
  18. +1 −0 lib/Transforms/Instrumentation/CMakeLists.txt
  19. +1 −0 lib/Transforms/Instrumentation/Instrumentation.cpp
  20. +608 −0 lib/Transforms/Instrumentation/SafeStack.cpp
  21. +22 −0 test/Transforms/SafeStack/addr-taken.ll
  22. +39 −0 test/Transforms/SafeStack/array-aligned.ll
  23. +38 −0 test/Transforms/SafeStack/array.ll
  24. +20 −0 test/Transforms/SafeStack/call.ll
  25. +17 −0 test/Transforms/SafeStack/cast.ll
  26. +26 −0 test/Transforms/SafeStack/constant-gep-call.ll
  27. +20 −0 test/Transforms/SafeStack/constant-gep.ll
  28. +28 −0 test/Transforms/SafeStack/constant-geps.ll
  29. +21 −0 test/Transforms/SafeStack/dynamic-alloca.ll
  30. +23 −0 test/Transforms/SafeStack/escape-addr-pointer.ll
  31. +23 −0 test/Transforms/SafeStack/escape-bitcast-store.ll
  32. +20 −0 test/Transforms/SafeStack/escape-bitcast-store2.ll
  33. +16 −0 test/Transforms/SafeStack/escape-call.ll
  34. +24 −0 test/Transforms/SafeStack/escape-casted-pointer.ll
  35. +20 −0 test/Transforms/SafeStack/escape-gep-call.ll
  36. +34 −0 test/Transforms/SafeStack/escape-gep-invoke.ll
  37. +18 −0 test/Transforms/SafeStack/escape-gep-negative.ll
  38. +22 −0 test/Transforms/SafeStack/escape-gep-ptrtoint.ll
  39. +23 −0 test/Transforms/SafeStack/escape-gep-store.ll
  40. +36 −0 test/Transforms/SafeStack/escape-phi-call.ll
  41. +22 −0 test/Transforms/SafeStack/escape-select-call.ll
  42. +21 −0 test/Transforms/SafeStack/escape-vector.ll
  43. +33 −0 test/Transforms/SafeStack/invoke.ll
  44. +25 −0 test/Transforms/SafeStack/no-attr.ll
  45. +50 −0 test/Transforms/SafeStack/phi-cycle.ll
  46. +37 −0 test/Transforms/SafeStack/setjmp.ll
  47. +42 −0 test/Transforms/SafeStack/setjmp2.ll
  48. +41 −0 test/Transforms/SafeStack/struct.ll
View
@@ -1319,6 +1319,15 @@ example:
``setjmp`` is an example of such a function. The compiler disables
some optimizations (like tail calls) in the caller of these
functions.
+``safestack``
+ This attribute indicates that
+ `SafeStack <http://clang.llvm.org/docs/SafeStack.html>`_
+ protection is enabled for this function.
+
+ If a function that has a ``safestack`` attribute is inlined into a
+ function that doesn't have a ``safestack`` attribute or which has an
+ ``ssp``, ``sspstrong`` or ``sspreq`` attribute, then the resulting
+ function will have a ``safestack`` attribute.
``sanitize_address``
This attribute indicates that AddressSanitizer checks
(dynamic address safety analysis) are enabled for this function.
@@ -403,7 +403,8 @@ namespace bitc {
ATTR_KIND_JUMP_TABLE = 40,
ATTR_KIND_DEREFERENCEABLE = 41,
ATTR_KIND_DEREFERENCEABLE_OR_NULL = 42,
- ATTR_KIND_CONVERGENT = 43
+ ATTR_KIND_CONVERGENT = 43,
+ ATTR_KIND_SAFESTACK = 44,
};
enum ComdatSelectionKindCodes {
@@ -108,6 +108,7 @@ class Attribute {
StackProtect, ///< Stack protection.
StackProtectReq, ///< Stack protection required.
StackProtectStrong, ///< Strong Stack protection.
+ SafeStack, ///< Safe Stack protection.
StructRet, ///< Hidden pointer to structure to return
SanitizeAddress, ///< AddressSanitizer is on.
SanitizeThread, ///< ThreadSanitizer is on.
@@ -242,6 +242,7 @@ void initializeRegionOnlyViewerPass(PassRegistry&);
void initializeRegionPrinterPass(PassRegistry&);
void initializeRegionViewerPass(PassRegistry&);
void initializeRewriteStatepointsForGCPass(PassRegistry&);
+void initializeSafeStackPass(PassRegistry&);
void initializeSCCPPass(PassRegistry&);
void initializeSROAPass(PassRegistry&);
void initializeSROA_DTPass(PassRegistry&);
@@ -131,6 +131,7 @@ namespace {
(void) llvm::createRegionPrinterPass();
(void) llvm::createRegionViewerPass();
(void) llvm::createSCCPPass();
+ (void) llvm::createSafeStackPass();
(void) llvm::createScalarReplAggregatesPass();
(void) llvm::createSingleLoopExtractorPass();
(void) llvm::createStripSymbolsPass();
@@ -132,6 +132,10 @@ inline ModulePass *createDataFlowSanitizerPassForJIT(
// checking on loads, stores, and other memory intrinsics.
FunctionPass *createBoundsCheckingPass();
+/// \brief This pass splits the stack into a safe stack and an unsafe stack to
+/// protect against stack-based overflow vulnerabilities.
+FunctionPass *createSafeStackPass();
+
} // End llvm namespace
#endif
@@ -628,6 +628,7 @@ lltok::Kind LLLexer::LexIdentifier() {
KEYWORD(ssp);
KEYWORD(sspreq);
KEYWORD(sspstrong);
+ KEYWORD(safestack);
KEYWORD(sanitize_address);
KEYWORD(sanitize_thread);
KEYWORD(sanitize_memory);
@@ -958,6 +958,7 @@ bool LLParser::ParseFnAttributeValuePairs(AttrBuilder &B,
case lltok::kw_ssp: B.addAttribute(Attribute::StackProtect); break;
case lltok::kw_sspreq: B.addAttribute(Attribute::StackProtectReq); break;
case lltok::kw_sspstrong: B.addAttribute(Attribute::StackProtectStrong); break;
+ case lltok::kw_safestack: B.addAttribute(Attribute::SafeStack); break;
case lltok::kw_sanitize_address: B.addAttribute(Attribute::SanitizeAddress); break;
case lltok::kw_sanitize_thread: B.addAttribute(Attribute::SanitizeThread); break;
case lltok::kw_sanitize_memory: B.addAttribute(Attribute::SanitizeMemory); break;
@@ -1267,6 +1268,7 @@ bool LLParser::ParseOptionalParamAttrs(AttrBuilder &B) {
case lltok::kw_ssp:
case lltok::kw_sspreq:
case lltok::kw_sspstrong:
+ case lltok::kw_safestack:
case lltok::kw_uwtable:
HaveError |= Error(Lex.getLoc(), "invalid use of function-only attribute");
break;
@@ -1343,6 +1345,7 @@ bool LLParser::ParseOptionalReturnAttrs(AttrBuilder &B) {
case lltok::kw_ssp:
case lltok::kw_sspreq:
case lltok::kw_sspstrong:
+ case lltok::kw_safestack:
case lltok::kw_uwtable:
HaveError |= Error(Lex.getLoc(), "invalid use of function-only attribute");
break;
@@ -135,6 +135,7 @@ namespace lltok {
kw_ssp,
kw_sspreq,
kw_sspstrong,
+ kw_safestack,
kw_sret,
kw_sanitize_thread,
kw_sanitize_memory,
@@ -1146,6 +1146,8 @@ static Attribute::AttrKind getAttrFromCode(uint64_t Code) {
return Attribute::StackProtectReq;
case bitc::ATTR_KIND_STACK_PROTECT_STRONG:
return Attribute::StackProtectStrong;
+ case bitc::ATTR_KIND_SAFESTACK:
+ return Attribute::SafeStack;
case bitc::ATTR_KIND_STRUCT_RET:
return Attribute::StructRet;
case bitc::ATTR_KIND_SANITIZE_ADDRESS:
@@ -232,6 +232,8 @@ static uint64_t getAttrKindEncoding(Attribute::AttrKind Kind) {
return bitc::ATTR_KIND_STACK_PROTECT_REQ;
case Attribute::StackProtectStrong:
return bitc::ATTR_KIND_STACK_PROTECT_STRONG;
+ case Attribute::SafeStack:
+ return bitc::ATTR_KIND_SAFESTACK;
case Attribute::StructRet:
return bitc::ATTR_KIND_STRUCT_RET;
case Attribute::SanitizeAddress:
@@ -22,4 +22,4 @@ subdirectories = AsmPrinter SelectionDAG MIRParser
type = Library
name = CodeGen
parent = Libraries
-required_libraries = Analysis Core MC Scalar Support Target TransformUtils
+required_libraries = Analysis Core Instrumentation MC Scalar Support Target TransformUtils
@@ -24,6 +24,7 @@
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/raw_ostream.h"
+#include "llvm/Transforms/Instrumentation.h"
#include "llvm/Transforms/Scalar.h"
#include "llvm/Transforms/Utils/SymbolRewriter.h"
@@ -456,6 +457,9 @@ void TargetPassConfig::addCodeGenPrepare() {
void TargetPassConfig::addISelPrepare() {
addPreISel();
+ // Add both the safe stack and the stack protection passes: each of them will
+ // only protect functions that have corresponding attributes.
+ addPass(createSafeStackPass());
addPass(createStackProtectorPass(TM));
if (PrintISelInput)
@@ -252,6 +252,8 @@ std::string Attribute::getAsString(bool InAttrGrp) const {
return "sspreq";
if (hasAttribute(Attribute::StackProtectStrong))
return "sspstrong";
+ if (hasAttribute(Attribute::SafeStack))
+ return "safestack";
if (hasAttribute(Attribute::StructRet))
return "sret";
if (hasAttribute(Attribute::SanitizeThread))
@@ -437,6 +439,7 @@ uint64_t AttributeImpl::getAttrMask(Attribute::AttrKind Val) {
case Attribute::NonNull: return 1ULL << 44;
case Attribute::JumpTable: return 1ULL << 45;
case Attribute::Convergent: return 1ULL << 46;
+ case Attribute::SafeStack: return 1ULL << 47;
case Attribute::Dereferenceable:
llvm_unreachable("dereferenceable attribute not supported in raw format");
break;
View
@@ -1251,6 +1251,7 @@ void Verifier::VerifyAttributeTypes(AttributeSet Attrs, unsigned Idx,
I->getKindAsEnum() == Attribute::StackProtect ||
I->getKindAsEnum() == Attribute::StackProtectReq ||
I->getKindAsEnum() == Attribute::StackProtectStrong ||
+ I->getKindAsEnum() == Attribute::SafeStack ||
I->getKindAsEnum() == Attribute::NoRedZone ||
I->getKindAsEnum() == Attribute::NoImplicitFloat ||
I->getKindAsEnum() == Attribute::Naked ||
@@ -513,6 +513,7 @@ void CppWriter::printAttributes(const AttributeSet &PAL,
HANDLE_ATTR(StackProtect);
HANDLE_ATTR(StackProtectReq);
HANDLE_ATTR(StackProtectStrong);
+ HANDLE_ATTR(SafeStack);
HANDLE_ATTR(NoCapture);
HANDLE_ATTR(NoRedZone);
HANDLE_ATTR(NoImplicitFloat);
@@ -93,19 +93,26 @@ static void AdjustCallerSSPLevel(Function *Caller, Function *Callee) {
// clutter to the IR.
AttrBuilder B;
B.addAttribute(Attribute::StackProtect)
- .addAttribute(Attribute::StackProtectStrong);
+ .addAttribute(Attribute::StackProtectStrong)
+ .addAttribute(Attribute::StackProtectReq);
AttributeSet OldSSPAttr = AttributeSet::get(Caller->getContext(),
AttributeSet::FunctionIndex,
B);
- if (Callee->hasFnAttribute(Attribute::StackProtectReq)) {
+ if (Callee->hasFnAttribute(Attribute::SafeStack)) {
+ Caller->removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
+ Caller->addFnAttr(Attribute::SafeStack);
+ } else if (Callee->hasFnAttribute(Attribute::StackProtectReq) &&
+ !Caller->hasFnAttribute(Attribute::SafeStack)) {
Caller->removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
Caller->addFnAttr(Attribute::StackProtectReq);
} else if (Callee->hasFnAttribute(Attribute::StackProtectStrong) &&
+ !Caller->hasFnAttribute(Attribute::SafeStack) &&
!Caller->hasFnAttribute(Attribute::StackProtectReq)) {
Caller->removeAttributes(AttributeSet::FunctionIndex, OldSSPAttr);
Caller->addFnAttr(Attribute::StackProtectStrong);
} else if (Callee->hasFnAttribute(Attribute::StackProtect) &&
+ !Caller->hasFnAttribute(Attribute::SafeStack) &&
!Caller->hasFnAttribute(Attribute::StackProtectReq) &&
!Caller->hasFnAttribute(Attribute::StackProtectStrong))
Caller->addFnAttr(Attribute::StackProtect);
@@ -6,6 +6,7 @@ add_llvm_library(LLVMInstrumentation
MemorySanitizer.cpp
Instrumentation.cpp
InstrProfiling.cpp
+ SafeStack.cpp
SanitizerCoverage.cpp
ThreadSanitizer.cpp
@@ -30,6 +30,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) {
initializeThreadSanitizerPass(Registry);
initializeSanitizerCoverageModulePass(Registry);
initializeDataFlowSanitizerPass(Registry);
+ initializeSafeStackPass(Registry);
}
/// LLVMInitializeInstrumentation - C binding for
Oops, something went wrong.

0 comments on commit 7ffec83

Please sign in to comment.