Bug 1023755 - PJS: Array.prototype.scanPar per-element thunk reads and writes result array; vulnerable to bailout-and-restart. r=nmatsakis
authorLars T Hansen <lhansen@mozilla.com>
Wed, 11 Jun 2014 19:29:34 +0200
changeset 188468 661dfa9fb9499cfa07d6c30e2767d91cd8fe924e
parent 188467 63e8523b0976a77f42aca3870219c0f8606443c2
child 188469 4f6144466d92c0e2cf3cdb6969025c8c7ba008b8
push idunknown
push userunknown
push dateunknown
reviewersnmatsakis
bugs1023755
milestone33.0a1
Bug 1023755 - PJS: Array.prototype.scanPar per-element thunk reads and writes result array; vulnerable to bailout-and-restart. r=nmatsakis
js/src/builtin/Array.js
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -859,17 +859,22 @@ function ArrayScanPar(func, mode) {
     ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, func));
 
   var self = ToObject(this);
   var length = self.length;
 
   if (length === 0)
     ThrowError(JSMSG_EMPTY_ARRAY_REDUCE);
 
+  // We need two buffers because phase2() will read an intermediate result and
+  // write a final result; that is safe against bailout-and-restart only if
+  // the intermediate and final buffers are distinct.  (Bug 1023755)
+  // Obviously paying for a second buffer is undesirable.
   var buffer = NewDenseArray(length);
+  var buffer2 = NewDenseArray(length);
 
   parallel: for (;;) { // see ArrayMapPar() to explain why for(;;) etc
     if (ShouldForceSequential())
       break parallel;
     if (!TRY_PARALLEL(mode))
       break parallel;
 
     var slicesInfo = ComputeSlicesInfo(length);
@@ -884,21 +889,22 @@ function ArrayScanPar(func, mode) {
     ARRAY_PUSH(intermediates, accumulator);
     for (var i = 1; i < numSlices - 1; i++) {
       accumulator = func(accumulator, buffer[finalElement(i)]);
       ARRAY_PUSH(intermediates, accumulator);
     }
 
     // Complete each slice using intermediates array (see comment on phase2()).
     //
-    // We start from slice 1 instead of 0 since there is no work to be done
-    // for slice 0.
-    if (numSlices > 1)
-      ForkJoin(phase2, 1, numSlices, ForkJoinMode(mode), buffer);
-    return buffer;
+    // Slice 0 must be handled specially - it's just a copy - since we don't
+    // have an identity value for the operation.
+    for ( var k=0, limit=finalElement(0) ; k <= limit ; k++ )
+      buffer2[k] = buffer[k];
+    ForkJoin(phase2, 1, numSlices, ForkJoinMode(mode), buffer2);
+    return buffer2;
   }
 
   // Sequential fallback:
   ASSERT_SEQUENTIAL_IS_OK(mode);
   scan(self[0], 0, length);
   return buffer;
 
   function scan(accumulator, start, end) {
@@ -981,17 +987,17 @@ function ArrayScanPar(func, mode) {
   function phase2(workerId, sliceStart, sliceEnd) {
     var sliceShift = slicesInfo.shift;
     var sliceId;
     while (GET_SLICE(sliceStart, sliceEnd, sliceId)) {
       var indexPos = SLICE_START_INDEX(sliceShift, sliceId);
       var indexEnd = SLICE_END_INDEX(sliceShift, indexPos, length);
       var intermediate = intermediates[sliceId - 1];
       for (; indexPos < indexEnd; indexPos++)
-        UnsafePutElements(buffer, indexPos, func(intermediate, buffer[indexPos]));
+        UnsafePutElements(buffer2, indexPos, func(intermediate, buffer[indexPos]));
     }
     return sliceId;
   }
 
   return undefined;
 }
 
 /**