DataFlowSanitizer: Prefix the name of each instrumented function with "dfs$".

DFSan changes the ABI of each function in the module.  This makes it possible
for a function with the native ABI to be called with the instrumented ABI,
or vice versa, thus possibly invoking undefined behavior.  A simple way
of statically detecting instances of this problem is to prepend the prefix
"dfs$" to the name of each instrumented-ABI function.

This will not catch every such problem; in particular function pointers passed
across the instrumented-native barrier cannot be used on the other side.
These problems could potentially be caught dynamically.

Differential Revision: http://llvm-reviews.chandlerc.com/D1373

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@189052 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Peter Collingbourne 2013-08-22 20:08:08 +00:00
parent 094637408b
commit f1366c5524
10 changed files with 87 additions and 29 deletions

View File

@ -179,11 +179,13 @@ class DataFlowSanitizer : public ModulePass {
Value *getShadowAddress(Value *Addr, Instruction *Pos); Value *getShadowAddress(Value *Addr, Instruction *Pos);
Value *combineShadows(Value *V1, Value *V2, Instruction *Pos); Value *combineShadows(Value *V1, Value *V2, Instruction *Pos);
bool isInstrumented(Function *F); bool isInstrumented(const Function *F);
bool isInstrumented(const GlobalAlias *GA);
FunctionType *getArgsFunctionType(FunctionType *T); FunctionType *getArgsFunctionType(FunctionType *T);
FunctionType *getCustomFunctionType(FunctionType *T); FunctionType *getCustomFunctionType(FunctionType *T);
InstrumentedABI getInstrumentedABI(); InstrumentedABI getInstrumentedABI();
WrapperKind getWrapperKind(Function *F); WrapperKind getWrapperKind(Function *F);
void addGlobalNamePrefix(GlobalValue *GV);
public: public:
DataFlowSanitizer(StringRef ABIListFile = StringRef(), DataFlowSanitizer(StringRef ABIListFile = StringRef(),
@ -343,10 +345,14 @@ bool DataFlowSanitizer::doInitialization(Module &M) {
return true; return true;
} }
bool DataFlowSanitizer::isInstrumented(Function *F) { bool DataFlowSanitizer::isInstrumented(const Function *F) {
return !ABIList->isIn(*F, "uninstrumented"); return !ABIList->isIn(*F, "uninstrumented");
} }
bool DataFlowSanitizer::isInstrumented(const GlobalAlias *GA) {
return !ABIList->isIn(*GA, "uninstrumented");
}
DataFlowSanitizer::InstrumentedABI DataFlowSanitizer::getInstrumentedABI() { DataFlowSanitizer::InstrumentedABI DataFlowSanitizer::getInstrumentedABI() {
return ClArgsABI ? IA_Args : IA_TLS; return ClArgsABI ? IA_Args : IA_TLS;
} }
@ -362,6 +368,25 @@ DataFlowSanitizer::WrapperKind DataFlowSanitizer::getWrapperKind(Function *F) {
return WK_Warning; return WK_Warning;
} }
void DataFlowSanitizer::addGlobalNamePrefix(GlobalValue *GV) {
std::string GVName = GV->getName(), Prefix = "dfs$";
GV->setName(Prefix + GVName);
// Try to change the name of the function in module inline asm. We only do
// this for specific asm directives, currently only ".symver", to try to avoid
// corrupting asm which happens to contain the symbol name as a substring.
// Note that the substitution for .symver assumes that the versioned symbol
// also has an instrumented name.
std::string Asm = GV->getParent()->getModuleInlineAsm();
std::string SearchStr = ".symver " + GVName + ",";
size_t Pos = Asm.find(SearchStr);
if (Pos != std::string::npos) {
Asm.replace(Pos, SearchStr.size(),
".symver " + Prefix + GVName + "," + Prefix);
GV->getParent()->setModuleInlineAsm(Asm);
}
}
bool DataFlowSanitizer::runOnModule(Module &M) { bool DataFlowSanitizer::runOnModule(Module &M) {
if (!DL) if (!DL)
return false; return false;
@ -415,6 +440,21 @@ bool DataFlowSanitizer::runOnModule(Module &M) {
FnsToInstrument.push_back(&*i); FnsToInstrument.push_back(&*i);
} }
// Give function aliases prefixes when necessary.
for (Module::alias_iterator i = M.alias_begin(), e = M.alias_end(); i != e;) {
GlobalAlias *GA = &*i;
++i;
// Don't stop on weak. We assume people aren't playing games with the
// instrumentedness of overridden weak aliases.
if (Function *F = dyn_cast<Function>(
GA->resolveAliasedGlobal(/*stopOnWeak=*/false))) {
bool GAInst = isInstrumented(GA), FInst = isInstrumented(F);
if (GAInst && FInst) {
addGlobalNamePrefix(GA);
}
}
}
AttrBuilder B; AttrBuilder B;
B.addAttribute(Attribute::ReadOnly).addAttribute(Attribute::ReadNone); B.addAttribute(Attribute::ReadOnly).addAttribute(Attribute::ReadNone);
ReadOnlyNoneAttrs = AttributeSet::get(*Ctx, AttributeSet::FunctionIndex, B); ReadOnlyNoneAttrs = AttributeSet::get(*Ctx, AttributeSet::FunctionIndex, B);
@ -427,12 +467,13 @@ bool DataFlowSanitizer::runOnModule(Module &M) {
Function &F = **i; Function &F = **i;
FunctionType *FT = F.getFunctionType(); FunctionType *FT = F.getFunctionType();
if (FT->getNumParams() == 0 && !FT->isVarArg() && bool IsZeroArgsVoidRet = (FT->getNumParams() == 0 && !FT->isVarArg() &&
FT->getReturnType()->isVoidTy()) FT->getReturnType()->isVoidTy());
continue;
if (isInstrumented(&F)) { if (isInstrumented(&F)) {
if (getInstrumentedABI() == IA_Args) { // Instrumented functions get a 'dfs$' prefix. This allows us to more
// easily identify cases of mismatching ABIs.
if (getInstrumentedABI() == IA_Args && !IsZeroArgsVoidRet) {
FunctionType *NewFT = getArgsFunctionType(FT); FunctionType *NewFT = getArgsFunctionType(FT);
Function *NewF = Function::Create(NewFT, F.getLinkage(), "", &M); Function *NewF = Function::Create(NewFT, F.getLinkage(), "", &M);
NewF->copyAttributesFrom(&F); NewF->copyAttributesFrom(&F);
@ -463,13 +504,16 @@ bool DataFlowSanitizer::runOnModule(Module &M) {
NewF->takeName(&F); NewF->takeName(&F);
F.eraseFromParent(); F.eraseFromParent();
*i = NewF; *i = NewF;
addGlobalNamePrefix(NewF);
} else {
addGlobalNamePrefix(&F);
} }
// Hopefully, nobody will try to indirectly call a vararg // Hopefully, nobody will try to indirectly call a vararg
// function... yet. // function... yet.
} else if (FT->isVarArg()) { } else if (FT->isVarArg()) {
UnwrappedFnMap[&F] = &F; UnwrappedFnMap[&F] = &F;
*i = 0; *i = 0;
} else { } else if (!IsZeroArgsVoidRet || getWrapperKind(&F) == WK_Custom) {
// Build a wrapper function for F. The wrapper simply calls F, and is // Build a wrapper function for F. The wrapper simply calls F, and is
// added to FnsToInstrument so that any instrumentation according to its // added to FnsToInstrument so that any instrumentation according to its
// WrapperKind is done in the second pass below. // WrapperKind is done in the second pass below.

View File

@ -16,7 +16,7 @@ declare void @custom1(i32 %a, i32 %b)
declare i32 @custom2(i32 %a, i32 %b) declare i32 @custom2(i32 %a, i32 %b)
; CHECK: @f ; CHECK: @"dfs$f"
define void @f() { define void @f() {
; CHECK: %[[LABELRETURN:.*]] = alloca i16 ; CHECK: %[[LABELRETURN:.*]] = alloca i16
@ -37,7 +37,7 @@ define void @f() {
; CHECK: insertvalue {{.*}}[[RVSHADOW]], 1 ; CHECK: insertvalue {{.*}}[[RVSHADOW]], 1
; CHECK: ret { i32, i16 } ; CHECK: ret { i32, i16 }
; CHECK: @g ; CHECK: @"dfs$g"
define i32 (i32, i32)* @g() { define i32 (i32, i32)* @g() {
; CHECK: ret {{.*}} @"dfsw$custom2" ; CHECK: ret {{.*}} @"dfsw$custom2"
ret i32 (i32, i32)* @custom2 ret i32 (i32, i32)* @custom2

View File

@ -1,7 +1,7 @@
; RUN: opt < %s -dfsan -verify -dfsan-args-abi -S | FileCheck %s ; RUN: opt < %s -dfsan -verify -dfsan-args-abi -S | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
; CHECK-LABEL: @unreachable_bb1 ; CHECK-LABEL: @"dfs$unreachable_bb1"
define i8 @unreachable_bb1() { define i8 @unreachable_bb1() {
; CHECK: ret { i8, i16 } { i8 1, i16 0 } ; CHECK: ret { i8, i16 } { i8 1, i16 0 }
; CHECK-NOT: bb2: ; CHECK-NOT: bb2:
@ -21,7 +21,7 @@ bb4:
declare void @abort() noreturn declare void @abort() noreturn
; CHECK-LABEL: @unreachable_bb2 ; CHECK-LABEL: @"dfs$unreachable_bb2"
define i8 @unreachable_bb2() { define i8 @unreachable_bb2() {
call void @abort() noreturn call void @abort() noreturn
; CHECK-NOT: i8 12 ; CHECK-NOT: i8 12

View File

@ -2,7 +2,7 @@
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
define i8 @add(i8 %a, i8 %b) { define i8 @add(i8 %a, i8 %b) {
; CHECK: @add ; CHECK: @"dfs$add"
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: call{{.*}}__dfsan_union ; CHECK: call{{.*}}__dfsan_union
@ -14,7 +14,7 @@ define i8 @add(i8 %a, i8 %b) {
} }
define i8 @sub(i8 %a, i8 %b) { define i8 @sub(i8 %a, i8 %b) {
; CHECK: @sub ; CHECK: @"dfs$sub"
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: call{{.*}}__dfsan_union ; CHECK: call{{.*}}__dfsan_union
@ -26,7 +26,7 @@ define i8 @sub(i8 %a, i8 %b) {
} }
define i8 @mul(i8 %a, i8 %b) { define i8 @mul(i8 %a, i8 %b) {
; CHECK: @mul ; CHECK: @"dfs$mul"
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: call{{.*}}__dfsan_union ; CHECK: call{{.*}}__dfsan_union
@ -38,7 +38,7 @@ define i8 @mul(i8 %a, i8 %b) {
} }
define i8 @sdiv(i8 %a, i8 %b) { define i8 @sdiv(i8 %a, i8 %b) {
; CHECK: @sdiv ; CHECK: @"dfs$sdiv"
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: call{{.*}}__dfsan_union ; CHECK: call{{.*}}__dfsan_union
@ -50,7 +50,7 @@ define i8 @sdiv(i8 %a, i8 %b) {
} }
define i8 @udiv(i8 %a, i8 %b) { define i8 @udiv(i8 %a, i8 %b) {
; CHECK: @udiv ; CHECK: @"dfs$udiv"
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: call{{.*}}__dfsan_union ; CHECK: call{{.*}}__dfsan_union

View File

@ -7,10 +7,10 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3
declare i32 @f(i32) declare i32 @f(i32)
declare float @llvm.sqrt.f32(float) declare float @llvm.sqrt.f32(float)
; CHECK: @call ; CHECK: @"dfs$call"
define i32 @call() { define i32 @call() {
; CHECK: store{{.*}}__dfsan_arg_tls ; CHECK: store{{.*}}__dfsan_arg_tls
; CHECK: call{{.*}}@f ; CHECK: call{{.*}}@"dfs$f"
; CHECK: load{{.*}}__dfsan_retval_tls ; CHECK: load{{.*}}__dfsan_retval_tls
%r = call i32 @f(i32 0) %r = call i32 @f(i32 0)

View File

@ -3,14 +3,14 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3
declare i32 @g() declare i32 @g()
; CHECK: define { i32, i16 } @f(i32, i16) ; CHECK: define { i32, i16 } @"dfs$f"(i32, i16)
define i32 @f(i32) { define i32 @f(i32) {
; CHECK: [[LOCALLABELALLOCA:%.*]] = alloca i16 ; CHECK: [[LOCALLABELALLOCA:%.*]] = alloca i16
; CHECK: [[ARGCMP:%.*]] = icmp ne i16 %1, 0 ; CHECK: [[ARGCMP:%.*]] = icmp ne i16 %1, 0
; CHECK: br i1 [[ARGCMP]] ; CHECK: br i1 [[ARGCMP]]
%i = alloca i32 %i = alloca i32
store i32 %0, i32* %i store i32 %0, i32* %i
; CHECK: [[CALL:%.*]] = call { i32, i16 } @g() ; CHECK: [[CALL:%.*]] = call { i32, i16 } @"dfs$g"()
; CHECK: [[CALLLABEL:%.*]] = extractvalue { i32, i16 } [[CALL]], 1 ; CHECK: [[CALLLABEL:%.*]] = extractvalue { i32, i16 } [[CALL]], 1
; CHECK: [[CALLCMP:%.*]] = icmp ne i16 [[CALLLABEL]], 0 ; CHECK: [[CALLCMP:%.*]] = icmp ne i16 [[CALLLABEL]], 0
; CHECK: br i1 [[CALLCMP]] ; CHECK: br i1 [[CALLCMP]]

View File

@ -2,7 +2,7 @@
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
define i8 @load8(i8* %p) { define i8 @load8(i8* %p) {
; CHECK: @load8 ; CHECK: @"dfs$load8"
; CHECK: ptrtoint ; CHECK: ptrtoint
; CHECK: and ; CHECK: and
; CHECK: mul ; CHECK: mul
@ -15,7 +15,7 @@ define i8 @load8(i8* %p) {
} }
define i16 @load16(i16* %p) { define i16 @load16(i16* %p) {
; CHECK: @load16 ; CHECK: @"dfs$load16"
; CHECK: ptrtoint ; CHECK: ptrtoint
; CHECK: and ; CHECK: and
; CHECK: mul ; CHECK: mul
@ -31,7 +31,7 @@ define i16 @load16(i16* %p) {
} }
define i32 @load32(i32* %p) { define i32 @load32(i32* %p) {
; CHECK: @load32 ; CHECK: @"dfs$load32"
; CHECK: ptrtoint ; CHECK: ptrtoint
; CHECK: and ; CHECK: and
; CHECK: mul ; CHECK: mul
@ -54,7 +54,7 @@ define i32 @load32(i32* %p) {
} }
define i64 @load64(i64* %p) { define i64 @load64(i64* %p) {
; CHECK: @load64 ; CHECK: @"dfs$load64"
; CHECK: ptrtoint ; CHECK: ptrtoint
; CHECK: and ; CHECK: and
; CHECK: mul ; CHECK: mul

View File

@ -4,7 +4,7 @@ target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f3
declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i32, i1) declare void @llvm.memset.p0i8.i64(i8* nocapture, i8, i64, i32, i1)
define void @ms(i8* %p, i8 %v) { define void @ms(i8* %p, i8 %v) {
; CHECK-LABEL: @ms(i8*, i8, i16, i16) ; CHECK-LABEL: @"dfs$ms"(i8*, i8, i16, i16)
; CHECK: call void @__dfsan_set_label(i16 %3, i8* %0, i64 1) ; CHECK: call void @__dfsan_set_label(i16 %3, i8* %0, i64 1)
call void @llvm.memset.p0i8.i64(i8* %p, i8 %v, i64 1, i32 1, i1 1) call void @llvm.memset.p0i8.i64(i8* %p, i8 %v, i64 1, i32 1, i1 1)
ret void ret void

View File

@ -0,0 +1,14 @@
; RUN: opt < %s -dfsan -S | FileCheck %s
; RUN: opt < %s -dfsan -dfsan-args-abi -S | FileCheck %s
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
; CHECK: module asm ".symver dfs$f1,dfs$f@@version1"
module asm ".symver f1,f@@version1"
; CHECK: @"dfs$f2" = alias {{.*}} @"dfs$f1"
@f2 = alias void ()* @f1
; CHECK: define void @"dfs$f1"
define void @f1() {
ret void
}

View File

@ -2,7 +2,7 @@
target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128" target datalayout = "e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64-S128"
define void @store8(i8 %v, i8* %p) { define void @store8(i8 %v, i8* %p) {
; CHECK: @store8 ; CHECK: @"dfs$store8"
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: ptrtoint ; CHECK: ptrtoint
; CHECK: and ; CHECK: and
@ -16,7 +16,7 @@ define void @store8(i8 %v, i8* %p) {
} }
define void @store16(i16 %v, i16* %p) { define void @store16(i16 %v, i16* %p) {
; CHECK: @store16 ; CHECK: @"dfs$store16"
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: ptrtoint ; CHECK: ptrtoint
; CHECK: and ; CHECK: and
@ -32,7 +32,7 @@ define void @store16(i16 %v, i16* %p) {
} }
define void @store32(i32 %v, i32* %p) { define void @store32(i32 %v, i32* %p) {
; CHECK: @store32 ; CHECK: @"dfs$store32"
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: ptrtoint ; CHECK: ptrtoint
; CHECK: and ; CHECK: and
@ -52,7 +52,7 @@ define void @store32(i32 %v, i32* %p) {
} }
define void @store64(i64 %v, i64* %p) { define void @store64(i64 %v, i64* %p) {
; CHECK: @store64 ; CHECK: @"dfs$store64"
; CHECK: load{{.*}}__dfsan_arg_tls ; CHECK: load{{.*}}__dfsan_arg_tls
; CHECK: ptrtoint ; CHECK: ptrtoint
; CHECK: and ; CHECK: and