summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeniy Stepanov <eugeni.stepanov@gmail.com>2012-12-25 12:39:56 +0000
committerEvgeniy Stepanov <eugeni.stepanov@gmail.com>2012-12-25 12:39:56 +0000
commit0231c50f42e735739041f3b4b4ce17e1742bed69 (patch)
treeecbef2c2cc839771ee6fa59694daef1ab675cafc
parent97edeb3e6270b05cb3ece0d5b6f0ea1f0ba1398a (diff)
MemorySanitizer unit tests.
git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@171062 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--cmake/Modules/CompilerRTLink.cmake14
-rw-r--r--lib/msan/CMakeLists.txt27
-rw-r--r--lib/msan/tests/CMakeLists.txt153
-rw-r--r--lib/msan/tests/msan_test.cc1601
-rw-r--r--lib/msan/tests/msandr_test_so.cc36
-rw-r--r--lib/msan/tests/msandr_test_so.h23
6 files changed, 1840 insertions, 14 deletions
diff --git a/cmake/Modules/CompilerRTLink.cmake b/cmake/Modules/CompilerRTLink.cmake
new file mode 100644
index 000000000..85030a725
--- /dev/null
+++ b/cmake/Modules/CompilerRTLink.cmake
@@ -0,0 +1,14 @@
+include(LLVMParseArguments)
+
+# Link a shared library with just-built Clang.
+# clang_link_shared(<output.so>
+# OBJECTS <list of input objects>
+# LINKFLAGS <list of link flags>
+# DEPS <list of dependencies>)
+macro(clang_link_shared so_file)
+ parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN})
+ add_custom_command(
+ OUTPUT ${so_file}
+ COMMAND clang -o "${so_file}" -shared ${SOURCE_LINKFLAGS} ${SOURCE_OBJECTS}
+ DEPENDS clang ${SOURCE_DEPS})
+endmacro()
diff --git a/lib/msan/CMakeLists.txt b/lib/msan/CMakeLists.txt
index bd4a50ffa..44b00695a 100644
--- a/lib/msan/CMakeLists.txt
+++ b/lib/msan/CMakeLists.txt
@@ -1,6 +1,7 @@
-# Build for the MemorySanitizer runtime support library.
+include_directories(..)
-set(MSAN_SOURCES
+# Runtime library sources and build flags.
+set(MSAN_RTL_SOURCES
msan.cc
msan_allocator.cc
msan_interceptors.cc
@@ -8,28 +9,26 @@ set(MSAN_SOURCES
msan_new_delete.cc
msan_platform_limits_posix.cc
)
-
-include_directories(..)
-
-set(MSAN_CFLAGS
+set(MSAN_RTL_CFLAGS
${SANITIZER_COMMON_CFLAGS}
-fPIE
- -ffreestanding
- -g
- -fno-omit-frame-pointer)
-set(MSAN_COMMON_DEFINITIONS)
+ # Prevent clang from generating libc calls.
+ -ffreestanding)
+# Static runtime library.
set(MSAN_RUNTIME_LIBRARIES)
add_library(clang_rt.msan-x86_64 STATIC
- ${MSAN_SOURCES}
+ ${MSAN_RTL_SOURCES}
$<TARGET_OBJECTS:RTInterception.x86_64>
$<TARGET_OBJECTS:RTSanitizerCommon.x86_64>
)
set_target_compile_flags(clang_rt.msan-x86_64
- ${MSAN_CFLAGS} ${TARGET_X86_64_CFLAGS}
+ ${MSAN_RTL_CFLAGS} ${TARGET_X86_64_CFLAGS}
)
list(APPEND MSAN_RUNTIME_LIBRARIES clang_rt.msan-x86_64)
-set_property(TARGET ${MSAN_RUNTIME_LIBRARIES} APPEND PROPERTY
- COMPILE_DEFINITIONS ${MSAN_COMMON_DEFINITIONS})
add_clang_compiler_rt_libraries(${MSAN_RUNTIME_LIBRARIES})
+
+if(LLVM_INCLUDE_TESTS)
+ add_subdirectory(tests)
+endif()
diff --git a/lib/msan/tests/CMakeLists.txt b/lib/msan/tests/CMakeLists.txt
new file mode 100644
index 000000000..f82d5669c
--- /dev/null
+++ b/lib/msan/tests/CMakeLists.txt
@@ -0,0 +1,153 @@
+include(CheckCXXCompilerFlag)
+include(CompilerRTCompile)
+include(CompilerRTLink)
+include(CompilerRTUnittests)
+
+include_directories(..)
+include_directories(../..)
+
+# Instrumented libcxx sources and build flags.
+# FIXME: only do this if libcxx is checked out.
+set(MSAN_LIBCXX_PATH ${LLVM_MAIN_SRC_DIR}/projects/libcxx)
+file(GLOB MSAN_LIBCXX_SOURCES ${MSAN_LIBCXX_PATH}/src/*.cpp)
+set(MSAN_LIBCXX_CFLAGS
+ -I${MSAN_LIBCXX_PATH}/include
+ -fsanitize=memory
+ -fsanitize-memory-track-origins
+ -fPIC
+ -Wno-\#warnings
+ -g
+ -O2
+ -std=c++0x
+ -fstrict-aliasing
+ -fno-exceptions
+ -nostdinc++
+ -fno-omit-frame-pointer
+ -mno-omit-leaf-frame-pointer)
+set(MSAN_LIBCXX_LINK_FLAGS
+ -nodefaultlibs
+ -lpthread
+ -lrt
+ -lc
+ -lstdc++
+ -fsanitize=memory)
+
+# Unittest sources and build flags.
+set(MSAN_UNITTEST_SOURCE msan_test.cc)
+set(MSAN_UNITTEST_HEADERS msandr_test_so.h)
+set(MSANDR_UNITTEST_SOURCE msandr_test_so.cc)
+set(MSAN_UNITTEST_COMMON_CFLAGS
+ -I${MSAN_LIBCXX_PATH}/include
+ ${COMPILER_RT_GTEST_INCLUDE_CFLAGS}
+ -I${COMPILER_RT_SOURCE_DIR}/include
+ -I${COMPILER_RT_SOURCE_DIR}/lib
+ -I${COMPILER_RT_SOURCE_DIR}/lib/msan
+ -std=c++0x
+ -stdlib=libc++
+ -fPIE
+ -g
+ -O2
+ -fno-exceptions
+ -fno-omit-frame-pointer
+ -mno-omit-leaf-frame-pointer
+)
+set(MSAN_UNITTEST_INSTRUMENTED_CFLAGS
+ ${MSAN_UNITTEST_COMMON_CFLAGS}
+ -fsanitize=memory
+ -fsanitize-memory-track-origins
+ -mllvm -msan-keep-going=1
+)
+set(MSAN_UNITTEST_LINK_FLAGS
+ -fsanitize=memory
+ -pie
+)
+
+# Compile source for the given architecture, using compiler
+# options in ${ARGN}, and add it to the object list.
+macro(msan_compile obj_list source arch)
+ get_filename_component(basename ${source} NAME)
+ set(output_obj "${basename}.${arch}.o")
+ get_target_flags_for_arch(${arch} TARGET_CFLAGS)
+ clang_compile(${output_obj} ${source}
+ CFLAGS ${ARGN} ${TARGET_CFLAGS}
+ DEPS gtest ${MSAN_RUNTIME_LIBRARIES} ${MSAN_UNITTEST_HEADERS})
+ list(APPEND ${obj_list} ${output_obj})
+endmacro()
+
+macro(msan_link_shared so_list so_name arch)
+ parse_arguments(SOURCE "OBJECTS;LINKFLAGS;DEPS" "" ${ARGN})
+ get_unittest_directory(OUTPUT_DIR)
+ file(MAKE_DIRECTORY ${OUTPUT_DIR})
+ set(output_so "${OUTPUT_DIR}/${so_name}.${arch}.so")
+ get_target_flags_for_arch(${arch} TARGET_LINKFLAGS)
+ clang_link_shared(${output_so}
+ OBJECTS ${SOURCE_OBJECTS}
+ LINKFLAGS ${TARGET_LINKFLAGS} ${SOURCE_LINKFLAGS}
+ DEPS ${SOURCE_DEPS})
+ list(APPEND ${so_list} ${output_so})
+endmacro()
+
+# Link MSan unit test for a given architecture from a set
+# of objects in ${ARGN}.
+macro(add_msan_test test_suite test_name arch)
+ get_target_flags_for_arch(${arch} TARGET_LINK_FLAGS)
+ get_unittest_directory(OUTPUT_DIR)
+ add_compiler_rt_test(${test_suite} ${test_name}
+ OBJECTS ${ARGN}
+ DEPS ${MSAN_RUNTIME_LIBRARIES} ${ARGN}
+ LINK_FLAGS ${MSAN_UNITTEST_LINK_FLAGS}
+ ${TARGET_LINK_FLAGS}
+ "-Wl,-rpath=${OUTPUT_DIR}")
+endmacro()
+
+# Main MemorySanitizer unit tests.
+add_custom_target(MsanUnitTests)
+set_target_properties(MsanUnitTests PROPERTIES FOLDER "MSan unit tests")
+
+# Adds MSan unit tests and benchmarks for architecture.
+macro(add_msan_tests_for_arch arch)
+ # Build gtest instrumented with MSan.
+ set(MSAN_INST_GTEST)
+ msan_compile(MSAN_INST_GTEST ${COMPILER_RT_GTEST_SOURCE} ${arch}
+ ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS})
+
+ # Build libcxx instrumented with MSan.
+ set(MSAN_INST_LIBCXX_OBJECTS)
+ foreach(SOURCE ${MSAN_LIBCXX_SOURCES})
+ msan_compile(MSAN_INST_LIBCXX_OBJECTS ${SOURCE} ${arch}
+ ${MSAN_LIBCXX_CFLAGS})
+ endforeach(SOURCE)
+
+ set(MSAN_INST_LIBCXX)
+ msan_link_shared(MSAN_INST_LIBCXX "libcxx" ${arch}
+ OBJECTS ${MSAN_INST_LIBCXX_OBJECTS}
+ LINKFLAGS ${MSAN_LIBCXX_LINK_FLAGS}
+ DEPS ${MSAN_INST_LIBCXX_OBJECTS} ${MSAN_RUNTIME_LIBRARIES})
+
+ # Instrumented tests.
+ set(MSAN_INST_TEST_OBJECTS)
+ msan_compile(MSAN_INST_TEST_OBJECTS ${MSAN_UNITTEST_SOURCE} ${arch}
+ ${MSAN_UNITTEST_INSTRUMENTED_CFLAGS})
+
+ # Uninstrumented shared object for MSanDR tests.
+ set(MSANDR_TEST_OBJECTS)
+ msan_compile(MSANDR_TEST_OBJECTS ${MSANDR_UNITTEST_SOURCE} ${arch}
+ ${MSAN_UNITTEST_COMMON_CFLAGS})
+
+ # Uninstrumented shared library tests.
+ set(MSANDR_TEST_SO)
+ msan_link_shared(MSANDR_TEST_SO "libmsandr_test" ${arch}
+ OBJECTS ${MSANDR_TEST_OBJECTS}
+ DEPS ${MSANDR_TEST_OBJECTS} ${MSAN_RUNTIME_LIBRARIES})
+
+ # Link everything together.
+ add_msan_test(MsanUnitTests "Msan-${arch}-Test" ${arch}
+ ${MSAN_INST_TEST_OBJECTS} ${MSAN_INST_GTEST}
+ ${MSAN_INST_LIBCXX} ${MSANDR_TEST_SO})
+endmacro()
+
+if("${CMAKE_HOST_SYSTEM}" STREQUAL "${CMAKE_SYSTEM}" AND UNIX AND NOT ANDROID)
+ if(CAN_TARGET_X86_64)
+ add_msan_tests_for_arch(x86_64)
+ endif()
+endif()
diff --git a/lib/msan/tests/msan_test.cc b/lib/msan/tests/msan_test.cc
new file mode 100644
index 000000000..cc7062771
--- /dev/null
+++ b/lib/msan/tests/msan_test.cc
@@ -0,0 +1,1601 @@
+//===-- msan_test.cc ------------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// MemorySanitizer unit tests.
+//===----------------------------------------------------------------------===//
+
+#include "sanitizer/msan_interface.h"
+#include "msandr_test_so.h"
+#include "gtest/gtest.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <assert.h>
+#include <wchar.h>
+
+#include <unistd.h>
+#include <limits.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/resource.h>
+#include <sys/ioctl.h>
+#include <sys/utsname.h>
+#include <sys/mman.h>
+#include <sys/vfs.h>
+
+#if defined(__i386__) || defined(__x86_64__)
+# include <emmintrin.h>
+# define MSAN_HAS_M128 1
+#else
+# define MSAN_HAS_M128 0
+#endif
+
+typedef unsigned char U1;
+typedef unsigned short U2; // NOLINT
+typedef unsigned int U4;
+typedef unsigned long long U8; // NOLINT
+typedef signed char S1;
+typedef signed short S2; // NOLINT
+typedef signed int S4;
+typedef signed long long S8; // NOLINT
+#define NOINLINE __attribute__((noinline))
+#define INLINE __attribute__((always_inline))
+
+
+#define EXPECT_POISONED(action) \
+ do { \
+ __msan_set_expect_umr(1); \
+ action; \
+ __msan_set_expect_umr(0); \
+ } while (0)
+
+#define EXPECT_POISONED_O(action, origin) \
+ do { \
+ __msan_set_expect_umr(1); \
+ action; \
+ __msan_set_expect_umr(0); \
+ if (TrackingOrigins()) \
+ EXPECT_EQ(origin, __msan_get_origin_tls()); \
+ } while (0)
+
+#define EXPECT_POISONED_S(action, stack_origin) \
+ do { \
+ __msan_set_expect_umr(1); \
+ action; \
+ __msan_set_expect_umr(0); \
+ u32 id = __msan_get_origin_tls(); \
+ const char *str = __msan_get_origin_descr_if_stack(id); \
+ if (!str || strcmp(str, stack_origin)) { \
+ fprintf(stderr, "EXPECT_POISONED_S: id=%u %s, %s", \
+ id, stack_origin, str); \
+ EXPECT_EQ(1, 0); \
+ } \
+ } while (0)
+
+
+static U8 poisoned_array[100];
+template<class T>
+T *GetPoisoned(int i = 0, T val = 0) {
+ T *res = (T*)&poisoned_array[i];
+ *res = val;
+ __msan_poison(&poisoned_array[i], sizeof(T));
+ return res;
+}
+
+template<class T>
+T *GetPoisonedO(int i, u32 origin, T val = 0) {
+ T *res = (T*)&poisoned_array[i];
+ *res = val;
+ __msan_poison(&poisoned_array[i], sizeof(T));
+ __msan_set_origin(&poisoned_array[i], sizeof(T), origin);
+ return res;
+}
+
+// This function returns its parameter but in such a way that compiler
+// can not prove it.
+template<class T>
+NOINLINE
+static T Ident(T t) {
+ volatile T ret = t;
+ return ret;
+}
+
+static bool TrackingOrigins() {
+ S8 x;
+ __msan_set_origin(&x, sizeof(x), 0x1234);
+ u32 origin = __msan_get_origin(&x);
+ __msan_set_origin(&x, sizeof(x), 0);
+ return origin == 0x1234;
+}
+
+template<class T> NOINLINE T ReturnPoisoned() { return *GetPoisoned<T>(); }
+
+static volatile S1 v_s1;
+static volatile S2 v_s2;
+static volatile S4 v_s4;
+static volatile S8 v_s8;
+static volatile U1 v_u1;
+static volatile U2 v_u2;
+static volatile U4 v_u4;
+static volatile U8 v_u8;
+static void* volatile v_p;
+static volatile double v_d;
+static volatile int g_one = 1;
+static volatile int g_zero = 0;
+static volatile int g_0 = 0;
+static volatile int g_1 = 1;
+
+#if MSAN_HAS_M128
+static volatile __m128i v_m128;
+#endif
+
+S4 a_s4[100];
+S8 a_s8[100];
+
+TEST(MemorySanitizer, NegativeTest1) {
+ S4 *x = GetPoisoned<S4>();
+ if (g_one)
+ *x = 0;
+ v_s4 = *x;
+}
+
+TEST(MemorySanitizer, PositiveTest1) {
+ // Load to store.
+ EXPECT_POISONED(v_s1 = *GetPoisoned<S1>());
+ EXPECT_POISONED(v_s2 = *GetPoisoned<S2>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<S4>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<S8>());
+
+ // S->S conversions.
+ EXPECT_POISONED(v_s2 = *GetPoisoned<S1>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<S1>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<S1>());
+
+ EXPECT_POISONED(v_s1 = *GetPoisoned<S2>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<S2>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<S2>());
+
+ EXPECT_POISONED(v_s1 = *GetPoisoned<S4>());
+ EXPECT_POISONED(v_s2 = *GetPoisoned<S4>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<S4>());
+
+ EXPECT_POISONED(v_s1 = *GetPoisoned<S8>());
+ EXPECT_POISONED(v_s2 = *GetPoisoned<S8>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<S8>());
+
+ // ZExt
+ EXPECT_POISONED(v_s2 = *GetPoisoned<U1>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<U1>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<U1>());
+ EXPECT_POISONED(v_s4 = *GetPoisoned<U2>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<U2>());
+ EXPECT_POISONED(v_s8 = *GetPoisoned<U4>());
+
+ // Unary ops.
+ EXPECT_POISONED(v_s4 = - *GetPoisoned<S4>());
+
+ EXPECT_POISONED(a_s4[g_zero] = 100 / *GetPoisoned<S4>(0, 1));
+
+
+ a_s4[g_zero] = 1 - *GetPoisoned<S4>();
+ a_s4[g_zero] = 1 + *GetPoisoned<S4>();
+}
+
+TEST(MemorySanitizer, Phi1) {
+ S4 c;
+ if (g_one) {
+ c = *GetPoisoned<S4>();
+ } else {
+ __msan_break_optimization(0);
+ c = 0;
+ }
+ EXPECT_POISONED(v_s4 = c);
+}
+
+TEST(MemorySanitizer, Phi2) {
+ S4 i = *GetPoisoned<S4>();
+ S4 n = g_one;
+ EXPECT_POISONED(for (; i < g_one; i++););
+ EXPECT_POISONED(v_s4 = i);
+}
+
+NOINLINE void Arg1ExpectUMR(S4 a1) { EXPECT_POISONED(v_s4 = a1); }
+NOINLINE void Arg2ExpectUMR(S4 a1, S4 a2) { EXPECT_POISONED(v_s4 = a2); }
+NOINLINE void Arg3ExpectUMR(S1 a1, S4 a2, S8 a3) { EXPECT_POISONED(v_s8 = a3); }
+
+TEST(MemorySanitizer, ArgTest) {
+ Arg1ExpectUMR(*GetPoisoned<S4>());
+ Arg2ExpectUMR(0, *GetPoisoned<S4>());
+ Arg3ExpectUMR(0, 1, *GetPoisoned<S8>());
+}
+
+
+TEST(MemorySanitizer, CallAndRet) {
+ if (!__msan_has_dynamic_component()) return;
+ ReturnPoisoned<S1>();
+ ReturnPoisoned<S2>();
+ ReturnPoisoned<S4>();
+ ReturnPoisoned<S8>();
+
+ EXPECT_POISONED(v_s1 = ReturnPoisoned<S1>());
+ EXPECT_POISONED(v_s2 = ReturnPoisoned<S2>());
+ EXPECT_POISONED(v_s4 = ReturnPoisoned<S4>());
+ EXPECT_POISONED(v_s8 = ReturnPoisoned<S8>());
+}
+
+// malloc() in the following test may be optimized to produce a compile-time
+// undef value. Check that we trap on the volatile assignment anyway.
+TEST(MemorySanitizer, DISABLED_MallocNoIdent) {
+ S4 *x = (int*)malloc(sizeof(S4));
+ EXPECT_POISONED(v_s4 = *x);
+ free(x);
+}
+
+TEST(MemorySanitizer, Malloc) {
+ S4 *x = (int*)Ident(malloc(sizeof(S4)));
+ EXPECT_POISONED(v_s4 = *x);
+ free(x);
+}
+
+TEST(MemorySanitizer, Realloc) {
+ S4 *x = (int*)Ident(realloc(0, sizeof(S4)));
+ EXPECT_POISONED(v_s4 = x[0]);
+ x[0] = 1;
+ x = (int*)Ident(realloc(x, 2 * sizeof(S4)));
+ v_s4 = x[0]; // Ok, was inited before.
+ EXPECT_POISONED(v_s4 = x[1]);
+ x = (int*)Ident(realloc(x, 3 * sizeof(S4)));
+ v_s4 = x[0]; // Ok, was inited before.
+ EXPECT_POISONED(v_s4 = x[2]);
+ EXPECT_POISONED(v_s4 = x[1]);
+ x[2] = 1; // Init this here. Check that after realloc it is poisoned again.
+ x = (int*)Ident(realloc(x, 2 * sizeof(S4)));
+ v_s4 = x[0]; // Ok, was inited before.
+ EXPECT_POISONED(v_s4 = x[1]);
+ x = (int*)Ident(realloc(x, 3 * sizeof(S4)));
+ EXPECT_POISONED(v_s4 = x[1]);
+ EXPECT_POISONED(v_s4 = x[2]);
+ free(x);
+}
+
+TEST(MemorySanitizer, Calloc) {
+ S4 *x = (int*)Ident(calloc(1, sizeof(S4)));
+ v_s4 = *x; // Should not be poisoned.
+ // EXPECT_EQ(0, *x);
+ free(x);
+}
+
+TEST(MemorySanitizer, AndOr) {
+ U4 *p = GetPoisoned<U4>();
+ // We poison two bytes in the midle of a 4-byte word to make the test
+ // correct regardless of endianness.
+ ((U1*)p)[1] = 0;
+ ((U1*)p)[2] = 0xff;
+ v_u4 = *p & 0x00ffff00;
+ v_u4 = *p & 0x00ff0000;
+ v_u4 = *p & 0x0000ff00;
+ EXPECT_POISONED(v_u4 = *p & 0xff000000);
+ EXPECT_POISONED(v_u4 = *p & 0x000000ff);
+ EXPECT_POISONED(v_u4 = *p & 0x0000ffff);
+ EXPECT_POISONED(v_u4 = *p & 0xffff0000);
+
+ v_u4 = *p | 0xff0000ff;
+ v_u4 = *p | 0xff00ffff;
+ v_u4 = *p | 0xffff00ff;
+ EXPECT_POISONED(v_u4 = *p | 0xff000000);
+ EXPECT_POISONED(v_u4 = *p | 0x000000ff);
+ EXPECT_POISONED(v_u4 = *p | 0x0000ffff);
+ EXPECT_POISONED(v_u4 = *p | 0xffff0000);
+
+ EXPECT_POISONED(v_u4 = *GetPoisoned<bool>() & *GetPoisoned<bool>());
+}
+
+template<class T>
+static void testNot(T value, T shadow) {
+ __msan_partial_poison(&value, &shadow, sizeof(T));
+ volatile bool v_T = !value;
+}
+
+TEST(MemorySanitizer, Not) {
+ testNot<U4>(0x0, 0x0);
+ testNot<U4>(0xFFFFFFFF, 0x0);
+ EXPECT_POISONED(testNot<U4>(0xFFFFFFFF, 0xFFFFFFFF));
+ testNot<U4>(0xFF000000, 0x0FFFFFFF);
+ testNot<U4>(0xFF000000, 0x00FFFFFF);
+ testNot<U4>(0xFF000000, 0x0000FFFF);
+ testNot<U4>(0xFF000000, 0x00000000);
+ EXPECT_POISONED(testNot<U4>(0xFF000000, 0xFF000000));
+ testNot<U4>(0xFF800000, 0xFF000000);
+ EXPECT_POISONED(testNot<U4>(0x00008000, 0x00008000));
+
+ testNot<U1>(0x0, 0x0);
+ testNot<U1>(0xFF, 0xFE);
+ testNot<U1>(0xFF, 0x0);
+ EXPECT_POISONED(testNot<U1>(0xFF, 0xFF));
+
+ EXPECT_POISONED(testNot<void*>((void*)0xFFFFFF, (void*)(-1)));
+ testNot<void*>((void*)0xFFFFFF, (void*)(-2));
+}
+
+TEST(MemorySanitizer, Shift) {
+ U4 *up = GetPoisoned<U4>();
+ ((U1*)up)[0] = 0;
+ ((U1*)up)[3] = 0xff;
+ v_u4 = *up >> 30;
+ v_u4 = *up >> 24;
+ EXPECT_POISONED(v_u4 = *up >> 23);
+ EXPECT_POISONED(v_u4 = *up >> 10);
+
+ v_u4 = *up << 30;
+ v_u4 = *up << 24;
+ EXPECT_POISONED(v_u4 = *up << 23);
+ EXPECT_POISONED(v_u4 = *up << 10);
+
+ S4 *sp = (S4*)up;
+ v_s4 = *sp >> 30;
+ v_s4 = *sp >> 24;
+ EXPECT_POISONED(v_s4 = *sp >> 23);
+ EXPECT_POISONED(v_s4 = *sp >> 10);
+
+ sp = GetPoisoned<S4>();
+ ((S1*)sp)[1] = 0;
+ ((S1*)sp)[2] = 0;
+ EXPECT_POISONED(v_s4 = *sp >> 31);
+
+ v_s4 = 100;
+ EXPECT_POISONED(v_s4 = v_s4 >> *GetPoisoned<S4>());
+ v_u4 = 100;
+ EXPECT_POISONED(v_u4 = v_u4 >> *GetPoisoned<S4>());
+ v_u4 = 100;
+ EXPECT_POISONED(v_u4 = v_u4 << *GetPoisoned<S4>());
+}
+
+NOINLINE static int GetPoisonedZero() {
+ int *zero = new int;
+ *zero = 0;
+ __msan_poison(zero, sizeof(*zero));
+ int res = *zero;
+ delete zero;
+ return res;
+}
+
+TEST(MemorySanitizer, LoadFromDirtyAddress) {
+ int *a = new int;
+ *a = 0;
+ EXPECT_POISONED(__msan_break_optimization((void*)(U8)a[GetPoisonedZero()]));
+ delete a;
+}
+
+TEST(MemorySanitizer, StoreToDirtyAddress) {
+ int *a = new int;
+ EXPECT_POISONED(a[GetPoisonedZero()] = 0);
+ __msan_break_optimization(a);
+ delete a;
+}
+
+
+NOINLINE void StackTestFunc() {
+ S4 p4;
+ S4 ok4 = 1;
+ S2 p2;
+ S2 ok2 = 1;
+ S1 p1;
+ S1 ok1 = 1;
+ __msan_break_optimization(&p4);
+ __msan_break_optimization(&ok4);
+ __msan_break_optimization(&p2);
+ __msan_break_optimization(&ok2);
+ __msan_break_optimization(&p1);
+ __msan_break_optimization(&ok1);
+
+ EXPECT_POISONED(v_s4 = p4);
+ EXPECT_POISONED(v_s2 = p2);
+ EXPECT_POISONED(v_s1 = p1);
+ v_s1 = ok1;
+ v_s2 = ok2;
+ v_s4 = ok4;
+}
+
+TEST(MemorySanitizer, StackTest) {
+ StackTestFunc();
+}
+
+NOINLINE void StackStressFunc() {
+ int foo[10000];
+ __msan_break_optimization(foo);
+}
+
+TEST(MemorySanitizer, DISABLED_StackStressTest) {
+ for (int i = 0; i < 1000000; i++)
+ StackStressFunc();
+}
+
+template<class T>
+void TestFloatingPoint() {
+ static volatile T v;
+ static T g[100];
+ __msan_break_optimization(&g);
+ T *x = GetPoisoned<T>();
+ T *y = GetPoisoned<T>(1);
+ EXPECT_POISONED(v = *x);
+ EXPECT_POISONED(v_s8 = *x);
+ EXPECT_POISONED(v_s4 = *x);
+ g[0] = *x;
+ g[1] = *x + *y;
+ g[2] = *x - *y;
+ g[3] = *x * *y;
+}
+
+TEST(MemorySanitizer, FloatingPointTest) {
+ TestFloatingPoint<float>();
+ TestFloatingPoint<double>();
+}
+
+TEST(MemorySanitizer, DynMem) {
+ S4 x = 0;
+ S4 *y = GetPoisoned<S4>();
+ memcpy(y, &x, g_one * sizeof(S4));
+ v_s4 = *y;
+}
+
+static char *DynRetTestStr;
+
+TEST(MemorySanitizer, DynRet) {
+ if (!__msan_has_dynamic_component()) return;
+ ReturnPoisoned<S8>();
+ v_s4 = clearenv();
+}
+
+
+TEST(MemorySanitizer, DynRet1) {
+ if (!__msan_has_dynamic_component()) return;
+ ReturnPoisoned<S8>();
+}
+
+struct LargeStruct {
+ S4 x[10];
+};
+
+NOINLINE
+LargeStruct LargeRetTest() {
+ LargeStruct res;
+ res.x[0] = *GetPoisoned<S4>();
+ res.x[1] = *GetPoisoned<S4>();
+ res.x[2] = *GetPoisoned<S4>();
+ res.x[3] = *GetPoisoned<S4>();
+ res.x[4] = *GetPoisoned<S4>();
+ res.x[5] = *GetPoisoned<S4>();
+ res.x[6] = *GetPoisoned<S4>();
+ res.x[7] = *GetPoisoned<S4>();
+ res.x[8] = *GetPoisoned<S4>();
+ res.x[9] = *GetPoisoned<S4>();
+ return res;
+}
+
+TEST(MemorySanitizer, LargeRet) {
+ LargeStruct a = LargeRetTest();
+ EXPECT_POISONED(v_s4 = a.x[0]);
+ EXPECT_POISONED(v_s4 = a.x[9]);
+}
+
+TEST(MemorySanitizer, fread) {
+ char *x = new char[32];
+ FILE *f = fopen("/proc/self/stat", "r");
+ assert(f);
+ fread(x, 1, 32, f);
+ v_s1 = x[0];
+ v_s1 = x[16];
+ v_s1 = x[31];
+ fclose(f);
+ delete x;
+}
+
+TEST(MemorySanitizer, read) {
+ char *x = new char[32];
+ int fd = open("/proc/self/stat", O_RDONLY);
+ assert(fd > 0);
+ int sz = read(fd, x, 32);
+ assert(sz == 32);
+ v_s1 = x[0];
+ v_s1 = x[16];
+ v_s1 = x[31];
+ close(fd);
+ delete x;
+}
+
+TEST(MemorySanitizer, pread) {
+ char *x = new char[32];
+ int fd = open("/proc/self/stat", O_RDONLY);
+ assert(fd > 0);
+ int sz = pread(fd, x, 32, 0);
+ assert(sz == 32);
+ v_s1 = x[0];
+ v_s1 = x[16];
+ v_s1 = x[31];
+ close(fd);
+ delete x;
+}
+
+// FIXME: fails now.
+TEST(MemorySanitizer, DISABLED_ioctl) {
+ struct winsize ws;
+ EXPECT_EQ(ioctl(2, TIOCGWINSZ, &ws), 0);
+ v_s4 = ws.ws_col;
+}
+
+TEST(MemorySanitizer, readlink) {
+ char *x = new char[1000];
+ readlink("/proc/self/exe", x, 1000);
+ v_s1 = x[0];
+ delete [] x;
+}
+
+
+TEST(MemorySanitizer, stat) {
+ struct stat* st = new struct stat;
+ int res = stat("/proc/self/stat", st);
+ assert(!res);
+ v_u8 = st->st_dev;
+ v_u8 = st->st_mode;
+ v_u8 = st->st_size;
+}
+
+TEST(MemorySanitizer, statfs) {
+ struct statfs* st = new struct statfs;
+ int res = statfs("/", st);
+ assert(!res);
+ v_u8 = st->f_type;
+ v_u8 = st->f_bfree;
+ v_u8 = st->f_namelen;
+}
+
+TEST(MemorySanitizer, pipe) {
+ int* pipefd = new int[2];
+ int res = pipe(pipefd);
+ assert(!res);
+ v_u8 = pipefd[0];
+ v_u8 = pipefd[1];
+ close(pipefd[0]);
+ close(pipefd[1]);
+}
+
+TEST(MemorySanitizer, getcwd) {
+ char path[PATH_MAX + 1];
+ char* res = getcwd(path, sizeof(path));
+ assert(res);
+ v_s1 = path[0];
+}
+
+TEST(MemorySanitizer, realpath) {
+ const char* relpath = ".";
+ char path[PATH_MAX + 1];
+ char* res = realpath(relpath, path);
+ assert(res);
+ v_s1 = path[0];
+}
+
+TEST(MemorySanitizer, memcpy) {
+ char* x = new char[2];
+ char* y = new char[2];
+ x[0] = 1;
+ x[1] = *GetPoisoned<char>();
+ memcpy(y, x, 2);
+ v_s4 = y[0];
+ EXPECT_POISONED(v_s4 = y[1]);
+}
+
+TEST(MemorySanitizer, memmove) {
+ char* x = new char[2];
+ char* y = new char[2];
+ x[0] = 1;
+ x[1] = *GetPoisoned<char>();
+ memmove(y, x, 2);
+ v_s4 = y[0];
+ EXPECT_POISONED(v_s4 = y[1]);
+}
+
+TEST(MemorySanitizer, strdup) {
+ char *x = strdup("zzz");
+ v_s1 = *x;
+ free(x);
+}
+
+template<class T, int size>
+void TestOverlapMemmove() {
+ T *x = new T[size];
+ assert(size >= 3);
+ x[2] = 0;
+ memmove(x, x + 1, (size - 1) * sizeof(T));
+ v_s8 = x[1];
+ if (!__msan_has_dynamic_component()) {
+ // FIXME: under DR we will lose this information
+ // because accesses in memmove will unpoisin the shadow.
+ // We need to use our own memove implementation instead of libc's.
+ EXPECT_POISONED(v_s8 = x[0]);
+ EXPECT_POISONED(v_s8 = x[2]);
+ }
+ delete [] x;
+}
+
+TEST(MemorySanitizer, overlap_memmove) {
+ TestOverlapMemmove<U1, 10>();
+ TestOverlapMemmove<U1, 1000>();
+ TestOverlapMemmove<U8, 4>();
+ TestOverlapMemmove<U8, 1000>();
+}
+
+TEST(MemorySanitizer, strcpy) { // NOLINT
+ char* x = new char[3];
+ char* y = new char[3];
+ x[0] = 'a';
+ x[1] = *GetPoisoned<char>(1, 1);
+ x[2] = 0;
+ strcpy(y, x); // NOLINT
+ v_s4 = y[0];
+ EXPECT_POISONED(v_s4 = y[1]);
+ v_s4 = y[2];
+}
+
+TEST(MemorySanitizer, strncpy) { // NOLINT
+ char* x = new char[3];
+ char* y = new char[3];
+ x[0] = 'a';
+ x[1] = *GetPoisoned<char>(1, 1);
+ x[2] = 0;
+ strncpy(y, x, 2); // NOLINT
+ v_s4 = y[0];
+ EXPECT_POISONED(v_s4 = y[1]);
+ EXPECT_POISONED(v_s4 = y[2]);
+}
+
+TEST(MemorySanitizer, strtol) {
+ char *e;
+ assert(1 == strtol("1", &e, 10));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, strtoll) {
+ char *e;
+ assert(1 == strtoll("1", &e, 10));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, strtoul) {
+ char *e;
+ assert(1 == strtoul("1", &e, 10));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, strtoull) {
+ char *e;
+ assert(1 == strtoull("1", &e, 10));
+ v_s8 = (S8) e;
+}
+
+TEST(MemorySanitizer, sprintf) { // NOLINT
+ char buff[10];
+ __msan_break_optimization(buff);
+ EXPECT_POISONED(v_s1 = buff[0]);
+ int res = sprintf(buff, "%d", 1234567); // NOLINT
+ assert(res == 7);
+ assert(buff[0] == '1');
+ assert(buff[1] == '2');
+ assert(buff[2] == '3');
+ assert(buff[6] == '7');
+ assert(buff[7] == 0);
+ EXPECT_POISONED(v_s1 = buff[8]);
+}
+
+TEST(MemorySanitizer, snprintf) {
+ char buff[10];
+ __msan_break_optimization(buff);
+ EXPECT_POISONED(v_s1 = buff[0]);
+ int res = snprintf(buff, sizeof(buff), "%d", 1234567);
+ assert(res == 7);
+ assert(buff[0] == '1');
+ assert(buff[1] == '2');
+ assert(buff[2] == '3');
+ assert(buff[6] == '7');
+ assert(buff[7] == 0);
+ EXPECT_POISONED(v_s1 = buff[8]);
+}
+
+TEST(MemorySanitizer, swprintf) {
+ wchar_t buff[10];
+ assert(sizeof(wchar_t) == 4);
+ __msan_break_optimization(buff);
+ EXPECT_POISONED(v_s1 = buff[0]);
+ int res = swprintf(buff, 9, L"%d", 1234567);
+ assert(res == 7);
+ assert(buff[0] == '1');
+ assert(buff[1] == '2');
+ assert(buff[2] == '3');
+ assert(buff[6] == '7');
+ assert(buff[7] == 0);
+ EXPECT_POISONED(v_s4 = buff[8]);
+}
+
+TEST(MemorySanitizer, wcstombs) {
+ const wchar_t *x = L"abc";
+ char buff[10];
+ int res = wcstombs(buff, x, 4);
+ EXPECT_EQ(res, 3);
+ EXPECT_EQ(buff[0], 'a');
+ EXPECT_EQ(buff[1], 'b');
+ EXPECT_EQ(buff[2], 'c');
+}
+
+TEST(MemorySanitizer, gettimeofday) {
+ struct timeval tv;
+ struct timezone tz;
+ __msan_break_optimization(&tv);
+ __msan_break_optimization(&tz);
+ assert(sizeof(tv) == 16);
+ assert(sizeof(tz) == 8);
+ EXPECT_POISONED(v_s8 = tv.tv_sec);
+ EXPECT_POISONED(v_s8 = tv.tv_usec);
+ EXPECT_POISONED(v_s4 = tz.tz_minuteswest);
+ EXPECT_POISONED(v_s4 = tz.tz_dsttime);
+ assert(0 == gettimeofday(&tv, &tz));
+ v_s8 = tv.tv_sec;
+ v_s8 = tv.tv_usec;
+ v_s4 = tz.tz_minuteswest;
+ v_s4 = tz.tz_dsttime;
+}
+
+TEST(MemorySanitizer, mmap) {
+ const int size = 4096;
+ void *p1, *p2;
+ p1 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ __msan_poison(p1, size);
+ munmap(p1, size);
+ for (int i = 0; i < 1000; i++) {
+ p2 = mmap(0, size, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0);
+ if (p2 == p1)
+ break;
+ else
+ munmap(p2, size);
+ }
+ if (p1 == p2) {
+ v_s1 = *(char*)p2;
+ munmap(p2, size);
+ }
+}
+
+// FIXME: enable and add ecvt.
+// FIXME: check why msandr does nt handle fcvt.
+TEST(MemorySanitizer, fcvt) {
+ int a, b;
+ __msan_break_optimization(&a);
+ __msan_break_optimization(&b);
+ EXPECT_POISONED(v_s4 = a);
+ EXPECT_POISONED(v_s4 = b);
+ char *str = fcvt(12345.6789, 10, &a, &b);
+ v_s4 = a;
+ v_s4 = b;
+}
+
+TEST(MemorySanitizer, LoadUnpoisoned) {
+ S8 s = *GetPoisoned<S8>();
+ EXPECT_POISONED(v_s8 = s);
+ S8 safe = *GetPoisoned<S8>();
+ __msan_load_unpoisoned(&s, sizeof(s), &safe);
+ v_s8 = safe;
+}
+
+struct StructWithDtor {
+ ~StructWithDtor();
+};
+
+NOINLINE StructWithDtor::~StructWithDtor() {
+ __msan_break_optimization(0);
+}
+
+NOINLINE void ExpectGood(int a) { v_s4 = a; }
+NOINLINE void ExpectPoisoned(int a) {
+ EXPECT_POISONED(v_s4 = a);
+}
+
+TEST(MemorySanitizer, Invoke) {
+ StructWithDtor s; // Will cause the calls to become invokes.
+ ExpectGood(0);
+ ExpectPoisoned(*GetPoisoned<int>());
+ ExpectGood(0);
+ ExpectPoisoned(*GetPoisoned<int>());
+ EXPECT_POISONED(v_s4 = ReturnPoisoned<S4>());
+}
+
+TEST(MemorySanitizer, ptrtoint) {
+ // Test that shadow is propagated through pointer-to-integer conversion.
+ void* p = (void*)0xABCD;
+ __msan_poison(((char*)&p) + 1, sizeof(p));
+ v_u1 = (((uptr)p) & 0xFF) == 0;
+
+ void* q = (void*)0xABCD;
+ __msan_poison(&q, sizeof(q) - 1);
+ EXPECT_POISONED(v_u1 = (((uptr)q) & 0xFF) == 0);
+}
+
+static void vaargsfn2(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_d = va_arg(vl, double));
+ va_end(vl);
+}
+
+static void vaargsfn(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ // The following call will overwrite __msan_param_tls.
+ // Checks after it test that arg shadow was somehow saved across the call.
+ vaargsfn2(1, 2, 3, 4, *GetPoisoned<double>());
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgTest) {
+ int* x = GetPoisoned<int>();
+ int* y = GetPoisoned<int>(4);
+ vaargsfn(1, 13, *x, 42, *y);
+}
+
+static void vaargsfn_many(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgManyTest) {
+ int* x = GetPoisoned<int>();
+ int* y = GetPoisoned<int>(4);
+ vaargsfn_many(1, 2, *x, 3, 4, 5, 6, 7, 8, 9, *y);
+}
+
+static void vaargsfn_pass2(va_list vl) {
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+}
+
+static void vaargsfn_pass(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ vaargsfn_pass2(vl);
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgPass) {
+ int* x = GetPoisoned<int>();
+ int* y = GetPoisoned<int>(4);
+ vaargsfn_pass(1, *x, 2, 3, *y);
+}
+
+static void vaargsfn_copy2(va_list vl) {
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+}
+
+static void vaargsfn_copy(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_list vl2;
+ va_copy(vl2, vl);
+ vaargsfn_copy2(vl2);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgCopy) {
+ int* x = GetPoisoned<int>();
+ int* y = GetPoisoned<int>(4);
+ vaargsfn_copy(1, 2, *x, 3, *y);
+}
+
+static void vaargsfn_ptr(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_p = va_arg(vl, int*);
+ EXPECT_POISONED(v_p = va_arg(vl, int*));
+ v_p = va_arg(vl, int*);
+ EXPECT_POISONED(v_p = va_arg(vl, double*));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgPtr) {
+ int** x = GetPoisoned<int*>();
+ double** y = GetPoisoned<double*>(8);
+ int z;
+ vaargsfn_ptr(1, &z, *x, &z, *y);
+}
+
+static void vaargsfn_overflow(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+ v_s4 = va_arg(vl, int);
+
+ v_d = va_arg(vl, double);
+ v_d = va_arg(vl, double);
+ v_d = va_arg(vl, double);
+ EXPECT_POISONED(v_d = va_arg(vl, double));
+ v_d = va_arg(vl, double);
+ EXPECT_POISONED(v_p = va_arg(vl, int*));
+ v_d = va_arg(vl, double);
+ v_d = va_arg(vl, double);
+
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ EXPECT_POISONED(v_d = va_arg(vl, double));
+ EXPECT_POISONED(v_p = va_arg(vl, int*));
+
+ v_s4 = va_arg(vl, int);
+ v_d = va_arg(vl, double);
+ v_p = va_arg(vl, int*);
+
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ EXPECT_POISONED(v_d = va_arg(vl, double));
+ EXPECT_POISONED(v_p = va_arg(vl, int*));
+
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgOverflow) {
+ int* x = GetPoisoned<int>();
+ double* y = GetPoisoned<double>(8);
+ int** p = GetPoisoned<int*>(16);
+ int z;
+ vaargsfn_overflow(1,
+ 1, 2, *x, 4, 5, 6,
+ 1.1, 2.2, 3.3, *y, 5.5, *p, 7.7, 8.8,
+ // the following args will overflow for sure
+ *x, *y, *p,
+ 7, 9.9, &z,
+ *x, *y, *p);
+}
+
+static void vaargsfn_tlsoverwrite2(int guard, ...) {
+ va_list vl;
+ va_start(vl, guard);
+ v_s4 = va_arg(vl, int);
+ va_end(vl);
+}
+
+static void vaargsfn_tlsoverwrite(int guard, ...) {
+ // This call will overwrite TLS contents unless it's backed up somewhere.
+ vaargsfn_tlsoverwrite2(2, 42);
+ va_list vl;
+ va_start(vl, guard);
+ EXPECT_POISONED(v_s4 = va_arg(vl, int));
+ va_end(vl);
+}
+
+TEST(MemorySanitizer, VAArgTLSOverwrite) {
+ int* x = GetPoisoned<int>();
+ vaargsfn_tlsoverwrite(1, *x);
+}
+
+struct StructByVal {
+ int a, b, c, d, e, f;
+};
+
+NOINLINE void StructByValTestFunc(struct StructByVal s) {
+ v_s4 = s.a;
+ EXPECT_POISONED(v_s4 = s.b);
+ v_s4 = s.c;
+ EXPECT_POISONED(v_s4 = s.d);
+ v_s4 = s.e;
+ EXPECT_POISONED(v_s4 = s.f);
+}
+
+NOINLINE void StructByValTestFunc1(struct StructByVal s) {
+ StructByValTestFunc(s);
+}
+
+NOINLINE void StructByValTestFunc2(int z, struct StructByVal s) {
+ StructByValTestFunc(s);
+}
+
+TEST(MemorySanitizer, StructByVal) {
+ // Large aggregates are passed as "byval" pointer argument in LLVM.
+ struct StructByVal s;
+ s.a = 1;
+ s.b = *GetPoisoned<int>();
+ s.c = 2;
+ s.d = *GetPoisoned<int>();
+ s.e = 3;
+ s.f = *GetPoisoned<int>();
+ StructByValTestFunc(s);
+ StructByValTestFunc1(s);
+ StructByValTestFunc2(0, s);
+}
+
+
+#if MSAN_HAS_M128
+NOINLINE __m128i m128Eq(__m128i *a, __m128i *b) { return *a == *b; }
+NOINLINE __m128i m128Lt(__m128i *a, __m128i *b) { return *a < *b; }
+TEST(MemorySanitizer, m128) {
+ __m128i a = _mm_set1_epi16(0x1234);
+ __m128i b = _mm_set1_epi16(0x7890);
+ v_m128 = m128Eq(&a, &b);
+ v_m128 = m128Lt(&a, &b);
+}
+// FIXME: add more tests for __m128i.
+#endif // MSAN_HAS_M128
+
+// We should not complain when copying this poisoned hole.
+struct StructWithHole {
+ U4 a;
+ // 4-byte hole.
+ U8 b;
+};
+
+NOINLINE StructWithHole ReturnStructWithHole() {
+ StructWithHole res;
+ __msan_poison(&res, sizeof(res));
+ res.a = 1;
+ res.b = 2;
+ return res;
+}
+
+TEST(MemorySanitizer, StructWithHole) {
+ StructWithHole a = ReturnStructWithHole();
+ __msan_break_optimization(&a);
+}
+
+template <class T>
+NOINLINE T ReturnStruct() {
+ T res;
+ __msan_poison(&res, sizeof(res));
+ res.a = 1;
+ return res;
+}
+
+template <class T>
+NOINLINE void TestReturnStruct() {
+ T s1 = ReturnStruct<T>();
+ v_s4 = s1.a;
+ EXPECT_POISONED(v_s4 = s1.b);
+}
+
+struct SSS1 {
+ int a, b, c;
+};
+struct SSS2 {
+ int b, a, c;
+};
+struct SSS3 {
+ int b, c, a;
+};
+struct SSS4 {
+ int c, b, a;
+};
+
+struct SSS5 {
+ int a;
+ float b;
+};
+struct SSS6 {
+ int a;
+ double b;
+};
+struct SSS7 {
+ S8 b;
+ int a;
+};
+struct SSS8 {
+ S2 b;
+ S8 a;
+};
+
+TEST(MemorySanitizer, IntStruct3) {
+ TestReturnStruct<SSS1>();
+ TestReturnStruct<SSS2>();
+ TestReturnStruct<SSS3>();
+ TestReturnStruct<SSS4>();
+ TestReturnStruct<SSS5>();
+ TestReturnStruct<SSS6>();
+ TestReturnStruct<SSS7>();
+ TestReturnStruct<SSS8>();
+}
+
+struct LongStruct {
+ U1 a1, b1;
+ U2 a2, b2;
+ U4 a4, b4;
+ U8 a8, b8;
+};
+
+NOINLINE LongStruct ReturnLongStruct1() {
+ LongStruct res;
+ __msan_poison(&res, sizeof(res));
+ res.a1 = res.a2 = res.a4 = res.a8 = 111;
+ // leaves b1, .., b8 poisoned.
+ return res;
+}
+
+NOINLINE LongStruct ReturnLongStruct2() {
+ LongStruct res;
+ __msan_poison(&res, sizeof(res));
+ res.b1 = res.b2 = res.b4 = res.b8 = 111;
+ // leaves a1, .., a8 poisoned.
+ return res;
+}
+
+TEST(MemorySanitizer, LongStruct) {
+ LongStruct s1 = ReturnLongStruct1();
+ __msan_print_shadow(&s1, sizeof(s1));
+ v_u1 = s1.a1;
+ v_u2 = s1.a2;
+ v_u4 = s1.a4;
+ v_u8 = s1.a8;
+
+ EXPECT_POISONED(v_u1 = s1.b1);
+ EXPECT_POISONED(v_u2 = s1.b2);
+ EXPECT_POISONED(v_u4 = s1.b4);
+ EXPECT_POISONED(v_u8 = s1.b8);
+
+ LongStruct s2 = ReturnLongStruct2();
+ __msan_print_shadow(&s2, sizeof(s2));
+ v_u1 = s2.b1;
+ v_u2 = s2.b2;
+ v_u4 = s2.b4;
+ v_u8 = s2.b8;
+
+ EXPECT_POISONED(v_u1 = s2.a1);
+ EXPECT_POISONED(v_u2 = s2.a2);
+ EXPECT_POISONED(v_u4 = s2.a4);
+ EXPECT_POISONED(v_u8 = s2.a8);
+}
+
+TEST(MemorySanitizer, getrlimit) {
+ struct rlimit limit;
+ __msan_poison(&limit, sizeof(limit));
+ int result = getrlimit(RLIMIT_DATA, &limit);
+ assert(result == 0);
+ volatile rlim_t t;
+ t = limit.rlim_cur;
+ t = limit.rlim_max;
+}
+
+static void* SimpleThread_threadfn(void* data) {
+ return new int;
+}
+
+TEST(MemorySanitizer, SimpleThread) {
+ pthread_t t;
+ void* p;
+ int res = pthread_create(&t, NULL, SimpleThread_threadfn, NULL);
+ assert(!res);
+ res = pthread_join(t, &p);
+ assert(!res);
+ if (!__msan_has_dynamic_component()) // FIXME: intercept pthread_join (?).
+ __msan_unpoison(&p, sizeof(p));
+ delete (int*)p;
+}
+
+TEST(MemorySanitizer, uname) {
+ struct utsname u;
+ int res = uname(&u);
+ assert(!res);
+ v_u8 = strlen(u.sysname);
+ v_u8 = strlen(u.nodename);
+ v_u8 = strlen(u.release);
+ v_u8 = strlen(u.version);
+ v_u8 = strlen(u.machine);
+}
+
+template<class T>
+static void testSlt(T value, T shadow) {
+ __msan_partial_poison(&value, &shadow, sizeof(T));
+ volatile bool zzz = true;
+ // This "|| zzz" trick somehow makes LLVM emit "icmp slt" instead of
+ // a shift-and-trunc to get at the highest bit.
+ volatile bool v_T = value < 0 || zzz;
+}
+
+TEST(MemorySanitizer, SignedCompareWithZero) {
+ testSlt<S4>(0xF, 0xF);
+ testSlt<S4>(0xF, 0xFF);
+ testSlt<S4>(0xF, 0xFFFFFF);
+ testSlt<S4>(0xF, 0x7FFFFFF);
+ EXPECT_POISONED(testSlt<S4>(0xF, 0x80FFFFFF));
+ EXPECT_POISONED(testSlt<S4>(0xF, 0xFFFFFFFF));
+}
+
+extern "C" {
+NOINLINE void ZZZZZZZZZZZZZZ() {
+ __msan_break_optimization(0);
+
+ // v_s1 = ReturnPoisoned<S1>();
+ // a_s8[g_zero] = *GetPoisoned<S8>() - 1;
+ // v_s4 = a_s4[g_zero];
+ __msan_break_optimization(0);
+}
+}
+
+TEST(MemorySanitizer, ZZZTest) {
+ ZZZZZZZZZZZZZZ();
+}
+
+TEST(MemorySanitizerDr, StoreInDSOTest) {
+ if (!__msan_has_dynamic_component()) return;
+ char* s = new char[10];
+ dso_memfill(s, 9);
+ v_s1 = s[5];
+ EXPECT_POISONED(v_s1 = s[9]);
+}
+
+int return_poisoned_int() {
+ return ReturnPoisoned<U8>();
+}
+
+TEST(MemorySanitizerDr, ReturnFromDSOTest) {
+ if (!__msan_has_dynamic_component()) return;
+ v_u8 = dso_callfn(return_poisoned_int);
+}
+
+NOINLINE int TrashParamTLS(long long x, long long y, long long z) { //NOLINT
+ EXPECT_POISONED(v_s8 = x);
+ EXPECT_POISONED(v_s8 = y);
+ EXPECT_POISONED(v_s8 = z);
+ return 0;
+}
+
+static int CheckParamTLS(long long x, long long y, long long z) { //NOLINT
+ v_s8 = x;
+ v_s8 = y;
+ v_s8 = z;
+ return 0;
+}
+
+TEST(MemorySanitizerDr, CallFromDSOTest) {
+ if (!__msan_has_dynamic_component()) return;
+ S8* x = GetPoisoned<S8>();
+ S8* y = GetPoisoned<S8>();
+ S8* z = GetPoisoned<S8>();
+ v_s4 = TrashParamTLS(*x, *y, *z);
+ v_u8 = dso_callfn1(CheckParamTLS);
+}
+
+static void StackStoreInDSOFn(int* x, int* y) {
+ v_s4 = *x;
+ v_s4 = *y;
+}
+
+TEST(MemorySanitizerDr, StackStoreInDSOTest) {
+ if (!__msan_has_dynamic_component()) return;
+ dso_stack_store(StackStoreInDSOFn, 1);
+}
+
+TEST(MemorySanitizerOrigins, SetGet) {
+ EXPECT_EQ(TrackingOrigins(), __msan_get_track_origins());
+ if (!TrackingOrigins()) return;
+ int x;
+ __msan_set_origin(&x, sizeof(x), 1234);
+ EXPECT_EQ(1234, __msan_get_origin(&x));
+ __msan_set_origin(&x, sizeof(x), 5678);
+ EXPECT_EQ(5678, __msan_get_origin(&x));
+ __msan_set_origin(&x, sizeof(x), 0);
+ EXPECT_EQ(0, __msan_get_origin(&x));
+}
+
+namespace {
+struct S {
+ U4 dummy;
+ U2 a;
+ U2 b;
+};
+
+// http://code.google.com/p/memory-sanitizer/issues/detail?id=6
+TEST(MemorySanitizerOrigins, DISABLED_InitializedStoreDoesNotChangeOrigin) {
+ if (!TrackingOrigins()) return;
+
+ S s;
+ u32 origin = rand(); // NOLINT
+ s.a = *GetPoisonedO<U2>(0, origin);
+ EXPECT_EQ(origin, __msan_get_origin(&s.a));
+ EXPECT_EQ(origin, __msan_get_origin(&s.b));
+
+ s.b = 42;
+ EXPECT_EQ(origin, __msan_get_origin(&s.a));
+ EXPECT_EQ(origin, __msan_get_origin(&s.b));
+}
+} // namespace
+
+template<class T, class BinaryOp>
+INLINE
+void BinaryOpOriginTest(BinaryOp op) {
+ u32 ox = rand(); //NOLINT
+ u32 oy = rand(); //NOLINT
+ T *x = GetPoisonedO<T>(0, ox, 0);
+ T *y = GetPoisonedO<T>(1, oy, 0);
+ T *z = GetPoisonedO<T>(2, 0, 0);
+
+ *z = op(*x, *y);
+ u32 origin = __msan_get_origin(z);
+ EXPECT_POISONED_O(v_s8 = *z, origin);
+ EXPECT_EQ(true, origin == ox || origin == oy);
+
+ // y is poisoned, x is not.
+ *x = 10101;
+ *y = *GetPoisonedO<T>(1, oy);
+ __msan_break_optimization(x);
+ __msan_set_origin(z, sizeof(*z), 0);
+ *z = op(*x, *y);
+ EXPECT_POISONED_O(v_s8 = *z, oy);
+ EXPECT_EQ(__msan_get_origin(z), oy);
+
+ // x is poisoned, y is not.
+ *x = *GetPoisonedO<T>(0, ox);
+ *y = 10101010;
+ __msan_break_optimization(y);
+ __msan_set_origin(z, sizeof(*z), 0);
+ *z = op(*x, *y);
+ EXPECT_POISONED_O(v_s8 = *z, ox);
+ EXPECT_EQ(__msan_get_origin(z), ox);
+}
+
+template<class T> INLINE T XOR(const T &a, const T&b) { return a ^ b; }
+template<class T> INLINE T ADD(const T &a, const T&b) { return a + b; }
+template<class T> INLINE T SUB(const T &a, const T&b) { return a - b; }
+template<class T> INLINE T MUL(const T &a, const T&b) { return a * b; }
+template<class T> INLINE T AND(const T &a, const T&b) { return a & b; }
+template<class T> INLINE T OR (const T &a, const T&b) { return a | b; }
+
+TEST(MemorySanitizerOrigins, BinaryOp) {
+ if (!TrackingOrigins()) return;
+ BinaryOpOriginTest<S8>(XOR<S8>);
+ BinaryOpOriginTest<U8>(ADD<U8>);
+ BinaryOpOriginTest<S4>(SUB<S4>);
+ BinaryOpOriginTest<S4>(MUL<S4>);
+ BinaryOpOriginTest<U4>(OR<U4>);
+ BinaryOpOriginTest<U4>(AND<U4>);
+ BinaryOpOriginTest<double>(ADD<U4>);
+ BinaryOpOriginTest<float>(ADD<S4>);
+ BinaryOpOriginTest<double>(ADD<double>);
+ BinaryOpOriginTest<float>(ADD<double>);
+}
+
+TEST(MemorySanitizerOrigins, Unary) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s4 = *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s2 = *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s1 = *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s4 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s2 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s1 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<U4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s4 = *GetPoisonedO<U4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s2 = *GetPoisonedO<U4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s1 = *GetPoisonedO<U4>(0, __LINE__), __LINE__);
+
+ EXPECT_POISONED_O(v_u8 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_u4 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_u2 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_u1 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+
+ EXPECT_POISONED_O(v_p = (void*)*GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_u8 = (U8)*GetPoisonedO<void*>(0, __LINE__), __LINE__);
+}
+
+TEST(MemorySanitizerOrigins, EQ) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_u1 = *GetPoisonedO<S4>(0, __LINE__) <= 11, __LINE__);
+ EXPECT_POISONED_O(v_u1 = *GetPoisonedO<S4>(0, __LINE__) == 11, __LINE__);
+ EXPECT_POISONED_O(v_u1 = *GetPoisonedO<float>(0, __LINE__) == 1.1, __LINE__);
+}
+
+TEST(MemorySanitizerOrigins, DIV) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_u8 = *GetPoisonedO<U8>(0, __LINE__) / 100, __LINE__);
+ EXPECT_POISONED_O(v_s4 = 100 / *GetPoisonedO<S4>(0, __LINE__, 1), __LINE__);
+}
+
+TEST(MemorySanitizerOrigins, SHIFT) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_u8 = *GetPoisonedO<U8>(0, __LINE__) >> 10, __LINE__);
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S8>(0, __LINE__) >> 10, __LINE__);
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S8>(0, __LINE__) << 10, __LINE__);
+ EXPECT_POISONED_O(v_u8 = 10U << *GetPoisonedO<U8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s8 = -10 >> *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+ EXPECT_POISONED_O(v_s8 = -10 << *GetPoisonedO<S8>(0, __LINE__), __LINE__);
+}
+
+template<class T, int N>
+void MemCpyTest() {
+ int ox = __LINE__;
+ T *x = new T[N];
+ T *y = new T[N];
+ T *z = new T[N];
+ __msan_poison(x, N * sizeof(T));
+ __msan_set_origin(x, N * sizeof(T), ox);
+ __msan_set_origin(y, N * sizeof(T), 777777);
+ __msan_set_origin(z, N * sizeof(T), 888888);
+ v_p = x;
+ memcpy(y, v_p, N * sizeof(T));
+ EXPECT_POISONED_O(v_s1 = y[0], ox);
+ EXPECT_POISONED_O(v_s1 = y[N/2], ox);
+ EXPECT_POISONED_O(v_s1 = y[N-1], ox);
+ v_p = x;
+ memmove(z, v_p, N * sizeof(T));
+ EXPECT_POISONED_O(v_s1 = z[0], ox);
+ EXPECT_POISONED_O(v_s1 = z[N/2], ox);
+ EXPECT_POISONED_O(v_s1 = z[N-1], ox);
+}
+
+TEST(MemorySanitizerOrigins, LargeMemCpy) {
+ if (!TrackingOrigins()) return;
+ MemCpyTest<U1, 10000>();
+ MemCpyTest<U8, 10000>();
+}
+
+TEST(MemorySanitizerOrigins, SmallMemCpy) {
+ if (!TrackingOrigins()) return;
+ MemCpyTest<U8, 1>();
+ MemCpyTest<U8, 2>();
+ MemCpyTest<U8, 3>();
+}
+
+TEST(MemorySanitizerOrigins, Select) {
+ if (!TrackingOrigins()) return;
+ v_s8 = g_one ? 1 : *GetPoisonedO<S4>(0, __LINE__);
+ EXPECT_POISONED_O(v_s8 = *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+ S4 x;
+ __msan_break_optimization(&x);
+ x = g_1 ? *GetPoisonedO<S4>(0, __LINE__) : 0;
+
+ EXPECT_POISONED_O(v_s8 = g_1 ? *GetPoisonedO<S4>(0, __LINE__) : 1, __LINE__);
+ EXPECT_POISONED_O(v_s8 = g_0 ? 1 : *GetPoisonedO<S4>(0, __LINE__), __LINE__);
+}
+
+extern "C"
+NOINLINE void AllocaTOTest() {
+ int ar[100];
+ __msan_break_optimization(ar);
+ v_s8 = ar[10];
+ // fprintf(stderr, "Descr: %s\n",
+ // __msan_get_origin_descr_if_stack(__msan_get_origin_tls()));
+}
+
+TEST(MemorySanitizerOrigins, Alloca) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_S(AllocaTOTest(), "ar@AllocaTOTest");
+ EXPECT_POISONED_S(AllocaTOTest(), "ar@AllocaTOTest");
+ EXPECT_POISONED_S(AllocaTOTest(), "ar@AllocaTOTest");
+ EXPECT_POISONED_S(AllocaTOTest(), "ar@AllocaTOTest");
+}
+
+// FIXME: replace with a lit-like test.
+TEST(MemorySanitizerOrigins, DISABLED_AllocaDeath) {
+ if (!TrackingOrigins()) return;
+ EXPECT_DEATH(AllocaTOTest(), "ORIGIN: stack allocation: ar@AllocaTOTest");
+}
+
+NOINLINE int RetvalOriginTest(u32 origin) {
+ int *a = new int;
+ __msan_break_optimization(a);
+ __msan_set_origin(a, sizeof(*a), origin);
+ int res = *a;
+ delete a;
+ return res;
+}
+
+TEST(MemorySanitizerOrigins, Retval) {
+ if (!TrackingOrigins()) return;
+ EXPECT_POISONED_O(v_s4 = RetvalOriginTest(__LINE__), __LINE__);
+}
+
+NOINLINE void ParamOriginTest(int param, u32 origin) {
+ EXPECT_POISONED_O(v_s4 = param, origin);
+}
+
+TEST(MemorySanitizerOrigins, Param) {
+ if (!TrackingOrigins()) return;
+ int *a = new int;
+ u32 origin = __LINE__;
+ __msan_break_optimization(a);
+ __msan_set_origin(a, sizeof(*a), origin);
+ ParamOriginTest(*a, origin);
+ delete a;
+}
+
+TEST(MemorySanitizerOrigins, Invoke) {
+ if (!TrackingOrigins()) return;
+ StructWithDtor s; // Will cause the calls to become invokes.
+ EXPECT_POISONED_O(v_s4 = RetvalOriginTest(__LINE__), __LINE__);
+}
+
+TEST(MemorySanitizerOrigins, strlen) {
+ S8 alignment;
+ __msan_break_optimization(&alignment);
+ char x[4] = {'a', 'b', 0, 0};
+ __msan_poison(&x[2], 1);
+ u32 origin = __LINE__;
+ __msan_set_origin(x, sizeof(x), origin);
+ EXPECT_POISONED_O(v_s4 = strlen(x), origin);
+}
+
+TEST(MemorySanitizerOrigins, wcslen) {
+ wchar_t w[3] = {'a', 'b', 0};
+ u32 origin = __LINE__;
+ __msan_set_origin(w, sizeof(w), origin);
+ __msan_poison(&w[2], sizeof(wchar_t));
+ EXPECT_POISONED_O(v_s4 = wcslen(w), origin);
+}
+
+#if MSAN_HAS_M128
+TEST(MemorySanitizerOrigins, StoreIntrinsic) {
+ __m128 x, y;
+ u32 origin = __LINE__;
+ __msan_set_origin(&x, sizeof(x), origin);
+ __msan_poison(&x, sizeof(x));
+ __builtin_ia32_storeups((float*)&y, x);
+ EXPECT_POISONED_O(v_m128 = y, origin);
+}
+#endif
+
+NOINLINE void RecursiveMalloc(int depth) {
+ static int count;
+ count++;
+ if ((count % (1024 * 1024)) == 0)
+ printf("RecursiveMalloc: %d\n", count);
+ int *x1 = new int;
+ int *x2 = new int;
+ __msan_break_optimization(x1);
+ __msan_break_optimization(x2);
+ if (depth > 0) {
+ RecursiveMalloc(depth-1);
+ RecursiveMalloc(depth-1);
+ }
+ delete x1;
+ delete x2;
+}
+
+TEST(MemorySanitizerStress, DISABLED_MallocStackTrace) {
+ RecursiveMalloc(22);
+}
+
+int main(int argc, char **argv) {
+ __msan_set_poison_in_malloc(1);
+ testing::InitGoogleTest(&argc, argv);
+ int res = RUN_ALL_TESTS();
+ return res;
+}
diff --git a/lib/msan/tests/msandr_test_so.cc b/lib/msan/tests/msandr_test_so.cc
new file mode 100644
index 000000000..fddd8ded8
--- /dev/null
+++ b/lib/msan/tests/msandr_test_so.cc
@@ -0,0 +1,36 @@
+//===-- msandr_test_so.cc ------------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// MemorySanitizer unit tests.
+//===----------------------------------------------------------------------===//
+
+#include "msandr_test_so.h"
+
+void dso_memfill(char* s, unsigned n) {
+ for (unsigned i = 0; i < n; ++i)
+ s[i] = i;
+}
+
+int dso_callfn(int (*fn)(void)) {
+ volatile int x = fn();
+ return x;
+}
+
+int dso_callfn1(int (*fn)(long long, long long, long long)) { //NOLINT
+ volatile int x = fn(1, 2, 3);
+ return x;
+}
+
+int dso_stack_store(void (*fn)(int*, int*), int x) {
+ int y = x + 1;
+ fn(&x, &y);
+ return y;
+}
diff --git a/lib/msan/tests/msandr_test_so.h b/lib/msan/tests/msandr_test_so.h
new file mode 100644
index 000000000..6119542ee
--- /dev/null
+++ b/lib/msan/tests/msandr_test_so.h
@@ -0,0 +1,23 @@
+//===-- msandr_test_so.h ----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file is a part of MemorySanitizer.
+//
+// MemorySanitizer unit tests.
+//===----------------------------------------------------------------------===//
+
+#ifndef MSANDR_MSANDR_TEST_SO_H
+#define MSANDR_MSANDR_TEST_SO_H
+
+void dso_memfill(char* s, unsigned n);
+int dso_callfn(int (*fn)(void));
+int dso_callfn1(int (*fn)(long long, long long, long long)); //NOLINT
+int dso_stack_store(void (*fn)(int*, int*), int x);
+
+#endif