Skip to content
This repository has been archived by the owner on Apr 23, 2020. It is now read-only.

Commit

Permalink
Protection against stack-based memory corruption errors using SafeStack
Browse files Browse the repository at this point in the history
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
pcc committed Jun 15, 2015
1 parent 3aef776 commit 7ffec83
Show file tree
Hide file tree
Showing 48 changed files with 1,417 additions and 4 deletions.
9 changes: 9 additions & 0 deletions docs/LangRef.rst
Expand Up @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion include/llvm/Bitcode/LLVMBitCodes.h
Expand Up @@ -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 {
Expand Down
1 change: 1 addition & 0 deletions include/llvm/IR/Attributes.h
Expand Up @@ -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.
Expand Down
1 change: 1 addition & 0 deletions include/llvm/InitializePasses.h
Expand Up @@ -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&);
Expand Down
1 change: 1 addition & 0 deletions include/llvm/LinkAllPasses.h
Expand Up @@ -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();
Expand Down
4 changes: 4 additions & 0 deletions include/llvm/Transforms/Instrumentation.h
Expand Up @@ -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
1 change: 1 addition & 0 deletions lib/AsmParser/LLLexer.cpp
Expand Up @@ -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);
Expand Down
3 changes: 3 additions & 0 deletions lib/AsmParser/LLParser.cpp
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions lib/AsmParser/LLToken.h
Expand Up @@ -135,6 +135,7 @@ namespace lltok {
kw_ssp,
kw_sspreq,
kw_sspstrong,
kw_safestack,
kw_sret,
kw_sanitize_thread,
kw_sanitize_memory,
Expand Down
2 changes: 2 additions & 0 deletions lib/Bitcode/Reader/BitcodeReader.cpp
Expand Up @@ -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:
Expand Down
2 changes: 2 additions & 0 deletions lib/Bitcode/Writer/BitcodeWriter.cpp
Expand Up @@ -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:
Expand Down
2 changes: 1 addition & 1 deletion lib/CodeGen/LLVMBuild.txt
Expand Up @@ -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
4 changes: 4 additions & 0 deletions lib/CodeGen/Passes.cpp
Expand Up @@ -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"

Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions lib/IR/Attributes.cpp
Expand Up @@ -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))
Expand Down Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions lib/IR/Verifier.cpp
Expand Up @@ -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 ||
Expand Down
1 change: 1 addition & 0 deletions lib/Target/CppBackend/CPPBackend.cpp
Expand Up @@ -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);
Expand Down
11 changes: 9 additions & 2 deletions lib/Transforms/IPO/Inliner.cpp
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions lib/Transforms/Instrumentation/CMakeLists.txt
Expand Up @@ -6,6 +6,7 @@ add_llvm_library(LLVMInstrumentation
MemorySanitizer.cpp
Instrumentation.cpp
InstrProfiling.cpp
SafeStack.cpp
SanitizerCoverage.cpp
ThreadSanitizer.cpp

Expand Down
1 change: 1 addition & 0 deletions lib/Transforms/Instrumentation/Instrumentation.cpp
Expand Up @@ -30,6 +30,7 @@ void llvm::initializeInstrumentation(PassRegistry &Registry) {
initializeThreadSanitizerPass(Registry);
initializeSanitizerCoverageModulePass(Registry);
initializeDataFlowSanitizerPass(Registry);
initializeSafeStackPass(Registry);
}

/// LLVMInitializeInstrumentation - C binding for
Expand Down

0 comments on commit 7ffec83

Please sign in to comment.