Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(475)

Unified Diff: src/x64/builtins-x64.cc

Issue 604064: Fix stack corruption when calling non-function. (Closed)
Patch Set: Created 10 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/runtime.js ('k') | src/x64/codegen-x64.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/x64/builtins-x64.cc
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index 8482023f361c1effe6e0211cdff1bf8be051b592..de30646d92c2967ecb0bdb58e637f2997fb22e0d 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -185,14 +185,14 @@ void Builtins::Generate_ArgumentsAdaptorTrampoline(MacroAssembler* masm) {
void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
// Stack Layout:
- // rsp: return address
- // +1: Argument n
- // +2: Argument n-1
+ // rsp[0]: Return address
+ // rsp[1]: Argument n
+ // rsp[2]: Argument n-1
// ...
- // +n: Argument 1 = receiver
- // +n+1: Argument 0 = function to call
+ // rsp[n]: Argument 1
+ // rsp[n+1]: Receiver (function to call)
//
- // rax contains the number of arguments, n, not counting the function.
+ // rax contains the number of arguments, n, not counting the receiver.
//
// 1. Make sure we have at least one argument.
{ Label done;
@@ -205,31 +205,26 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&done);
}
- // 2. Get the function to call from the stack.
- { Label done, non_function, function;
+ // 2. Get the function to call (passed as receiver) from the stack, check
+ // if it is a function.
+ Label non_function;
+ { Label function;
// The function to call is at position n+1 on the stack.
- __ movq(rdi, Operand(rsp, rax, times_pointer_size, +1 * kPointerSize));
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
__ JumpIfSmi(rdi, &non_function);
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
- __ j(equal, &function);
-
- // Non-function called: Clear the function to force exception.
- __ bind(&non_function);
- __ xor_(rdi, rdi);
- __ jmp(&done);
-
- // Function called: Change context eagerly to get the right global object.
+ __ j(not_equal, &non_function);
__ bind(&function);
- __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
-
- __ bind(&done);
}
- // 3. Make sure first argument is an object; convert if necessary.
- { Label call_to_object, use_global_receiver, patch_receiver, done;
- __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
+ // 3a. Patch the first argument if necessary when calling a function.
+ Label shift_arguments;
+ { Label convert_to_object, use_global_receiver, patch_receiver;
+ // Change context eagerly in case we need the global receiver.
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
- __ JumpIfSmi(rbx, &call_to_object);
+ __ movq(rbx, Operand(rsp, rax, times_pointer_size, 0));
+ __ JumpIfSmi(rbx, &convert_to_object);
__ CompareRoot(rbx, Heap::kNullValueRootIndex);
__ j(equal, &use_global_receiver);
@@ -237,31 +232,28 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ j(equal, &use_global_receiver);
__ CmpObjectType(rbx, FIRST_JS_OBJECT_TYPE, rcx);
- __ j(below, &call_to_object);
+ __ j(below, &convert_to_object);
__ CmpInstanceType(rcx, LAST_JS_OBJECT_TYPE);
- __ j(below_equal, &done);
-
- __ bind(&call_to_object);
- __ EnterInternalFrame(); // preserves rax, rbx, rdi
+ __ j(below_equal, &shift_arguments);
- // Store the arguments count on the stack (smi tagged).
+ __ bind(&convert_to_object);
+ __ EnterInternalFrame(); // In order to preserve argument count.
__ Integer32ToSmi(rax, rax);
__ push(rax);
- __ push(rdi); // save edi across the call
__ push(rbx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ movq(rbx, rax);
- __ pop(rdi); // restore edi after the call
- // Get the arguments count and untag it.
__ pop(rax);
__ SmiToInteger32(rax, rax);
-
__ LeaveInternalFrame();
+ // Restore the function to rdi.
+ __ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
__ jmp(&patch_receiver);
- // Use the global receiver object from the called function as the receiver.
+ // Use the global receiver object from the called function as the
+ // receiver.
__ bind(&use_global_receiver);
const int kGlobalIndex =
Context::kHeaderSize + Context::GLOBAL_INDEX * kPointerSize;
@@ -273,41 +265,47 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
__ bind(&patch_receiver);
__ movq(Operand(rsp, rax, times_pointer_size, 0), rbx);
- __ bind(&done);
+ __ jmp(&shift_arguments);
}
- // 4. Check that the function really is a function.
- { Label real_function;
- __ testq(rdi, rdi);
- __ j(not_zero, &real_function);
- __ xor_(rbx, rbx);
- // CALL_NON_FUNCTION will expect to find the non-function callee on the
- // expression stack of the caller. Transfer it from receiver to the
- // caller's expression stack (and make the first argument the receiver
- // for CALL_NON_FUNCTION) by decrementing the argument count.
- __ decq(rax);
- __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
- __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
- RelocInfo::CODE_TARGET);
- __ bind(&real_function);
- }
+ // 3b. Patch the first argument when calling a non-function. The
+ // CALL_NON_FUNCTION builtin expects the non-function callee as
+ // receiver, so overwrite the first argument which will ultimately
+ // become the receiver.
+ __ bind(&non_function);
+ __ movq(Operand(rsp, rax, times_pointer_size, 0), rdi);
+ __ xor_(rdi, rdi);
- // 5. Shift arguments and return address one slot down on the stack
- // (overwriting the receiver).
+ // 4. Shift arguments and return address one slot down on the stack
+ // (overwriting the original receiver). Adjust argument count to make
+ // the original first argument the new receiver.
+ __ bind(&shift_arguments);
{ Label loop;
__ movq(rcx, rax);
__ bind(&loop);
__ movq(rbx, Operand(rsp, rcx, times_pointer_size, 0));
__ movq(Operand(rsp, rcx, times_pointer_size, 1 * kPointerSize), rbx);
__ decq(rcx);
- __ j(not_sign, &loop);
+ __ j(not_sign, &loop); // While non-negative (to copy return address).
__ pop(rbx); // Discard copy of return address.
__ decq(rax); // One fewer argument (first argument is new receiver).
}
- // 6. Get the code to call from the function and check that the number of
- // expected arguments matches what we're providing.
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
+ { Label function;
+ __ testq(rdi, rdi);
+ __ j(not_zero, &function);
+ __ xor_(rbx, rbx);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
+ __ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
+ RelocInfo::CODE_TARGET);
+ __ bind(&function);
+ }
+
+ // 5b. Get the code to call from the function and check that the number of
+ // expected arguments matches what we're providing. If so, jump
+ // (tail-call) to the code in register edx without checking arguments.
__ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
__ movsxlq(rbx,
FieldOperand(rdx, SharedFunctionInfo::kFormalParameterCountOffset));
@@ -318,7 +316,6 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) {
Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
RelocInfo::CODE_TARGET);
- // 7. Jump (tail-call) to the code in register edx without checking arguments.
ParameterCount expected(0);
__ InvokeCode(rdx, expected, expected, JUMP_FUNCTION);
}
@@ -905,7 +902,11 @@ void Builtins::Generate_JSConstructCall(MacroAssembler* masm) {
// edi: called object
// eax: number of arguments
__ bind(&non_function_call);
- // Set expected number of arguments to zero (not changing eax).
+ // CALL_NON_FUNCTION expects the non-function constructor as receiver
+ // (instead of the original receiver from the call site). The receiver is
+ // stack element argc+1.
+ __ movq(Operand(rsp, rax, times_pointer_size, kPointerSize), rdi);
+ // Set expected number of arguments to zero (not changing rax).
__ movq(rbx, Immediate(0));
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION_AS_CONSTRUCTOR);
__ Jump(Handle<Code>(builtin(ArgumentsAdaptorTrampoline)),
« no previous file with comments | « src/runtime.js ('k') | src/x64/codegen-x64.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698