Skip to content

Commit

Permalink
Merge r243280 - Cap length of an array with spread to MIN_ARRAY_STORA…
Browse files Browse the repository at this point in the history
…GE_CONSTRUCTION_LENGTH.

https://bugs.webkit.org/show_bug.cgi?id=196055
<rdar://problem/49067448>

Reviewed by Yusuke Suzuki.

JSTests:

* stress/new_array_with_spread-should-cap-array-size-to-MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH.js: Added.

Source/JavaScriptCore:

We are doing this because:
1. We expect the array to be densely packed.
2. SpeculativeJIT::compileAllocateNewArrayWithSize() (and the FTL equivalent)
   expects the array length to be less than MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH
   if we don't want to use an ArrayStorage shape.
3. There's no reason why an array with spread needs to be that large anyway.
   MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH is plenty.

In this patch, we also add a debug assert in compileAllocateNewArrayWithSize() and
emitAllocateButterfly() to check for overflows.

* assembler/AbortReason.h:
* dfg/DFGOperations.cpp:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileCreateRest):
(JSC::DFG::SpeculativeJIT::compileNewArrayWithSpread):
(JSC::DFG::SpeculativeJIT::emitAllocateButterfly):
(JSC::DFG::SpeculativeJIT::compileAllocateNewArrayWithSize):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSpread):
* runtime/ArrayConventions.h:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):
  • Loading branch information
Mark Lam authored and carlosgcampos committed Apr 8, 2019
1 parent 53b2fcb commit 4d94440
Show file tree
Hide file tree
Showing 9 changed files with 124 additions and 4 deletions.
10 changes: 10 additions & 0 deletions JSTests/ChangeLog
@@ -1,3 +1,13 @@
2019-03-21 Mark Lam <mark.lam@apple.com>

Cap length of an array with spread to MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH.
https://bugs.webkit.org/show_bug.cgi?id=196055
<rdar://problem/49067448>

Reviewed by Yusuke Suzuki.

* stress/new_array_with_spread-should-cap-array-size-to-MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH.js: Added.

2019-03-18 Mark Lam <mark.lam@apple.com>

Structure::flattenDictionary() should clear unused property slots.
Expand Down
@@ -0,0 +1,44 @@
//@ skip if $memoryLimited or $buildType == "debug"

function test() {
function makeFoo(n) {
let src = "return function(a, b) { for (let i = 0; i < 20000; i++); return [";
for (let i = 0; i < n; i++) {
src += "...a";
if (i < n-1)
src += ",";
}
src += ",...b];}";
return (new Function(src))();
}

var NUM_SPREAD_ARGS = 8;
var foo = makeFoo(NUM_SPREAD_ARGS);

var b = [1.1, 1.1];
for (let i = 0; i < 10; i++)
foo(b, b);

function makeArray(len, v = 1.234) {
let a = [];
while (a.length < len)
a[a.length] = v;
return a;
}

var a = makeArray(0x20000040 / NUM_SPREAD_ARGS);
var c = []; c.length = 1;

var arr = foo(a, c);
print(arr.length);
}

var exception;
try {
test();
} catch (e) {
exception = e;
}

if (exception != "Error: Out of memory")
throw "FAILED";
32 changes: 32 additions & 0 deletions Source/JavaScriptCore/ChangeLog
@@ -1,3 +1,35 @@
2019-03-21 Mark Lam <mark.lam@apple.com>

Cap length of an array with spread to MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH.
https://bugs.webkit.org/show_bug.cgi?id=196055
<rdar://problem/49067448>

Reviewed by Yusuke Suzuki.

We are doing this because:
1. We expect the array to be densely packed.
2. SpeculativeJIT::compileAllocateNewArrayWithSize() (and the FTL equivalent)
expects the array length to be less than MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH
if we don't want to use an ArrayStorage shape.
3. There's no reason why an array with spread needs to be that large anyway.
MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH is plenty.

In this patch, we also add a debug assert in compileAllocateNewArrayWithSize() and
emitAllocateButterfly() to check for overflows.

