[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:
Evgeniy Stepanov 2014-10-22 00:12:40 +00:00
parent 875301b2c4
commit 35eb265421
4 changed files with 115 additions and 20 deletions

View File

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

View File

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

View File

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

View File

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