RESOLVED FIXED 165834
WebAssembly: WasmB3IRGenerator should throw exceptions instead of crash
https://bugs.webkit.org/show_bug.cgi?id=165834
Summary WebAssembly: WasmB3IRGenerator should throw exceptions instead of crash
Saam Barati
Reported 2016-12-13 17:37:41 PST
Should be an easy fix. My plan is to introduce a helper to WasmB3IRGenerator that just jumps to a particular location to throw an exception with some enum as an argument to the function call. The function call can switch on the enum to provide a nice error message.
Attachments
WIP (9.71 KB, patch)
2016-12-14 12:27 PST, Saam Barati
no flags
WIP RFC (21.01 KB, patch)
2016-12-14 13:15 PST, Saam Barati
no flags
patch (38.55 KB, patch)
2016-12-15 17:41 PST, Saam Barati
no flags
patch (38.55 KB, patch)
2016-12-15 17:43 PST, Saam Barati
keith_miller: review-
patch (43.70 KB, patch)
2016-12-15 23:29 PST, Saam Barati
no flags
patch (47.58 KB, patch)
2016-12-15 23:34 PST, Saam Barati
no flags
Saam Barati
Comment 1 2016-12-14 12:27:55 PST
Created attachment 297110 [details] WIP I just need to fill in the various exception checks now.
Saam Barati
Comment 2 2016-12-14 13:15:27 PST
JF Bastien
Comment 3 2016-12-14 13:25:48 PST
Comment on attachment 297118 [details] WIP RFC View in context: https://bugs.webkit.org/attachment.cgi?id=297118&action=review Looks good. > JSTests/wasm/function-tests/exceptions.js:20 > + .Function("foo", 0 /*['i32', 'i32'] => 'i32'*/) Why not let these auto-populate in the Type section? > JSTests/wasm/function-tests/exceptions.js:23 > + .CallIndirect(1, 0) // calling function of type ['i32'] => 'i32' Can call indirect also take a type signature? > JSTests/wasm/function-tests/exceptions.js:57 > + assert.throws(() => foo(1 + i, i), WebAssembly.RuntimeError, "Out of bounds call_indirect"); Check that negative also fails. > Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:282 > + // The thing that jumps here will move ExceptionType into the argumentGPR1 and jump here. Can you use a named constant for that GPR so it's more obvious / can't go out of sync? > Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:297 > + switch (type) { Can we just make these a static const char * array next to the enum? > Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:308 > + message = "Can't make a call_indirect to a null table entry"; I'd drop "can't make a " from the message: the issue is "call_indirect to a null table entry". > Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:311 > + message = "Tried to make a call_indirect to a signature that does not match"; Similar here: "call_indirect signature mismatch". > Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:859 > + // FIXME: is this even possible? Do we need to zero-fill missing arguments, and drop extra ones?
Saam Barati
Comment 4 2016-12-15 14:24:38 PST
Comment on attachment 297118 [details] WIP RFC View in context: https://bugs.webkit.org/attachment.cgi?id=297118&action=review >> JSTests/wasm/function-tests/exceptions.js:23 >> + .CallIndirect(1, 0) // calling function of type ['i32'] => 'i32' > > Can call indirect also take a type signature? We could make it do that but lets make that a separate patch >> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:282 >> + // The thing that jumps here will move ExceptionType into the argumentGPR1 and jump here. > > Can you use a named constant for that GPR so it's more obvious / can't go out of sync? That constant would only be used in one place. I don't think it's worth it. >> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:297 >> + switch (type) { > > Can we just make these a static const char * array next to the enum? I'm not a big fan of this. I could just make all exception types a macro? >> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:308 >> + message = "Can't make a call_indirect to a null table entry"; > > I'd drop "can't make a " from the message: the issue is "call_indirect to a null table entry". sounds good. >> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:311 >> + message = "Tried to make a call_indirect to a signature that does not match"; > > Similar here: "call_indirect signature mismatch". sounds good >> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:859 >> + // FIXME: is this even possible? > > Do we need to zero-fill missing arguments, and drop extra ones? No idea. I'll leave this for another patch.
Keith Miller
Comment 5 2016-12-15 14:35:39 PST
Comment on attachment 297118 [details] WIP RFC View in context: https://bugs.webkit.org/attachment.cgi?id=297118&action=review > Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:747 > + check->setGenerator([=] (CCallHelpers& jit, const B3::StackmapGenerationParams&) { Doesn't this copy the entire B3IRGenerator for each exception generator? Maybe this should just capture the address of m_exceptionHandler? >>> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:859 >>> + // FIXME: is this even possible? >> >> Do we need to zero-fill missing arguments, and drop extra ones? > > No idea. I'll leave this for another patch. I don't think this is possible anymore. > Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:923 > + return jsEntrypoint->codeRef(); Aren't you required to keep the byproducts alive along with the code ref?
Saam Barati
Comment 6 2016-12-15 17:41:57 PST
Saam Barati
Comment 7 2016-12-15 17:43:46 PST
WebKit Commit Bot
Comment 8 2016-12-15 17:44:52 PST
Attachment 297266 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:328: Non-label code inside switch statements should be indented. [whitespace/indent] [4] Total errors found: 1 in 12 files If any of these errors are false positives, please file a bug against check-webkit-style.
Keith Miller
Comment 9 2016-12-15 18:31:38 PST
Comment on attachment 297266 [details] patch View in context: https://bugs.webkit.org/attachment.cgi?id=297266&action=review It seems like emitExceptionHandler should only be called if it's actually needed. I think I would add a member, m_needsExceptionHandler that is set in emitExceptionCheck. > Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:296 > + jit.addLinkTask([=] (LinkBuffer& linkBuffer) { It's a little weird that you capture the entire B3IRGenerator here.
Saam Barati
Comment 10 2016-12-15 18:37:37 PST
Comment on attachment 297266 [details] patch View in context: https://bugs.webkit.org/attachment.cgi?id=297266&action=review >> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:296 >> + jit.addLinkTask([=] (LinkBuffer& linkBuffer) { > > It's a little weird that you capture the entire B3IRGenerator here. What do you propose instead?
Saam Barati
Comment 11 2016-12-15 18:38:15 PST
(In reply to comment #9) > Comment on attachment 297266 [details] > patch > > View in context: > https://bugs.webkit.org/attachment.cgi?id=297266&action=review > > It seems like emitExceptionHandler should only be called if it's actually > needed. I think I would add a member, m_needsExceptionHandler that is set in > emitExceptionCheck. Ok, sounds good.
Keith Miller
Comment 12 2016-12-15 18:40:21 PST
Comment on attachment 297266 [details] patch View in context: https://bugs.webkit.org/attachment.cgi?id=297266&action=review >>> Source/JavaScriptCore/wasm/WasmB3IRGenerator.cpp:296 >>> + jit.addLinkTask([=] (LinkBuffer& linkBuffer) { >> >> It's a little weird that you capture the entire B3IRGenerator here. > > What do you propose instead? I would just capture m_exceptionHandler.
Oliver Hunt
Comment 13 2016-12-15 23:20:05 PST
Could you explain in more detail what your goal is here? My principle concern is that it seems like you're trying to continue executing despite having generated incorrect code. The correct course of action is to terminate, so there needs to be a very compelling reason not to. If it's for debugging or similar, i'd rather you replace "call a function with an enum" with jit.move($enumValue, regT0) hit.breakpoint() or similar.
Saam Barati
Comment 14 2016-12-15 23:28:34 PST
(In reply to comment #13) > Could you explain in more detail what your goal is here? My principle > concern is that it seems like you're trying to continue executing despite > having generated incorrect code. > > The correct course of action is to terminate, so there needs to be a very > compelling reason not to. > > If it's for debugging or similar, i'd rather you replace "call a function > with an enum" with > > jit.move($enumValue, regT0) > hit.breakpoint() > > or similar. This is not "trying to continue executing despite having generated incorrect code". There are operations in Wasm that *are* supposed to throw real JS exceptions. For example: - division by zero - out of bounds memory access - Trying to call an "imported" function with a signature mismatch. So these are things that need to be validated at runtime because they can't be validated at compile time. Therefore, we must validate such things and throw exceptions if the validation fails.
Saam Barati
Comment 15 2016-12-15 23:29:40 PST
Created attachment 297302 [details] patch All Wasm functions now share the exact same exception thrower thunk. This should reduce code size by at least 20 bytes per function.
Saam Barati
Comment 16 2016-12-15 23:31:28 PST
(In reply to comment #14) > (In reply to comment #13) > > Could you explain in more detail what your goal is here? My principle > > concern is that it seems like you're trying to continue executing despite > > having generated incorrect code. > > > > The correct course of action is to terminate, so there needs to be a very > > compelling reason not to. > > > > If it's for debugging or similar, i'd rather you replace "call a function > > with an enum" with > > > > jit.move($enumValue, regT0) > > hit.breakpoint() > > > > or similar. > > This is not "trying to continue executing despite having generated incorrect > code". > There are operations in Wasm that *are* supposed to throw real JS exceptions. > For example: > - division by zero > - out of bounds memory access > - Trying to call an "imported" function with a signature mismatch. > > So these are things that need to be validated at runtime because they can't > be validated at compile time. Therefore, we must validate such things and > throw exceptions if the validation fails. To elaborate a bit, when Keith began to implement Wasm, he didn't write the infrastructure needed to throw exceptions. I recently made our unwinder Wasm-aware, so we can now throw exceptions from (and through) Wasm code. Keith originally added breakpoints() for places that needed to throw exceptions. This patch just fills in some of the various breakpoints() with jumps to the thing that throws an exception.
Saam Barati
Comment 17 2016-12-15 23:34:55 PST
Created attachment 297303 [details] patch rebased
WebKit Commit Bot
Comment 18 2016-12-15 23:37:01 PST
Attachment 297303 [details] did not pass style-queue: ERROR: Source/JavaScriptCore/wasm/WasmExceptionType.h:53: Non-label code inside switch statements should be indented. [whitespace/indent] [4] Total errors found: 1 in 15 files If any of these errors are false positives, please file a bug against check-webkit-style.
JF Bastien
Comment 19 2016-12-16 10:14:26 PST
Comment on attachment 297303 [details] patch Looks good.
Keith Miller
Comment 20 2016-12-16 10:53:42 PST
Comment on attachment 297303 [details] patch r=me.
WebKit Commit Bot
Comment 21 2016-12-16 11:24:44 PST
Comment on attachment 297303 [details] patch Clearing flags on attachment: 297303 Committed r209928: <http://trac.webkit.org/changeset/209928>
WebKit Commit Bot
Comment 22 2016-12-16 11:24:49 PST
All reviewed patches have been landed. Closing bug.
Oliver Hunt
Comment 23 2016-12-16 11:34:11 PST
> To elaborate a bit, when Keith began to implement Wasm, he didn't write the > infrastructure needed to throw exceptions. I recently made our unwinder > Wasm-aware, so we can now throw exceptions from (and through) Wasm code. > Keith originally added breakpoints() for places that needed to throw > exceptions. This patch just fills in some of the various breakpoints() with > jumps to the thing that throws an exception. Ok, i understand now -- it was not clear to me that the existing breakpoints were (essentially) for unimplemented checks, so i interpreted the breakpoints as being the standard RELEASE_ASSERT of the jit work. That said i wonder if we could do something to make the existing jit asserts plant info into the crash trace (write error code to register or similar)
Saam Barati
Comment 24 2016-12-16 11:38:38 PST
(In reply to comment #23) > > To elaborate a bit, when Keith began to implement Wasm, he didn't write the > > infrastructure needed to throw exceptions. I recently made our unwinder > > Wasm-aware, so we can now throw exceptions from (and through) Wasm code. > > Keith originally added breakpoints() for places that needed to throw > > exceptions. This patch just fills in some of the various breakpoints() with > > jumps to the thing that throws an exception. > > Ok, i understand now -- it was not clear to me that the existing breakpoints > were (essentially) for unimplemented checks, so i interpreted the > breakpoints as being the standard RELEASE_ASSERT of the jit work. > > That said i wonder if we could do something to make the existing jit asserts > plant info into the crash trace (write error code to register or similar) Yeah for things that are really crashes, we should use the JIT assert mechanism that's already in place instead of the breakpoint().
Note You need to log in before you can comment on or make changes to this bug.