Fix `lsan` test suite
diff --git a/emcc.py b/emcc.py index 7ae2a2b..83ebb31 100755 --- a/emcc.py +++ b/emcc.py
@@ -2134,12 +2134,18 @@ else: settings.UBSAN_RUNTIME = 2 - if 'leak' in sanitize: - settings.USE_LSAN = 1 - default_setting('EXIT_RUNTIME', 1) - - if settings.LINKABLE: - exit_with_error('LSan does not support dynamic linking') + sanitizer_mem = 0 + if sanitize: + # sanitizers do at least 9 page allocs of a single page during startup. + sanitizer_mem += webassembly.WASM_PAGE_SIZE * 9 + sanitizer_mem += 2097152 * 10 + # we also allocate at least 11 "regions". Each region is kRegionSize (2 << 20) but + # MmapAlignedOrDieOnFatalError adds another 2 << 20 for alignment. + sanitizer_mem += (1 << 21) * 11 + # When running in the threaded mode asan needs to allocate an array of kMaxNumberOfThreads + # (1 << 22) pointers. See compiler-rt/lib/asan/asan_thread.cpp. + if settings.USE_PTHREADS: + sanitizer_mem += (1 << 22) * 4 if 'address' in sanitize: settings.USE_ASAN = 1 @@ -2213,6 +2219,18 @@ if settings.LINKABLE: exit_with_error('ASan does not support dynamic linking') + elif 'leak' in sanitize: + settings.USE_LSAN = 1 + default_setting('EXIT_RUNTIME', 1) + + if settings.LINKABLE: + exit_with_error('LSan does not support dynamic linking') + + if sanitizer_mem: + # Increase the size of the initial memory according to how much memory + # we think the sanitizers will use. + logger.debug(f'adding {sanitizer_mem} bytes of memory for sanitizer support') + settings.INITIAL_MEMORY += sanitizer_mem if sanitize and settings.GENERATE_SOURCE_MAP: settings.LOAD_SOURCE_MAP = 1
diff --git a/system/lib/compiler-rt/lib/lsan/lsan_interceptors.cpp b/system/lib/compiler-rt/lib/lsan/lsan_interceptors.cpp index e4e1d75..aa5e4dc 100644 --- a/system/lib/compiler-rt/lib/lsan/lsan_interceptors.cpp +++ b/system/lib/compiler-rt/lib/lsan/lsan_interceptors.cpp
@@ -394,6 +394,7 @@ #endif #if SANITIZER_EMSCRIPTEN +#define __ATTRP_C11_THREAD ((void*)(uptr)-1) extern "C" { int emscripten_builtin_pthread_create(void *thread, void *attr, void *(*callback)(void *), void *arg); @@ -452,7 +453,7 @@ ENSURE_LSAN_INITED; EnsureMainThreadIDIsCorrect(); __sanitizer_pthread_attr_t myattr; - if (!attr) { + if (!attr || attr == __ATTRP_C11_THREAD) { pthread_attr_init(&myattr); attr = &myattr; }
diff --git a/system/lib/compiler-rt/lib/sanitizer_common/sanitizer_emscripten.cpp b/system/lib/compiler-rt/lib/sanitizer_common/sanitizer_emscripten.cpp index 785ebf9..4feac4a 100644 --- a/system/lib/compiler-rt/lib/sanitizer_common/sanitizer_emscripten.cpp +++ b/system/lib/compiler-rt/lib/sanitizer_common/sanitizer_emscripten.cpp
@@ -60,14 +60,7 @@ void ListOfModules::fallbackInit() { clear(); } -SANITIZER_WEAK_ATTRIBUTE int -real_sigaction(int signum, const void *act, void *oldact); - int internal_sigaction(int signum, const void *act, void *oldact) { -#if !SANITIZER_GO - if (&real_sigaction) - return real_sigaction(signum, act, oldact); -#endif return sigaction(signum, (const struct sigaction *)act, (struct sigaction *)oldact); }
diff --git a/tests/core/test_dynamic_cast.cpp b/tests/core/test_dynamic_cast.cpp index fc0d64c..2b59957 100644 --- a/tests/core/test_dynamic_cast.cpp +++ b/tests/core/test_dynamic_cast.cpp
@@ -16,4 +16,6 @@ int main() { Support* p = new Derived; dynamic_cast<Derived*>(p)->f(); + delete p; + return 0; }
diff --git a/tests/core/test_em_asm_2.cpp b/tests/core/test_em_asm_2.cpp index 91e40e3..92721ad 100644 --- a/tests/core/test_em_asm_2.cpp +++ b/tests/core/test_em_asm_2.cpp
@@ -102,11 +102,11 @@ i = EM_ASM_INT("{console.log('5. got int ' + $0); return 7.5;}", 42); printf("5. returned int %d\n", i); printf("\nEM_ASM_INT: Return an integer in a single brief statement.\n"); - i = EM_ASM_INT(return HEAP8.length); printf("1. returned statement %d\n", i); - i = EM_ASM_INT("return HEAP8.length+1"); printf("2. returned statement %d\n", i); - i = EM_ASM_INT({"return HEAP8.length+2"}); printf("3. returned statement %d\n", i); - i = EM_ASM_INT({return HEAP8.length+3}); printf("4. returned statement %d\n", i); - i = EM_ASM_INT("return HEAP8.length+4"); printf("5. returned statement %d\n", i); + i = EM_ASM_INT(return 42); printf("1. returned statement %d\n", i); + i = EM_ASM_INT("return 42+1"); printf("2. returned statement %d\n", i); + i = EM_ASM_INT({"return 42+2"}); printf("3. returned statement %d\n", i); + i = EM_ASM_INT({return 42+3}); printf("4. returned statement %d\n", i); + i = EM_ASM_INT("return 42+4"); printf("5. returned statement %d\n", i); // Note that expressions do not evaluate to return values, but the "return" keyword is needed. That is, the following line would return undefined and store i <- 0. // i = EM_ASM_INT(HEAP8.length); printf("returned statement %d\n", i);
diff --git a/tests/core/test_em_asm_2.out b/tests/core/test_em_asm_2.out index 10ca9d7..9ee29c6 100644 --- a/tests/core/test_em_asm_2.out +++ b/tests/core/test_em_asm_2.out
@@ -113,11 +113,11 @@ 5. returned int 7 EM_ASM_INT: Return an integer in a single brief statement. -1. returned statement 16777216 -2. returned statement 16777217 -3. returned statement 16777218 -4. returned statement 16777219 -5. returned statement 16777220 +1. returned statement 42 +2. returned statement 43 +3. returned statement 44 +4. returned statement 45 +5. returned statement 46 EM_ASM_DOUBLE: Pass no parameters, return a double. 1. returning double
diff --git a/tests/core/test_emptyclass.cpp b/tests/core/test_emptyclass.cpp index 09327f9..8f9d88b 100644 --- a/tests/core/test_emptyclass.cpp +++ b/tests/core/test_emptyclass.cpp
@@ -12,7 +12,6 @@ }; int main(int argc, const char *argv[]) { - new Randomized(55); - + Randomized(55); return 0; }
diff --git a/tests/test_core.py b/tests/test_core.py index cb4dc7a..b09b4e0 100644 --- a/tests/test_core.py +++ b/tests/test_core.py
@@ -146,7 +146,8 @@ self.get_setting('STACK_OVERFLOW_CHECK', 0) < 2 and \ not self.get_setting('MINIMAL_RUNTIME') and \ not self.get_setting('SAFE_HEAP') and \ - '-fsanitize=address' not in self.emcc_args + '-fsanitize=address' not in self.emcc_args and \ + '-fsanitize=leak' not in self.emcc_args def also_with_wasmfs(func): @@ -602,6 +603,7 @@ ''') @no_asan('asan errors on corner cases we check') + @no_lsan('lsan errors on corner cases we check') def test_aligned_alloc(self): self.do_runf(test_file('test_aligned_alloc.c'), '', emcc_args=['-Wno-non-power-of-two-alignment']) @@ -815,6 +817,7 @@ self.do_core_test('test_stack_placement.c') @no_asan('asan does not support main modules') + @no_lsan('asan does not support main modules') @no_wasm2js('MAIN_MODULE support') def test_stack_placement_pic(self): self.set_setting('TOTAL_STACK', 1024) @@ -1210,7 +1213,8 @@ # empty list acts the same as fully disabled self.assertEqual(empty_size, disabled_size) # big change when we disable exception catching of the function - self.assertGreater(size - empty_size, 0.01 * size) + if '-fsanitize=leak' not in self.emcc_args: + self.assertGreater(size - empty_size, 0.01 * size) # full disable can remove a little bit more self.assertLess(disabled_size, fake_size) @@ -1495,6 +1499,7 @@ struct Classey { virtual void doIt() = 0; + virtual ~Classey() = default; }; struct D1 : Classey { @@ -1509,11 +1514,11 @@ return 0; }); - int main(int argc, char **argv) - { + int main(int argc, char **argv) { Classey *p = argc == 100 ? new D1() : (Classey*)%s; p->doIt(); + delete p; return 0; } @@ -1696,7 +1701,7 @@ self.set_setting('EXPORTED_FUNCTIONS', ['_main', '_save_me_aimee']) self.do_core_test('test_emscripten_api.cpp') - if '-fsanitize=address' not in self.emcc_args: + if '-fsanitize=address' not in self.emcc_args and '-fsanitize=leak' not in self.emcc_args: # test EXPORT_ALL (this is not compatible with asan, which doesn't # support dynamic linking at all or the LINKING flag) self.set_setting('EXPORTED_FUNCTIONS', []) @@ -1838,7 +1843,6 @@ self.do_core_test('test_em_asm.cpp', force_c=True) # Tests various different ways to invoke the EM_ASM(), EM_ASM_INT() and EM_ASM_DOUBLE() macros. - @no_asan('Cannot use ASan: test depends exactly on heap size') def test_em_asm_2(self): self.do_core_test('test_em_asm_2.cpp') self.emcc_args.append('-std=gnu89') @@ -1847,7 +1851,6 @@ # Tests various different ways to invoke the MAIN_THREAD_EM_ASM(), MAIN_THREAD_EM_ASM_INT() and MAIN_THREAD_EM_ASM_DOUBLE() macros. # This test is identical to test_em_asm_2, just search-replaces EM_ASM to MAIN_THREAD_EM_ASM on the test file. That way if new # test cases are added to test_em_asm_2.cpp for EM_ASM, they will also get tested in MAIN_THREAD_EM_ASM form. - @no_asan('Cannot use ASan: test depends exactly on heap size') def test_main_thread_em_asm(self): src = read_file(test_file('core/test_em_asm_2.cpp')) create_file('src.cpp', src.replace('EM_ASM', 'MAIN_THREAD_EM_ASM')) @@ -1898,10 +1901,8 @@ 'linked_c': (['-s', 'MAIN_MODULE'], True), }) def test_em_js(self, args, force_c): - if 'MAIN_MODULE' in args and not self.is_wasm(): - self.skipTest('main module support for non-wasm') - if '-fsanitize=address' in self.emcc_args: - self.skipTest('no dynamic library support in asan yet') + if 'MAIN_MODULE' in args: + self.check_dylink() self.emcc_args += args + ['-s', 'EXPORTED_FUNCTIONS=_main,_malloc'] self.do_core_test('test_em_js.cpp', force_c=force_c) @@ -2034,6 +2035,7 @@ 'grow': (['-sALLOW_MEMORY_GROWTH', '-sMAXIMUM_MEMORY=18MB'],) }) @no_asan('requires more memory when growing') + @no_lsan('requires more memory when growing') def test_aborting_new(self, args): # test that C++ new properly errors if we fail to malloc when growth is # enabled, with or without growth @@ -2042,6 +2044,7 @@ @no_wasm2js('no WebAssembly.Memory()') @no_asan('ASan alters the memory size') + @no_lsan('LSan alters the memory size') def test_module_wasm_memory(self): self.emcc_args += ['--pre-js', test_file('core/test_module_wasm_memory.js')] self.set_setting('IMPORTED_MEMORY') @@ -5157,6 +5160,7 @@ def test_minimal_runtime_utf8_invalid(self): self.set_setting('EXPORTED_RUNTIME_METHODS', ['UTF8ToString', 'stringToUTF8']) self.set_setting('MINIMAL_RUNTIME') + self.emcc_args += ['--pre-js', test_file('minimal_runtime_exit_handling.js')] for decoder_mode in [False, True]: self.set_setting('TEXTDECODER', decoder_mode) print(str(decoder_mode)) @@ -5727,6 +5731,7 @@ # node is slower, and fail on 64-bit @require_v8 @no_asan('depends on the specifics of memory size, which for asan we are forced to increase') + @no_lsan('depends on the specifics of memory size, which for lsan we are forced to increase') def test_dlmalloc_inline(self): # needed with typed arrays self.set_setting('INITIAL_MEMORY', '128mb') @@ -5738,6 +5743,7 @@ # node is slower, and fail on 64-bit @require_v8 @no_asan('depends on the specifics of memory size, which for asan we are forced to increase') + @no_lsan('depends on the specifics of memory size, which for lsan we are forced to increase') def test_dlmalloc(self): # needed with typed arrays self.set_setting('INITIAL_MEMORY', '128mb') @@ -5769,11 +5775,13 @@ # Tests that a large allocation should gracefully fail @no_asan('the memory size limit here is too small for asan') + @no_lsan('the memory size limit here is too small for lsan') def test_dlmalloc_large(self): self.emcc_args += ['-s', 'ABORTING_MALLOC=0', '-s', 'ALLOW_MEMORY_GROWTH=1', '-s', 'MAXIMUM_MEMORY=128MB'] self.do_runf(test_file('dlmalloc_test_large.c'), '0 0 0 1') @no_asan('asan also changes malloc, and that ends up linking in new twice') + @no_lsan('lsan also changes malloc, and that ends up linking in new twice') def test_dlmalloc_partial(self): # present part of the symbols of dlmalloc, not all src = read_file(test_file('new.cpp')).replace('{{{ NEW }}}', 'new int').replace('{{{ DELETE }}}', 'delete') + ''' @@ -5787,6 +5795,7 @@ self.do_run(src, 'new 4!\n*1,0*') @no_asan('asan also changes malloc, and that ends up linking in new twice') + @no_lsan('lsan also changes malloc, and that ends up linking in new twice') def test_dlmalloc_partial_2(self): if 'SAFE_HEAP' in str(self.emcc_args): self.skipTest('we do unsafe stuff here') @@ -6273,7 +6282,7 @@ self.set_setting('ALLOW_MEMORY_GROWTH', 0) do_test() - if '-fsanitize=address' in self.emcc_args: + if '-fsanitize=address' in self.emcc_args or '-fsanitize=leak' in self.emcc_args: # In ASan mode we need a large initial memory (or else wasm-ld fails). # The OpenJPEG CMake will build several executables (which we need parts # of in our testing, see above), so we must enable the flag for them all. @@ -7598,6 +7607,7 @@ self.do_core_test('test_asyncify_during_exit.cpp', emcc_args=['-DNO_ASYNC'], out_suffix='_no_async') @no_asan('asyncify stack operations confuse asan') + @no_lsan('undefined symbol __global_base') @no_wasm2js('dynamic linking support in wasm2js') def test_asyncify_main_module(self): self.set_setting('ASYNCIFY', 1) @@ -7623,7 +7633,7 @@ second_size = os.path.getsize('emscripten_lazy_load_code.wasm.lazy.wasm') print('first wasm size', first_size) print('second wasm size', second_size) - if not conditional and self.is_optimizing() and '-g' not in self.emcc_args: + if not conditional and self.is_optimizing() and '-g' not in self.emcc_args and '-fsanitize=leak' not in self.emcc_args: # If the call to lazy-load is unconditional, then the optimizer can dce # out more than half self.assertLess(first_size, 0.6 * second_size) @@ -7688,6 +7698,7 @@ # Test basic wasm2js functionality in all core compilation modes. @no_asan('no wasm2js support yet in asan') + @no_lsan('no wasm2js support yet in lsan') def test_wasm2js(self): if not self.is_wasm(): self.skipTest('redundant to test wasm2js in wasm2js* mode') @@ -7703,6 +7714,7 @@ self.assertNotExists('test_hello_world.js.mem') @no_asan('no wasm2js support yet in asan') + @no_lsan('no wasm2js support yet in lsan') def test_maybe_wasm2js(self): if not self.is_wasm(): self.skipTest('redundant to test wasm2js in wasm2js* mode') @@ -7872,10 +7884,12 @@ # Tests that we can use the dlmalloc mallinfo() function to obtain information # about malloc()ed blocks and compute how much memory is used/freed. @no_asan('mallinfo is not part of ASan malloc') + @no_lsan('mallinfo is not part of LSan malloc') def test_mallinfo(self): self.do_runf(test_file('mallinfo.cpp'), 'OK.') @no_asan('cannot replace malloc/free with ASan') + @no_lsan('cannot replace malloc/free with LSan') def test_wrap_malloc(self): self.do_runf(test_file('wrap_malloc.cpp'), 'OK.') @@ -7952,6 +7966,7 @@ self.set_setting('WASM_ASYNC_COMPILATION', 0) self.maybe_closure() self.set_setting('MINIMAL_RUNTIME') + self.emcc_args += ['--pre-js', test_file('minimal_runtime_exit_handling.js')] self.do_runf(test_file('declare_asm_module_exports.cpp'), 'jsFunction: 1') # Tests that -s MINIMAL_RUNTIME=1 works well in different build modes @@ -7977,6 +7992,7 @@ @no_asan('TODO: ASan support in minimal runtime') def test_minimal_runtime_hello_printf(self, extra_setting): self.set_setting('MINIMAL_RUNTIME') + self.emcc_args += ['--pre-js', test_file('minimal_runtime_exit_handling.js')] self.set_setting(extra_setting) # $FS is not fully compatible with MINIMAL_RUNTIME so fails with closure # compiler. lsan also pulls in $FS @@ -7988,6 +8004,7 @@ @no_asan('TODO: ASan support in minimal runtime') def test_minimal_runtime_safe_heap(self): self.set_setting('MINIMAL_RUNTIME') + self.emcc_args += ['--pre-js', test_file('minimal_runtime_exit_handling.js')] self.set_setting('SAFE_HEAP') # $FS is not fully compatible with MINIMAL_RUNTIME so fails with closure # compiler. @@ -8000,6 +8017,7 @@ @no_asan('TODO: ASan support in minimal runtime') def test_minimal_runtime_global_initializer(self): self.set_setting('MINIMAL_RUNTIME') + self.emcc_args += ['--pre-js', test_file('minimal_runtime_exit_handling.js')] self.maybe_closure() self.do_runf(test_file('test_global_initializer.cpp'), 't1 > t0: 1') @@ -8010,6 +8028,7 @@ @no_wasm2js('TODO: sanitizers in wasm2js') @no_asan('-fsanitize-minimal-runtime cannot be used with ASan') + @no_lsan('-fsanitize-minimal-runtime cannot be used with LSan') def test_ubsan_minimal_too_many_errors(self): self.emcc_args += ['-fsanitize=undefined', '-fsanitize-minimal-runtime'] if not self.is_wasm(): @@ -8022,6 +8041,7 @@ @no_wasm2js('TODO: sanitizers in wasm2js') @no_asan('-fsanitize-minimal-runtime cannot be used with ASan') + @no_lsan('-fsanitize-minimal-runtime cannot be used with LSan') def test_ubsan_minimal_errors_same_place(self): self.emcc_args += ['-fsanitize=undefined', '-fsanitize-minimal-runtime'] if not self.is_wasm(): @@ -8527,6 +8547,7 @@ # Could also test with -s ALLOW_TABLE_GROWTH=1 self.set_setting('RESERVED_FUNCTION_POINTERS', 2) self.set_setting('MINIMAL_RUNTIME') + self.emcc_args += ['--pre-js', test_file('minimal_runtime_exit_handling.js')] self.emcc_args += ['-lexports.js'] self.do_core_test('test_get_exported_function.cpp')
diff --git a/tests/third_party/libiberty/cp-demangle.c b/tests/third_party/libiberty/cp-demangle.c index 0954350..dd1ec0e 100644 --- a/tests/third_party/libiberty/cp-demangle.c +++ b/tests/third_party/libiberty/cp-demangle.c
@@ -4220,7 +4220,9 @@ int main(int argc, char **argv) { int status; - printf("*%s*\n", __cxa_demangle(argv[1], 0, 0, &status)); + char* s = __cxa_demangle(argv[1], 0, 0, &status); + printf("*%s*\n", s); + free(s); return 0; }