[msan] Handle param-tls overflow.
ParamTLS (shadow for function arguments) is of limited size. This change makes all arguments that do not fit unpoisoned, and avoids writing past the end of a TLS buffer. llvm-svn: 220351
This commit is contained in:
parent
875301b2c4
commit
35eb265421
|
@ -39,22 +39,22 @@ static bool msan_running_under_dr;
|
|||
// Function argument shadow. Each argument starts at the next available 8-byte
|
||||
// aligned address.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
THREADLOCAL u64 __msan_param_tls[kMsanParamTlsSizeInWords];
|
||||
THREADLOCAL u64 __msan_param_tls[kMsanParamTlsSize / sizeof(u64)];
|
||||
|
||||
// Function argument origin. Each argument starts at the same offset as the
|
||||
// corresponding shadow in (__msan_param_tls). Slightly weird, but changing this
|
||||
// would break compatibility with older prebuilt binaries.
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
THREADLOCAL u32 __msan_param_origin_tls[kMsanParamTlsSizeInWords];
|
||||
THREADLOCAL u32 __msan_param_origin_tls[kMsanParamTlsSize / sizeof(u32)];
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
THREADLOCAL u64 __msan_retval_tls[kMsanRetvalTlsSizeInWords];
|
||||
THREADLOCAL u64 __msan_retval_tls[kMsanRetvalTlsSize / sizeof(u64)];
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
THREADLOCAL u32 __msan_retval_origin_tls;
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSizeInWords];
|
||||
THREADLOCAL u64 __msan_va_arg_tls[kMsanParamTlsSize / sizeof(u64)];
|
||||
|
||||
SANITIZER_INTERFACE_ATTRIBUTE
|
||||
THREADLOCAL u64 __msan_va_arg_overflow_size_tls;
|
||||
|
|
|
@ -32,8 +32,9 @@
|
|||
#define MEM_IS_SHADOW(mem) \
|
||||
((uptr)mem >= 0x200000000000ULL && (uptr)mem <= 0x400000000000ULL)
|
||||
|
||||
const int kMsanParamTlsSizeInWords = 100;
|
||||
const int kMsanRetvalTlsSizeInWords = 100;
|
||||
// These constants must be kept in sync with the ones in MemorySanitizer.cc.
|
||||
const int kMsanParamTlsSize = 800;
|
||||
const int kMsanRetvalTlsSize = 800;
|
||||
|
||||
namespace __msan {
|
||||
extern int msan_inited;
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
// ParamTLS has limited size. Everything that does not fit is considered fully
|
||||
// initialized.
|
||||
|
||||
// RUN: %clangxx_msan -m64 -O0 %s -o %t && %run %t
|
||||
// RUN: %clangxx_msan -fsanitize-memory-track-origins -m64 -O0 %s -o %t && %run %t
|
||||
// RUN: %clangxx_msan -fsanitize-memory-track-origins=2 -m64 -O0 %s -o %t && %run %t
|
||||
|
||||
#include <sanitizer/msan_interface.h>
|
||||
#include <assert.h>
|
||||
|
||||
// This test assumes that ParamTLS size is 800 bytes.
|
||||
|
||||
// This test passes poisoned values through function argument list.
|
||||
// In case of overflow, argument is unpoisoned.
|
||||
#define OVERFLOW(x) assert(__msan_test_shadow(&x, sizeof(x)) == -1)
|
||||
// In case of no overflow, it is still poisoned.
|
||||
#define NO_OVERFLOW(x) assert(__msan_test_shadow(&x, sizeof(x)) == 0)
|
||||
|
||||
template<int N>
|
||||
struct S {
|
||||
char x[N];
|
||||
};
|
||||
|
||||
void f100(S<100> s) {
|
||||
NO_OVERFLOW(s);
|
||||
}
|
||||
|
||||
void f800(S<800> s) {
|
||||
NO_OVERFLOW(s);
|
||||
}
|
||||
|
||||
void f801(S<801> s) {
|
||||
OVERFLOW(s);
|
||||
}
|
||||
|
||||
void f1000(S<1000> s) {
|
||||
OVERFLOW(s);
|
||||
}
|
||||
|
||||
void f_many(int a, double b, S<800> s, int c, double d) {
|
||||
NO_OVERFLOW(a);
|
||||
NO_OVERFLOW(b);
|
||||
OVERFLOW(s);
|
||||
OVERFLOW(c);
|
||||
OVERFLOW(d);
|
||||
}
|
||||
|
||||
// -8 bytes for "int a", aligned by 8
|
||||
// -2 to make "int c" a partial fit
|
||||
void f_many2(int a, S<800 - 8 - 2> s, int c, double d) {
|
||||
NO_OVERFLOW(a);
|
||||
NO_OVERFLOW(s);
|
||||
OVERFLOW(c);
|
||||
OVERFLOW(d);
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
S<100> s100;
|
||||
S<800> s800;
|
||||
S<801> s801;
|
||||
S<1000> s1000;
|
||||
f100(s100);
|
||||
f800(s800);
|
||||
f801(s801);
|
||||
f1000(s1000);
|
||||
|
||||
int i;
|
||||
double d;
|
||||
f_many(i, d, s800, i, d);
|
||||
|
||||
S<800 - 8 - 2> s788;
|
||||
f_many2(i, s788, i, d);
|
||||
return 0;
|
||||
}
|
|
@ -127,6 +127,10 @@ static const uint64_t kOriginOffset64 = 1ULL << 45;
|
|||
static const unsigned kMinOriginAlignment = 4;
|
||||
static const unsigned kShadowTLSAlignment = 8;
|
||||
|
||||
// These constants must be kept in sync with the ones in msan.h.
|
||||
static const unsigned kParamTLSSize = 800;
|
||||
static const unsigned kRetvalTLSSize = 800;
|
||||
|
||||
// Accesses sizes are powers of two: 1, 2, 4, 8.
|
||||
static const size_t kNumberOfAccessSizes = 4;
|
||||
|
||||
|
@ -356,7 +360,7 @@ void MemorySanitizer::initializeCallbacks(Module &M) {
|
|||
|
||||
// Create globals.
|
||||
RetvalTLS = new GlobalVariable(
|
||||
M, ArrayType::get(IRB.getInt64Ty(), 8), false,
|
||||
M, ArrayType::get(IRB.getInt64Ty(), kRetvalTLSSize / 8), false,
|
||||
GlobalVariable::ExternalLinkage, nullptr, "__msan_retval_tls", nullptr,
|
||||
GlobalVariable::InitialExecTLSModel);
|
||||
RetvalOriginTLS = new GlobalVariable(
|
||||
|
@ -364,16 +368,16 @@ void MemorySanitizer::initializeCallbacks(Module &M) {
|
|||
"__msan_retval_origin_tls", nullptr, GlobalVariable::InitialExecTLSModel);
|
||||
|
||||
ParamTLS = new GlobalVariable(
|
||||
M, ArrayType::get(IRB.getInt64Ty(), 1000), false,
|
||||
M, ArrayType::get(IRB.getInt64Ty(), kParamTLSSize / 8), false,
|
||||
GlobalVariable::ExternalLinkage, nullptr, "__msan_param_tls", nullptr,
|
||||
GlobalVariable::InitialExecTLSModel);
|
||||
ParamOriginTLS = new GlobalVariable(
|
||||
M, ArrayType::get(OriginTy, 1000), false, GlobalVariable::ExternalLinkage,
|
||||
nullptr, "__msan_param_origin_tls", nullptr,
|
||||
GlobalVariable::InitialExecTLSModel);
|
||||
M, ArrayType::get(OriginTy, kParamTLSSize / 4), false,
|
||||
GlobalVariable::ExternalLinkage, nullptr, "__msan_param_origin_tls",
|
||||
nullptr, GlobalVariable::InitialExecTLSModel);
|
||||
|
||||
VAArgTLS = new GlobalVariable(
|
||||
M, ArrayType::get(IRB.getInt64Ty(), 1000), false,
|
||||
M, ArrayType::get(IRB.getInt64Ty(), kParamTLSSize / 8), false,
|
||||
GlobalVariable::ExternalLinkage, nullptr, "__msan_va_arg_tls", nullptr,
|
||||
GlobalVariable::InitialExecTLSModel);
|
||||
VAArgOverflowSizeTLS = new GlobalVariable(
|
||||
|
@ -952,6 +956,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
|
|||
? MS.DL->getTypeAllocSize(FArg.getType()->getPointerElementType())
|
||||
: MS.DL->getTypeAllocSize(FArg.getType());
|
||||
if (A == &FArg) {
|
||||
bool Overflow = ArgOffset + Size > kParamTLSSize;
|
||||
Value *Base = getShadowPtrForArgument(&FArg, EntryIRB, ArgOffset);
|
||||
if (FArg.hasByValAttr()) {
|
||||
// ByVal pointer itself has clean shadow. We copy the actual
|
||||
|
@ -962,19 +967,32 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
|
|||
Type *EltType = A->getType()->getPointerElementType();
|
||||
ArgAlign = MS.DL->getABITypeAlignment(EltType);
|
||||
}
|
||||
unsigned CopyAlign = std::min(ArgAlign, kShadowTLSAlignment);
|
||||
Value *Cpy = EntryIRB.CreateMemCpy(
|
||||
getShadowPtr(V, EntryIRB.getInt8Ty(), EntryIRB), Base, Size,
|
||||
CopyAlign);
|
||||
DEBUG(dbgs() << " ByValCpy: " << *Cpy << "\n");
|
||||
(void)Cpy;
|
||||
if (Overflow) {
|
||||
// ParamTLS overflow.
|
||||
EntryIRB.CreateMemSet(
|
||||
getShadowPtr(V, EntryIRB.getInt8Ty(), EntryIRB),
|
||||
Constant::getNullValue(EntryIRB.getInt8Ty()), Size, ArgAlign);
|
||||
} else {
|
||||
unsigned CopyAlign = std::min(ArgAlign, kShadowTLSAlignment);
|
||||
Value *Cpy = EntryIRB.CreateMemCpy(
|
||||
getShadowPtr(V, EntryIRB.getInt8Ty(), EntryIRB), Base, Size,
|
||||
CopyAlign);
|
||||
DEBUG(dbgs() << " ByValCpy: " << *Cpy << "\n");
|
||||
(void)Cpy;
|
||||
}
|
||||
*ShadowPtr = getCleanShadow(V);
|
||||
} else {
|
||||
*ShadowPtr = EntryIRB.CreateAlignedLoad(Base, kShadowTLSAlignment);
|
||||
if (Overflow) {
|
||||
// ParamTLS overflow.
|
||||
*ShadowPtr = getCleanShadow(V);
|
||||
} else {
|
||||
*ShadowPtr =
|
||||
EntryIRB.CreateAlignedLoad(Base, kShadowTLSAlignment);
|
||||
}
|
||||
}
|
||||
DEBUG(dbgs() << " ARG: " << FArg << " ==> " <<
|
||||
**ShadowPtr << "\n");
|
||||
if (MS.TrackOrigins) {
|
||||
if (MS.TrackOrigins && !Overflow) {
|
||||
Value *OriginPtr =
|
||||
getOriginPtrForArgument(&FArg, EntryIRB, ArgOffset);
|
||||
setOrigin(A, EntryIRB.CreateLoad(OriginPtr));
|
||||
|
@ -2329,6 +2347,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
|
|||
assert(A->getType()->isPointerTy() &&
|
||||
"ByVal argument is not a pointer!");
|
||||
Size = MS.DL->getTypeAllocSize(A->getType()->getPointerElementType());
|
||||
if (ArgOffset + Size > kParamTLSSize) break;
|
||||
unsigned ParamAlignment = CS.getParamAlignment(i + 1);
|
||||
unsigned Alignment = std::min(ParamAlignment, kShadowTLSAlignment);
|
||||
Store = IRB.CreateMemCpy(ArgShadowBase,
|
||||
|
@ -2336,6 +2355,7 @@ struct MemorySanitizerVisitor : public InstVisitor<MemorySanitizerVisitor> {
|
|||
Size, Alignment);
|
||||
} else {
|
||||
Size = MS.DL->getTypeAllocSize(A->getType());
|
||||
if (ArgOffset + Size > kParamTLSSize) break;
|
||||
Store = IRB.CreateAlignedStore(ArgShadow, ArgShadowBase,
|
||||
kShadowTLSAlignment);
|
||||
Constant *Cst = dyn_cast<Constant>(ArgShadow);
|
||||
|
|
Loading…
Reference in New Issue