* assembler/AbortReason.h:
* dfg/DFGOperations.cpp:
* dfg/DFGSpeculativeJIT.cpp:
(JSC::DFG::SpeculativeJIT::compileCreateRest):
(JSC::DFG::SpeculativeJIT::compileNewArrayWithSpread):
(JSC::DFG::SpeculativeJIT::emitAllocateButterfly):
(JSC::DFG::SpeculativeJIT::compileAllocateNewArrayWithSize):
* ftl/FTLLowerDFGToB3.cpp:
(JSC::FTL::DFG::LowerDFGToB3::compileNewArrayWithSpread):
* runtime/ArrayConventions.h:
* runtime/CommonSlowPaths.cpp:
(JSC::SLOW_PATH_DECL):

2019-03-18 Mark Lam <mark.lam@apple.com>

Structure::flattenDictionary() should clear unused property slots.
Expand Down
3 changes: 2 additions & 1 deletion Source/JavaScriptCore/assembler/AbortReason.h
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2014-2016 Apple Inc. All rights reserved.
* Copyright (C) 2014-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
Expand Down Expand Up @@ -73,6 +73,7 @@ enum AbortReason {
RepatchInsaneArgumentCount = 310,
TGInvalidPointer = 320,
TGNotSupported = 330,
UncheckedOverflow = 335,
YARRNoInputConsumed = 340,
};

