From 9c9c8d7bb5681c9fe2a0c2bbe21f97f8b33d9c46 Mon Sep 17 00:00:00 2001
From: jgruber <jgruber@chromium.org>
Date: Tue, 6 Dec 2016 06:08:41 -0800
Subject: [PATCH] [stubs] Add option to allow LO space allocation

Passing kAllowLargeObjectAllocation now allocates in LOS if necessary.
Allow such allocations when growing fixed arrays in RegExp's @@match
and @@split operations.

BUG=chromium:670671

Review-Url: https://codereview.chromium.org/2555703003
Cr-Commit-Position: refs/heads/master@{#41526}
---
 src/builtins/builtins-regexp.cc        |  5 ++++-
 src/code-stub-assembler.cc             | 16 ++++++++++++++++
 src/code-stub-assembler.h              |  3 ++-
 src/runtime/runtime-internal.cc        |  2 +-
 test/mjsunit/regress/regress-670671.js | 23 +++++++++++++++++++++++
 5 files changed, 46 insertions(+), 3 deletions(-)
 create mode 100644 test/mjsunit/regress/regress-670671.js

diff --git a/src/builtins/builtins-regexp.cc b/src/builtins/builtins-regexp.cc
index a08823553f..86871d24c1 100644
--- a/src/builtins/builtins-regexp.cc
+++ b/src/builtins/builtins-regexp.cc
@@ -1465,9 +1465,12 @@ class GrowableFixedArray {
 
     const ElementsKind kind = FAST_ELEMENTS;
     const WriteBarrierMode barrier_mode = UPDATE_WRITE_BARRIER;
+    const CodeStubAssembler::AllocationFlags flags =
+        CodeStubAssembler::kAllowLargeObjectAllocation;
 
     Node* const from_array = var_array_.value();
-    Node* const to_array = a->AllocateFixedArray(kind, new_capacity, mode);
+    Node* const to_array =
+        a->AllocateFixedArray(kind, new_capacity, mode, flags);
     a->CopyFixedArrayElements(kind, from_array, kind, to_array,
                               current_capacity, new_capacity, barrier_mode,
                               mode);
diff --git a/src/code-stub-assembler.cc b/src/code-stub-assembler.cc
index 776408ae08..27db1b8516 100644
--- a/src/code-stub-assembler.cc
+++ b/src/code-stub-assembler.cc
@@ -783,6 +783,22 @@ Node* CodeStubAssembler::AllocateRawUnaligned(Node* size_in_bytes,
   Label runtime_call(this, Label::kDeferred), no_runtime_call(this);
   Label merge_runtime(this, &result);
 
+  if (flags & kAllowLargeObjectAllocation) {
+    Label next(this);
+    GotoIf(IsRegularHeapObjectSize(size_in_bytes), &next);
+
+    Node* runtime_flags = SmiConstant(
+        Smi::FromInt(AllocateDoubleAlignFlag::encode(false) |
+                     AllocateTargetSpace::encode(AllocationSpace::LO_SPACE)));
+    Node* const runtime_result =
+        CallRuntime(Runtime::kAllocateInTargetSpace, NoContextConstant(),
+                    SmiTag(size_in_bytes), runtime_flags);
+    result.Bind(runtime_result);
+    Goto(&merge_runtime);
+
+    Bind(&next);
+  }
+
   Node* new_top = IntPtrAdd(top, size_in_bytes);
   Branch(UintPtrGreaterThanOrEqual(new_top, limit), &runtime_call,
          &no_runtime_call);
diff --git a/src/code-stub-assembler.h b/src/code-stub-assembler.h
index 27669d097e..7451f1ffcc 100644
--- a/src/code-stub-assembler.h
+++ b/src/code-stub-assembler.h
@@ -58,7 +58,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
   enum AllocationFlag : uint8_t {
     kNone = 0,
     kDoubleAlignment = 1,
-    kPretenured = 1 << 1
+    kPretenured = 1 << 1,
+    kAllowLargeObjectAllocation = 1 << 2,
   };
 
   typedef base::Flags<AllocationFlag> AllocationFlags;
diff --git a/src/runtime/runtime-internal.cc b/src/runtime/runtime-internal.cc
index 43c0a8b36d..aa8b3d091d 100644
--- a/src/runtime/runtime-internal.cc
+++ b/src/runtime/runtime-internal.cc
@@ -273,9 +273,9 @@ RUNTIME_FUNCTION(Runtime_AllocateInTargetSpace) {
   CONVERT_SMI_ARG_CHECKED(flags, 1);
   CHECK(IsAligned(size, kPointerSize));
   CHECK(size > 0);
-  CHECK(size <= kMaxRegularHeapObjectSize);
   bool double_align = AllocateDoubleAlignFlag::decode(flags);
   AllocationSpace space = AllocateTargetSpace::decode(flags);
+  CHECK(size <= kMaxRegularHeapObjectSize || space == LO_SPACE);
   return *isolate->factory()->NewFillerObject(size, double_align, space);
 }
 
diff --git a/test/mjsunit/regress/regress-670671.js b/test/mjsunit/regress/regress-670671.js
new file mode 100644
index 0000000000..06925de5b9
--- /dev/null
+++ b/test/mjsunit/regress/regress-670671.js
@@ -0,0 +1,23 @@
+// Copyright 2016 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Trigger an infinite loop through RegExp.prototype[@@match], which results
+// in unbounded growth of the results array.
+
+// Limit the number of iterations to avoid OOM while still triggering large
+// object space allocation.
+const min_ptr_size = 4;
+const max_regular_heap_object_size = 507136;
+const num_iterations = max_regular_heap_object_size / min_ptr_size;
+
+let i = 0;
+
+const re = /foo.bar/;
+const RegExpPrototypeExec = RegExp.prototype.exec;
+re.exec = (str) => {
+  return (i++ < num_iterations) ? RegExpPrototypeExec.call(re, str) : null;
+};
+re.__defineGetter__("global", () => true);  // Triggers infinite loop.
+
+"foo*bar".match(re);  // Should not crash.