From d5feab0c53d473e561035b080dc47c5ad5891334 Mon Sep 17 00:00:00 2001 From: Ryan Macnak Date: Mon, 7 Oct 2019 16:24:22 +0000 Subject: [PATCH] [vm] Create builds for LeakSanitizer, MemorySanitizer and ThreadSanitizer. Change-Id: I65905ec76fcde8b7f4063cb5b80a3d034b453153 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/120323 Commit-Queue: Ryan Macnak Reviewed-by: Chinmay Garde Reviewed-by: Siva Annamalai --- build/config/linux/BUILD.gn | 2 +- build/config/sanitizers/sanitizers.gni | 2 +- runtime/BUILD.gn | 3 + runtime/platform/memory_sanitizer.h | 8 ++ .../vm/dart/transferable_throws_oom_test.dart | 5 + runtime/vm/compiler/backend/il.cc | 4 + runtime/vm/heap/scavenger.cc | 9 +- runtime/vm/interpreter.h | 2 +- tests/standalone_2/io/file_error2_test.dart | 1 + tools/bots/test_matrix.json | 108 +++++++++++++++++- tools/gn.py | 15 ++- 11 files changed, 151 insertions(+), 8 deletions(-) diff --git a/build/config/linux/BUILD.gn b/build/config/linux/BUILD.gn index 141ea689b88..04e5808f471 100644 --- a/build/config/linux/BUILD.gn +++ b/build/config/linux/BUILD.gn @@ -16,7 +16,7 @@ config("sdk") { "-Wl,--exclude-libs=libc++.a", ] - if (is_asan) { + if (is_asan || is_lsan || is_msan || is_tsan) { ldflags += [ "-lrt" ] } diff --git a/build/config/sanitizers/sanitizers.gni b/build/config/sanitizers/sanitizers.gni index a7b965824a7..a66b043528e 100644 --- a/build/config/sanitizers/sanitizers.gni +++ b/build/config/sanitizers/sanitizers.gni @@ -6,7 +6,7 @@ declare_args() { # Use libc++ (buildtools/third_party/libc++ and # buildtools/third_party/libc++abi) instead of stdlibc++ as standard library. # This is intended to be used for instrumented builds. - use_custom_libcxx = (is_asan && is_linux) || is_tsan || is_msan + use_custom_libcxx = (is_asan && is_linux) || is_lsan || is_msan || is_tsan # Track where uninitialized memory originates from. From fastest to slowest: # 0 - no tracking, 1 - track only the initial allocation site, 2 - track the diff --git a/runtime/BUILD.gn b/runtime/BUILD.gn index 41776f581b6..56eb2f6155a 100644 --- a/runtime/BUILD.gn +++ b/runtime/BUILD.gn @@ -186,6 +186,9 @@ config("dart_config") { if (defined(is_asan) && is_asan) { ldflags += [ "-fsanitize=address" ] } + if (defined(is_lsan) && is_lsan) { + ldflags += [ "-fsanitize=leak" ] + } if (defined(is_msan) && is_msan) { ldflags += [ "-fsanitize=memory" ] } diff --git a/runtime/platform/memory_sanitizer.h b/runtime/platform/memory_sanitizer.h index 324d73e37ff..f9e6f0d39cc 100644 --- a/runtime/platform/memory_sanitizer.h +++ b/runtime/platform/memory_sanitizer.h @@ -11,16 +11,24 @@ // told about areas that are initialized by generated code. #if defined(__has_feature) #if __has_feature(memory_sanitizer) +extern "C" void __msan_poison(const volatile void*, size_t); extern "C" void __msan_unpoison(const volatile void*, size_t); +#define MSAN_POISON(ptr, len) __msan_poison(ptr, len) #define MSAN_UNPOISON(ptr, len) __msan_unpoison(ptr, len) #define NO_SANITIZE_MEMORY __attribute__((no_sanitize_memory)) #else // __has_feature(memory_sanitizer) +#define MSAN_POISON(ptr, len) \ + do { \ + } while (false && (ptr) == 0 && (len) == 0) #define MSAN_UNPOISON(ptr, len) \ do { \ } while (false && (ptr) == 0 && (len) == 0) #define NO_SANITIZE_MEMORY #endif // __has_feature(memory_sanitizer) #else // defined(__has_feature) +#define MSAN_POISON(ptr, len) \ + do { \ + } while (false && (ptr) == 0 && (len) == 0) #define MSAN_UNPOISON(ptr, len) \ do { \ } while (false && (ptr) == 0 && (len) == 0) diff --git a/runtime/tests/vm/dart/transferable_throws_oom_test.dart b/runtime/tests/vm/dart/transferable_throws_oom_test.dart index c1b21f06f4a..fcf9e089fc1 100644 --- a/runtime/tests/vm/dart/transferable_throws_oom_test.dart +++ b/runtime/tests/vm/dart/transferable_throws_oom_test.dart @@ -2,6 +2,11 @@ // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. +// Customize ASAN options for this test with 'allocator_may_return_null=1' as +// it tries to allocate a large memory buffer. +// Environment=ASAN_OPTIONS=handle_segv=0:detect_stack_use_after_return=1:allocator_may_return_null=1 +// Environment=MSAN_OPTIONS=handle_segv=0:detect_stack_use_after_return=1:allocator_may_return_null=1 + // Test that ensures correct exception when running out of memory for // really large transferable. diff --git a/runtime/vm/compiler/backend/il.cc b/runtime/vm/compiler/backend/il.cc index b7adcf953c6..61eb9779cc2 100644 --- a/runtime/vm/compiler/backend/il.cc +++ b/runtime/vm/compiler/backend/il.cc @@ -283,6 +283,10 @@ void HierarchyInfo::BuildRangesForJIT(ClassTable* table, qsort(cids_array, cids.length(), sizeof(intptr_t), [](const void* a, const void* b) { + // MSAN seems unaware of allocations inside qsort. The linker flag + // -fsanitize=memory should give us a MSAN-aware version of libc... + MSAN_UNPOISON(static_cast(a), sizeof(intptr_t)); + MSAN_UNPOISON(static_cast(b), sizeof(intptr_t)); return static_cast(*static_cast(a) - *static_cast(b)); }); diff --git a/runtime/vm/heap/scavenger.cc b/runtime/vm/heap/scavenger.cc index b92a8702e4c..e3be0fbe75c 100644 --- a/runtime/vm/heap/scavenger.cc +++ b/runtime/vm/heap/scavenger.cc @@ -375,6 +375,8 @@ SemiSpace* SemiSpace::New(intptr_t size_in_words, const char* name) { #ifdef DEBUG result->reserved_->Protect(VirtualMemory::kReadWrite); #endif + // Initialized by generated code. + MSAN_UNPOISON(result->reserved_->address(), size_in_words << kWordSizeLog2); return result; } @@ -392,18 +394,21 @@ SemiSpace* SemiSpace::New(intptr_t size_in_words, const char* name) { #if defined(DEBUG) memset(memory->address(), Heap::kZapByte, size_in_bytes); #endif // defined(DEBUG) + // Initialized by generated code. + MSAN_UNPOISON(memory->address(), size_in_bytes); return new SemiSpace(memory); } } void SemiSpace::Delete() { -#ifdef DEBUG if (reserved_ != nullptr) { const intptr_t size_in_bytes = size_in_words() << kWordSizeLog2; +#ifdef DEBUG memset(reserved_->address(), Heap::kZapByte, size_in_bytes); reserved_->Protect(VirtualMemory::kNoAccess); - } #endif + MSAN_POISON(reserved_->address(), size_in_bytes); + } SemiSpace* old_cache = nullptr; { MutexLocker locker(mutex_); diff --git a/runtime/vm/interpreter.h b/runtime/vm/interpreter.h index e7fa7d62042..c7e34450f11 100644 --- a/runtime/vm/interpreter.h +++ b/runtime/vm/interpreter.h @@ -263,7 +263,7 @@ class Interpreter { } #ifndef PRODUCT - bool is_debugging_; + bool is_debugging_ = false; #endif // !PRODUCT bool supports_unboxed_doubles_; diff --git a/tests/standalone_2/io/file_error2_test.dart b/tests/standalone_2/io/file_error2_test.dart index c282d038a9a..6e21417d1c8 100644 --- a/tests/standalone_2/io/file_error2_test.dart +++ b/tests/standalone_2/io/file_error2_test.dart @@ -7,6 +7,7 @@ // Customize ASAN options for this test with 'allocator_may_return_null=1' as // it tries to allocate a large memory buffer. // Environment=ASAN_OPTIONS=handle_segv=0:detect_stack_use_after_return=1:allocator_may_return_null=1 +// Environment=MSAN_OPTIONS=handle_segv=0:detect_stack_use_after_return=1:allocator_may_return_null=1 import "dart:io"; diff --git a/tools/bots/test_matrix.json b/tools/bots/test_matrix.json index 36a72116e5b..0cc4ec94215 100644 --- a/tools/bots/test_matrix.json +++ b/tools/bots/test_matrix.json @@ -285,6 +285,21 @@ "builder-tag": "asan", "timeout": 240 }}, + "dartk-lsan-(linux|mac|win)-(debug|product|release)-(ia32|x64)": { + "options": { + "builder-tag": "lsan", + "timeout": 240 + }}, + "dartk-msan-(linux|mac|win)-(debug|product|release)-(ia32|x64)": { + "options": { + "builder-tag": "msan", + "timeout": 240 + }}, + "dartk-tsan-(linux|mac|win)-(debug|product|release)-x64": { + "options": { + "builder-tag": "tsan", + "timeout": 240 + }}, "dart2js-(linux|mac|win)-chrome": { "options": { "use-sdk": true @@ -788,7 +803,7 @@ "arguments": ["runtime"], "environment": { "DART_USE_ASAN": 1, - "ASAN_OPTIONS": "handle_segv=0:detect_leaks=1:detect_stack_use_after_return=0:disable_coredump=0", + "ASAN_OPTIONS": "handle_segv=0:detect_leaks=1:detect_stack_use_after_return=0:disable_coredump=0:abort_on_error=1", "ASAN_SYMBOLIZER_PATH": "buildtools/linux-x64/clang/bin/llvm-symbolizer" } }, @@ -797,12 +812,101 @@ "arguments": [ "-ndartk-asan-linux-release-${arch}"], "environment": { - "ASAN_OPTIONS": "handle_segv=0:detect_leaks=1:detect_stack_use_after_return=0:disable_coredump=0", + "ASAN_OPTIONS": "handle_segv=0:detect_leaks=1:detect_stack_use_after_return=0:disable_coredump=0:abort_on_error=1", "ASAN_SYMBOLIZER_PATH": "buildtools/linux-x64/clang/bin/llvm-symbolizer" } } ] }, + { + "builders": [ + "vm-kernel-lsan-linux-release-ia32", + "vm-kernel-lsan-linux-release-x64" + ], + "meta": { + "description": "This configuration is used by the vm builders with leak sanitizing (lsan). We have to run gn.py with the DART_USE_LSAN options, which we do by running generate_buildfiles." + }, + "steps": [ + { + "name": "build dart", + "script": "tools/build.py", + "arguments": ["runtime"], + "environment": { + "DART_USE_LSAN": 1, + "ASAN_OPTIONS": "handle_segv=0:detect_leaks=1:detect_stack_use_after_return=0:disable_coredump=0:abort_on_error=1", + "ASAN_SYMBOLIZER_PATH": "buildtools/linux-x64/clang/bin/llvm-symbolizer" + } + }, + { + "name": "vm tests", + "arguments": [ + "-ndartk-lsan-linux-release-${arch}"], + "environment": { + "ASAN_OPTIONS": "handle_segv=0:detect_leaks=1:detect_stack_use_after_return=0:disable_coredump=0:abort_on_error=1", + "ASAN_SYMBOLIZER_PATH": "buildtools/linux-x64/clang/bin/llvm-symbolizer" + } + } + ] + }, + { + "builders": [ + "vm-kernel-msan-linux-release-ia32", + "vm-kernel-msan-linux-release-x64" + ], + "meta": { + "description": "This configuration is used by the vm builders with memory sanitizing (msan). We have to run gn.py with the DART_USE_MSAN options, which we do by running generate_buildfiles." + }, + "steps": [ + { + "name": "build dart", + "script": "tools/build.py", + "arguments": ["runtime"], + "environment": { + "DART_USE_MSAN": 1, + "MSAN_OPTIONS": "handle_segv=0:detect_leaks=1:detect_stack_use_after_return=0:disable_coredump=0:abort_on_error=1", + "MSAN_SYMBOLIZER_PATH": "buildtools/linux-x64/clang/bin/llvm-symbolizer" + } + }, + { + "name": "vm tests", + "arguments": [ + "-ndartk-msan-linux-release-${arch}"], + "environment": { + "MSAN_OPTIONS": "handle_segv=0:detect_leaks=1:detect_stack_use_after_return=0:disable_coredump=0:abort_on_error=1", + "MSAN_SYMBOLIZER_PATH": "buildtools/linux-x64/clang/bin/llvm-symbolizer" + } + } + ] + }, + { + "builders": [ + "vm-kernel-tsan-linux-release-x64" + ], + "meta": { + "description": "This configuration is used by the vm builders with thread sanitizing (tsan). We have to run gn.py with the DART_USE_TSAN options, which we do by running generate_buildfiles." + }, + "steps": [ + { + "name": "build dart", + "script": "tools/build.py", + "arguments": ["runtime"], + "environment": { + "DART_USE_TSAN": 1, + "TSAN_OPTIONS": "handle_segv=0:disable_coredump=0:abort_on_error=1", + "TSAN_SYMBOLIZER_PATH": "buildtools/linux-x64/clang/bin/llvm-symbolizer" + } + }, + { + "name": "vm tests", + "arguments": [ + "-ndartk-tsan-linux-release-${arch}"], + "environment": { + "TSAN_OPTIONS": "handle_segv=0:disable_coredump=0:abort_on_error=1", + "TSAN_SYMBOLIZER_PATH": "buildtools/linux-x64/clang/bin/llvm-symbolizer" + } + } + ] + }, { "builders": [ "vm-kernel-linux-release-simarm", diff --git a/tools/gn.py b/tools/gn.py index 0d5ac797b67..14f5a4b5d42 100755 --- a/tools/gn.py +++ b/tools/gn.py @@ -20,6 +20,7 @@ GN = os.path.join(DART_ROOT, 'buildtools', 'gn') # Environment variables for default settings. DART_USE_ASAN = "DART_USE_ASAN" # Use instead of --asan +DART_USE_LSAN = "DART_USE_LSAN" # Use instead of --lsan DART_USE_MSAN = "DART_USE_MSAN" # Use instead of --msan DART_USE_TSAN = "DART_USE_TSAN" # Use instead of --tsan DART_USE_TOOLCHAIN = "DART_USE_TOOLCHAIN" # Use instread of --toolchain-prefix @@ -35,6 +36,10 @@ def UseASAN(): return DART_USE_ASAN in os.environ +def UseLSAN(): + return DART_USE_LSAN in os.environ + + def UseMSAN(): return DART_USE_MSAN in os.environ @@ -152,7 +157,7 @@ def ParseStringMap(key, string_map): def UseSanitizer(args): - return args.asan or args.msan or args.tsan + return args.asan or args.lsan or args.msan or args.tsan def DontUseClang(args, target_os, host_cpu, target_cpu): @@ -249,6 +254,7 @@ def ToGnArgs(args, mode, arch, target_os, use_nnbd): gn_args['dart_vm_code_coverage'] = enable_code_coverage gn_args['is_asan'] = args.asan and gn_args['is_clang'] + gn_args['is_lsan'] = args.lsan and gn_args['is_clang'] gn_args['is_msan'] = args.msan and gn_args['is_clang'] gn_args['is_tsan'] = args.tsan and gn_args['is_clang'] @@ -460,6 +466,13 @@ def parse_args(args): default=False, dest='exclude_kernel_service', action='store_true') + other_group.add_argument( + '--lsan', + help='Build with LSAN', + default=UseLSAN(), + action='store_true') + other_group.add_argument( + '--no-lsan', help='Disable LSAN', dest='lsan', action='store_false') other_group.add_argument( '--msan', help='Build with MSAN',