ubsan: Add checking for invalid downcasts. Per [expr.static.cast]p2 and p11,

base-to-derived casts have undefined behavior if the object is not actually an
instance of the derived type.

llvm-svn: 175078
This commit is contained in:
Richard Smith 2013-02-13 21:18:23 +00:00
parent 6c95b97bf6
commit 2c5868c334
5 changed files with 88 additions and 5 deletions

View File

@ -501,11 +501,22 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc,
return;
llvm::Value *Cond = 0;
llvm::BasicBlock *Done = 0;
if (SanOpts->Null) {
// 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.
// Skip the remaining checks in that case.
Done = createBasicBlock("null");
llvm::BasicBlock *Rest = createBasicBlock("not.null");
Builder.CreateCondBr(Cond, Rest, Done);
EmitBlock(Rest);
Cond = 0;
}
}
if (SanOpts->ObjectSize && !Ty->isIncompleteType()) {
@ -561,7 +572,8 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc,
// or call a non-static member function
CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
if (SanOpts->Vptr &&
(TCK == TCK_MemberAccess || TCK == TCK_MemberCall) &&
(TCK == TCK_MemberAccess || TCK == TCK_MemberCall ||
TCK == TCK_DowncastPointer || TCK == TCK_DowncastReference) &&
RD && RD->hasDefinition() && RD->isDynamicClass()) {
// Compute a hash of the mangled name of the type.
//
@ -611,6 +623,11 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc,
"dynamic_type_cache_miss", StaticData, DynamicData,
CRK_AlwaysRecoverable);
}
if (Done) {
Builder.CreateBr(Done);
EmitBlock(Done);
}
}
@ -2639,6 +2656,12 @@ LValue CodeGenFunction::EmitCastLValue(const CastExpr *E) {
LValue LV = EmitLValue(E->getSubExpr());
// C++11 [expr.static.cast]p2: Behavior is undefined if a downcast is
// performed and the object is not of the derived type.
if (SanitizePerformTypeCheck)
EmitTypeCheck(TCK_DowncastReference, E->getExprLoc(),
LV.getAddress(), E->getType());
// Perform the base-to-derived conversion
llvm::Value *Derived =
GetAddressOfDerivedClass(LV.getAddress(), DerivedClassDecl,

View File

@ -1220,7 +1220,15 @@ Value *ScalarExprEmitter::VisitCastExpr(CastExpr *CE) {
const CXXRecordDecl *DerivedClassDecl = DestTy->getPointeeCXXRecordDecl();
assert(DerivedClassDecl && "BaseToDerived arg isn't a C++ object pointer!");
return CGF.GetAddressOfDerivedClass(Visit(E), DerivedClassDecl,
llvm::Value *V = Visit(E);
// C++11 [expr.static.cast]p11: Behavior is undefined if a downcast is
// performed and the object is not of the derived type.
if (CGF.SanitizePerformTypeCheck)
CGF.EmitTypeCheck(CodeGenFunction::TCK_DowncastPointer, CE->getExprLoc(),
V, DestTy->getPointeeType());
return CGF.GetAddressOfDerivedClass(V, DerivedClassDecl,
CE->path_begin(), CE->path_end(),
ShouldNullCheckClassCastValue(CE));
}

View File

@ -1895,7 +1895,13 @@ public:
/// Must be an object within its lifetime.
TCK_MemberCall,
/// Checking the 'this' pointer for a constructor call.
TCK_ConstructorCall
TCK_ConstructorCall,
/// Checking the operand of a static_cast to a derived pointer type. Must be
/// null or an object within its lifetime.
TCK_DowncastPointer,
/// Checking the operand of a static_cast to a derived reference type. Must
/// be an object within its lifetime.
TCK_DowncastReference
};
/// \brief Emit a check that \p V is the address of storage of the

View File

@ -6,6 +6,8 @@ struct S {
virtual int f();
};
struct T : S {};
// CHECK: @_Z17reference_binding
void reference_binding(int *p, S *q) {
// C++ core issue 453: If an lvalue to which a reference is directly bound
@ -173,3 +175,47 @@ int bad_enum_value() {
int c = e3;
return a + b + c;
}
// CHECK: @_Z20bad_downcast_pointer
void bad_downcast_pointer(S *p) {
// CHECK: %[[NONNULL:.*]] = icmp ne {{.*}}, null
// CHECK: br i1 %[[NONNULL]],
// CHECK: %[[SIZE:.*]] = call i64 @llvm.objectsize.i64(
// CHECK: %[[E1:.*]] = icmp uge i64 %[[SIZE]], 24
// CHECK: %[[MISALIGN:.*]] = and i64 %{{.*}}, 7
// CHECK: %[[E2:.*]] = icmp eq i64 %[[MISALIGN]], 0
// CHECK: %[[E12:.*]] = and i1 %[[E1]], %[[E2]]
// CHECK: br i1 %[[E12]],
// CHECK: call void @__ubsan_handle_type_mismatch
// CHECK: br label
// CHECK: br i1 %{{.*}},
// CHECK: call void @__ubsan_handle_dynamic_type_cache_miss
// CHECK: br label
(void) static_cast<T*>(p);
}
// CHECK: @_Z22bad_downcast_reference
void bad_downcast_reference(S &p) {
// CHECK: %[[E1:.*]] = icmp ne {{.*}}, null
// CHECK-NOT: br i1
// CHECK: %[[SIZE:.*]] = call i64 @llvm.objectsize.i64(
// CHECK: %[[E2:.*]] = icmp uge i64 %[[SIZE]], 24
// CHECK: %[[E12:.*]] = and i1 %[[E1]], %[[E2]]
// CHECK: %[[MISALIGN:.*]] = and i64 %{{.*}}, 7
// CHECK: %[[E3:.*]] = icmp eq i64 %[[MISALIGN]], 0
// CHECK: %[[E123:.*]] = and i1 %[[E12]], %[[E3]]
// CHECK: br i1 %[[E123]],
// CHECK: call void @__ubsan_handle_type_mismatch
// CHECK: br label
// CHECK: br i1 %{{.*}},
// CHECK: call void @__ubsan_handle_dynamic_type_cache_miss
// CHECK: br label
(void) static_cast<T&>(p);
}