Title: [203199] trunk/Source/_javascript_Core
Revision
203199
Author
[email protected]
Date
2016-07-13 16:38:44 -0700 (Wed, 13 Jul 2016)

Log Message

Crashes with detached ArrayBuffers
https://bugs.webkit.org/show_bug.cgi?id=157088
<rdar://problem/27327362>

Reviewed by Filip Pizlo.

TypedArray.prototype.fill was incorrect because it should perform
ToNumber coercion each time it tries to store the
object. Currently, we only perform the coercion once at the
beginning of the loop. If we find that we need to improve the
performance of this function, we can add a faster C++ path back
that only handles the primitive case.

This patch also moves the isNeutered() checks from put and
putByIndex into setIndex. This fixes an issue where setIndex might
store to a no longer valid offset.

* builtins/TypedArrayPrototype.js:
(globalPrivate.typedArrayClampArgumentToStartOrEnd):
(fill):
* runtime/JSGenericTypedArrayView.h:
(JSC::JSGenericTypedArrayView::setIndexQuickly):
(JSC::JSGenericTypedArrayView::setIndex):
(JSC::JSGenericTypedArrayView::setRangeToValue): Deleted.
* runtime/JSGenericTypedArrayViewInlines.h:
(JSC::JSGenericTypedArrayView<Adaptor>::put): Deleted.
(JSC::JSGenericTypedArrayView<Adaptor>::putByIndex): Deleted.
* runtime/JSGenericTypedArrayViewPrototypeFunctions.h:
(JSC::genericTypedArrayViewProtoFuncFill): Deleted.
* runtime/JSTypedArrayViewPrototype.cpp:
(JSC::JSTypedArrayViewPrototype::finishCreation):
(JSC::typedArrayViewProtoFuncFill): Deleted.
* tests/stress/typedarray-fill.js:
* tests/stress/typedarray-functions-with-neutered.js:
(defaultForArg):
(test2):
(checkArgumentsForType): Deleted.
(checkArguments): Deleted.

Modified Paths

Diff

Modified: trunk/Source/_javascript_Core/ChangeLog (203198 => 203199)


--- trunk/Source/_javascript_Core/ChangeLog	2016-07-13 23:33:39 UTC (rev 203198)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-07-13 23:38:44 UTC (rev 203199)
@@ -1,3 +1,44 @@
+2016-07-13  Keith Miller  <[email protected]>
+
+        Crashes with detached ArrayBuffers
+        https://bugs.webkit.org/show_bug.cgi?id=157088
+        <rdar://problem/27327362>
+
+        Reviewed by Filip Pizlo.
+
+        TypedArray.prototype.fill was incorrect because it should perform
+        ToNumber coercion each time it tries to store the
+        object. Currently, we only perform the coercion once at the
+        beginning of the loop. If we find that we need to improve the
+        performance of this function, we can add a faster C++ path back
+        that only handles the primitive case.
+
+        This patch also moves the isNeutered() checks from put and
+        putByIndex into setIndex. This fixes an issue where setIndex might
+        store to a no longer valid offset.
+
+        * builtins/TypedArrayPrototype.js:
+        (globalPrivate.typedArrayClampArgumentToStartOrEnd):
+        (fill):
+        * runtime/JSGenericTypedArrayView.h:
+        (JSC::JSGenericTypedArrayView::setIndexQuickly):
+        (JSC::JSGenericTypedArrayView::setIndex):
+        (JSC::JSGenericTypedArrayView::setRangeToValue): Deleted.
+        * runtime/JSGenericTypedArrayViewInlines.h:
+        (JSC::JSGenericTypedArrayView<Adaptor>::put): Deleted.
+        (JSC::JSGenericTypedArrayView<Adaptor>::putByIndex): Deleted.
+        * runtime/JSGenericTypedArrayViewPrototypeFunctions.h:
+        (JSC::genericTypedArrayViewProtoFuncFill): Deleted.
+        * runtime/JSTypedArrayViewPrototype.cpp:
+        (JSC::JSTypedArrayViewPrototype::finishCreation):
+        (JSC::typedArrayViewProtoFuncFill): Deleted.
+        * tests/stress/typedarray-fill.js:
+        * tests/stress/typedarray-functions-with-neutered.js:
+        (defaultForArg):
+        (test2):
+        (checkArgumentsForType): Deleted.
+        (checkArguments): Deleted.
+
 2016-07-13  Enrica Casucci  <[email protected]>
 
         Update supported platforms in xcconfig files to match the sdk names.

