From: Richard Smith Date: Thu, 25 Oct 2012 02:14:12 +0000 (+0000) Subject: -fcatch-undefined-behavior checking for appropriate vptr value: Clang CodeGen side. X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=8e1cee6f23e2552b96b81e5ef419ab3f69c5b5c2;p=clang -fcatch-undefined-behavior checking for appropriate vptr value: Clang CodeGen side. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@166661 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/lib/CodeGen/CGExpr.cpp b/lib/CodeGen/CGExpr.cpp index 8af1889a6a..21df6e8cfa 100644 --- a/lib/CodeGen/CGExpr.cpp +++ b/lib/CodeGen/CGExpr.cpp @@ -27,6 +27,7 @@ #include "llvm/LLVMContext.h" #include "llvm/MDBuilder.h" #include "llvm/DataLayout.h" +#include "llvm/ADT/Hashing.h" using namespace clang; using namespace CodeGen; @@ -558,6 +559,18 @@ unsigned CodeGenFunction::getAccessedFieldNo(unsigned Idx, ->getZExtValue(); } +/// Emit the hash_16_bytes function from include/llvm/ADT/Hashing.h. +static llvm::Value *emitHash16Bytes(CGBuilderTy &Builder, llvm::Value *Low, + llvm::Value *High) { + llvm::Value *KMul = Builder.getInt64(0x9ddfea08eb382d69ULL); + llvm::Value *K47 = Builder.getInt64(47); + llvm::Value *A0 = Builder.CreateMul(Builder.CreateXor(Low, High), KMul); + llvm::Value *A1 = Builder.CreateXor(Builder.CreateLShr(A0, K47), A0); + llvm::Value *B0 = Builder.CreateMul(Builder.CreateXor(High, A1), KMul); + llvm::Value *B1 = Builder.CreateXor(Builder.CreateLShr(B0, K47), B0); + return Builder.CreateMul(B1, KMul); +} + void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, llvm::Value *Address, QualType Ty, CharUnits Alignment) { @@ -613,6 +626,62 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, }; EmitCheck(Cond, "type_mismatch", StaticData, Address); } + + CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); + if (TCK != TCK_ConstructorCall && + RD && RD->hasDefinition() && RD->isDynamicClass()) { + // Check that the vptr indicates that there is a subobject of type Ty at + // offset zero within this object. + // FIXME: Produce a diagnostic if the user tries to combine this check with + // -fno-rtti. + + // Compute a hash of the mangled name of the type. + // + // FIXME: This is not guaranteed to be deterministic! Move to a + // fingerprinting mechanism once LLVM provides one. For the time + // being the implementation happens to be deterministic. + llvm::SmallString<64> MangledName; + llvm::raw_svector_ostream Out(MangledName); + CGM.getCXXABI().getMangleContext().mangleCXXRTTI(Ty.getUnqualifiedType(), + Out); + llvm::hash_code TypeHash = hash_value(Out.str()); + + // Load the vptr, and compute hash_16_bytes(TypeHash, vptr). + llvm::Value *Low = llvm::ConstantInt::get(Int64Ty, TypeHash); + llvm::Type *VPtrTy = llvm::PointerType::get(IntPtrTy, 0); + llvm::Value *VPtrAddr = Builder.CreateBitCast(Address, VPtrTy); + llvm::Value *VPtrVal = Builder.CreateLoad(VPtrAddr); + llvm::Value *High = Builder.CreateZExt(VPtrVal, Int64Ty); + + llvm::Value *Hash = emitHash16Bytes(Builder, Low, High); + Hash = Builder.CreateTrunc(Hash, IntPtrTy); + + // Look the hash up in our cache. + const int CacheSize = 128; + llvm::Type *HashTable = llvm::ArrayType::get(IntPtrTy, CacheSize); + llvm::Value *Cache = CGM.CreateRuntimeVariable(HashTable, + "__ubsan_vptr_type_cache"); + llvm::Value *Slot = Builder.CreateAnd(Hash, + llvm::ConstantInt::get(IntPtrTy, + CacheSize-1)); + llvm::Value *Indices[] = { Builder.getInt32(0), Slot }; + llvm::Value *CacheVal = + Builder.CreateLoad(Builder.CreateInBoundsGEP(Cache, Indices)); + + // If the hash isn't in the cache, call a runtime handler to perform the + // hard work of checking whether the vptr is for an object of the right + // type. This will either fill in the cache and return, or produce a + // diagnostic. + llvm::Constant *StaticData[] = { + EmitCheckSourceLocation(Loc), + EmitCheckTypeDescriptor(Ty), + CGM.GetAddrOfRTTIDescriptor(Ty.getUnqualifiedType()), + llvm::ConstantInt::get(Int8Ty, TCK) + }; + llvm::Value *DynamicData[] = { Address, Hash }; + EmitCheck(Builder.CreateICmpEQ(CacheVal, Hash), + "dynamic_type_cache_miss", StaticData, DynamicData, true); + } } @@ -2042,7 +2111,8 @@ llvm::Constant *CodeGenFunction::EmitCheckSourceLocation(SourceLocation Loc) { void CodeGenFunction::EmitCheck(llvm::Value *Checked, StringRef CheckName, llvm::ArrayRef StaticArgs, - llvm::ArrayRef DynamicArgs) { + llvm::ArrayRef DynamicArgs, + bool Recoverable) { llvm::BasicBlock *Cont = createBasicBlock("cont"); // If -fcatch-undefined-behavior is not enabled, just emit a trap. This @@ -2096,17 +2166,23 @@ void CodeGenFunction::EmitCheck(llvm::Value *Checked, StringRef CheckName, llvm::FunctionType *FnType = llvm::FunctionType::get(CGM.VoidTy, ArgTypes, false); llvm::AttrBuilder B; - B.addAttribute(llvm::Attributes::NoReturn) - .addAttribute(llvm::Attributes::NoUnwind) - .addAttribute(llvm::Attributes::UWTable); + if (!Recoverable) { + B.addAttribute(llvm::Attributes::NoReturn) + .addAttribute(llvm::Attributes::NoUnwind); + } + B.addAttribute(llvm::Attributes::UWTable); llvm::Value *Fn = CGM.CreateRuntimeFunction(FnType, ("__ubsan_handle_" + CheckName).str(), llvm::Attributes::get(getLLVMContext(), B)); llvm::CallInst *HandlerCall = Builder.CreateCall(Fn, Args); - HandlerCall->setDoesNotReturn(); - HandlerCall->setDoesNotThrow(); - Builder.CreateUnreachable(); + if (Recoverable) { + Builder.CreateBr(Cont); + } else { + HandlerCall->setDoesNotReturn(); + HandlerCall->setDoesNotThrow(); + Builder.CreateUnreachable(); + } EmitBlock(Cont); } diff --git a/lib/CodeGen/CGExprCXX.cpp b/lib/CodeGen/CGExprCXX.cpp index e05199165c..c9f24a61bc 100644 --- a/lib/CodeGen/CGExprCXX.cpp +++ b/lib/CodeGen/CGExprCXX.cpp @@ -37,8 +37,9 @@ RValue CodeGenFunction::EmitCXXMemberCall(const CXXMethodDecl *MD, // C++11 [class.mfct.non-static]p2: // If a non-static member function of a class X is called for an object that // is not of type X, or of a type derived from X, the behavior is undefined. - EmitTypeCheck(TCK_MemberCall, CallLoc, This, - getContext().getRecordType(MD->getParent())); + EmitTypeCheck(isa(MD) ? TCK_ConstructorCall + : TCK_MemberCall, + CallLoc, This, getContext().getRecordType(MD->getParent())); CallArgList Args; diff --git a/lib/CodeGen/CodeGenFunction.h b/lib/CodeGen/CodeGenFunction.h index 34fc8b9b35..c3f6c8e245 100644 --- a/lib/CodeGen/CodeGenFunction.h +++ b/lib/CodeGen/CodeGenFunction.h @@ -1871,7 +1871,9 @@ public: TCK_MemberAccess, /// Checking the 'this' pointer for a call to a non-static member function. /// Must be an object within its lifetime. - TCK_MemberCall + TCK_MemberCall, + /// Checking the 'this' pointer for a constructor call. + TCK_ConstructorCall }; /// \brief Emit a check that \p V is the address of storage of the @@ -2576,7 +2578,8 @@ public: /// conditional branch to it. void EmitCheck(llvm::Value *Checked, StringRef CheckName, llvm::ArrayRef StaticArgs, - llvm::ArrayRef DynamicArgs); + llvm::ArrayRef DynamicArgs, + bool Recoverable = false); /// EmitCallArg - Emit a single call argument. void EmitCallArg(CallArgList &args, const Expr *E, QualType ArgType); diff --git a/lib/Driver/Tools.cpp b/lib/Driver/Tools.cpp index c7b811f446..51228d7c1f 100644 --- a/lib/Driver/Tools.cpp +++ b/lib/Driver/Tools.cpp @@ -5914,6 +5914,9 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, AddLinkerInputs(ToolChain, Inputs, Args, CmdArgs); + // Call this before we add the C++ ABI library. + addUbsanRTLinux(getToolChain(), Args, CmdArgs); + if (D.CCCIsCXX && !Args.hasArg(options::OPT_nostdlib) && !Args.hasArg(options::OPT_nodefaultlibs)) { @@ -5966,7 +5969,6 @@ void linuxtools::Link::ConstructJob(Compilation &C, const JobAction &JA, } addProfileRT(getToolChain(), Args, CmdArgs, getToolChain().getTriple()); - addUbsanRTLinux(getToolChain(), Args, CmdArgs); C.addCommand(new Command(JA, *this, ToolChain.Linker.c_str(), CmdArgs)); }