Bug 48485

Summary: Crash in Function.prototype.call.apply
Product: WebKit Reporter: Charles L <bz>
Component: JavaScriptCoreAssignee: Nobody <webkit-unassigned>
Status: RESOLVED FIXED    
Severity: Normal CC: ap, ggaren, oliver, zherczeg
Priority: P1 Keywords: InRadar, Regression
Version: 528+ (Nightly build)   
Hardware: Mac (Intel)   
OS: OS X 10.5   
Attachments:
Description Flags
Crash test
none
Patch barraclough: review+

Charles L
Reported 2010-10-27 16:55:47 PDT
Created attachment 72114 [details] Crash test The browser crashes when the following function is run: Function.prototype.call.apply(fn, arguments) where fn is any function, and the arguments object has at least 3 items. I have experienced this only in recent nightly builds, using Mac OS X 10.5.8. The crash doesn't happen while the script debugger is on, or when it is run from the location bar, or when fn has been successfully called before.
Attachments
Crash test (423 bytes, text/html)
2010-10-27 16:55 PDT, Charles L
no flags
Patch (9.44 KB, patch)
2010-11-03 16:17 PDT, Oliver Hunt
barraclough: review+
Alexey Proskuryakov
Comment 1 2010-10-27 21:51:03 PDT
Confirmed with a local debug build of r70400. #0 0x101ccc9d0 in WTF::RefPtr<JSC::JSGlobalData>::get at RefPtr.h:59 #1 0x101d6cc95 in JSC::JSGlobalObject::globalData at JSGlobalObject.h:279 #2 0x101ce66e9 in JSC::Parser::parse<JSC::FunctionBodyNode> at Parser.h:87 #3 0x101cdee51 in JSC::FunctionExecutable::compileForCallInternal at Executable.cpp:181 #4 0x101c78d00 in JSC::FunctionExecutable::compileForCall at Executable.h:315 #5 0x101cf1bae in JSC::Interpreter::executeCall at Interpreter.cpp:795 #6 0x101ca909f in JSC::call at CallData.cpp:38 #7 0x101ceb580 in JSC::functionProtoFuncCall at FunctionPrototype.cpp:147 #8 0x5711998001aa in ?? #9 0x101cf6a86 in JSC::JITCode::execute at JITCode.h:77 #10 0x101cf2bf1 in JSC::Interpreter::execute at Interpreter.cpp:746 #11 0x101cc26c7 in JSC::evaluate at Completion.cpp:63 #12 0x10310b4a0 in WebCore::JSMainThreadExecState::evaluate at JSMainThreadExecState.h:54
Geoffrey Garen
Comment 2 2010-10-28 11:55:17 PDT
Zoltan Herczeg
Comment 3 2010-10-28 15:00:40 PDT
Interpreter::executeCall CallFrame* newCallFrame = CallFrame::create(oldEnd); size_t dst = 0; newCallFrame->r(0) = thisValue; ArgList::const_iterator end = args.end(); for (ArgList::const_iterator it = args.begin(); it != end; ++it) newCallFrame->r(++dst) = *it; oldEnd < callFrame, so newCallFrame->r(...) overwrites callframe fields. I will continue the debugging tomorrow morning. Hopefully I could find a fix.
Zoltan Herczeg
Comment 4 2010-10-29 09:33:24 PDT
Hi guys, actually I am little confused with this bug as I don't exactly remember how JS call system should work, and I need a little help. This is a simplified example: function g() { Function.prototype.call.apply(g, arguments); } g(0, 0, 0) The byte-code of function g(): [ 0] enter [ 1] init_lazy_reg r1 [ 3] init_lazy_reg r0 [ 5] resolve_global r2, Function(@id0) [ 10] get_by_id r2, r2, prototype(@id1) [ 18] get_by_id r2, r2, call(@id2) [ 26] get_by_id r3, r2, apply(@id3) [ 34] jneq_ptr r3, r-1220017856, 22(->56) [ 38] mov r4, r2 [ 41] get_global_var r6, -6 [ 44] mov r7, r1 [ 47] load_varargs r5, r7 [ 50] call_varargs r4, r5, 12 [ 54] jmp 17(->71) [ 56] mov r4, r2 [ 59] get_global_var r5, -6 [ 62] create_arguments r1 [ 64] mov r6, r1 [ 67] call r3, 3, 13 [ 71] ret undefined(@k0) Before the "enter", the RegisterFile is extended, since the 'g' expects 0 parameter and got 3. Thus op_call_arityCheck() set the registerFile->m_end to 0x..100 (memory returned by mmap is always page aligned, thus the last 3 digit is always the same). As far as I remember, this should be the end of the memory used by the JS functon, shouldn't it? The emit_op_load_varargs does not call the helper-stub function, it just extend %edi (call frame ptr) to 0x...118, which is 3 registers beyond the end of the registerFile, and should be never written by JIT code, shoudn't it? call_varargs pass %edi as the call frame, and in Interpreter::executeCall oldEnd still points to 0x..100, and the callFrame to 0x...118. (I think an assert would be useful here). for (ArgList::const_iterator it = args.begin(); it != end; ++it) newCallFrame->r(++dst) = *it; The 'for' above overwrites callFrame members (still in Interpreter::executeCall). As far as I remember, registerFile->m_end points after the last available register, and no writes should happen after it. Thus I would blame emit_op_load_varargs at the moment, but I would be curious about your opinion.
Oliver Hunt
Comment 5 2010-11-03 14:16:37 PDT
I don't see any error in load_varargs, i see the badness in call_varargs, the callframe register is extended to include the registerOffset, but loadvarargs has not bounds checked that extension, only the space for arguments.
Oliver Hunt
Comment 6 2010-11-03 15:09:01 PDT
I have a fix, just building webkit
Oliver Hunt
Comment 7 2010-11-03 16:17:40 PDT
Oliver Hunt
Comment 8 2010-11-03 16:43:07 PDT
Charles L
Comment 9 2010-11-05 20:33:25 PDT
Thank you!
Note You need to log in before you can comment on or make changes to this bug.