Modified: trunk/Source/_javascript_Core/builtins/TypedArrayPrototype.js (203198 => 203199)


--- trunk/Source/_javascript_Core/builtins/TypedArrayPrototype.js	2016-07-13 23:33:39 UTC (rev 203198)
+++ trunk/Source/_javascript_Core/builtins/TypedArrayPrototype.js	2016-07-13 23:38:44 UTC (rev 203199)
@@ -52,6 +52,22 @@
     return constructor;
 }
 
+@globalPrivate
+function typedArrayClampArgumentToStartOrEnd(value, length, undefinedValue)
+{
+    "use strict";
+
+    if (value === @undefined)
+        return undefinedValue;
+
+    let int = @toInteger(value);
+    if (int < 0) {
+        int += length;
+        return int < 0 ? 0 : int;
+    }
+    return int > length ? length : int;
+}
+
 function values()
 {
     "use strict";
@@ -90,6 +106,29 @@
     return true;
 }
 
+function fill(value /* [, start [, end]] */)
+{
+    "use strict";
+
+    let length = @typedArrayLength(this);
+    let start;
+    let end;
+
+    if (arguments.length > 1) {
+        start = arguments[1];
+        if (arguments.length > 2) {
+            end = arguments[2];
+        }
+    }
+
+    start = @typedArrayClampArgumentToStartOrEnd(start, length, 0);
+    end = @typedArrayClampArgumentToStartOrEnd(end, length, length);
+
+    for (let i = start; i < end; i++)
+        this[i] = value;
+    return this;
+}
+
 function find(callback /* [, thisArg] */)
 {
     "use strict";

Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayView.h (203198 => 203199)


--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayView.h	2016-07-13 23:33:39 UTC (rev 203198)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayView.h	2016-07-13 23:38:44 UTC (rev 203199)
@@ -88,6 +88,8 @@
     Unobservable,
 };
 
