Blob Blame History Raw
#
#  Backport of Mozilla bug 1459761 (part 1 and 2), which (as a side effect)
#  fixes the issues with www.bing.com (cpu hog and crash, as of 20220408)
#

diff -Nrup mozilla/js/public/GCAPI.h mozilla-OK/js/public/GCAPI.h
--- mozilla/js/public/GCAPI.h	2022-01-25 01:04:25.000000000 +0300
+++ mozilla-OK/js/public/GCAPI.h	2022-06-16 01:15:02.428154583 +0300
@@ -352,6 +352,7 @@ struct Zone;
     D(FULL_CELL_PTR_BUFFER)                     \
     D(FULL_SLOT_BUFFER)                         \
     D(FULL_SHAPE_BUFFER)                        \
+    D(TOO_MUCH_WASM_MEMORY)                     \
                                                 \
     /* These are reserved for future use. */    \
     D(RESERVED0)                                \
@@ -363,7 +364,6 @@ struct Zone;
     D(RESERVED6)                                \
     D(RESERVED7)                                \
     D(RESERVED8)                                \
-    D(RESERVED9)                                \
                                                 \
     /* Reasons from Firefox */                  \
     D(DOM_WINDOW_UTILS)                         \
diff -Nrup mozilla/js/src/jsgc.cpp mozilla-OK/js/src/jsgc.cpp
--- mozilla/js/src/jsgc.cpp	2022-06-16 01:44:01.965221336 +0300
+++ mozilla-OK/js/src/jsgc.cpp	2022-06-16 01:15:21.916020554 +0300
@@ -7376,6 +7376,7 @@ IsDeterministicGCReason(JS::gcreason::Re
       case JS::gcreason::DESTROY_RUNTIME:
       case JS::gcreason::LAST_DITCH:
       case JS::gcreason::TOO_MUCH_MALLOC:
+      case JS::gcreason::TOO_MUCH_WASM_MEMORY:
       case JS::gcreason::ALLOC_TRIGGER:
       case JS::gcreason::DEBUG_GC:
       case JS::gcreason::CC_FORCED:
diff -Nrup mozilla/js/src/vm/ArrayBufferObject.cpp mozilla-OK/js/src/vm/ArrayBufferObject.cpp
--- mozilla/js/src/vm/ArrayBufferObject.cpp	2022-06-08 22:10:28.000000000 +0300
+++ mozilla-OK/js/src/vm/ArrayBufferObject.cpp	2022-06-16 01:29:54.461028434 +0300
@@ -56,11 +56,13 @@
 
 using JS::ToInt32;
 
+using mozilla::Atomic;
 using mozilla::DebugOnly;
 using mozilla::CheckedInt;
 using mozilla::Some;
 using mozilla::Maybe;
 using mozilla::Nothing;
+using mozilla::Unused;
 
 using namespace js;
 using namespace js::gc;
@@ -92,11 +94,29 @@ js::ToClampedIndex(JSContext* cx, Handle
 // bug 1068684, bug 1073934 for details.  The limiting case seems to be
 // Windows Vista Home 64-bit, where the per-process address space is limited
 // to 8TB.  Thus we track the number of live objects, and set a limit of
-// 1000 live objects per process; we run synchronous GC if necessary; and
-// we throw an OOM error if the per-process limit is exceeded.
-static mozilla::Atomic<int32_t, mozilla::ReleaseAcquire> liveBufferCount(0);
+// 1000 live objects per process and we throw an OOM error if the per-process
+// limit is exceeded.
+//
+// Since the MaximumLiveMappedBuffers limit is not generally accounted for by
+// any existing GC-trigger heuristics, we need an extra heuristic for triggering
+// GCs when the caller is allocating memories rapidly without other garbage.
+// Thus, once the live buffer count crosses a certain threshold, we start
+// triggering GCs every N allocations. As we get close to the limit, perform
+// expensive non-incremental full GCs as a last-ditch effort to avoid
+// unnecessary failure. The *Sans use a ton of vmem for bookkeeping leaving a
+// lot less for the program so use a lower limit.
 
+#if defined(MOZ_TSAN) || defined(MOZ_ASAN)
+static const int32_t MaximumLiveMappedBuffers = 500;
+#else
 static const int32_t MaximumLiveMappedBuffers = 1000;
+#endif
+static const int32_t StartTriggeringAtLiveBufferCount = 100;
+static const int32_t StartSyncFullGCAtLiveBufferCount = MaximumLiveMappedBuffers - 100;
+static const int32_t AllocatedBuffersPerTrigger = 100;
+
+static Atomic<int32_t, mozilla::ReleaseAcquire> liveBufferCount(0);
+static Atomic<int32_t, mozilla::ReleaseAcquire> allocatedSinceLastTrigger(0);
 
 int32_t
 js::LiveMappedBufferCount()
@@ -679,10 +699,6 @@ class js::WasmArrayRawBuffer
         return maxSize_;
     }
 
-    size_t allocatedBytes() const {
-        return mappedSize_ + gc::SystemPageSize();
-    }
-
 #ifndef WASM_HUGE_MEMORY
     uint32_t boundsCheckLimit() const {
         MOZ_ASSERT(mappedSize_ <= UINT32_MAX);
@@ -843,9 +859,24 @@ ArrayBufferObject::createForWasm(JSConte
 #endif
     }
 
+    // See MaximumLiveMappedBuffers comment above.
+    if (liveBufferCount > StartSyncFullGCAtLiveBufferCount) {
+        JS::PrepareForFullGC(cx);
+        JS::GCForReason(cx, GC_NORMAL, JS::gcreason::TOO_MUCH_WASM_MEMORY);
+        allocatedSinceLastTrigger = 0;
+    } else if (liveBufferCount > StartTriggeringAtLiveBufferCount) {
+        allocatedSinceLastTrigger++;
+        if (allocatedSinceLastTrigger > AllocatedBuffersPerTrigger) {
+            Unused << cx->runtime()->gc.triggerGC(JS::gcreason::TOO_MUCH_WASM_MEMORY);
+            allocatedSinceLastTrigger = 0;
+        }
+    } else {
+        allocatedSinceLastTrigger = 0;
+    }
+
     auto contents = BufferContents::create<WASM>(wasmBuf->dataPointer());
     buffer->initialize(initialSize, contents, OwnsData);
-    cx->updateMallocCounter(wasmBuf->mappedSize());
+    cx->updateMallocCounter(initialSize);
     return buffer;
 }
 
@@ -1148,8 +1179,6 @@ ArrayBufferObject::create(JSContext* cx,
             size_t nAllocated = nbytes;
             if (contents.kind() == MAPPED)
                 nAllocated = JS_ROUNDUP(nbytes, js::gc::SystemPageSize());
-            else if (contents.kind() == WASM)
-                nAllocated = contents.wasmBuffer()->allocatedBytes();
             cx->updateMallocCounter(nAllocated);
         }
     } else {