[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:
Alexey Samsonov 2014-07-10 22:34:19 +00:00
parent f34b454219
commit 848560125d
4 changed files with 68 additions and 34 deletions

View File

@ -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) {

View File

@ -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);
}

View File

@ -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

View File

@ -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;
}