forked from OSchip/llvm-project
Implement function type checker for the undefined behavior sanitizer.
This uses function prefix data to store function type information at the function pointer. Differential Revision: http://llvm-reviews.chandlerc.com/D1338 llvm-svn: 193058
This commit is contained in:
parent
f7ef3fd810
commit
b453cd64a7
|
@ -921,6 +921,8 @@ are listed below.
|
|||
destination.
|
||||
- ``-fsanitize=float-divide-by-zero``: Floating point division by
|
||||
zero.
|
||||
- ``-fsanitize=function``: Indirect call of a function through a
|
||||
function pointer of the wrong type (C++ and x86/x86_64 only).
|
||||
- ``-fsanitize=integer-divide-by-zero``: Integer division by zero.
|
||||
- ``-fsanitize=null``: Use of a null pointer or creation of a null
|
||||
reference.
|
||||
|
|
|
@ -64,6 +64,7 @@ SANITIZER("bounds", Bounds)
|
|||
SANITIZER("enum", Enum)
|
||||
SANITIZER("float-cast-overflow", FloatCastOverflow)
|
||||
SANITIZER("float-divide-by-zero", FloatDivideByZero)
|
||||
SANITIZER("function", Function)
|
||||
SANITIZER("integer-divide-by-zero", IntegerDivideByZero)
|
||||
SANITIZER("null", Null)
|
||||
SANITIZER("object-size", ObjectSize)
|
||||
|
@ -84,9 +85,9 @@ SANITIZER("dataflow", DataFlow)
|
|||
// ABI or address space layout implications, and only catch undefined behavior.
|
||||
SANITIZER_GROUP("undefined", Undefined,
|
||||
Alignment | Bool | Bounds | Enum | FloatCastOverflow |
|
||||
FloatDivideByZero | IntegerDivideByZero | Null | ObjectSize |
|
||||
Return | Shift | SignedIntegerOverflow | Unreachable |
|
||||
VLABound | Vptr)
|
||||
FloatDivideByZero | Function | IntegerDivideByZero | Null |
|
||||
ObjectSize | Return | Shift | SignedIntegerOverflow |
|
||||
Unreachable | VLABound | Vptr)
|
||||
|
||||
// -fsanitize=undefined-trap (and its alias -fcatch-undefined-behavior) includes
|
||||
// all sanitizers included by -fsanitize=undefined, except those that require
|
||||
|
|
|
@ -165,7 +165,7 @@ static Value *EmitFAbs(CodeGenFunction &CGF, Value *V, QualType ValTy) {
|
|||
|
||||
static RValue emitLibraryCall(CodeGenFunction &CGF, const FunctionDecl *Fn,
|
||||
const CallExpr *E, llvm::Value *calleeValue) {
|
||||
return CGF.EmitCall(E->getCallee()->getType(), calleeValue,
|
||||
return CGF.EmitCall(E->getCallee()->getType(), calleeValue, E->getLocStart(),
|
||||
ReturnValueSlot(), E->arg_begin(), E->arg_end(), Fn);
|
||||
}
|
||||
|
||||
|
|
|
@ -44,8 +44,8 @@ RValue CGCUDARuntime::EmitCUDAKernelCallExpr(CodeGenFunction &CGF,
|
|||
}
|
||||
|
||||
llvm::Value *Callee = CGF.EmitScalarExpr(E->getCallee());
|
||||
CGF.EmitCall(E->getCallee()->getType(), Callee, ReturnValue,
|
||||
E->arg_begin(), E->arg_end(), TargetDecl);
|
||||
CGF.EmitCall(E->getCallee()->getType(), Callee, E->getLocStart(),
|
||||
ReturnValue, E->arg_begin(), E->arg_end(), TargetDecl);
|
||||
CGF.EmitBranch(ContBlock);
|
||||
|
||||
CGF.EmitBlock(ContBlock);
|
||||
|
|
|
@ -2931,8 +2931,8 @@ RValue CodeGenFunction::EmitCallExpr(const CallExpr *E,
|
|||
}
|
||||
|
||||
llvm::Value *Callee = EmitScalarExpr(E->getCallee());
|
||||
return EmitCall(E->getCallee()->getType(), Callee, ReturnValue,
|
||||
E->arg_begin(), E->arg_end(), TargetDecl);
|
||||
return EmitCall(E->getCallee()->getType(), Callee, E->getLocStart(),
|
||||
ReturnValue, E->arg_begin(), E->arg_end(), TargetDecl);
|
||||
}
|
||||
|
||||
LValue CodeGenFunction::EmitBinaryOperatorLValue(const BinaryOperator *E) {
|
||||
|
@ -3103,6 +3103,7 @@ LValue CodeGenFunction::EmitStmtExprLValue(const StmtExpr *E) {
|
|||
}
|
||||
|
||||
RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
|
||||
SourceLocation CallLoc,
|
||||
ReturnValueSlot ReturnValue,
|
||||
CallExpr::const_arg_iterator ArgBeg,
|
||||
CallExpr::const_arg_iterator ArgEnd,
|
||||
|
@ -3123,6 +3124,51 @@ RValue CodeGenFunction::EmitCall(QualType CalleeType, llvm::Value *Callee,
|
|||
if (const FunctionDecl* FD = dyn_cast_or_null<const FunctionDecl>(TargetDecl))
|
||||
ForceColumnInfo = FD->isInlineSpecified();
|
||||
|
||||
if (getLangOpts().CPlusPlus && SanOpts->Function &&
|
||||
(!TargetDecl || !isa<FunctionDecl>(TargetDecl))) {
|
||||
if (llvm::Constant *PrefixSig =
|
||||
CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) {
|
||||
llvm::Constant *FTRTTIConst =
|
||||
CGM.GetAddrOfRTTIDescriptor(QualType(FnType, 0), /*ForEH=*/true);
|
||||
llvm::Type *PrefixStructTyElems[] = {
|
||||
PrefixSig->getType(),
|
||||
FTRTTIConst->getType()
|
||||
};
|
||||
llvm::StructType *PrefixStructTy = llvm::StructType::get(
|
||||
CGM.getLLVMContext(), PrefixStructTyElems, /*isPacked=*/true);
|
||||
|
||||
llvm::Value *CalleePrefixStruct = Builder.CreateBitCast(
|
||||
Callee, llvm::PointerType::getUnqual(PrefixStructTy));
|
||||
llvm::Value *CalleeSigPtr =
|
||||
Builder.CreateConstGEP2_32(CalleePrefixStruct, 0, 0);
|
||||
llvm::Value *CalleeSig = Builder.CreateLoad(CalleeSigPtr);
|
||||
llvm::Value *CalleeSigMatch = Builder.CreateICmpEQ(CalleeSig, PrefixSig);
|
||||
|
||||
llvm::BasicBlock *Cont = createBasicBlock("cont");
|
||||
llvm::BasicBlock *TypeCheck = createBasicBlock("typecheck");
|
||||
Builder.CreateCondBr(CalleeSigMatch, TypeCheck, Cont);
|
||||
|
||||
EmitBlock(TypeCheck);
|
||||
llvm::Value *CalleeRTTIPtr =
|
||||
Builder.CreateConstGEP2_32(CalleePrefixStruct, 0, 1);
|
||||
llvm::Value *CalleeRTTI = Builder.CreateLoad(CalleeRTTIPtr);
|
||||
llvm::Value *CalleeRTTIMatch =
|
||||
Builder.CreateICmpEQ(CalleeRTTI, FTRTTIConst);
|
||||
llvm::Constant *StaticData[] = {
|
||||
EmitCheckSourceLocation(CallLoc),
|
||||
EmitCheckTypeDescriptor(CalleeType)
|
||||
};
|
||||
EmitCheck(CalleeRTTIMatch,
|
||||
"function_type_mismatch",
|
||||
StaticData,
|
||||
Callee,
|
||||
CRK_Recoverable);
|
||||
|
||||
Builder.CreateBr(Cont);
|
||||
EmitBlock(Cont);
|
||||
}
|
||||
}
|
||||
|
||||
CallArgList Args;
|
||||
EmitCallArgs(Args, dyn_cast<FunctionProtoType>(FnType), ArgBeg, ArgEnd,
|
||||
ForceColumnInfo);
|
||||
|
|
|
@ -86,7 +86,8 @@ RValue CodeGenFunction::EmitCXXMemberCallExpr(const CXXMemberCallExpr *CE,
|
|||
// The method is static, emit it as we would a regular call.
|
||||
llvm::Value *Callee = CGM.GetAddrOfFunction(MD);
|
||||
return EmitCall(getContext().getPointerType(MD->getType()), Callee,
|
||||
ReturnValue, CE->arg_begin(), CE->arg_end());
|
||||
CE->getLocStart(), ReturnValue, CE->arg_begin(),
|
||||
CE->arg_end());
|
||||
}
|
||||
|
||||
// Compute the object pointer.
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include "CGCXXABI.h"
|
||||
#include "CGDebugInfo.h"
|
||||
#include "CodeGenModule.h"
|
||||
#include "TargetInfo.h"
|
||||
#include "clang/AST/ASTContext.h"
|
||||
#include "clang/AST/Decl.h"
|
||||
#include "clang/AST/DeclCXX.h"
|
||||
|
@ -519,6 +520,22 @@ void CodeGenFunction::StartFunction(GlobalDecl GD,
|
|||
EmitOpenCLKernelMetadata(FD, Fn);
|
||||
}
|
||||
|
||||
// If we are checking function types, emit a function type signature as
|
||||
// prefix data.
|
||||
if (getLangOpts().CPlusPlus && SanOpts->Function) {
|
||||
if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) {
|
||||
if (llvm::Constant *PrefixSig =
|
||||
CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) {
|
||||
llvm::Constant *FTRTTIConst =
|
||||
CGM.GetAddrOfRTTIDescriptor(FD->getType(), /*ForEH=*/true);
|
||||
llvm::Constant *PrefixStructElems[] = { PrefixSig, FTRTTIConst };
|
||||
llvm::Constant *PrefixStructConst =
|
||||
llvm::ConstantStruct::getAnon(PrefixStructElems, /*Packed=*/true);
|
||||
Fn->setPrefixData(PrefixStructConst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
llvm::BasicBlock *EntryBB = createBasicBlock("entry", CurFn);
|
||||
|
||||
// Create a marker to make it easy to insert allocas into the entryblock
|
||||
|
|
|
@ -2073,6 +2073,7 @@ public:
|
|||
llvm::Instruction **callOrInvoke = 0);
|
||||
|
||||
RValue EmitCall(QualType FnType, llvm::Value *Callee,
|
||||
SourceLocation CallLoc,
|
||||
ReturnValueSlot ReturnValue,
|
||||
CallExpr::const_arg_iterator ArgBeg,
|
||||
CallExpr::const_arg_iterator ArgEnd,
|
||||
|
|
|
@ -601,6 +601,14 @@ public:
|
|||
return X86AdjustInlineAsmType(CGF, Constraint, Ty);
|
||||
}
|
||||
|
||||
llvm::Constant *getUBSanFunctionSignature(CodeGen::CodeGenModule &CGM) const {
|
||||
unsigned Sig = (0xeb << 0) | // jmp rel8
|
||||
(0x06 << 8) | // .+0x08
|
||||
('F' << 16) |
|
||||
('T' << 24);
|
||||
return llvm::ConstantInt::get(CGM.Int32Ty, Sig);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -1284,6 +1292,14 @@ public:
|
|||
return TargetCodeGenInfo::isNoProtoCallVariadic(args, fnType);
|
||||
}
|
||||
|
||||
llvm::Constant *getUBSanFunctionSignature(CodeGen::CodeGenModule &CGM) const {
|
||||
unsigned Sig = (0xeb << 0) | // jmp rel8
|
||||
(0x0a << 8) | // .+0x0c
|
||||
('F' << 16) |
|
||||
('T' << 24);
|
||||
return llvm::ConstantInt::get(CGM.Int32Ty, Sig);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
static std::string qualifyWindowsLibrary(llvm::StringRef Lib) {
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "llvm/ADT/SmallString.h"
|
||||
|
||||
namespace llvm {
|
||||
class Constant;
|
||||
class GlobalValue;
|
||||
class Type;
|
||||
class Value;
|
||||
|
@ -136,6 +137,13 @@ namespace clang {
|
|||
return "";
|
||||
}
|
||||
|
||||
/// Return a constant used by UBSan as a signature to identify functions
|
||||
/// possessing type information, or 0 if the platform is unsupported.
|
||||
virtual llvm::Constant *getUBSanFunctionSignature(
|
||||
CodeGen::CodeGenModule &CGM) const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// Determine whether a call to an unprototyped functions under
|
||||
/// the given calling convention should use the variadic
|
||||
/// convention or the non-variadic convention.
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// RUN: %clang_cc1 -std=c++11 -fsanitize=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum,bounds -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
|
||||
// RUN: %clang_cc1 -std=c++11 -fsanitize=signed-integer-overflow,integer-divide-by-zero,float-divide-by-zero,shift,unreachable,return,vla-bound,alignment,null,vptr,object-size,float-cast-overflow,bool,enum,bounds,function -emit-llvm %s -o - -triple x86_64-linux-gnu | FileCheck %s
|
||||
|
||||
struct S {
|
||||
double d;
|
||||
|
@ -372,6 +372,24 @@ void downcast_reference(B &b) {
|
|||
// CHECK-NEXT: br i1 [[AND]]
|
||||
}
|
||||
|
||||
// CHECK-LABEL: @_Z22indirect_function_callPFviE({{.*}} prefix <{ i32, i8* }> <{ i32 1413876459, i8* bitcast ({ i8*, i8* }* @_ZTIFvPFviEE to i8*) }>
|
||||
void indirect_function_call(void (*p)(int)) {
|
||||
// CHECK: [[PTR:%[0-9]*]] = bitcast void (i32)* {{.*}} to <{ i32, i8* }>*
|
||||
|
||||
// Signature check
|
||||
// CHECK-NEXT: [[SIGPTR:%[0-9]*]] = getelementptr <{ i32, i8* }>* [[PTR]], i32 0, i32 0
|
||||
// CHECK-NEXT: [[SIG:%[0-9]*]] = load i32* [[SIGPTR]]
|
||||
// CHECK-NEXT: [[SIGCMP:%[0-9]*]] = icmp eq i32 [[SIG]], 1413876459
|
||||
// CHECK-NEXT: br i1 [[SIGCMP]]
|
||||
|
||||
// RTTI pointer check
|
||||
// CHECK: [[RTTIPTR:%[0-9]*]] = getelementptr <{ i32, i8* }>* [[PTR]], i32 0, i32 1
|
||||
// CHECK-NEXT: [[RTTI:%[0-9]*]] = load i8** [[RTTIPTR]]
|
||||
// CHECK-NEXT: [[RTTICMP:%[0-9]*]] = icmp eq i8* [[RTTI]], bitcast ({ i8*, i8* }* @_ZTIFviE to i8*)
|
||||
// CHECK-NEXT: br i1 [[RTTICMP]]
|
||||
p(42);
|
||||
}
|
||||
|
||||
namespace CopyValueRepresentation {
|
||||
// CHECK-LABEL: define {{.*}} @_ZN23CopyValueRepresentation2S3aSERKS0_
|
||||
// CHECK-NOT: call {{.*}} @__ubsan_handle_load_invalid_value
|
||||
|
|
|
@ -5,13 +5,13 @@
|
|||
// CHECK-UNDEFINED-TRAP: "-fsanitize-undefined-trap-on-error"
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=undefined %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-UNDEFINED
|
||||
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|bounds|enum|bool),?){15}"}}
|
||||
// CHECK-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|vptr|object-size|float-cast-overflow|bounds|enum|bool),?){16}"}}
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=integer %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-INTEGER
|
||||
// CHECK-INTEGER: "-fsanitize={{((signed-integer-overflow|unsigned-integer-overflow|integer-divide-by-zero|shift),?){4}"}}
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=thread,undefined -fno-thread-sanitizer -fno-sanitize=float-cast-overflow,vptr,bool,enum %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-PARTIAL-UNDEFINED
|
||||
// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|shift|unreachable|return|vla-bound|alignment|null|object-size|bounds),?){11}"}}
|
||||
// CHECK-PARTIAL-UNDEFINED: "-fsanitize={{((signed-integer-overflow|integer-divide-by-zero|float-divide-by-zero|function|shift|unreachable|return|vla-bound|alignment|null|object-size|bounds),?){12}"}}
|
||||
|
||||
// RUN: %clang -target x86_64-linux-gnu -fsanitize=address-full %s -### 2>&1 | FileCheck %s --check-prefix=CHECK-ASAN-FULL
|
||||
// CHECK-ASAN-FULL: "-fsanitize={{((address|init-order|use-after-return|use-after-scope),?){4}"}}
|
||||
|
|
Loading…
Reference in New Issue