Index: docs/UsersManual.rst =================================================================== --- docs/UsersManual.rst +++ docs/UsersManual.rst @@ -921,6 +921,8 @@ 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. Index: include/clang/Basic/Sanitizers.def =================================================================== --- include/clang/Basic/Sanitizers.def +++ include/clang/Basic/Sanitizers.def @@ -64,6 +64,7 @@ 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 @@ // 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 Index: lib/CodeGen/CGBuiltin.cpp =================================================================== --- lib/CodeGen/CGBuiltin.cpp +++ lib/CodeGen/CGBuiltin.cpp @@ -165,7 +165,7 @@ 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); } Index: lib/CodeGen/CGCUDARuntime.cpp =================================================================== --- lib/CodeGen/CGCUDARuntime.cpp +++ lib/CodeGen/CGCUDARuntime.cpp @@ -44,8 +44,8 @@ } 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); Index: lib/CodeGen/CGExpr.cpp =================================================================== --- lib/CodeGen/CGExpr.cpp +++ lib/CodeGen/CGExpr.cpp @@ -2931,8 +2931,8 @@ } 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 @@ } 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,47 @@ if (const FunctionDecl* FD = dyn_cast_or_null(TargetDecl)) ForceColumnInfo = FD->isInlineSpecified(); + if (getLangOpts().CPlusPlus && SanOpts->Function && + !dyn_cast_or_null(TargetDecl)) { + if (llvm::Constant* PDSig = + CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) { + llvm::Constant *FTRTTIConst = + CGM.GetAddrOfRTTIDescriptor(QualType(FnType, 0), /*ForEH=*/true); + llvm::Type *PDStructTyElems[] = {PDSig->getType(), + FTRTTIConst->getType()}; + llvm::StructType *PDStructTy = llvm::StructType::get( + CGM.getLLVMContext(), PDStructTyElems, /*isPacked=*/true); + + llvm::Value *CalleePDStruct = Builder.CreateBitCast( + Callee, llvm::PointerType::getUnqual(PDStructTy)); + llvm::Value *CalleeSigPtr = + Builder.CreateConstGEP2_32(CalleePDStruct, 0, 0); + llvm::Value *CalleeSig = Builder.CreateLoad(CalleeSigPtr); + llvm::Value *CalleeSigMatch = Builder.CreateICmpEQ(CalleeSig, PDSig); + + llvm::BasicBlock *Cont = createBasicBlock("cont"); + llvm::BasicBlock *TypeCheck = createBasicBlock("typecheck"); + Builder.CreateCondBr(CalleeSigMatch, TypeCheck, Cont); + + EmitBlock(TypeCheck); + llvm::Value *CalleeRTTIPtr = + Builder.CreateConstGEP2_32(CalleePDStruct, 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(FnType), ArgBeg, ArgEnd, ForceColumnInfo); Index: lib/CodeGen/CGExprCXX.cpp =================================================================== --- lib/CodeGen/CGExprCXX.cpp +++ lib/CodeGen/CGExprCXX.cpp @@ -86,7 +86,8 @@ // 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. Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -2073,6 +2073,7 @@ 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, Index: lib/CodeGen/CodeGenFunction.cpp =================================================================== --- lib/CodeGen/CodeGenFunction.cpp +++ lib/CodeGen/CodeGenFunction.cpp @@ -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 @@ 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(D)) { + if (llvm::Constant *PDSig = + CGM.getTargetCodeGenInfo().getUBSanFunctionSignature(CGM)) { + llvm::Constant *FTRTTIConst = + CGM.GetAddrOfRTTIDescriptor(FD->getType(), /*ForEH=*/true); + llvm::Constant *PDStructElems[] = {PDSig, FTRTTIConst}; + llvm::Constant *PDStructConst = + llvm::ConstantStruct::getAnon(PDStructElems, /*Packed=*/true); + Fn->setPrefixData(PDStructConst); + } + } + } + llvm::BasicBlock *EntryBB = createBasicBlock("entry", CurFn); // Create a marker to make it easy to insert allocas into the entryblock Index: lib/CodeGen/TargetInfo.h =================================================================== --- lib/CodeGen/TargetInfo.h +++ lib/CodeGen/TargetInfo.h @@ -21,6 +21,7 @@ #include "llvm/ADT/SmallString.h" namespace llvm { + class Constant; class GlobalValue; class Type; class Value; @@ -136,6 +137,13 @@ 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. Index: lib/CodeGen/TargetInfo.cpp =================================================================== --- lib/CodeGen/TargetInfo.cpp +++ lib/CodeGen/TargetInfo.cpp @@ -601,6 +601,14 @@ 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 @@ 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) { Index: test/CodeGenCXX/catch-undef-behavior.cpp =================================================================== --- test/CodeGenCXX/catch-undef-behavior.cpp +++ test/CodeGenCXX/catch-undef-behavior.cpp @@ -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 @@ // 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 Index: test/Driver/fsanitize.c =================================================================== --- test/Driver/fsanitize.c +++ test/Driver/fsanitize.c @@ -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}"}}