RESOLVED FIXED 126088
CStack: callToJavaScript should do stack check for incoming args
https://bugs.webkit.org/show_bug.cgi?id=126088
Summary CStack: callToJavaScript should do stack check for incoming args
Mark Lam
Reported 2013-12-20 15:17:16 PST
This change will achieve the following: 1. Change callToJavaScript()'s prototype to: EncodedJSValue callToJavaScript(void*, VM*, ProtoCallFrame*); We now pass VM* instead of &vm.topCallFrame for the second argument. This gives us greater utility out of that arg. 2. Change callToJavaScript() to do a stack check to ensure that we have adequate stack space to copy all the args from the protoCallFrame. If not, it'll throw a StackOverflowError. 3. Removed JSStack::entryCheck() and calls to it. callToJavaScript now takes care of the stack check that ensures adequate stack space for incoming args. callToJavaScript does assume that we have adequate stack space for the VMEntrySentinelFrame, but that is ensured by our stack host zone.
Attachments
the patch. (16.70 KB, patch)
2013-12-20 15:32 PST, Mark Lam
msaboff: review+
Mark Lam
Comment 1 2013-12-20 15:32:53 PST
Created attachment 219804 [details] the patch.
Mark Lam
Comment 2 2013-12-20 16:01:10 PST
Comment on attachment 219804 [details] the patch. View in context: https://bugs.webkit.org/attachment.cgi?id=219804&action=review > Source/JavaScriptCore/llint/LLIntSlowPaths.h:126 > +extern "C" void llint_throw_stack_overflow_error(VM*, ProtoCallFrame*); The style checker was complaining about the use of underscores here. While the use of underscores is consistent with how LLINT slow paths roll, should I be switching to the webkit style of camel case here?
Mark Lam
Comment 3 2013-12-20 16:54:31 PST
Landed in r160947 on the jsCStack branch: <http://trac.webkit.org/r160947>. A review is still needed.
Michael Saboff
Comment 4 2013-12-20 17:08:49 PST
Comment on attachment 219804 [details] the patch. View in context: https://bugs.webkit.org/attachment.cgi?id=219804&action=review r=me modulo that verify that you can safely use vm->topCallFrame in llint_throw_stack_overflow_error > Source/JavaScriptCore/llint/LLIntSlowPaths.cpp:1394 > + ExecState* exec = vm->topCallFrame; Please verify that we can count of vm->topCallFrame being valid or null. I thought we could only count on its value when we call out to C++. The JS caller will set topCallFrame before making the call. I don't think it ever restores it to a prior value. Seems it could be bad if a JS function A calls B and B calls out to a helper. B is in now in topCallFrame. B exits and A now makes a call that happens to be a native function. I don't think that topCallFrame will get updated again before making the native call.
Mark Lam
Comment 5 2013-12-20 17:20:59 PST
(In reply to comment #4) > (From update of attachment 219804 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=219804&action=review > > r=me modulo that verify that you can safely use vm->topCallFrame in llint_throw_stack_overflow_error > > > Source/JavaScriptCore/llint/LLIntSlowPaths.cpp:1394 > > + ExecState* exec = vm->topCallFrame; > > Please verify that we can count of vm->topCallFrame being valid or null. I thought we could only count on its value when we call out to C++. The JS caller will set topCallFrame before making the call. I don't think it ever restores it to a prior value. Seems it could be bad if a JS function A calls B and B calls out to a helper. B is in now in topCallFrame. B exits and A now makes a call that happens to be a native function. I don't think that topCallFrame will get updated again before making the native call. Here are the critical check points: 1. JSStack initializes it to 0 initially. 2. callToJavaScript saves topCallFrame in VMEntrySentinelFrame::ScopeChain upon entry, and restores it before returning. 3. In LLInt slow paths, the NativeCallFrameTracer sets topCallFrame to the current CallFrame before executing native code. 4. JIT operations also use NativeCallFrameTracer to set topCallFrame. Note that in callToJavaScript, we’ve only just come from C++ code. Conceptually, we haven’t pushed any JS frames onto the stack yet. Hence, whatever value topCallFrame had before we called callToJavaScript is the topCallFrame value that we want. And by design, that’s the one we’re getting (because we haven’t set it to anything else yet). We are good to go.
Michael Saboff
Comment 6 2013-12-20 17:53:52 PST
(In reply to comment #5) > (In reply to comment #4) > > (From update of attachment 219804 [details] [details]) > > View in context: https://bugs.webkit.org/attachment.cgi?id=219804&action=review > > > > r=me modulo that verify that you can safely use vm->topCallFrame in llint_throw_stack_overflow_error > > > > > Source/JavaScriptCore/llint/LLIntSlowPaths.cpp:1394 > > > + ExecState* exec = vm->topCallFrame; > > > > Please verify that we can count of vm->topCallFrame being valid or null. I thought we could only count on its value when we call out to C++. The JS caller will set topCallFrame before making the call. I don't think it ever restores it to a prior value. Seems it could be bad if a JS function A calls B and B calls out to a helper. B is in now in topCallFrame. B exits and A now makes a call that happens to be a native function. I don't think that topCallFrame will get updated again before making the native call. > > Here are the critical check points: > > 1. JSStack initializes it to 0 initially. > 2. callToJavaScript saves topCallFrame in VMEntrySentinelFrame::ScopeChain upon entry, and restores it before returning. > 3. In LLInt slow paths, the NativeCallFrameTracer sets topCallFrame to the current CallFrame before executing native code. > 4. JIT operations also use NativeCallFrameTracer to set topCallFrame. > > Note that in callToJavaScript, we’ve only just come from C++ code. Conceptually, we haven’t pushed any JS frames onto the stack yet. Hence, whatever value topCallFrame had before we called callToJavaScript is the topCallFrame value that we want. And by design, that’s the one we’re getting (because we haven’t set it to anything else yet). > > We are good to go. There is also the reentrant case. When we start off, topCallFrame should be 0. We save it in callToJavaScript. The called JS can make a C++ call that will reenter callToJavaScript (e.g. eval). In that case topCallFrame is fine. I think we're okay, because callToJavaScript already relies on topCallFrame being valid.
Mark Lam
Comment 7 2013-12-23 09:47:26 PST
Thanks for the review. Review status updated in r160950: <http://trac.webkit.org/r160950>.
Geoffrey Garen
Comment 8 2014-01-02 12:39:39 PST
Comment on attachment 219804 [details] the patch. View in context: https://bugs.webkit.org/attachment.cgi?id=219804&action=review > Source/JavaScriptCore/llint/LLIntSlowPaths.cpp:1392 > +void llint_throw_stack_overflow_error(VM* vm, ProtoCallFrame* protoFrame) Is there anything that prevents us from passing ExecState* to this function? That would be much better. Then, we could reason about this code without a four-point logic map. Also, that's how other llint_ functions work.
Mark Lam
Comment 9 2014-01-02 12:46:05 PST
(In reply to comment #8) > (From update of attachment 219804 [details]) > View in context: https://bugs.webkit.org/attachment.cgi?id=219804&action=review > > > Source/JavaScriptCore/llint/LLIntSlowPaths.cpp:1392 > > +void llint_throw_stack_overflow_error(VM* vm, ProtoCallFrame* protoFrame) > > Is there anything that prevents us from passing ExecState* to this function? That would be much better. Then, we could reason about this code without a four-point logic map. Also, that's how other llint_ functions work. I chose to pass a VM* because there’s no guarantee that we have any valid CallFrames on the stack yet, and hence we don’t have a valid ExecState*. llint_throw_stack_overflow_error() is called from doCallToJavaScript() only if we fail to set up the CallFrame due to inadequate space for the incoming args. For other llint_ functions, they are guaranteed that there is at least one valid ExecState* that they can use.
Note You need to log in before you can comment on or make changes to this bug.