Index: compiler-rt/include/sanitizer/msan_interface.h =================================================================== --- compiler-rt/include/sanitizer/msan_interface.h +++ compiler-rt/include/sanitizer/msan_interface.h @@ -104,6 +104,14 @@ copy. Source and destination regions can overlap. */ void __msan_copy_shadow(const volatile void *dst, const volatile void *src, size_t size); + + /* Disables uninitialized memory checks in interceptors. */ + void __msan_scoped_disable_interceptor_checks(void); + + /* Re-enables uninitialized memory checks in interceptors after a previous + call to __msan_scoped_disable_interceptor_checks. */ + void __msan_scoped_enable_interceptor_checks(void); + #ifdef __cplusplus } // extern "C" #endif Index: compiler-rt/lib/fuzzer/FuzzerDriver.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerDriver.cpp +++ compiler-rt/lib/fuzzer/FuzzerDriver.cpp @@ -537,6 +537,8 @@ EF = new ExternalFunctions(); if (EF->LLVMFuzzerInitialize) EF->LLVMFuzzerInitialize(argc, argv); + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); const Vector Args(*argv, *argv + *argc); assert(!Args.empty()); ProgName = new std::string(Args[0]); Index: compiler-rt/lib/fuzzer/FuzzerExtFunctions.def =================================================================== --- compiler-rt/lib/fuzzer/FuzzerExtFunctions.def +++ compiler-rt/lib/fuzzer/FuzzerExtFunctions.def @@ -46,3 +46,6 @@ EXT_FUNC(__sanitizer_set_report_fd, void, (void*), false); EXT_FUNC(__sanitizer_dump_coverage, void, (const uintptr_t *, uintptr_t), false); +EXT_FUNC(__msan_scoped_disable_interceptor_checks, void, (), false); +EXT_FUNC(__msan_scoped_enable_interceptor_checks, void, (), false); +EXT_FUNC(__msan_unpoison, void, (const volatile void *, size_t size), false); Index: compiler-rt/lib/fuzzer/FuzzerInternal.h =================================================================== --- compiler-rt/lib/fuzzer/FuzzerInternal.h +++ compiler-rt/lib/fuzzer/FuzzerInternal.h @@ -152,6 +152,28 @@ static thread_local bool IsMyThread; }; +struct ScopedEnableMsanInterceptorChecks { + ScopedEnableMsanInterceptorChecks() { + if (EF->__msan_scoped_enable_interceptor_checks) + EF->__msan_scoped_enable_interceptor_checks(); + } + ~ScopedEnableMsanInterceptorChecks() { + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + } +}; + +struct ScopedDisableMsanInterceptorChecks { + ScopedDisableMsanInterceptorChecks() { + if (EF->__msan_scoped_disable_interceptor_checks) + EF->__msan_scoped_disable_interceptor_checks(); + } + ~ScopedDisableMsanInterceptorChecks() { + if (EF->__msan_scoped_enable_interceptor_checks) + EF->__msan_scoped_enable_interceptor_checks(); + } +}; + } // namespace fuzzer #endif // LLVM_FUZZER_INTERNAL_H Index: compiler-rt/lib/fuzzer/FuzzerLoop.cpp =================================================================== --- compiler-rt/lib/fuzzer/FuzzerLoop.cpp +++ compiler-rt/lib/fuzzer/FuzzerLoop.cpp @@ -179,6 +179,7 @@ void Fuzzer::DumpCurrentUnit(const char *Prefix) { if (!CurrentUnitData) return; // Happens when running individual inputs. + ScopedDisableMsanInterceptorChecks S; MD.PrintMutationSequence(); Printf("; base unit: %s\n", Sha1ToString(BaseSha1).c_str()); size_t UnitSize = CurrentUnitSize; @@ -516,19 +517,24 @@ // so that we reliably find buffer overflows in it. uint8_t *DataCopy = new uint8_t[Size]; memcpy(DataCopy, Data, Size); + if (EF->__msan_unpoison) + EF->__msan_unpoison(DataCopy, Size); if (CurrentUnitData && CurrentUnitData != Data) memcpy(CurrentUnitData, Data, Size); CurrentUnitSize = Size; - AllocTracer.Start(Options.TraceMalloc); - UnitStartTime = system_clock::now(); - TPC.ResetMaps(); - RunningCB = true; - int Res = CB(DataCopy, Size); - RunningCB = false; - UnitStopTime = system_clock::now(); - (void)Res; - assert(Res == 0); - HasMoreMallocsThanFrees = AllocTracer.Stop(); + { + ScopedEnableMsanInterceptorChecks S; + AllocTracer.Start(Options.TraceMalloc); + UnitStartTime = system_clock::now(); + TPC.ResetMaps(); + RunningCB = true; + int Res = CB(DataCopy, Size); + RunningCB = false; + UnitStopTime = system_clock::now(); + (void)Res; + assert(Res == 0); + HasMoreMallocsThanFrees = AllocTracer.Stop(); + } if (!LooseMemeq(DataCopy, Data, Size)) CrashOnOverwrittenData(); CurrentUnitSize = 0; Index: compiler-rt/lib/msan/msan_interceptors.cc =================================================================== --- compiler-rt/lib/msan/msan_interceptors.cc +++ compiler-rt/lib/msan/msan_interceptors.cc @@ -60,6 +60,9 @@ // True if this is a nested interceptor. static THREADLOCAL int in_interceptor_scope; +void __msan_scoped_disable_interceptor_checks() { ++in_interceptor_scope; } +void __msan_scoped_enable_interceptor_checks() { --in_interceptor_scope; } + struct InterceptorScope { InterceptorScope() { ++in_interceptor_scope; } ~InterceptorScope() { --in_interceptor_scope; } Index: compiler-rt/lib/msan/msan_interface_internal.h =================================================================== --- compiler-rt/lib/msan/msan_interface_internal.h +++ compiler-rt/lib/msan/msan_interface_internal.h @@ -174,6 +174,12 @@ SANITIZER_INTERFACE_ATTRIBUTE void __msan_copy_shadow(void *dst, const void *src, uptr size); + +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_scoped_disable_interceptor_checks(); + +SANITIZER_INTERFACE_ATTRIBUTE +void __msan_scoped_enable_interceptor_checks(); } // extern "C" #endif // MSAN_INTERFACE_INTERNAL_H Index: compiler-rt/test/fuzzer/UninitializedStrlen.cpp =================================================================== --- /dev/null +++ compiler-rt/test/fuzzer/UninitializedStrlen.cpp @@ -0,0 +1,14 @@ +#include +#include + +volatile size_t Sink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 4) return 0; + if (Data[0] == 'F' && Data[1] == 'U' && Data[2] == 'Z' && Data[3] == 'Z') { + char uninit[7]; + Sink = strlen(uninit); + } + return 0; +} + Index: compiler-rt/test/fuzzer/UseAfterDtor.cpp =================================================================== --- /dev/null +++ compiler-rt/test/fuzzer/UseAfterDtor.cpp @@ -0,0 +1,27 @@ +#include +#include + +struct Simple { + int x_; + Simple() { + x_ = 5; + } + ~Simple() { + x_ += 1; + } +}; + +Simple *volatile SimpleSink; + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) { + if (Size < 4) return 0; + if (Data[0] == 'F' && Data[1] == 'U' && Data[2] == 'Z' && Data[3] == 'Z') { + { + Simple S; + SimpleSink = &S; + } + if (SimpleSink->x_) fprintf(stderr, "Failed to catch use-after-dtor\n"); + } + return 0; +} + Index: compiler-rt/test/fuzzer/lit.cfg =================================================================== --- compiler-rt/test/fuzzer/lit.cfg +++ compiler-rt/test/fuzzer/lit.cfg @@ -49,7 +49,7 @@ libfuzzer_src_root = os.path.join(config.compiler_rt_src_root, "lib", "fuzzer") config.substitutions.append(('%libfuzzer_src', libfuzzer_src_root)) -def generate_compiler_cmd(is_cpp=True, fuzzer_enabled=True): +def generate_compiler_cmd(is_cpp=True, fuzzer_enabled=True, msan_enabled=False): compiler_cmd = config.clang extra_cmd = config.target_flags if config.clang and config.stdlib == 'libc++': @@ -63,7 +63,10 @@ link_cmd = '-lstdc++' std_cmd = '--driver-mode=g++ -std=c++11' if is_cpp else '' - sanitizers = ['address'] + if msan_enabled: + sanitizers = ['memory'] + else: + sanitizers = ['address'] if fuzzer_enabled: sanitizers.append('fuzzer') sanitizers_cmd = ('-fsanitize=%s' % ','.join(sanitizers)) @@ -93,6 +96,10 @@ generate_compiler_cmd(is_cpp=False, fuzzer_enabled=False) )) +config.substitutions.append(('%msan_compiler', + generate_compiler_cmd(is_cpp=True, fuzzer_enabled=True, msan_enabled=True) + )) + if config.host_os == 'Darwin': if config.target_arch in ["x86_64", "x86_64h"]: config.parallelism_group = "darwin-64bit-sanitizer" Index: compiler-rt/test/fuzzer/msan.test =================================================================== --- /dev/null +++ compiler-rt/test/fuzzer/msan.test @@ -0,0 +1,23 @@ +RUN: %msan_compiler %S/SimpleTest.cpp -o %t +RUN: not %run %t -seed=1 -runs=10000000 2>&1 | FileCheck %s --check-prefix=NO-REPORT + +RUN: %msan_compiler %S/SimpleCmpTest.cpp -o %t +RUN: not %run %t -seed=1 -runs=10000000 2>&1 | FileCheck %s --check-prefix=NO-REPORT + +RUN: %msan_compiler %S/MemcmpTest.cpp -o %t +RUN: not %run %t -seed=1 -runs=10000000 2>&1 | FileCheck %s --check-prefix=NO-REPORT + +RUN: %msan_compiler %S/StrcmpTest.cpp -o %t +RUN: not %run %t -seed=1 -runs=10000000 2>&1 | FileCheck %s --check-prefix=NO-REPORT + +NO-REPORT-NOT: MemorySanitizer +NO-REPORT: BINGO + + +RUN: %msan_compiler %S/UseAfterDtor.cpp -o %t +RUN: MSAN_OPTIONS=poison_in_dtor=1 not %run %t -seed=1 -runs=10000000 2>&1 | FileCheck %s --check-prefix=REPORT + +RUN: %msan_compiler %S/UninitializedStrlen.cpp -o %t +RUN: not %run %t -seed=1 -runs=10000000 2>&1 | FileCheck %s --check-prefix=REPORT + +REPORT: MemorySanitizer: use-of-uninitialized-value Index: compiler-rt/test/msan/scoped-interceptors.cc =================================================================== --- /dev/null +++ compiler-rt/test/msan/scoped-interceptors.cc @@ -0,0 +1,52 @@ +// RUN: %clangxx_msan %s -o %t +// RUN: %run %t --disable-checks 0 2>&1 | FileCheck --check-prefix=DISABLED --allow-empty %s +// RUN: %run %t --disable-checks 1 2>&1 | FileCheck --check-prefix=DISABLED --allow-empty %s +// RUN: %run %t --disable-checks 2 2>&1 | FileCheck --check-prefix=DISABLED --allow-empty %s +// RUN: %run %t --disable-checks 3 2>&1 | FileCheck --check-prefix=DISABLED --allow-empty %s +// RUN: not %run %t --reenable-checks 0 2>&1 | FileCheck --check-prefix=CASE-0 %s +// RUN: not %run %t --reenable-checks 1 2>&1 | FileCheck --check-prefix=CASE-1 %s +// RUN: not %run %t --reenable-checks 2 2>&1 | FileCheck --check-prefix=CASE-2 %s +// RUN: not %run %t --reenable-checks 3 2>&1 | FileCheck --check-prefix=CASE-3 %s + +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) { + assert(argc == 3); + __msan_scoped_disable_interceptor_checks(); + if (strcmp(argv[1], "--reenable-checks") == 0) + __msan_scoped_enable_interceptor_checks(); + + char uninit[7]; + switch (argv[2][0]) { + case '0': { + char *copy = strndup(uninit, sizeof(uninit)); // BOOM + free(copy); + break; + // CASE-0: Uninitialized bytes in __interceptor_strndup + } + case '1': { + puts(uninit); // BOOM + puts(uninit); // Ensure previous call did not enable interceptor checks. + break; + // CASE-1: Uninitialized bytes in __interceptor_puts + } + case '2': { + int cmp = memcmp(uninit, uninit, sizeof(uninit)); // BOOM + break; + // CASE-2: Uninitialized bytes in __interceptor_memcmp + } + case '3': { + size_t len = strlen(uninit); // BOOM + break; + // CASE-3: Uninitialized bytes in __interceptor_strlen + } + default: assert(0); + } + // DISABLED-NOT: Uninitialized bytes + return 0; +} +