Expand Down
7 changes: 6 additions & 1 deletion Source/JavaScriptCore/dfg/DFGOperations.cpp
@@ -1,5 +1,5 @@
/*
* Copyright (C) 2011-2018 Apple Inc. All rights reserved.
* Copyright (C) 2011-2019 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
Expand Down Expand Up @@ -2709,6 +2709,11 @@ JSCell* JIT_OPERATION operationNewArrayWithSpreadSlow(ExecState* exec, void* buf
}

unsigned length = checkedLength.unsafeGet();
if (UNLIKELY(length >= MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)) {
throwOutOfMemoryError(exec, scope);
return nullptr;
}

JSGlobalObject* globalObject = exec->lexicalGlobalObject();
Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous);

Expand Down
22 changes: 22 additions & 0 deletions Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp
Expand Up @@ -7612,6 +7612,10 @@ void SpeculativeJIT::compileCreateRest(Node* node)
GPRReg arrayLengthGPR = arrayLength.gpr();
GPRReg arrayResultGPR = arrayResult.gpr();

// We can tell compileAllocateNewArrayWithSize() that it does not need to check
// for large arrays and use ArrayStorage structure because arrayLength here will
// always be bounded by stack size. Realistically, we won't be able to push enough
// arguments to have arrayLength exceed MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH.
bool shouldAllowForArrayStorageStructureForLargeArrays = false;
ASSERT(m_jit.graph().globalObjectFor(node->origin.semantic)->restParameterStructure()->indexingMode() == ArrayWithContiguous || m_jit.graph().globalObjectFor(node->origin.semantic)->isHavingABadTime());
compileAllocateNewArrayWithSize(m_jit.graph().globalObjectFor(node->origin.semantic), arrayResultGPR, arrayLengthGPR, ArrayWithContiguous, shouldAllowForArrayStorageStructureForLargeArrays);
Expand Down Expand Up @@ -7965,7 +7969,12 @@ void SpeculativeJIT::compileNewArrayWithSpread(Node* node)
}
}

speculationCheck(Overflow, JSValueRegs(), nullptr, m_jit.branch32(MacroAssembler::AboveOrEqual, lengthGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)));

// We can tell compileAllocateNewArrayWithSize() that it does not need to
// check for large arrays and use ArrayStorage structure because we already
// ensured above that the spread array length will definitely fit in a
// non-ArrayStorage shaped array.
bool shouldAllowForArrayStorageStructureForLargeArrays = false;
ASSERT(m_jit.graph().globalObjectFor(node->origin.semantic)->restParameterStructure()->indexingType() == ArrayWithContiguous || m_jit.graph().globalObjectFor(node->origin.semantic)->isHavingABadTime());
compileAllocateNewArrayWithSize(m_jit.graph().globalObjectFor(node->origin.semantic), resultGPR, lengthGPR, ArrayWithContiguous, shouldAllowForArrayStorageStructureForLargeArrays);
Expand Down Expand Up @@ -11624,6 +11633,11 @@ void SpeculativeJIT::emitAllocateButterfly(GPRReg storageResultGPR, GPRReg sizeG
m_jit.zeroExtend32ToPtr(sizeGPR, scratch1);
m_jit.lshift32(TrustedImm32(3), scratch1);
m_jit.add32(TrustedImm32(sizeof(IndexingHeader)), scratch1, scratch2);
#if !ASSERT_DISABLED
MacroAssembler::Jump didNotOverflow = m_jit.branch32(MacroAssembler::AboveOrEqual, scratch2, sizeGPR);
m_jit.abortWithReason(UncheckedOverflow);
didNotOverflow.link(&m_jit);
#endif
m_jit.emitAllocateVariableSized(
storageResultGPR, m_jit.vm()->jsValueGigacageAuxiliarySpace, scratch2, scratch1, scratch3, slowCases);
m_jit.addPtr(TrustedImm32(sizeof(IndexingHeader)), storageResultGPR);
Expand Down Expand Up @@ -12923,6 +12937,14 @@ void SpeculativeJIT::compileAllocateNewArrayWithSize(JSGlobalObject* globalObjec
MacroAssembler::JumpList slowCases;
if (shouldConvertLargeSizeToArrayStorage)
slowCases.append(m_jit.branch32(MacroAssembler::AboveOrEqual, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH)));
#if !ASSERT_DISABLED
else {
MacroAssembler::Jump lengthIsWithinLimits;
lengthIsWithinLimits = m_jit.branch32(MacroAssembler::Below, sizeGPR, TrustedImm32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH));
m_jit.abortWithReason(UncheckedOverflow);
lengthIsWithinLimits.link(&m_jit);
}
#endif

// We can use resultGPR as a scratch right now.
emitAllocateButterfly(storageGPR, sizeGPR, scratchGPR, scratch2GPR, resultGPR, slowCases);
Expand Down
3 changes: 3 additions & 0 deletions Source/JavaScriptCore/ftl/FTLLowerDFGToB3.cpp
Expand Up @@ -5825,6 +5825,9 @@ class LowerDFGToB3 {
}
}

LValue exceedsMaxAllowedLength = m_out.aboveOrEqual(length, m_out.constInt32(MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH));
blessSpeculation(m_out.speculate(exceedsMaxAllowedLength), Overflow, noValue(), nullptr, m_origin);

RegisteredStructure structure = m_graph.registerStructure(m_graph.globalObjectFor(m_node->origin.semantic)->originalArrayStructureForIndexingType(ArrayWithContiguous));
ArrayValues arrayValues = allocateUninitializedContiguousJSArray(length, structure);
LValue result = arrayValues.array;
Expand Down
4 changes: 2 additions & 2 deletions Source/JavaScriptCore/runtime/ArrayConventions.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
* Copyright (C) 2003-2017 Apple Inc. All rights reserved.
* Copyright (C) 2003-2019 Apple Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
Expand Down Expand Up @@ -65,7 +65,7 @@ namespace JSC {
#define MIN_SPARSE_ARRAY_INDEX 100000U
// If you try to allocate a contiguous array larger than this, then we will allocate an ArrayStorage
// array instead. We allow for an array that occupies 1GB of VM.
#define MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH 1024 * 1024 * 1024 / 8
#define MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH (1024 * 1024 * 1024 / 8)
#define MAX_STORAGE_VECTOR_INDEX (MAX_STORAGE_VECTOR_LENGTH - 1)
// 0xFFFFFFFF is a bit weird -- is not an array index even though it's an integer.
#define MAX_ARRAY_INDEX 0xFFFFFFFEU
Expand Down
3 changes: 3 additions & 0 deletions Source/JavaScriptCore/runtime/CommonSlowPaths.cpp
Expand Up @@ -1249,6 +1249,9 @@ SLOW_PATH_DECL(slow_path_new_array_with_spread)
THROW(createOutOfMemoryError(exec));

unsigned arraySize = checkedArraySize.unsafeGet();
if (UNLIKELY(arraySize >= MIN_ARRAY_STORAGE_CONSTRUCTION_LENGTH))
THROW(createOutOfMemoryError(exec));

JSGlobalObject* globalObject = exec->lexicalGlobalObject();
Structure* structure = globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous);

Expand Down

0 comments on commit 4d94440

Please sign in to comment.