diff --git a/system/lib/libcxx/new.cpp b/system/lib/libcxx/new.cpp index 901e78565857b..bd0b42f7cb7c0 100644 --- a/system/lib/libcxx/new.cpp +++ b/system/lib/libcxx/new.cpp @@ -74,8 +74,17 @@ operator new(std::size_t size) _THROW_BAD_ALLOC else #ifndef _LIBCPP_NO_EXCEPTIONS throw std::bad_alloc(); +#else +#ifdef __EMSCRIPTEN__ + // Abort here so that when exceptions are disabled, we do not just + // return 0 when malloc returns 0. + // We could also do this with set_new_handler, but that adds a + // global constructor and a table entry, overhead that we can avoid + // by doing it this way. + abort(); #else break; +#endif #endif } return p; diff --git a/system/lib/libcxxabi/src/stdlib_new_delete.cpp b/system/lib/libcxxabi/src/stdlib_new_delete.cpp index 698c5f7c290c0..0fcb486ec663a 100644 --- a/system/lib/libcxxabi/src/stdlib_new_delete.cpp +++ b/system/lib/libcxxabi/src/stdlib_new_delete.cpp @@ -38,8 +38,17 @@ operator new(std::size_t size) _THROW_BAD_ALLOC else #ifndef _LIBCXXABI_NO_EXCEPTIONS throw std::bad_alloc(); +#else +#ifdef __EMSCRIPTEN__ + // Abort here so that when exceptions are disabled, we do not just + // return 0 when malloc returns 0. + // We could also do this with set_new_handler, but that adds a + // global constructor and a table entry, overhead that we can avoid + // by doing it this way. + abort(); #else break; +#endif #endif } return p; diff --git a/tests/core/test_aborting_new.cpp b/tests/core/test_aborting_new.cpp new file mode 100644 index 0000000000000..a4d4c8b77cbf4 --- /dev/null +++ b/tests/core/test_aborting_new.cpp @@ -0,0 +1,23 @@ +#include <emscripten.h> +#include <stdio.h> +#include <vector> + +EMSCRIPTEN_KEEPALIVE extern "C" void allocate_too_much() { + std::vector<int> x; + puts("allocating more than TOTAL_MEMORY; this will fail."); + x.resize(20 * 1024 * 1024); + puts("oh no, it didn't fail!"); +} + +int main() { + EM_ASM({ + // Catch the failure here so we can report it. + try { + _allocate_too_much(); + out("no abort happened"); + } catch (e) { + assert(("" + e).indexOf("abort") >= 0, "expect an abort from new"); + out("new aborted as expected"); + } + }); +} diff --git a/tests/core/test_aborting_new.txt b/tests/core/test_aborting_new.txt new file mode 100644 index 0000000000000..9ff167b0e499b --- /dev/null +++ b/tests/core/test_aborting_new.txt @@ -0,0 +1 @@ +new aborted as expected diff --git a/tests/test_core.py b/tests/test_core.py index 876934937fc55..909a4b8fba675 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -2169,6 +2169,16 @@ def test_memorygrowth_3_force_fail_reallocBuffer(self): self.emcc_args += ['-Wno-almost-asm', '-s', 'ALLOW_MEMORY_GROWTH=1', '-s', 'TEST_MEMORY_GROWTH_FAILS=1'] self.do_run_in_out_file_test('tests', 'core', 'test_memorygrowth_3') + @parameterized({ + 'nogrow': (['-s', 'ALLOW_MEMORY_GROWTH=0'],), + 'grow': (['-s', 'ALLOW_MEMORY_GROWTH=1'],) + }) + 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 + self.emcc_args += ['-Wno-almost-asm', '-s', 'MAXIMUM_MEMORY=18MB'] + args + self.do_run_in_out_file_test('tests', 'core', 'test_aborting_new') + @no_asmjs() @no_wasm2js('no WebAssembly.Memory()') @no_asan('ASan alters the memory size')