Catching out-of-bounds memory accesses

6 views
Skip to first unread message

John Dallman

unread,
Sep 3, 2025, 9:09:53 AM (5 days ago) Sep 3
to emscripte...@googlegroups.com
I'm porting a math library and its test harness to WebAssembly. The test harness is allowed to be specific to Node.js, but the library is not. Both are compiled from C and C++ code, which is my comfort zone. JavaScript is a language I don't know well and haven't done anything difficult in.  

I'm aware that WASI doesn't support traditional signals. At present, when I intentionally set off an access violation, I get "RuntimeError: memory access out of bounds" and a traceback as Node.js exits.

Is there a way to catch these errors and prevent Node.js exiting? Ideally, I'd be able to notify the test harness in some way that this had happened. If this involves JavaScript, please explain slowly and gently: I'm from the C world and new to web applications. 

The reason I'm asking this is that I will have to provide support to customers when the WebAssembly version of the library is released, and prefer to have my answers ready ahead of time. My employer wants to maintain their good reputation for customer service, and goes as far as having tests for deliberately-set-off runtime errors as part of routine testing, so that we can document what happens and how to handle them.

Thanks very much,

John 

Brooke Vibber

unread,
Sep 3, 2025, 2:32:32 PM (5 days ago) Sep 3
to emscripte...@googlegroups.com
On Wed, Sep 3, 2025 at 6:09 AM John Dallman <jgdats...@gmail.com> wrote:
I'm aware that WASI doesn't support traditional signals. At present, when I intentionally set off an access violation, I get "RuntimeError: memory access out of bounds" and a traceback as Node.js exits.

Is there a way to catch these errors and prevent Node.js exiting? Ideally, I'd be able to notify the test harness in some way that this had happened. If this involves JavaScript, please explain slowly and gently: I'm from the C world and new to web applications. 

These can be caught like a standard JavaScript exception by the surrounding test harness JS, something like:

try {
  Module.run_my_c_code();
} catch (e) {
  // Probably out of bounds access, or divide by zero etc
  console.log("Error while running run_my_c_code: " + e);
}

The stack trace attached to the exception may not be very useful, but this gives your test harness a chance to process the failure at least. :D

Note that this will only catch WebAssembly accesses outside of linear memory -- a NULL dereference, read or write, will *not* trigger a runtime error -- but any actually out-of-bounds accesses, or other operations like divide by zero that trap, can be caught this way even on a fully optimized production build.


You might also look into the SAFE_HEAP build option in emscripten, which runs all memory accesses through a double-check for out-of-bounds or NULL dereference and logs it. Check emscripten's src/settings.js for comments documenting this and other build-time options.

-- brooke

Sam Clegg

unread,
Sep 3, 2025, 7:52:10 PM (5 days ago) Sep 3
to emscripte...@googlegroups.com
On Wed, Sep 3, 2025 at 6:09 AM John Dallman <jgdats...@gmail.com> wrote:
I'm porting a math library and its test harness to WebAssembly. The test harness is allowed to be specific to Node.js, but the library is not. Both are compiled from C and C++ code, which is my comfort zone. JavaScript is a language I don't know well and haven't done anything difficult in.  

I'm aware that WASI doesn't support traditional signals. At present, when I intentionally set off an access violation, I get "RuntimeError: memory access out of bounds" and a traceback as Node.js exits.

Is there a way to catch these errors and prevent Node.js exiting? Ideally, I'd be able to notify the test harness in some way that this had happened. If this involves JavaScript, please explain slowly and gently: I'm from the C world and new to web applications. 

Is the test harness and the library-under-test designed to be compiled into the same executable?    i.e. on other platforms does it somehow catch and recover from sefaults?

From the JS side you basically have two choice:

1. Wrap your calls in a JS try/catch and inspect the exception you caught and then (somehow?) continue with the test suite.  (This is what Brooke suggested already)
2. Install a global `onerror` handler that will catch all exceptions (much like the global signal handler on linux).  See https://nodejs.org/api/process.html#event-uncaughtexception.

I suppose in either the case the tricky part is going to be continuing the test suite where you left off.   How does that work in the native case?

 

The reason I'm asking this is that I will have to provide support to customers when the WebAssembly version of the library is released, and prefer to have my answers ready ahead of time. My employer wants to maintain their good reputation for customer service, and goes as far as having tests for deliberately-set-off runtime errors as part of routine testing, so that we can document what happens and how to handle them.

