-
Notifications
You must be signed in to change notification settings - Fork 171
Fix fully initializing JSStackFrame #328
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@bnoordhuis @chqrlie PTAL. |
quickjs.c
Outdated
@@ -14574,6 +14574,7 @@ static JSValue JS_CallInternal(JSContext *caller_ctx, JSValue func_obj, | |||
if (js_check_stack_overflow(rt, alloca_size)) | |||
return JS_ThrowStackOverflow(caller_ctx); | |||
|
|||
sf->cur_pc = NULL; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not the correct fix: you should instead initialize sf->cur_pc
along with pc
on line 14603, before the stack frame gets linked.
See the explanation in the issue conversation #323
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will do!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You mean to initialize it to NULL? Or to find all places in the opcode handling where it needs to be set to pc? Both?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, not to NULL
:
pc = b->byte_code_buf;
s->cur_pc = pc;
This allows build_backtrace()
to find the source position for the code that ultimately caused the error to be thrown even if it is triggered by an opcode that did not set sf->cur
prior to calling some function in JS_CallInternal()
and no other opcode did it before said call. Your minimal reproducible example only generated 3 OP_get_xxx
opcodes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Many more opcodes may need to set sf->cur_pc
... almost all of those that test for exceptions and goto exception
if we want a debugger to track the call chain properly when triggered by the JS_Error
constructor.
Without such a debugger (wish I could spend more time on that) we should set sf->cur_pc = pc
for all opcodes that may cause a recursive call to JS_CallInternal()
.
OP_get_length
OP_push_this
before the call toJS_ToObject
OP_throw_error
OP_apply_eval
OP_import
OP_get_var_undef
,OP_get_var
,OP_put_var
,OP_put_var_init
,OP_put_var_strict
OP_define_var
,OP_define_func
OP_for_in_start
,OP_for_in_next
,OP_for_of_start
,OP_for_of_next
,OP_for_await_of_start
OP_iterator_next
OP_iterator_call
OP_get_private_field
,OP_put_private_field
OP_get_array_el
,OP_get_array_el2
,OP_get_ref_value
,OP_get_super_value
OP_put_array_el
,OP_put_ref_value
,OP_put_super_value
OP_append
,OP_copy_data_properties
OP_add
before the call tojs_add_slow()
OP_add_loc
before the calls toJS_ToPrimitiveFree()
andjs_add_slow()
- at the label
binary_arith_slow:
OP_plus
,OP_neg
,OP_inc
,OP_dec
before the calls tojs_unary_arith_slow()
OP_post_inc
,OP_post_dec
OP_inc_loc
,OP_dec_loc
before the calls tojs_unary_arith_slow()
OP_not
before the call tojs_not_slow()
OP_shl
,OP_shr
,OP_sar
,OP_and
,OP_or
,OP_xor
before the calls tojs_binary_logic_slow()
- if the
OP_CMP
macro before theslow_call
OP_to_object
- all the
OP_with_xxx
opcodes
Instead of carefully, I should have written painstakingly :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wondered if setting cur_pc before the dispatch could have adverse affects.
I don't think so. The stack frame is just uninitialized there and it is a potential problem.
The alternate path where the generator function is restarted gets pc
from sf->cur_pc
and branches to restart:
, so no conflict there either.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alright. So then it looks like we could get away with doing that instead of setting it in each opcode. I'll dig some more tonight!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No, initializing it before the switch is a safety measure to avoid a potential crash if sf->cur_pc
gets used without having been set, but it is not sufficient for build_backtrace()
to get the correct source location when the stack trace is built.
We use pc
instead of sf->cur_pc
for the dispatch to optimize the code and let the compiler use a register instead of updating the stack frame for each opcode. Same for sp
and sf->cur_sp
.
sf->cur_pc
must be synchronized with pc
before a recursive call to JS_CallInternal
so the stack frame contains the proper address for build_backtrace()
to compute the source location of the caller.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see. In that case it might be better to initialize it to NULL and add an asset inside build_backtrace so we can catch 'em all. WDYT?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We could do that. sf->cur_pc
should not be null for bytecoded functions, but is null for C functions.
6e65cb7
to
9fa6978
Compare
Valgrind run on this branch: https://github.com/quickjs-ng/quickjs/actions/runs/8404692156 |
Caught some more, new CI: https://github.com/quickjs-ng/quickjs/actions/runs/8405035727 |
The culprit is |
Alright, got the tests green! There might me more places missing, but the test suite does not exercise those :-/ I kicked off a Valgrind run too, let's see 😅 |
Valgrind is green too: https://github.com/quickjs-ng/quickjs/actions/runs/8409873927 |
Interesting to see that this fixes a v8 test... |
Fixes: #323