Index: lib/CodeGen/CGClass.cpp =================================================================== --- lib/CodeGen/CGClass.cpp +++ lib/CodeGen/CGClass.cpp @@ -2068,11 +2068,9 @@ SourceLocation Loc) { const CXXRecordDecl *ClassDecl = D->getParent(); - // 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(CodeGenFunction::TCK_ConstructorCall, Loc, - This.getPointer(), getContext().getRecordType(ClassDecl)); + if (!NewPointerChecksAreEmitted) + EmitTypeCheck(CodeGenFunction::TCK_ConstructorCall, Loc, This.getPointer(), + getContext().getRecordType(ClassDecl)); if (D->isTrivial() && D->isDefaultConstructor()) { assert(Args.size() == 1 && "trivial default ctor with args"); Index: lib/CodeGen/CGExprCXX.cpp =================================================================== --- lib/CodeGen/CGExprCXX.cpp +++ lib/CodeGen/CGExprCXX.cpp @@ -1271,6 +1271,7 @@ Address NewPtr, llvm::Value *NumElements, llvm::Value *AllocSizeWithoutCookie) { ApplyDebugLocation DL(CGF, E); + if (E->isArray()) CGF.EmitNewArrayInitializer(E, ElementType, ElementTy, NewPtr, NumElements, AllocSizeWithoutCookie); @@ -1705,6 +1706,13 @@ result = Address(Builder.CreateLaunderInvariantGroup(result.getPointer()), result.getAlignment()); + // Emit sanitizer checks for pointer value now, so that in the case of an + // array it was checked only once and not at each constructor call. + NewPointerChecksRAII PC(this); + EmitTypeCheck(CodeGenFunction::TCK_ConstructorCall, + E->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(), + result.getPointer(), allocType); + EmitNewInitializer(*this, E, allocType, elementTy, result, numElements, allocSizeWithoutCookie); if (E->isArray()) { Index: lib/CodeGen/CodeGenFunction.h =================================================================== --- lib/CodeGen/CodeGenFunction.h +++ lib/CodeGen/CodeGenFunction.h @@ -458,6 +458,23 @@ ~SanitizerScope(); }; + /// True if sanitizer checks for the pointer returned by 'new' are generated. + bool NewPointerChecksAreEmitted = false; + + /// RAII object to track generation of sanitizer checks for result of 'new'. + class NewPointerChecksRAII { + CodeGenFunction *CGF; + bool PrevValue; + public: + NewPointerChecksRAII(CodeGenFunction *CGF) + : CGF(CGF), PrevValue(CGF->NewPointerChecksAreEmitted) { + CGF->NewPointerChecksAreEmitted = true; + } + ~NewPointerChecksRAII() { + CGF->NewPointerChecksAreEmitted = PrevValue; + } + }; + /// In C++, whether we are code generating a thunk. This controls whether we /// should emit cleanups. bool CurFuncIsThunk = false; Index: test/CodeGenCXX/ubsan-new-checks.cpp =================================================================== --- /dev/null +++ test/CodeGenCXX/ubsan-new-checks.cpp @@ -0,0 +1,114 @@ +// RUN: %clang_cc1 -triple x86_64-linux-gnu -std=c++11 -S -emit-llvm -fsanitize=alignment %s -o - | FileCheck %s + +struct alignas(32) S1 { + int x; + S1(); +}; + +struct alignas(32) S2 { + int x; +}; + +struct alignas(32) S3 { + int x; + S3(int *p = new int[4]); +}; + +typedef __attribute__((ext_vector_type(2), aligned(32))) float float32x2_t; + + +void *operator new (unsigned long, void *p) { return p; } +void *operator new[] (unsigned long, void *p) { return p; } + +S1 *func_01() { + // CHECK-LABEL: define {{.*}} @_Z7func_01v + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + // CHECK: call void @_ZN2S1C1Ev( + // CHECK-NOT: and i64 %{{.*}}, 31, !nosanitize + // CHECK-NOT: icmp eq i64 %{{.*}}, 0, !nosanitize + return new S1[20]; +} + +S2 *func_02() { + // CHECK-LABEL: define {{.*}} @_Z7func_02v + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + return new S2; +} + +S2 *func_03() { + // CHECK-LABEL: define {{.*}} @_Z7func_03v + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + // CHECK-NOT: and i64 %{{.*}}, 31, !nosanitize + // CHECK-NOT: icmp eq i64 %{{.*}}, 0, !nosanitize + return new S2[20]; +} + +float32x2_t *func_04() { + // CHECK-LABEL: define {{.*}} @_Z7func_04v + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + return new float32x2_t; +} + +float32x2_t *func_05() { + // CHECK-LABEL: define {{.*}} @_Z7func_05v + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + // CHECK-NOT: and i64 %{{.*}}, 31, !nosanitize + // CHECK-NOT: icmp eq i64 %{{.*}}, 0, !nosanitize + return new float32x2_t[20]; +} + +S3 *func_07() { + // CHECK-LABEL: define {{.*}} @_Z7func_07v + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + // CHECK: and i64 %{{.*}}, 3, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + return new S3; +} + +S3 *func_08() { + // CHECK-LABEL: define {{.*}} @_Z7func_08v + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + // CHECK: and i64 %{{.*}}, 3, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + return new S3[10]; +} + + +S2 *func_10(void *p) { + // CHECK-LABEL: define {{.*}} @_Z7func_10Pv + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + return new(p) S2; +} + +S2 *func_11(void *p) { + // CHECK-LABEL: define {{.*}} @_Z7func_11Pv + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + // CHECK-NOT: and i64 %{{.*}}, 31, !nosanitize + // CHECK-NOT: icmp eq i64 %{{.*}}, 0, !nosanitize + return new(p) S2[10]; +} + +float32x2_t *func_12() { + // CHECK-LABEL: define {{.*}} @_Z7func_12v + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + return new float32x2_t; +} + +float32x2_t *func_13() { + // CHECK-LABEL: define {{.*}} @_Z7func_13v + // CHECK: and i64 %{{.*}}, 31, !nosanitize + // CHECK: icmp eq i64 %{{.*}}, 0, !nosanitize + // CHECK-NOT: and i64 %{{.*}}, 31, !nosanitize + // CHECK-NOT: icmp eq i64 %{{.*}}, 0, !nosanitize + return new float32x2_t[20]; +}