+static const char* typedArrayBufferHasBeenDetachedErrorMessage = "Underlying ArrayBuffer has been detached from the view";
+
 template<typename Adaptor>
 class JSGenericTypedArrayView : public JSArrayBufferView {
 public:
@@ -160,6 +162,7 @@
     
     void setIndexQuickly(unsigned i, JSValue value)
     {
+        ASSERT(!value.isObject());
         setIndexQuicklyToNativeValue(i, toNativeFromValue<Adaptor>(value));
     }
     
@@ -169,6 +172,11 @@
         if (exec->hadException())
             return false;
 
+        if (isNeutered()) {
+            throwTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
+            return false;
+        }
+
         if (i >= m_length)
             return false;
 
@@ -180,22 +188,6 @@
 
     static bool toAdaptorNativeFromValue(ExecState* exec, JSValue jsValue, ElementType& result) { return toNativeFromValue<Adaptor>(exec, jsValue, result); }
 
-    bool setRangeToValue(ExecState* exec, unsigned start, unsigned end, JSValue jsValue)
-    {
-        ASSERT(0 <= start && start <= end && end <= m_length);
-
-        typename Adaptor::Type value = toNativeFromValue<Adaptor>(exec, jsValue);
-        if (exec->hadException())
-            return false;
-
-        // We might want to do something faster here (e.g. SIMD) if this is too slow.
-        typename Adaptor::Type* array = typedVector();
-        for (unsigned i = start; i < end; ++i)
-            array[i] = value;
-
-        return true;
-    }
-
     void sort()
     {
         switch (Adaptor::typeValue) {

Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewInlines.h (203198 => 203199)


--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewInlines.h	2016-07-13 23:33:39 UTC (rev 203198)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewInlines.h	2016-07-13 23:38:44 UTC (rev 203199)
@@ -37,8 +37,6 @@
 
 namespace JSC {
 
-static const char* typedArrayBufferHasBeenDetachedErrorMessage = "Underlying ArrayBuffer has been detached from the view";
-
 template<typename Adaptor>
 JSGenericTypedArrayView<Adaptor>::JSGenericTypedArrayView(
     VM& vm, ConstructionContext& context)
@@ -325,9 +323,6 @@
 {
     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
 
-    if (thisObject->isNeutered())
-        return reject(exec, true, typedArrayBufferHasBeenDetachedErrorMessage);
-
     // https://tc39.github.io/ecma262/#sec-integer-indexed-exotic-objects-set-p-v-receiver
     // Ignore the receiver even if the receiver is altered to non base value.
     // 9.4.5.5-2-b-i Return ? IntegerIndexedElementSet(O, numericIndex, V).
@@ -408,9 +403,6 @@
 {
     JSGenericTypedArrayView* thisObject = jsCast<JSGenericTypedArrayView*>(cell);
 
-    if (thisObject->isNeutered())
-        return reject(exec, true, typedArrayBufferHasBeenDetachedErrorMessage);
-
     if (propertyName > MAX_ARRAY_INDEX) {
         PutPropertySlot slot(JSValue(thisObject), shouldThrow);
         return thisObject->methodTable()->put(thisObject, exec, Identifier::from(exec, propertyName), value, slot);

Modified: trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewPrototypeFunctions.h (203198 => 203199)


--- trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewPrototypeFunctions.h	2016-07-13 23:33:39 UTC (rev 203198)
+++ trunk/Source/_javascript_Core/runtime/JSGenericTypedArrayViewPrototypeFunctions.h	2016-07-13 23:38:44 UTC (rev 203199)
@@ -169,39 +169,6 @@
 }
 
 template<typename ViewClass>
-EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncFill(ExecState* exec)
-{
-    // 22.2.3.8
-    VM& vm = exec->vm();
-    ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());
-    if (thisObject->isNeutered())
-        return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
-
-    JSValue valueToInsert = exec->argument(0);
-    if (exec->hadException())
-        return JSValue::encode(jsUndefined());
-
-    unsigned length = thisObject->length();
-    unsigned begin = argumentClampedIndexFromStartOrEnd(exec, 1, length);
-    if (vm.exception())
-        return encodedJSValue();
-    unsigned end = argumentClampedIndexFromStartOrEnd(exec, 2, length, length);
-    if (vm.exception())
-        return encodedJSValue();
-
-    if (thisObject->isNeutered())
-        return throwVMTypeError(exec, typedArrayBufferHasBeenDetachedErrorMessage);
-
-    if (end < begin)
-        return JSValue::encode(exec->thisValue());
-
-    if (!thisObject->setRangeToValue(exec, begin, end, valueToInsert))
-        return JSValue::encode(jsUndefined());
-
-    return JSValue::encode(exec->thisValue());
-}
-
-template<typename ViewClass>
 EncodedJSValue JSC_HOST_CALL genericTypedArrayViewProtoFuncIncludes(ExecState* exec)
 {
     ViewClass* thisObject = jsCast<ViewClass*>(exec->thisValue());

Modified: trunk/Source/_javascript_Core/runtime/JSTypedArrayViewPrototype.cpp (203198 => 203199)


--- trunk/Source/_javascript_Core/runtime/JSTypedArrayViewPrototype.cpp	2016-07-13 23:33:39 UTC (rev 203198)
+++ trunk/Source/_javascript_Core/runtime/JSTypedArrayViewPrototype.cpp	2016-07-13 23:38:44 UTC (rev 203199)
@@ -114,14 +114,6 @@
     CALL_GENERIC_TYPEDARRAY_PROTOTYPE_FUNCTION(genericTypedArrayViewProtoFuncCopyWithin);
 }
 
-static EncodedJSValue JSC_HOST_CALL typedArrayViewProtoFuncFill(ExecState* exec)
-{
-    JSValue thisValue = exec->thisValue();
-    if (!thisValue.isObject())
-        return throwVMTypeError(exec, ASCIILiteral("Receiver should be a typed array view but was not an object"));
-    CALL_GENERIC_TYPEDARRAY_PROTOTYPE_FUNCTION(genericTypedArrayViewProtoFuncFill);
-}
-
 static EncodedJSValue JSC_HOST_CALL typedArrayViewProtoFuncIncludes(ExecState* exec)
 {
     JSValue thisValue = exec->thisValue();
@@ -267,8 +259,12 @@
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("filter", typedArrayPrototypeFilterCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("sort", typedArrayPrototypeSortCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->builtinNames().entriesPublicName(), typedArrayPrototypeEntriesCodeGenerator, DontEnum);
+<<<<<<< 89717806169cc89a6e2a731666fb44bd8845272e
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("fill", typedArrayViewProtoFuncFill, DontEnum, 1);
     JSC_NATIVE_FUNCTION_WITHOUT_TRANSITION("includes", typedArrayViewProtoFuncIncludes, DontEnum, 1);
+=======
+    JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("fill", typedArrayPrototypeFillCodeGenerator, DontEnum);
+>>>>>>> Crashes with detached ArrayBuffers
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("find", typedArrayPrototypeFindCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION("findIndex", typedArrayPrototypeFindIndexCodeGenerator, DontEnum);
     JSC_BUILTIN_FUNCTION_WITHOUT_TRANSITION(vm.propertyNames->forEach, typedArrayPrototypeForEachCodeGenerator, DontEnum);

Modified: trunk/Source/_javascript_Core/tests/stress/typedarray-fill.js (203198 => 203199)


--- trunk/Source/_javascript_Core/tests/stress/typedarray-fill.js	2016-07-13 23:33:39 UTC (rev 203198)
+++ trunk/Source/_javascript_Core/tests/stress/typedarray-fill.js	2016-07-13 23:38:44 UTC (rev 203199)
@@ -32,4 +32,18 @@
 shouldBeTrue("testPrototypeFunction('fill', '(4, NaN, 5)', [14, 15, 10, 13, 44], [4, 4, 4, 4, 4])");
 shouldBeTrue("testPrototypeFunction('fill', '(4, -3, -2)', [14, 15, 10, 13, 44], [14, 15, 4, 13, 44])");
 shouldBeTrue("testPrototypeFunction('fill', '(4, 5, 5)', [14, 15, 10, 13, 44], [14, 15, 10, 13, 44])");
+
+debug("4.0 Coercion Testing");
+for (constructor of typedArrays) {
+    count = 0;
+    let p = new Proxy({}, { get(target, name) {
+        count++;
+        return target[name];
+    }});
+    new constructor(10).fill(p);
+    shouldBeTrue("count === 40");
+}
+
+
+
 finishJSTest();

Modified: trunk/Source/_javascript_Core/tests/stress/typedarray-functions-with-neutered.js (203198 => 203199)


--- trunk/Source/_javascript_Core/tests/stress/typedarray-functions-with-neutered.js	2016-07-13 23:33:39 UTC (rev 203198)
+++ trunk/Source/_javascript_Core/tests/stress/typedarray-functions-with-neutered.js	2016-07-13 23:38:44 UTC (rev 203199)
@@ -77,7 +77,7 @@
 prototypeFunctions = [
     { func:proto.copyWithin, args:["prim", "prim", "prim"] },
     { func:proto.every, args:["func"] },
-    { func:proto.fill, args:["ins", "prim", "prim"] },
+    { func:proto.fill, args:["prim", "prim", "prim"] },
     { func:proto.filter, args:["func"] },
     { func:proto.find, args:["func"] },
     { func:proto.findIndex, args:["func"] },
@@ -95,12 +95,14 @@
     { func:proto.subarray, args:["prim", "prim"] },
 ];
 
-function defaultForArg(arg)
+function defaultForArg(arg, argNum)
 {
     if (arg === "func")
-        return () => { return 1; }
+        return () => { return argNum; }
+    if (arg === "array")
+        return [1,2];
 
-    return 1;
+    return argNum;
 }
 
 function callWithArgs(func, array, args) {
@@ -127,34 +129,40 @@
         if (arg === "na")
             continue;
 
-        let len = 10;
+        let array = new constructor(10);
         if (arg === "func") {
-            let array = new constructor(len);
             callArgs[argNum] = () => {
                 transferArrayBuffer(array.buffer);
                 return func === array.every ? 1 : 0;
             };
             callWithArgs(func, array, callArgs);
-        }
-
-        if (arg === "prim") {
-            let array = new constructor(len)
+        } else if (arg === "prim") {
             callArgs[argNum] = { [Symbol.toPrimitive]() {
                 transferArrayBuffer(array.buffer);
+                return argNum;
+            } };
+            callWithArgs(func, array, callArgs);
+        } else if (arg === "array") {
+            callArgs[argNum] = new Array(4);
+            callArgs[argNum].fill(2);
+            let desc = { get: () => {
+                transferArrayBuffer(array.buffer);
                 return 1;
             } };
+            Object.defineProperty(callArgs[argNum], 1, desc);
             callWithArgs(func, array, callArgs);
-        }
+        } else
+            throw new Error(arg);
     }
 }
 
 function checkArguments({func, args}) {
     for (constructor of typedArrays)
-        checkArgumentsForType(func, args, constructor)
+        checkArgumentsForType(func, args, constructor);
 }
 
-function test() {
+function test2() {
     prototypeFunctions.forEach(checkArguments);
 }
 
-test();
+test2();
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to