forked from OSchip/llvm-project
[UBSan] Introduce type-based blacklisting.
Teach UBSan vptr checker to ignore technically invalud down-casts on blacklisted types. Based on http://reviews.llvm.org/D4407 by Byoungyoung Lee! llvm-svn: 212770
This commit is contained in:
parent
f34b454219
commit
848560125d
|
@ -544,44 +544,48 @@ void CodeGenFunction::EmitTypeCheck(TypeCheckKind TCK, SourceLocation Loc,
|
|||
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);
|
||||
// Blacklist based on the mangled type.
|
||||
if (!CGM.getSanitizerBlacklist().isBlacklistedType(Out.str())) {
|
||||
llvm::hash_code TypeHash = hash_value(Out.str());
|
||||
|
||||
llvm::Value *Hash = emitHash16Bytes(Builder, Low, High);
|
||||
Hash = Builder.CreateTrunc(Hash, IntPtrTy);
|
||||
// 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);
|
||||
|
||||
// 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));
|
||||
llvm::Value *Hash = emitHash16Bytes(Builder, Low, High);
|
||||
Hash = Builder.CreateTrunc(Hash, IntPtrTy);
|
||||
|
||||
// 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,
|
||||
CRK_AlwaysRecoverable);
|
||||
// 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,
|
||||
CRK_AlwaysRecoverable);
|
||||
}
|
||||
}
|
||||
|
||||
if (Done) {
|
||||
|
|
|
@ -46,3 +46,7 @@ bool SanitizerBlacklist::isIn(const llvm::GlobalVariable &G,
|
|||
SCL->inSection("global", G.getName(), Category) ||
|
||||
SCL->inSection("type", GetGlobalTypeString(G), Category);
|
||||
}
|
||||
|
||||
bool SanitizerBlacklist::isBlacklistedType(StringRef MangledTypeName) const {
|
||||
return SCL->inSection("type", MangledTypeName);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ public:
|
|||
bool isIn(const llvm::Function &F) const;
|
||||
bool isIn(const llvm::GlobalVariable &G,
|
||||
const StringRef Category = StringRef()) const;
|
||||
bool isBlacklistedType(StringRef MangledTypeName) const;
|
||||
};
|
||||
} // end namespace CodeGen
|
||||
} // end namespace clang
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// Verify ubsan vptr does not check down-casts on blacklisted types.
|
||||
// RUN: echo "type:_ZTI3Foo" > %t-type.blacklist
|
||||
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -emit-llvm %s -o - | FileCheck %s --check-prefix=DEFAULT
|
||||
// RUN: %clang_cc1 -triple x86_64-linux-gnu -fsanitize=vptr -fsanitize-blacklist=%t-type.blacklist -emit-llvm %s -o - | FileCheck %s --check-prefix=TYPE
|
||||
|
||||
// REQUIRES: shell
|
||||
|
||||
class Bar {
|
||||
public:
|
||||
virtual ~Bar() {}
|
||||
};
|
||||
class Foo : public Bar {};
|
||||
|
||||
Bar bar;
|
||||
|
||||
// DEFAULT: @_Z7checkmev
|
||||
// TYPE: @_Z7checkmev
|
||||
void checkme() {
|
||||
// DEFAULT: call void @__ubsan_handle_dynamic_type_cache_miss({{.*}} (%class.Bar* @bar to
|
||||
// TYPE-NOT: @__ubsan_handle_dynamic_type_cache_miss
|
||||
Foo* foo = static_cast<Foo*>(&bar); // down-casting
|
||||
// DEFAULT: ret void
|
||||
// TYPE: ret void
|
||||
return;
|
||||
}
|
Loading…
Reference in New Issue