Thanks very much,

John 

--
You received this message because you are subscribed to the Google Groups "emscripten-discuss" group.
To unsubscribe from this group and stop receiving emails from it, send an email to emscripten-disc...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/emscripten-discuss/CAH1xqgnMY0%2BoOLMs51mo2x%2BBUhWdZq%3DkxFp-En7AX3OsVqcoQQ%40mail.gmail.com.

John Dallman

unread,
6:40 AM (9 hours ago) 6:40 AM
to emscripte...@googlegroups.com
> Is the test harness and the library-under-test designed to be compiled into the 
> same executable? 

Yes. We prefer to have the library-under-test be a shared object or Windows DLL, on platforms where that's possible, but we can have the harness and the library linked together, and that's what I'm planning to do for WebAssembly. I'm trying to avoid producing a JS wrapper for an API with hundreds of functions, hundreds of structs, and thousands of constants. It also passes lots of pointers to code and data through the interface. My customers who want a WebAssembly version of the library already have C/C++ or Swift code that calls it and want to use it that way.  

> i.e. on other platforms does it somehow catch and recover from sefaults?

Yes.On platforms with signals, those are turned on for segmentation faults (and for some other signals, depending on the platform). The code is C, which sets regular checkpoints with setjmp() and the signal handling function longjmp()s to the latest checkpoint with a "test aborted" value. That's the basic idea, though it's rather more complicated in practice. 

The JS code I'm going to use will be very minimal. The test harness already has its own scripting language (a LISP dialect) built into it, and we have hundreds of thousands (not hyperbole) of test cases already written in it. So I'm not going to try to organise and control the testing from JS, because that would be a duplication of work already done. I'm just going to start the test harness and let it do its thing. 

I am not an experienced JS coder - I learned some of the language for the first time for this project - and the product is not targeted to the general web application market.

> 1. Wrap your calls in a JS try/catch and inspect the exception you caught 
> and then (somehow?) continue with the test suite.  (This is what Brooke 
> suggested already)

I have a couple of questions about that:

When the catch gets called, is it called by the same thread as did the segmentation violation? 

Is that thread's stack still intact? Has it been unwound? 

If it is the same thread and the stack is intact, then I should be able to call the function within the library that does signal handling on other platforms, and have it do its longjmp()s. If those conditions don't apply, then things are going to get more difficult.   

> 2. Install a global `onerror` handler that will catch all exceptions (much like the 
> global signal handler on linux).  See https://nodejs.org/api/process.html#event-uncaughtexception.

That says it's unsafe to resume, so that probably also applies to the case above? 

Thanks,

John
John

Brooke Vibber

unread,
1:52 PM (2 hours ago) 1:52 PM
to emscripte...@googlegroups.com
On Mon, Sep 8, 2025 at 3:40 AM John Dallman <jgdats...@gmail.com> wrote:
> 1. Wrap your calls in a JS try/catch and inspect the exception you caught 
> and then (somehow?) continue with the test suite.  (This is what Brooke 
> suggested already)

I have a couple of questions about that:

When the catch gets called, is it called by the same thread as did the segmentation violation? 

Yes, the try/catch stays in the same thread, it's just up the call stack. This means if you trap on a pthread, you would need a try/catch on the pthread setup, not in your test harness that calls things on the main thread. I don't know how to set this up offhand.

Is that thread's stack still intact? Has it been unwound? 

If it is the same thread and the stack is intact, then I should be able to call the function within the library that does signal handling on other platforms, and have it do its longjmp()s. If those conditions don't apply, then things are going to get more difficult.   

The WASM code will stop at the point of the trap, and the WASM stack will be unwound back to the call point where the JS catch can grab it. However the *C stack* is not unwound, nor are C++ exception handlers called, because the WASM runtime knows nothing about these (they are creations of the C ABI and not inherent parts of WASM).

So you can expect to end up with linear memory in an inconsistent state. You certainly can't recover execution from the next instruction or anything like that. You know an error took place during the call, and that the module is likely inconsistent and unusable now.
 

> 2. Install a global `onerror` handler that will catch all exceptions (much like the 
> global signal handler on linux).  See https://nodejs.org/api/process.html#event-uncaughtexception.

That says it's unsafe to resume, so that probably also applies to the case above? 

Correct.

-- brooke 

Reply all
Reply to author
Forward
0 new messages