diff options
-rw-r--r-- | clang/lib/CodeGen/CGClass.cpp | 23 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExpr.cpp | 24 | ||||
-rw-r--r-- | clang/lib/CodeGen/CGExprScalar.cpp | 6 | ||||
-rw-r--r-- | clang/lib/CodeGen/CodeGenFunction.h | 13 | ||||
-rw-r--r-- | clang/test/CodeGenCXX/catch-undef-behavior.cpp | 34 | ||||
-rw-r--r-- | compiler-rt/lib/ubsan/ubsan_handlers.cc | 6 | ||||
-rw-r--r-- | compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp | 17 | ||||
-rw-r--r-- | compiler-rt/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp | 16 |
8 files changed, 112 insertions, 27 deletions
diff --git a/clang/lib/CodeGen/CGClass.cpp b/clang/lib/CodeGen/CGClass.cpp index 49760853dc8..d7010c7e916 100644 --- a/clang/lib/CodeGen/CGClass.cpp +++ b/clang/lib/CodeGen/CGClass.cpp @@ -134,12 +134,11 @@ ApplyNonVirtualAndVirtualOffset(CodeGenFunction &CGF, llvm::Value *ptr, return ptr; } -llvm::Value * -CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value, - const CXXRecordDecl *Derived, - CastExpr::path_const_iterator PathBegin, - CastExpr::path_const_iterator PathEnd, - bool NullCheckValue) { +llvm::Value *CodeGenFunction::GetAddressOfBaseClass( + llvm::Value *Value, const CXXRecordDecl *Derived, + CastExpr::path_const_iterator PathBegin, + CastExpr::path_const_iterator PathEnd, bool NullCheckValue, + SourceLocation Loc) { assert(PathBegin != PathEnd && "Base path should not be empty!"); CastExpr::path_const_iterator Start = PathBegin; @@ -176,9 +175,16 @@ CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value, llvm::Type *BasePtrTy = ConvertType((PathEnd[-1])->getType())->getPointerTo(); + QualType DerivedTy = getContext().getRecordType(Derived); + CharUnits DerivedAlign = getContext().getTypeAlignInChars(DerivedTy); + // If the static offset is zero and we don't have a virtual step, // just do a bitcast; null checks are unnecessary. if (NonVirtualOffset.isZero() && !VBase) { + if (sanitizePerformTypeCheck()) { + EmitTypeCheck(TCK_Upcast, Loc, Value, DerivedTy, DerivedAlign, + !NullCheckValue); + } return Builder.CreateBitCast(Value, BasePtrTy); } @@ -197,6 +203,11 @@ CodeGenFunction::GetAddressOfBaseClass(llvm::Value *Value, EmitBlock(notNullBB); } + if (sanitizePerformTypeCheck()) { + EmitTypeCheck(VBase ? TCK_UpcastToVirtualBase : TCK_Upcast, Loc, Value, + DerivedTy, DerivedAlign, true); + } + // Compute the virtual offset. llvm::Value *VirtualOffset = nullptr; if (VBase) { diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 54c2fd26473..b7504ca1223 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -377,7 +377,7 @@ LValue CodeGenFunction::EmitMaterializeTemporaryExpr( GetAddressOfBaseClass(Object, Adjustment.DerivedToBase.DerivedClass, Adjustment.DerivedToBase.BasePath->path_begin(), Adjustment.DerivedToBase.BasePath->path_end(), - /*NullCheckValue=*/ false); + /*NullCheckValue=*/ false, E->getExprLoc()); break; case SubobjectAdjustment::FieldAdjustment: { @@ -448,8 +448,8 @@ bool CodeGenFunction::sanitizePerformTypeCheck() const { } void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, - llvm::Value *Address, - QualType Ty, CharUnits Alignment) { + llvm::Value *Address, QualType Ty, + CharUnits Alignment, bool SkipNullCheck) { if (!sanitizePerformTypeCheck()) return; @@ -464,13 +464,15 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, llvm::Value *Cond = nullptr; llvm::BasicBlock *Done = nullptr; - if (SanOpts->Null || TCK == TCK_DowncastPointer) { + bool AllowNullPointers = TCK == TCK_DowncastPointer || TCK == TCK_Upcast || + TCK == TCK_UpcastToVirtualBase; + if ((SanOpts->Null || AllowNullPointers) && !SkipNullCheck) { // The glvalue must not be an empty glvalue. Cond = Builder.CreateICmpNE( Address, llvm::Constant::getNullValue(Address->getType())); - if (TCK == TCK_DowncastPointer) { - // When performing a pointer downcast, it's OK if the value is null. + if (AllowNullPointers) { + // When performing pointer casts, it's OK if the value is null. // Skip the remaining checks in that case. Done = createBasicBlock("null"); llvm::BasicBlock *Rest = createBasicBlock("not.null"); @@ -536,7 +538,8 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, CXXRecordDecl *RD = Ty->getAsCXXRecordDecl(); if (SanOpts->Vptr && (TCK == TCK_MemberAccess || TCK == TCK_MemberCall || - TCK == TCK_DowncastPointer || TCK == TCK_DowncastReference) && + TCK == TCK_DowncastPointer || TCK == TCK_DowncastReference || + TCK == TCK_UpcastToVirtualBase) && RD && RD->hasDefinition() && RD->isDynamicClass()) { // Compute a hash of the mangled name of the type. // @@ -2874,10 +2877,9 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) { llvm::Value *This = LV.getAddress(); // Perform the derived-to-base conversion - llvm::Value *Base = - GetAddressOfBaseClass(This, DerivedClassDecl, - E->path_begin(), E->path_end(), - /*NullCheckValue=*/false); + llvm::Value *Base = GetAddressOfBaseClass( + This, DerivedClassDecl, E->path_begin(), E->path_end(), + /*NullCheckValue=*/false, E->getExprLoc()); return MakeAddrLValue(Base, E->getType()); } diff --git a/clang/lib/CodeGen/CGExprScalar.cpp b/clang/lib/CodeGen/CGExprScalar.cpp index 5ca2414efa7..8d8fd39cb01 100644 --- a/clang/lib/CodeGen/CGExprScalar.cpp +++ b/clang/lib/CodeGen/CGExprScalar.cpp @@ -1388,9 +1388,9 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) { E->getType()->getPointeeCXXRecordDecl(); assert(DerivedClassDecl && "DerivedToBase arg isn't a C++ object pointer!"); - return CGF.GetAddressOfBaseClass(Visit(E), DerivedClassDecl, - CE->path_begin(), CE->path_end(), - ShouldNullCheckClassCastValue(CE)); + return CGF.GetAddressOfBaseClass( + Visit(E), DerivedClassDecl, CE->path_begin(), CE->path_end(), + ShouldNullCheckClassCastValue(CE), CE->getExprLoc()); } case CK_Dynamic: { Value *V = Visit(const_cast<Expr*>(E)); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index f20b758a3b4..865f1c9668f 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -1681,7 +1681,7 @@ public: const CXXRecordDecl *Derived, CastExpr::path_const_iterator PathBegin, CastExpr::path_const_iterator PathEnd, - bool NullCheckValue); + bool NullCheckValue, SourceLocation Loc); llvm::Value *GetAddressOfDerivedClass(llvm::Value *Value, const CXXRecordDecl *Derived, @@ -1776,7 +1776,13 @@ public: TCK_DowncastPointer, /// Checking the operand of a static_cast to a derived reference type. Must /// be an object within its lifetime. - TCK_DowncastReference + TCK_DowncastReference, + /// Checking the operand of a cast to a base object. Must be suitably sized + /// and aligned. + TCK_Upcast, + /// Checking the operand of a cast to a virtual base object. Must be an + /// object within its lifetime. + TCK_UpcastToVirtualBase }; /// \brief Whether any type-checking sanitizers are enabled. If \c false, @@ -1786,7 +1792,8 @@ public: /// \brief Emit a check that \p V is the address of storage of the /// appropriate size and alignment for an object of type \p Type. void EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc, llvm::Value *V, - QualType Type, CharUnits Alignment = CharUnits::Zero()); + QualType Type, CharUnits Alignment = CharUnits::Zero(), + bool SkipNullCheck = false); /// \brief Emit a check that \p Base points into an array object, which /// we can access at index \p Index. \p Accessed should be \c false if we diff --git a/clang/test/CodeGenCXX/catch-undef-behavior.cpp b/clang/test/CodeGenCXX/catch-undef-behavior.cpp index 333855d0ba6..5bf31c1ca40 100644 --- a/clang/test/CodeGenCXX/catch-undef-behavior.cpp +++ b/clang/test/CodeGenCXX/catch-undef-behavior.cpp @@ -404,6 +404,40 @@ void indirect_function_call(void (*p)(int)) { p(42); } +namespace UpcastPointerTest { +struct S {}; +struct T : S { double d; }; +struct V : virtual S {}; + +// CHECK-LABEL: upcast_pointer +S* upcast_pointer(T* t) { + // Check for null pointer + // CHECK: %[[NONNULL:.*]] = icmp ne {{.*}}, null + // CHECK: br i1 %[[NONNULL]] + + // Check alignment + // CHECK: %[[MISALIGN:.*]] = and i64 %{{.*}}, 7 + // CHECK: icmp eq i64 %[[MISALIGN]], 0 + + // CHECK: call void @__ubsan_handle_type_mismatch + return t; +} + +V getV(); + +// CHECK-LABEL: upcast_to_vbase +void upcast_to_vbase() { + // No need to check for null here, as we have a temporary here. + + // CHECK-NOT: br i1 + + // CHECK: call i64 @llvm.objectsize + // CHECK: call void @__ubsan_handle_type_mismatch + // CHECK: call void @__ubsan_handle_dynamic_type_cache_miss + const S& s = getV(); +} +} + namespace CopyValueRepresentation { // CHECK-LABEL: define {{.*}} @_ZN23CopyValueRepresentation2S3aSERKS0_ // CHECK-NOT: call {{.*}} @__ubsan_handle_load_invalid_value diff --git a/compiler-rt/lib/ubsan/ubsan_handlers.cc b/compiler-rt/lib/ubsan/ubsan_handlers.cc index f8365a7d2fe..a0ecff94359 100644 --- a/compiler-rt/lib/ubsan/ubsan_handlers.cc +++ b/compiler-rt/lib/ubsan/ubsan_handlers.cc @@ -30,10 +30,10 @@ static bool ignoreReport(SourceLocation SLoc, ReportOptions Opts) { } namespace __ubsan { - const char *TypeCheckKinds[] = { +const char *TypeCheckKinds[] = { "load of", "store to", "reference binding to", "member access within", - "member call on", "constructor call on", "downcast of", "downcast of" - }; + "member call on", "constructor call on", "downcast of", "downcast of", + "upcast of", "cast to virtual base of"}; } static void handleTypeMismatchImpl(TypeMismatchData *Data, ValueHandle Pointer, diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp index d5d70f2e552..79f5136db96 100644 --- a/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/misaligned.cpp @@ -1,11 +1,12 @@ // RUN: %clangxx -fsanitize=alignment -g %s -O3 -o %t -// RUN: %run %t l0 && %run %t s0 && %run %t r0 && %run %t m0 && %run %t f0 && %run %t n0 +// RUN: %run %t l0 && %run %t s0 && %run %t r0 && %run %t m0 && %run %t f0 && %run %t n0 && %run %t u0 // RUN: %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --strict-whitespace // RUN: %run %t s1 2>&1 | FileCheck %s --check-prefix=CHECK-STORE // RUN: %run %t r1 2>&1 | FileCheck %s --check-prefix=CHECK-REFERENCE // RUN: %run %t m1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMBER // RUN: %run %t f1 2>&1 | FileCheck %s --check-prefix=CHECK-MEMFUN // RUN: %run %t n1 2>&1 | FileCheck %s --check-prefix=CHECK-NEW +// RUN: %run %t u1 2>&1 | FileCheck %s --check-prefix=CHECK-UPCAST // RUN: UBSAN_OPTIONS=print_stacktrace=1 %run %t l1 2>&1 | FileCheck %s --check-prefix=CHECK-LOAD --check-prefix=CHECK-%os-STACK-LOAD // RUN: %clangxx -fsanitize=alignment -fno-sanitize-recover %s -O3 -o %t @@ -20,12 +21,17 @@ struct S { int k; }; +struct T : S { + int t; +}; + int main(int, char **argv) { char c[] __attribute__((aligned(8))) = { 0, 0, 0, 0, 1, 2, 3, 4, 5 }; // Pointer value may be unspecified here, but behavior is not undefined. int *p = (int*)&c[4 + argv[1][1] - '0']; S *s = (S*)p; + T *t = (T*)p; void *wild = reinterpret_cast<void *>(0x123L); @@ -81,6 +87,15 @@ int main(int, char **argv) { // CHECK-NEW-NEXT: {{^ \^}} return (new (s) S)->k && 0; + case 'u': { + // CHECK-UPCAST: misaligned.cpp:[[@LINE+4]]:17: runtime error: upcast of misaligned address [[PTR:0x[0-9a-f]*]] for type 'T', which requires 4 byte alignment + // CHECK-UPCAST-NEXT: [[PTR]]: note: pointer points here + // CHECK-UPCAST-NEXT: {{^ 00 00 00 01 02 03 04 05}} + // CHECK-UPCAST-NEXT: {{^ \^}} + S *s2 = (S*)t; + return s2->f(); + } + case 'w': // CHECK-WILD: misaligned.cpp:[[@LINE+3]]:35: runtime error: member access within misaligned address 0x000000000123 for type 'S', which requires 4 byte alignment // CHECK-WILD-NEXT: 0x000000000123: note: pointer points here diff --git a/compiler-rt/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp b/compiler-rt/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp new file mode 100644 index 00000000000..d5130fe23ae --- /dev/null +++ b/compiler-rt/test/ubsan/TestCases/TypeCheck/vptr-virtual-base.cpp @@ -0,0 +1,16 @@ +// RUN: %clangxx -fsanitize=vptr -fno-sanitize-recover -g %s -O3 -o %t +// RUN: not %run %t 2>&1 | FileCheck %s + +struct S { virtual int f() { return 0; } }; +struct T : virtual S {}; + +struct Foo { virtual int f() { return 0; } }; + +int main(int argc, char **argv) { + Foo foo; + T *t = (T*)&foo; + S *s = t; + // CHECK: vptr-virtual-base.cpp:[[@LINE-1]]:10: runtime error: cast to virtual base of address [[PTR:0x[0-9a-f]*]] which does not point to an object of type 'T' + // CHECK-NEXT: [[PTR]]: note: object is of type 'Foo' + return s->f(); +} |