From 35eb265421dc36b6db8f8181a6b8167eae3bf4bd Mon Sep 17 00:00:00 2001 From: Evgeniy Stepanov Date: Wed, 22 Oct 2014 00:12:40 +0000 Subject: [PATCH] [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 --- compiler-rt/lib/msan/msan.cc | 8 +- compiler-rt/lib/msan/msan.h | 5 +- compiler-rt/test/msan/param_tls_limit.cc | 74 +++++++++++++++++++ .../Instrumentation/MemorySanitizer.cpp | 48 ++++++++---- 4 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 compiler-rt/test/msan/param_tls_limit.cc diff --git a/compiler-rt/lib/msan/msan.cc b/compiler-rt/lib/msan/msan.cc index 820654469b4f..846e884d13fe 100644 --- a/compiler-rt/lib/msan/msan.cc +++ b/compiler-rt/lib/msan/msan.cc @@ -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; diff --git a/compiler-rt/lib/msan/msan.h b/compiler-rt/lib/msan/msan.h index 7bdb6a57cdc7..ad66016b91c0 100644 --- a/compiler-rt/lib/msan/msan.h +++ b/compiler-rt/lib/msan/msan.h @@ -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; diff --git a/compiler-rt/test/msan/param_tls_limit.cc b/compiler-rt/test/msan/param_tls_limit.cc new file mode 100644 index 000000000000..869afc935773 --- /dev/null +++ b/compiler-rt/test/msan/param_tls_limit.cc @@ -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 +#include + +// 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 +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; +} diff --git a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp index dc4fc9a017bb..96e9022331f2 100644 --- a/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp +++ b/llvm/lib/Transforms/Instrumentation/MemorySanitizer.cpp @@ -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 { ? 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 { 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 { 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 { Size, Alignment); } else { Size = MS.DL->getTypeAllocSize(A->getType()); + if (ArgOffset + Size > kParamTLSSize) break; Store = IRB.CreateAlignedStore(ArgShadow, ArgShadowBase, kShadowTLSAlignment); Constant *Cst = dyn_cast(ArgShadow);