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;
 }