Changeset 179538 in webkit
- Timestamp:
- Feb 2, 2015 9:20:59 PM (9 years ago)
- Location:
- trunk/Source/JavaScriptCore
- Files:
-
- 6 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/JavaScriptCore/ChangeLog
r179536 r179538 1 2015-02-02 Filip Pizlo <fpizlo@apple.com> 2 3 arguments[-1] should have well-defined behavior 4 https://bugs.webkit.org/show_bug.cgi?id=141183 5 6 Reviewed by Mark Lam. 7 8 According to JSC's internal argument numbering, 0 is "this" and 1 is the first argument. 9 In the "arguments[i]" expression, "this" is not accessible and i = 0 refers to the first 10 argument. Previously we handled the bounds check in "arguments[i]" - where "arguments" is 11 statically known to be the current function's arguments object - as follows: 12 13 add 1, i 14 branchAboveOrEqual i, callFrame.ArgumentCount, slowPath 15 16 The problem with this is that if i = -1, this passes the test, and we end up accessing 17 what would be the "this" argument slot. That's wrong, since we should really be bottoming 18 out in arguments["-1"], which is usually undefined but could be anything. It's even worse 19 if the function is inlined or if we're in a constructor - in that case the "this" slot 20 could be garbage. 21 22 It turns out that we had this bug in all of our engines. 23 24 This fixes the issue by changing the algorithm to: 25 26 load32 callFrame.ArgumentCount, tmp 27 sub 1, tmp 28 branchAboveOrEqual i, tmp, slowPath 29 30 In some engines, we would have used the modified "i" (the one that had 1 added to it) for 31 the subsequent argument load; since we don't do this anymore I also had to change some of 32 the offsets on the BaseIndex arguments load. 33 34 This also includes tests that are written in such a way as to get coverage on LLInt and 35 Baseline JIT (get-my-argument-by-val-wrap-around-no-warm-up), DFG and FTL 36 (get-my-argument-by-val-wrap-around), and DFG when we're being paranoid about the user 37 overwriting the "arguments" variable (get-my-argument-by-val-safe-wrap-around). This also 38 includes off-by-1 out-of-bounds tests for each of these cases, since in the process of 39 writing the patch I broke the arguments[arguments.length] case in the DFG and didn't see 40 any test failures. 41 42 * dfg/DFGSpeculativeJIT32_64.cpp: 43 (JSC::DFG::SpeculativeJIT::compile): 44 * dfg/DFGSpeculativeJIT64.cpp: 45 (JSC::DFG::SpeculativeJIT::compile): 46 * ftl/FTLLowerDFGToLLVM.cpp: 47 (JSC::FTL::LowerDFGToLLVM::compileGetMyArgumentByVal): 48 * jit/AssemblyHelpers.h: 49 (JSC::AssemblyHelpers::offsetOfArguments): 50 (JSC::AssemblyHelpers::offsetOfArgumentsIncludingThis): Deleted. 51 * jit/JITOpcodes.cpp: 52 (JSC::JIT::emit_op_get_argument_by_val): 53 * jit/JITOpcodes32_64.cpp: 54 (JSC::JIT::emit_op_get_argument_by_val): 55 * llint/LowLevelInterpreter.asm: 56 * llint/LowLevelInterpreter32_64.asm: 57 * llint/LowLevelInterpreter64.asm: 58 * tests/stress/get-my-argument-by-val-out-of-bounds-no-warm-up.js: Added. 59 (foo): 60 * tests/stress/get-my-argument-by-val-out-of-bounds.js: Added. 61 (foo): 62 * tests/stress/get-my-argument-by-val-safe-out-of-bounds.js: Added. 63 (foo): 64 * tests/stress/get-my-argument-by-val-safe-wrap-around.js: Added. 65 (foo): 66 * tests/stress/get-my-argument-by-val-wrap-around-no-warm-up.js: Added. 67 (foo): 68 * tests/stress/get-my-argument-by-val-wrap-around.js: Added. 69 (foo): 70 1 71 2015-02-02 Filip Pizlo <fpizlo@apple.com> 2 72 -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT32_64.cpp
r179478 r179538 4348 4348 } 4349 4349 4350 m_jit.add32(TrustedImm32(1), indexGPR, resultPayloadGPR);4351 4352 4350 if (node->origin.semantic.inlineCallFrame) { 4353 4351 speculationCheck( … … 4355 4353 m_jit.branch32( 4356 4354 JITCompiler::AboveOrEqual, 4357 resultPayloadGPR,4358 Imm32(node->origin.semantic.inlineCallFrame->arguments.size() )));4355 indexGPR, 4356 Imm32(node->origin.semantic.inlineCallFrame->arguments.size() - 1))); 4359 4357 } else { 4358 m_jit.load32(JITCompiler::payloadFor(JSStack::ArgumentCount), resultPayloadGPR); 4359 m_jit.sub32(TrustedImm32(1), resultPayloadGPR); 4360 4360 speculationCheck( 4361 4361 Uncountable, JSValueRegs(), 0, 4362 m_jit.branch32( 4363 JITCompiler::AboveOrEqual, 4364 resultPayloadGPR, 4365 JITCompiler::payloadFor(JSStack::ArgumentCount))); 4362 m_jit.branch32(JITCompiler::AboveOrEqual, indexGPR, resultPayloadGPR)); 4366 4363 } 4367 4364 … … 4400 4397 m_jit.load32( 4401 4398 JITCompiler::BaseIndex( 4402 GPRInfo::callFrameRegister, resultPayloadGPR, JITCompiler::TimesEight,4403 m_jit.offsetOfArguments IncludingThis(node->origin.semantic) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)),4399 GPRInfo::callFrameRegister, indexGPR, JITCompiler::TimesEight, 4400 m_jit.offsetOfArguments(node->origin.semantic) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), 4404 4401 resultTagGPR); 4405 4402 m_jit.load32( 4406 4403 JITCompiler::BaseIndex( 4407 GPRInfo::callFrameRegister, resultPayloadGPR, JITCompiler::TimesEight,4408 m_jit.offsetOfArguments IncludingThis(node->origin.semantic) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)),4404 GPRInfo::callFrameRegister, indexGPR, JITCompiler::TimesEight, 4405 m_jit.offsetOfArguments(node->origin.semantic) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), 4409 4406 resultPayloadGPR); 4410 4407 … … 4428 4425 TrustedImm32(JSValue::EmptyValueTag))); 4429 4426 4430 m_jit.add32(TrustedImm32(1), indexGPR, resultPayloadGPR);4431 4427 if (node->origin.semantic.inlineCallFrame) { 4432 4428 slowPath.append( 4433 4429 m_jit.branch32( 4434 4430 JITCompiler::AboveOrEqual, 4435 resultPayloadGPR,4436 Imm32(node->origin.semantic.inlineCallFrame->arguments.size() )));4431 indexGPR, 4432 Imm32(node->origin.semantic.inlineCallFrame->arguments.size() - 1))); 4437 4433 } else { 4434 m_jit.load32(JITCompiler::payloadFor(JSStack::ArgumentCount), resultPayloadGPR); 4435 m_jit.sub32(TrustedImm32(1), resultPayloadGPR); 4438 4436 slowPath.append( 4439 m_jit.branch32( 4440 JITCompiler::AboveOrEqual, 4441 resultPayloadGPR, 4442 JITCompiler::payloadFor(JSStack::ArgumentCount))); 4437 m_jit.branch32(JITCompiler::AboveOrEqual, indexGPR, resultPayloadGPR)); 4443 4438 } 4444 4439 … … 4476 4471 m_jit.load32( 4477 4472 JITCompiler::BaseIndex( 4478 GPRInfo::callFrameRegister, resultPayloadGPR, JITCompiler::TimesEight,4479 m_jit.offsetOfArguments IncludingThis(node->origin.semantic) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)),4473 GPRInfo::callFrameRegister, indexGPR, JITCompiler::TimesEight, 4474 m_jit.offsetOfArguments(node->origin.semantic) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)), 4480 4475 resultTagGPR); 4481 4476 m_jit.load32( 4482 4477 JITCompiler::BaseIndex( 4483 GPRInfo::callFrameRegister, resultPayloadGPR, JITCompiler::TimesEight,4484 m_jit.offsetOfArguments IncludingThis(node->origin.semantic) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)),4478 GPRInfo::callFrameRegister, indexGPR, JITCompiler::TimesEight, 4479 m_jit.offsetOfArguments(node->origin.semantic) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)), 4485 4480 resultPayloadGPR); 4486 4481 -
trunk/Source/JavaScriptCore/dfg/DFGSpeculativeJIT64.cpp
r179478 r179538 4395 4395 } 4396 4396 4397 m_jit.add32(TrustedImm32(1), indexGPR, resultGPR);4398 4397 if (node->origin.semantic.inlineCallFrame) { 4399 4398 speculationCheck( … … 4401 4400 m_jit.branch32( 4402 4401 JITCompiler::AboveOrEqual, 4403 resultGPR,4404 Imm32(node->origin.semantic.inlineCallFrame->arguments.size() )));4402 indexGPR, 4403 Imm32(node->origin.semantic.inlineCallFrame->arguments.size() - 1))); 4405 4404 } else { 4405 m_jit.load32(JITCompiler::payloadFor(JSStack::ArgumentCount), resultGPR); 4406 m_jit.sub32(TrustedImm32(1), resultGPR); 4406 4407 speculationCheck( 4407 4408 Uncountable, JSValueRegs(), 0, 4408 m_jit.branch32( 4409 JITCompiler::AboveOrEqual, 4410 resultGPR, 4411 JITCompiler::payloadFor(JSStack::ArgumentCount))); 4409 m_jit.branch32(JITCompiler::AboveOrEqual, indexGPR, resultGPR)); 4412 4410 } 4413 4411 … … 4439 4437 slowArgumentOutOfBounds.link(&m_jit); 4440 4438 4441 m_jit.signExtend32ToPtr(resultGPR, resultGPR);4442 4443 4439 m_jit.load64( 4444 4440 JITCompiler::BaseIndex( 4445 GPRInfo::callFrameRegister, resultGPR, JITCompiler::TimesEight, m_jit.offsetOfArgumentsIncludingThis(node->origin.semantic)),4441 GPRInfo::callFrameRegister, indexGPR, JITCompiler::TimesEight, m_jit.offsetOfArguments(node->origin.semantic)), 4446 4442 resultGPR); 4447 4443 … … 4464 4460 m_jit.graph().machineArgumentsRegisterFor(node->origin.semantic)))); 4465 4461 4466 m_jit.add32(TrustedImm32(1), indexGPR, resultGPR);4467 4462 if (node->origin.semantic.inlineCallFrame) { 4468 4463 slowPath.append( … … 4470 4465 JITCompiler::AboveOrEqual, 4471 4466 resultGPR, 4472 Imm32(node->origin.semantic.inlineCallFrame->arguments.size() )));4467 Imm32(node->origin.semantic.inlineCallFrame->arguments.size() - 1))); 4473 4468 } else { 4469 m_jit.load32(JITCompiler::payloadFor(JSStack::ArgumentCount), resultGPR); 4470 m_jit.sub32(TrustedImm32(1), resultGPR); 4474 4471 slowPath.append( 4475 m_jit.branch32( 4476 JITCompiler::AboveOrEqual, 4477 resultGPR, 4478 JITCompiler::payloadFor(JSStack::ArgumentCount))); 4472 m_jit.branch32(JITCompiler::AboveOrEqual, indexGPR, resultGPR)); 4479 4473 } 4480 4474 … … 4506 4500 slowArgumentOutOfBounds.link(&m_jit); 4507 4501 4508 m_jit.signExtend32ToPtr(resultGPR, resultGPR);4509 4510 4502 m_jit.load64( 4511 4503 JITCompiler::BaseIndex( 4512 GPRInfo::callFrameRegister, resultGPR, JITCompiler::TimesEight, m_jit.offsetOfArgumentsIncludingThis(node->origin.semantic)),4504 GPRInfo::callFrameRegister, indexGPR, JITCompiler::TimesEight, m_jit.offsetOfArguments(node->origin.semantic)), 4513 4505 resultGPR); 4514 4506 -
trunk/Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
r179515 r179538 1 1 /* 2 * Copyright (C) 2013 , 2014Apple Inc. All rights reserved.2 * Copyright (C) 2013-2015 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 1998 1998 CodeOrigin codeOrigin = m_node->origin.semantic; 1999 1999 2000 LValue zeroBasedIndex = lowInt32(m_node->child1()); 2001 LValue oneBasedIndex = m_out.add(zeroBasedIndex, m_out.int32One); 2000 LValue index = lowInt32(m_node->child1()); 2002 2001 2003 2002 LValue limit; 2004 2003 if (codeOrigin.inlineCallFrame) 2005 limit = m_out.constInt32(codeOrigin.inlineCallFrame->arguments.size() );2004 limit = m_out.constInt32(codeOrigin.inlineCallFrame->arguments.size() - 1); 2006 2005 else 2007 limit = m_out. load32(payloadFor(JSStack::ArgumentCount));2008 2009 speculate(Uncountable, noValue(), 0, m_out.aboveOrEqual( oneBasedIndex, limit));2006 limit = m_out.sub(m_out.load32(payloadFor(JSStack::ArgumentCount)), m_out.int32One); 2007 2008 speculate(Uncountable, noValue(), 0, m_out.aboveOrEqual(index, limit)); 2010 2009 2011 2010 SymbolTable* symbolTable = m_graph.baselineCodeBlockFor(codeOrigin)->symbolTable(); … … 2033 2032 2034 2033 LValue pointer = m_out.baseIndex( 2035 base.value(), m_out.zeroExt( zeroBasedIndex, m_out.intPtr), ScaleEight);2034 base.value(), m_out.zeroExt(index, m_out.intPtr), ScaleEight); 2036 2035 setJSValue(m_out.load64(TypedPointer(m_heaps.variables.atAnyIndex(), pointer))); 2037 2036 } -
trunk/Source/JavaScriptCore/jit/AssemblyHelpers.h
r175593 r179538 604 604 } 605 605 606 int offsetOfArguments IncludingThis(InlineCallFrame* inlineCallFrame)606 int offsetOfArguments(InlineCallFrame* inlineCallFrame) 607 607 { 608 608 if (!inlineCallFrame) 609 return CallFrame::argumentOffset IncludingThis(0) * sizeof(Register);609 return CallFrame::argumentOffset(0) * sizeof(Register); 610 610 if (inlineCallFrame->arguments.size() <= 1) 611 611 return 0; 612 612 ValueRecovery recovery = inlineCallFrame->arguments[1]; 613 613 RELEASE_ASSERT(recovery.technique() == DisplacedInJSStack); 614 return (recovery.virtualRegister().offset() - 1) * sizeof(Register);615 } 616 617 int offsetOfArguments IncludingThis(const CodeOrigin& codeOrigin)618 { 619 return offsetOfArguments IncludingThis(codeOrigin.inlineCallFrame);620 } 621 614 return recovery.virtualRegister().offset() * sizeof(Register); 615 } 616 617 int offsetOfArguments(const CodeOrigin& codeOrigin) 618 { 619 return offsetOfArguments(codeOrigin.inlineCallFrame); 620 } 621 622 622 void emitLoadStructure(RegisterID source, RegisterID dest, RegisterID scratch) 623 623 { -
trunk/Source/JavaScriptCore/jit/JITOpcodes.cpp
r179372 r179538 921 921 emitGetVirtualRegister(property, regT1); 922 922 addSlowCase(emitJumpIfNotImmediateInteger(regT1)); 923 add32(TrustedImm32(1), regT1);924 // regT1 now contains the integer index of the argument we want, including this925 923 emitGetFromCallFrameHeader32(JSStack::ArgumentCount, regT2); 924 sub32(TrustedImm32(1), regT2); 926 925 addSlowCase(branch32(AboveOrEqual, regT1, regT2)); 927 926 928 927 signExtend32ToPtr(regT1, regT1); 929 load64(BaseIndex(callFrameRegister, regT1, TimesEight, CallFrame:: thisArgumentOffset() * static_cast<int>(sizeof(Register))), regT0);928 load64(BaseIndex(callFrameRegister, regT1, TimesEight, CallFrame::argumentOffset(0) * static_cast<int>(sizeof(Register))), regT0); 930 929 emitValueProfilingSite(); 931 930 emitPutVirtualRegister(dst, regT0); -
trunk/Source/JavaScriptCore/jit/JITOpcodes32_64.cpp
r178856 r179538 1045 1045 emitLoad(property, regT1, regT2); 1046 1046 addSlowCase(branch32(NotEqual, regT1, TrustedImm32(JSValue::Int32Tag))); 1047 add32(TrustedImm32(1), regT2);1048 1047 // regT2 now contains the integer index of the argument we want, including this 1049 1048 load32(payloadFor(JSStack::ArgumentCount), regT3); 1049 sub32(TrustedImm32(1), regT3); 1050 1050 addSlowCase(branch32(AboveOrEqual, regT2, regT3)); 1051 1051 1052 loadPtr(BaseIndex(callFrameRegister, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload) + CallFrame:: thisArgumentOffset() * static_cast<int>(sizeof(Register))), regT0);1053 loadPtr(BaseIndex(callFrameRegister, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag) + CallFrame:: thisArgumentOffset() * static_cast<int>(sizeof(Register))), regT1);1052 loadPtr(BaseIndex(callFrameRegister, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.payload) + CallFrame::argumentOffset(0) * static_cast<int>(sizeof(Register))), regT0); 1053 loadPtr(BaseIndex(callFrameRegister, regT2, TimesEight, OBJECT_OFFSETOF(JSValue, u.asBits.tag) + CallFrame::argumentOffset(0) * static_cast<int>(sizeof(Register))), regT1); 1054 1054 emitValueProfilingSite(); 1055 1055 emitStore(dst, regT1, regT0); -
trunk/Source/JavaScriptCore/llint/LowLevelInterpreter.asm
r179429 r179538 1 # Copyright (C) 2011 , 2012, 2013, 2014Apple Inc. All rights reserved.1 # Copyright (C) 2011-2015 Apple Inc. All rights reserved. 2 2 # 3 3 # Redistribution and use in source and binary forms, with or without … … 54 54 const ArgumentCount = Callee + SlotSize 55 55 const ThisArgumentOffset = ArgumentCount + SlotSize 56 const FirstArgumentOffset = ThisArgumentOffset + SlotSize 56 57 const CallFrameHeaderSize = ThisArgumentOffset 57 58 -
trunk/Source/JavaScriptCore/llint/LowLevelInterpreter32_64.asm
r179372 r179538 1 # Copyright (C) 2011 , 2012, 2013, 2014Apple Inc. All rights reserved.1 # Copyright (C) 2011-2015 Apple Inc. All rights reserved. 2 2 # 3 3 # Redistribution and use in source and binary forms, with or without … … 1614 1614 bineq TagOffset[cfr, t0, 8], EmptyValueTag, .opGetArgumentByValSlow 1615 1615 loadConstantOrVariablePayload(t1, Int32Tag, t2, .opGetArgumentByValSlow) 1616 addi 1, t21617 1616 loadi ArgumentCount + PayloadOffset[cfr], t1 1617 subi 1, t1 1618 1618 biaeq t2, t1, .opGetArgumentByValSlow 1619 1619 loadi 4[PC], t3 1620 loadi ThisArgumentOffset + TagOffset[cfr, t2, 8], t01621 loadi ThisArgumentOffset + PayloadOffset[cfr, t2, 8], t11620 loadi FirstArgumentOffset + TagOffset[cfr, t2, 8], t0 1621 loadi FirstArgumentOffset + PayloadOffset[cfr, t2, 8], t1 1622 1622 storei t0, TagOffset[cfr, t3, 8] 1623 1623 storei t1, PayloadOffset[cfr, t3, 8] -
trunk/Source/JavaScriptCore/llint/LowLevelInterpreter64.asm
r179372 r179538 1 # Copyright (C) 2011 , 2012, 2013, 2014Apple Inc. All rights reserved.1 # Copyright (C) 2011-2015 Apple Inc. All rights reserved. 2 2 # 3 3 # Redistribution and use in source and binary forms, with or without … … 1472 1472 btqnz [cfr, t0, 8], .opGetArgumentByValSlow 1473 1473 loadConstantOrVariableInt32(t1, t2, .opGetArgumentByValSlow) 1474 addi 1, t21475 1474 loadi ArgumentCount + PayloadOffset[cfr], t1 1475 sxi2q t2, t2 1476 subi 1, t1 1476 1477 biaeq t2, t1, .opGetArgumentByValSlow 1477 1478 loadisFromInstruction(1, t3) 1478 1479 loadpFromInstruction(6, t1) 1479 loadq ThisArgumentOffset[cfr, t2, 8], t01480 loadq FirstArgumentOffset[cfr, t2, 8], t0 1480 1481 storeq t0, [cfr, t3, 8] 1481 1482 valueProfile(t0, 6, t1)
Note: See TracChangeset
for help on using the changeset viewer.