diff --git a/ChangeLog.md b/ChangeLog.md index 512f7422e2ee7..3dc0fe52e465d 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -17,6 +17,8 @@ See docs/process.md for how version tagging works. Current Trunk ------------- +- ASAN_SHADOW_SIZE is deprecated. When using AddressSanitizer, the correct + amount of shadow memory will now be calculated automatically. 2.0.5: 09/28/2020 ----------------- diff --git a/emcc.py b/emcc.py index 4b31668062fcb..c7e8a58344b00 100755 --- a/emcc.py +++ b/emcc.py @@ -1498,12 +1498,6 @@ def filter_out_duplicate_dynamic_libs(inputs): # so setErrNo JS library function can report errno back to C shared.Settings.EXPORTED_FUNCTIONS += ['___errno_location'] - if shared.Settings.GLOBAL_BASE < 0: - # default if nothing else sets it - # a higher global base is useful for optimizing load/store offsets, as it - # enables the --post-emscripten pass - shared.Settings.GLOBAL_BASE = 1024 - if shared.Settings.SAFE_HEAP: # SAFE_HEAP check includes calling emscripten_get_sbrk_ptr() from wasm shared.Settings.EXPORTED_FUNCTIONS += ['_emscripten_get_sbrk_ptr'] @@ -1777,9 +1771,20 @@ def include_and_export(name): '_asan_c_store_f', '_asan_c_store_d', ] - shared.Settings.GLOBAL_BASE = shared.Settings.ASAN_SHADOW_SIZE - shared.Settings.INITIAL_MEMORY += shared.Settings.ASAN_SHADOW_SIZE - assert shared.Settings.INITIAL_MEMORY < 2**32 + if shared.Settings.ASAN_SHADOW_SIZE != -1: + diagnostics.warning('emcc', 'ASAN_SHADOW_SIZE is ignored and will be removed in a future release') + + if shared.Settings.GLOBAL_BASE != -1: + exit_with_error("ASan does not support custom GLOBAL_BASE") + + max_mem = shared.Settings.INITIAL_MEMORY + if shared.Settings.ALLOW_MEMORY_GROWTH: + max_mem = shared.Settings.MAXIMUM_MEMORY + if max_mem == -1: + exit_with_error('ASan requires a finite MAXIMUM_MEMORY') + + shadow_size = max_mem // 8 + shared.Settings.GLOBAL_BASE = shadow_size if shared.Settings.SAFE_HEAP: # SAFE_HEAP instruments ASan's shadow memory accesses. @@ -1793,6 +1798,12 @@ def include_and_export(name): if sanitize and '-g4' in args: shared.Settings.LOAD_SOURCE_MAP = 1 + if shared.Settings.GLOBAL_BASE == -1: + # default if nothing else sets it + # a higher global base is useful for optimizing load/store offsets, as it + # enables the --post-emscripten pass + shared.Settings.GLOBAL_BASE = 1024 + # various settings require malloc/free support from JS if shared.Settings.RELOCATABLE or \ shared.Settings.BUILD_AS_WORKER or \ diff --git a/src/library.js b/src/library.js index 038c78c03bec6..f40f2fd7c6f45 100644 --- a/src/library.js +++ b/src/library.js @@ -551,21 +551,6 @@ LibraryManager.library = { return false; #endif } -#if USE_ASAN - // One byte of ASan's shadow memory shadows 8 bytes of real memory. Shadow memory area has a fixed size, - // so do not allow resizing past that limit. - maxHeapSize = Math.min(maxHeapSize, {{{ 8 * ASAN_SHADOW_SIZE }}}); - if (requestedSize > maxHeapSize) { -#if ASSERTIONS - err('Failed to grow the heap from ' + oldSize + ', as we reached the limit of our shadow memory. Increase ASAN_SHADOW_SIZE.'); -#endif -#if ABORTING_MALLOC - abortOnCannotGrowMemory(requestedSize); -#else - return false; -#endif - } -#endif var minHeapSize = 16777216; diff --git a/src/settings.js b/src/settings.js index bf1ba86bd8c2e..2788fd5770aba 100644 --- a/src/settings.js +++ b/src/settings.js @@ -1530,9 +1530,10 @@ var MINIFY_HTML = 1; // bisecting. var MAYBE_WASM2JS = 0; -// The size of our shadow memory. -// By default, we have 32 MiB. This supports 256 MiB of real memory. -var ASAN_SHADOW_SIZE = 33554432; +// This option is no longer used. The appropriate shadow memory size is now +// calculated from INITIAL_MEMORY and MAXIMUM_MEMORY. Will be removed in a +// future release. +var ASAN_SHADOW_SIZE = -1 // Internal: Tracks whether Emscripten should link in exception throwing (C++ // 'throw') support library. This does not need to be set directly, but pass diff --git a/tests/asan-no-leak.js b/tests/asan-no-leak.js index 0c5b359faf7d9..e50bde5ca7938 100644 --- a/tests/asan-no-leak.js +++ b/tests/asan-no-leak.js @@ -1 +1,5 @@ -Module = {ASAN_OPTIONS: 'detect_leaks=0'}; +if (Module != undefined) { + Module.ASAN_OPTIONS = 'detect_leaks=0'; +} else { + Module = {ASAN_OPTIONS: 'detect_leaks=0'}; +} diff --git a/tests/test_core.py b/tests/test_core.py index e91c8d0ae0aa9..a2a9b78249425 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -5774,7 +5774,10 @@ def test_fakestat(self): self.do_run_in_out_file_test('tests', 'core', 'test_fakestat.c') def test_mmap(self): - self.set_setting('INITIAL_MEMORY', 128 * 1024 * 1024) + # ASan needs more memory, but that is set up separately + if '-fsanitize=address' not in self.emcc_args: + self.set_setting('INITIAL_MEMORY', 128 * 1024 * 1024) + # needs to flush stdio streams self.set_setting('EXIT_RUNTIME', 1) self.do_run_in_out_file_test('tests', 'core', 'test_mmap.c') @@ -6278,12 +6281,6 @@ def run_all(x): @no_asan('autodebug logging interferes with asan') @with_env_modify({'EMCC_AUTODEBUG': '1'}) def test_autodebug_wasm(self): - # Autodebug does not work with too much shadow memory. - # Memory consumed by autodebug depends on the size of the WASM linear memory. - # With a large shadow memory, the JS engine runs out of memory. - if '-fsanitize=address' in self.emcc_args: - self.set_setting('ASAN_SHADOW_SIZE', 16 * 1024 * 1024) - # test that the program both works and also emits some of the logging # (but without the specific output, as it is logging the actual locals # used and so forth, which will change between opt modes and updates of @@ -8015,7 +8012,9 @@ def test_template_class_deduction(self): 'cpp': ['test_asan_no_error.cpp'], }) def test_asan_no_error(self, name): - self.emcc_args += ['-fsanitize=address', '-s', 'ALLOW_MEMORY_GROWTH=1'] + self.emcc_args.append('-fsanitize=address') + self.emcc_args.append('-sALLOW_MEMORY_GROWTH=1') + self.emcc_args.append('-sINITIAL_MEMORY=314572800') self.do_runf(path_from_root('tests', 'core', name), '', assert_returncode=NON_ZERO) # note: these tests have things like -fno-builtin-memset in order to avoid @@ -8082,7 +8081,9 @@ def test_asan(self, name, expected_output, cflags=None): if not self.get_setting('WASM'): self.skipTest('wasm2js has no ASan support') - self.emcc_args += ['-fsanitize=address', '-s', 'ALLOW_MEMORY_GROWTH=1'] + self.emcc_args.append('-fsanitize=address') + self.emcc_args.append('-sALLOW_MEMORY_GROWTH=1') + self.emcc_args.append('-sINITIAL_MEMORY=314572800') if cflags: self.emcc_args += cflags self.do_runf(path_from_root('tests', 'core', name), @@ -8091,14 +8092,16 @@ def test_asan(self, name, expected_output, cflags=None): @no_wasm2js('TODO: ASAN in wasm2js') def test_asan_js_stack_op(self): - self.emcc_args += ['-fsanitize=address', '-s', 'ALLOW_MEMORY_GROWTH=1'] + self.emcc_args.append('-fsanitize=address') + self.emcc_args.append('-sALLOW_MEMORY_GROWTH=1') + self.emcc_args.append('-sINITIAL_MEMORY=314572800') self.do_runf(path_from_root('tests', 'core', 'test_asan_js_stack_op.c'), expected_output='Hello, World!') @no_wasm2js('TODO: ASAN in wasm2js') def test_asan_api(self): self.emcc_args.append('-fsanitize=address') - self.set_setting('ALLOW_MEMORY_GROWTH') + self.set_setting('INITIAL_MEMORY', 314572800) self.do_run_in_out_file_test('tests', 'core', 'test_asan_api.c') @no_wasm2js('TODO: ASAN in wasm2js') @@ -8108,6 +8111,7 @@ def test_asan_modularized_with_closure(self): self.emcc_args.append('-sUSE_CLOSURE_COMPILER=1') self.emcc_args.append('-fsanitize=address') self.emcc_args.append('-sALLOW_MEMORY_GROWTH=1') + self.emcc_args.append('-sINITIAL_MEMORY=314572800') def post(filename): with open(filename, 'a') as f: @@ -8358,10 +8362,10 @@ def setUp(self): # Add DEFAULT_TO_CXX=0 strict = make_run('strict', emcc_args=[], settings={'STRICT': 1}) -lsan = make_run('lsan', emcc_args=['-fsanitize=leak'], settings={'ALLOW_MEMORY_GROWTH': 1}) -asan = make_run('asan', emcc_args=['-fsanitize=address'], settings={'ALLOW_MEMORY_GROWTH': 1, 'ASAN_SHADOW_SIZE': 128 * 1024 * 1024}) -asani = make_run('asani', emcc_args=['-fsanitize=address', '--pre-js', os.path.join(os.path.dirname(__file__), 'asan-no-leak.js')], - settings={'ALLOW_MEMORY_GROWTH': 1}) +lsan = make_run('lsan', emcc_args=['-fsanitize=leak', '-O2'], settings={'ALLOW_MEMORY_GROWTH': 1}) +asan = make_run('asan', emcc_args=['-fsanitize=address', '-O2'], settings={'ALLOW_MEMORY_GROWTH': 1, 'INITIAL_MEMORY': 314572800}) +asani = make_run('asani', emcc_args=['-fsanitize=address', '-O2', '--pre-js', os.path.join(os.path.dirname(__file__), 'asan-no-leak.js')], + settings={'ALLOW_MEMORY_GROWTH': 1, 'INITIAL_MEMORY': 314572800}) # Experimental modes (not tested by CI) lld = make_run('lld', emcc_args=[], settings={'LLD_REPORT_UNDEFINED': 1}) diff --git a/tests/test_other.py b/tests/test_other.py index 2ddf31c0a825a..1ae574a60be3e 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -8486,21 +8486,21 @@ def test_lsan_no_stack_trace(self): def test_asan_null_deref(self): self.do_smart_test(path_from_root('tests', 'other', 'test_asan_null_deref.c'), - emcc_args=['-fsanitize=address', '-s', 'ALLOW_MEMORY_GROWTH=1'], + emcc_args=['-fsanitize=address', '-sALLOW_MEMORY_GROWTH=1', '-sINITIAL_MEMORY=314572800'], assert_returncode=NON_ZERO, literals=[ 'AddressSanitizer: null-pointer-dereference on address', ]) def test_asan_no_stack_trace(self): self.do_smart_test(path_from_root('tests', 'other', 'test_lsan_leaks.c'), - emcc_args=['-fsanitize=address', '-s', 'ALLOW_MEMORY_GROWTH=1', '-DDISABLE_CONTEXT', '-s', 'EXIT_RUNTIME'], + emcc_args=['-fsanitize=address', '-sALLOW_MEMORY_GROWTH=1', '-sINITIAL_MEMORY=314572800', '-DDISABLE_CONTEXT', '-s', 'EXIT_RUNTIME'], assert_returncode=NON_ZERO, literals=[ 'Direct leak of 3427 byte(s) in 3 object(s) allocated from:', 'SUMMARY: AddressSanitizer: 3427 byte(s) leaked in 3 allocation(s).', ]) def test_asan_pthread_stubs(self): - self.do_smart_test(path_from_root('tests', 'other', 'test_asan_pthread_stubs.c'), emcc_args=['-fsanitize=address', '-s', 'ALLOW_MEMORY_GROWTH=1']) + self.do_smart_test(path_from_root('tests', 'other', 'test_asan_pthread_stubs.c'), emcc_args=['-fsanitize=address', '-sALLOW_MEMORY_GROWTH=1', '-sINITIAL_MEMORY=314572800']) def test_proxy_to_pthread_stack(self): with js_engines_modify([NODE_JS + ['--experimental-wasm-threads', '--experimental-wasm-bulk-memory']]): @@ -8601,7 +8601,7 @@ def test_mmap_and_munmap_anonymous(self): self.do_other_test('mmap_and_munmap_anonymous', emcc_args=['-s', 'NO_FILESYSTEM']) def test_mmap_and_munmap_anonymous_asan(self): - self.do_other_test('mmap_and_munmap_anonymous', emcc_args=['-s', 'NO_FILESYSTEM', '-fsanitize=address', '-s', 'ALLOW_MEMORY_GROWTH=1']) + self.do_other_test('mmap_and_munmap_anonymous', emcc_args=['-s', 'NO_FILESYSTEM', '-fsanitize=address', '-s', 'ALLOW_MEMORY_GROWTH=1', '-sINITIAL_MEMORY=314572800']) def test_mmap_memorygrowth(self): self.do_other_test('mmap_memorygrowth', ['-s', 'ALLOW_MEMORY_GROWTH=1'])