<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[214857] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/214857">214857</a></dd>
<dt>Author</dt> <dd>mark.lam@apple.com</dd>
<dt>Date</dt> <dd>2017-04-03 17:42:02 -0700 (Mon, 03 Apr 2017)</dd>
</dl>

<h3>Log Message</h3>
<pre>Fix incorrect capacity delta calculation reported in SparseArrayValueMap::add().
https://bugs.webkit.org/show_bug.cgi?id=170412
&lt;rdar://problem/29697336&gt;

Reviewed by Filip Pizlo.

JSTests:

* stress/regress-170412.js: Added.

Source/JavaScriptCore:

Here's an example of code that will trigger underflow in the &quot;deprecatedExtraMemory&quot;
reported by SparseArrayValueMap::add() that is added to Heap::m_deprecatedExtraMemorySize:
        
    arr = new Array;
    Object.defineProperty(arr, 18, ({writable: true, configurable: true}));
    for (var i = 0; i &lt; 3; ++i) {
        Array.prototype.push.apply(arr, [&quot;&quot;, () =&gt; {}, {}]);
        Array.prototype.sort.apply(arr, [() =&gt; {}, []]);
    }

However, Heap::m_deprecatedExtraMemorySize is only 1 of 3 values that are added
up to form the result of Heap::extraMemorySize().  Heap::m_extraMemorySize and
Heap::m_arrayBuffers.size() are the other 2.

While Heap::m_arrayBuffers.size() is bounded by actual allocated memory, both
Heap::m_deprecatedExtraMemorySize and Heap::m_extraMemorySize are added to
without any bounds checks, and they are only reset to 0 at the start of a full
GC.  As a result, if we have a long sequence of eden GCs with a lot of additions
to Heap::m_extraMemorySize and/or Heap::m_deprecatedExtraMemorySize, then these
values could theoretically overflow.  Coupling this with the underflow from
SparseArrayValueMap::add(), the result for Heap::extraMemorySize() can easily
overflow.  Note: Heap::extraMemorySize() is used to compute the value
currentHeapSize.

If multiple conditions line up just right, the above overflows can result in this
debug assertion failure during an eden GC:

    ASSERT(currentHeapSize &gt;= m_sizeAfterLastCollect);

Otherwise, the effects of the overflows will only result in the computed
currentHeapSize not being representative of actual memory usage, and therefore,
a full GC may be triggered earlier or later than is ideal.

This patch ensures that SparseArrayValueMap::add() cannot underflow
Heap::m_deprecatedExtraMemorySize.  It also adds overflows checks in the
calculations of Heap::m_deprecatedExtraMemorySize, Heap::m_extraMemorySize, and
Heap::extraMemorySize() so that their values are saturated appropriately to
ensure that GC collections are triggered based on representative memory usage.

* heap/Heap.cpp:
(JSC::Heap::deprecatedReportExtraMemorySlowCase):
(JSC::Heap::extraMemorySize):
(JSC::Heap::updateAllocationLimits):
(JSC::Heap::reportExtraMemoryVisited):
* runtime/SparseArrayValueMap.cpp:
(JSC::SparseArrayValueMap::add):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkJSTestsChangeLog">trunk/JSTests/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapcpp">trunk/Source/JavaScriptCore/heap/Heap.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeSparseArrayValueMapcpp">trunk/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkJSTestsstressregress170412js">trunk/JSTests/stress/regress-170412.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkJSTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/JSTests/ChangeLog (214856 => 214857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/ChangeLog        2017-04-04 00:41:19 UTC (rev 214856)
+++ trunk/JSTests/ChangeLog        2017-04-04 00:42:02 UTC (rev 214857)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+2017-04-03  Mark Lam  &lt;mark.lam@apple.com&gt;
+
+        Fix incorrect capacity delta calculation reported in SparseArrayValueMap::add().
+        https://bugs.webkit.org/show_bug.cgi?id=170412
+        &lt;rdar://problem/29697336&gt;
+
+        Reviewed by Filip Pizlo.
+
+        * stress/regress-170412.js: Added.
+
</ins><span class="cx"> 2017-04-03  Keith Miller  &lt;keith_miller@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         WebAssembly: Update spec tests
</span></span></pre></div>
<a id="trunkJSTestsstressregress170412js"></a>
<div class="addfile"><h4>Added: trunk/JSTests/stress/regress-170412.js (0 => 214857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/JSTests/stress/regress-170412.js                                (rev 0)
+++ trunk/JSTests/stress/regress-170412.js        2017-04-04 00:42:02 UTC (rev 214857)
</span><span class="lines">@@ -0,0 +1,8 @@
</span><ins>+// This test passes if it does not crash.
+
+arr = new Array;
+Object.defineProperty(arr, 18, ({writable: true, configurable: true}));
+for (var i = 0; i &lt; 3; ++i) {
+    Array.prototype.push.apply(arr, [&quot;&quot;, () =&gt; { }, {}]);
+    Array.prototype.sort.apply(arr, [edenGC, []]);
+}
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (214856 => 214857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2017-04-04 00:41:19 UTC (rev 214856)
+++ trunk/Source/JavaScriptCore/ChangeLog        2017-04-04 00:42:02 UTC (rev 214857)
</span><span class="lines">@@ -1,3 +1,58 @@
</span><ins>+2017-04-03  Mark Lam  &lt;mark.lam@apple.com&gt;
+
+        Fix incorrect capacity delta calculation reported in SparseArrayValueMap::add().
+        https://bugs.webkit.org/show_bug.cgi?id=170412
+        &lt;rdar://problem/29697336&gt;
+
+        Reviewed by Filip Pizlo.
+
+        Here's an example of code that will trigger underflow in the &quot;deprecatedExtraMemory&quot;
+        reported by SparseArrayValueMap::add() that is added to Heap::m_deprecatedExtraMemorySize:
+        
+            arr = new Array;
+            Object.defineProperty(arr, 18, ({writable: true, configurable: true}));
+            for (var i = 0; i &lt; 3; ++i) {
+                Array.prototype.push.apply(arr, [&quot;&quot;, () =&gt; {}, {}]);
+                Array.prototype.sort.apply(arr, [() =&gt; {}, []]);
+            }
+
+        However, Heap::m_deprecatedExtraMemorySize is only 1 of 3 values that are added
+        up to form the result of Heap::extraMemorySize().  Heap::m_extraMemorySize and
+        Heap::m_arrayBuffers.size() are the other 2.
+
+        While Heap::m_arrayBuffers.size() is bounded by actual allocated memory, both
+        Heap::m_deprecatedExtraMemorySize and Heap::m_extraMemorySize are added to
+        without any bounds checks, and they are only reset to 0 at the start of a full
+        GC.  As a result, if we have a long sequence of eden GCs with a lot of additions
+        to Heap::m_extraMemorySize and/or Heap::m_deprecatedExtraMemorySize, then these
+        values could theoretically overflow.  Coupling this with the underflow from
+        SparseArrayValueMap::add(), the result for Heap::extraMemorySize() can easily
+        overflow.  Note: Heap::extraMemorySize() is used to compute the value
+        currentHeapSize.
+
+        If multiple conditions line up just right, the above overflows can result in this
+        debug assertion failure during an eden GC:
+
+            ASSERT(currentHeapSize &gt;= m_sizeAfterLastCollect);
+
+        Otherwise, the effects of the overflows will only result in the computed
+        currentHeapSize not being representative of actual memory usage, and therefore,
+        a full GC may be triggered earlier or later than is ideal.
+
+        This patch ensures that SparseArrayValueMap::add() cannot underflow
+        Heap::m_deprecatedExtraMemorySize.  It also adds overflows checks in the
+        calculations of Heap::m_deprecatedExtraMemorySize, Heap::m_extraMemorySize, and
+        Heap::extraMemorySize() so that their values are saturated appropriately to
+        ensure that GC collections are triggered based on representative memory usage.
+
+        * heap/Heap.cpp:
+        (JSC::Heap::deprecatedReportExtraMemorySlowCase):
+        (JSC::Heap::extraMemorySize):
+        (JSC::Heap::updateAllocationLimits):
+        (JSC::Heap::reportExtraMemoryVisited):
+        * runtime/SparseArrayValueMap.cpp:
+        (JSC::SparseArrayValueMap::add):
+
</ins><span class="cx"> 2017-04-03  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Move the Liveness&lt;&gt; adapters from AirLiveness.h to AirLivenessAdapter.h.
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/Heap.cpp (214856 => 214857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/Heap.cpp        2017-04-04 00:41:19 UTC (rev 214856)
+++ trunk/Source/JavaScriptCore/heap/Heap.cpp        2017-04-04 00:42:02 UTC (rev 214857)
</span><span class="lines">@@ -458,7 +458,11 @@
</span><span class="cx"> 
</span><span class="cx"> void Heap::deprecatedReportExtraMemorySlowCase(size_t size)
</span><span class="cx"> {
</span><del>-    m_deprecatedExtraMemorySize += size;
</del><ins>+    // FIXME: Change this to use SaturatedArithmetic when available.
+    // https://bugs.webkit.org/show_bug.cgi?id=170411
+    Checked&lt;size_t, RecordOverflow&gt; checkedNewSize = m_deprecatedExtraMemorySize;
+    checkedNewSize += size;
+    m_deprecatedExtraMemorySize = UNLIKELY(checkedNewSize.hasOverflowed()) ? std::numeric_limits&lt;size_t&gt;::max() : checkedNewSize.unsafeGet();
</ins><span class="cx">     reportExtraMemoryAllocatedSlowCase(size);
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="lines">@@ -706,7 +710,15 @@
</span><span class="cx"> 
</span><span class="cx"> size_t Heap::extraMemorySize()
</span><span class="cx"> {
</span><del>-    return m_extraMemorySize + m_deprecatedExtraMemorySize + m_arrayBuffers.size();
</del><ins>+    // FIXME: Change this to use SaturatedArithmetic when available.
+    // https://bugs.webkit.org/show_bug.cgi?id=170411
+    Checked&lt;size_t, RecordOverflow&gt; checkedTotal = m_extraMemorySize;
+    checkedTotal += m_deprecatedExtraMemorySize;
+    checkedTotal += m_arrayBuffers.size();
+    size_t total = UNLIKELY(checkedTotal.hasOverflowed()) ? std::numeric_limits&lt;size_t&gt;::max() : checkedTotal.unsafeGet();
+
+    ASSERT(m_objectSpace.capacity() &gt;= m_objectSpace.size());
+    return std::min(total, std::numeric_limits&lt;size_t&gt;::max() - m_objectSpace.capacity());
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> size_t Heap::size()
</span><span class="lines">@@ -2112,6 +2124,11 @@
</span><span class="cx">     // It's up to the user to ensure that extraMemorySize() ends up corresponding to allocation-time
</span><span class="cx">     // extra memory reporting.
</span><span class="cx">     currentHeapSize += extraMemorySize();
</span><ins>+    if (!ASSERT_DISABLED) {
+        Checked&lt;size_t, RecordOverflow&gt; checkedCurrentHeapSize = m_totalBytesVisited;
+        checkedCurrentHeapSize += extraMemorySize();
+        ASSERT(!checkedCurrentHeapSize.hasOverflowed() &amp;&amp; checkedCurrentHeapSize.unsafeGet() == currentHeapSize);
+    }
</ins><span class="cx"> 
</span><span class="cx">     if (verbose)
</span><span class="cx">         dataLog(&quot;extraMemorySize() = &quot;, extraMemorySize(), &quot;, currentHeapSize = &quot;, currentHeapSize, &quot;\n&quot;);
</span><span class="lines">@@ -2386,7 +2403,12 @@
</span><span class="cx">     
</span><span class="cx">     for (;;) {
</span><span class="cx">         size_t oldSize = *counter;
</span><del>-        if (WTF::atomicCompareExchangeWeakRelaxed(counter, oldSize, oldSize + size))
</del><ins>+        // FIXME: Change this to use SaturatedArithmetic when available.
+        // https://bugs.webkit.org/show_bug.cgi?id=170411
+        Checked&lt;size_t, RecordOverflow&gt; checkedNewSize = oldSize;
+        checkedNewSize += size;
+        size_t newSize = UNLIKELY(checkedNewSize.hasOverflowed()) ? std::numeric_limits&lt;size_t&gt;::max() : checkedNewSize.unsafeGet();
+        if (WTF::atomicCompareExchangeWeakRelaxed(counter, oldSize, newSize))
</ins><span class="cx">             return;
</span><span class="cx">     }
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeSparseArrayValueMapcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp (214856 => 214857)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp        2017-04-04 00:41:19 UTC (rev 214856)
+++ trunk/Source/JavaScriptCore/runtime/SparseArrayValueMap.cpp        2017-04-04 00:42:02 UTC (rev 214857)
</span><span class="lines">@@ -1,5 +1,5 @@
</span><span class="cx"> /*
</span><del>- * Copyright (C) 2011-2016 Apple Inc. All rights reserved.
</del><ins>+ * Copyright (C) 2011-2017 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -84,7 +84,7 @@
</span><span class="cx">         result = m_map.add(i, entry);
</span><span class="cx">         capacity = m_map.capacity();
</span><span class="cx">     }
</span><del>-    if (capacity != m_reportedCapacity) {
</del><ins>+    if (capacity &gt; m_reportedCapacity) {
</ins><span class="cx">         // FIXME: Adopt reportExtraMemoryVisited, and switch to reportExtraMemoryAllocated.
</span><span class="cx">         // https://bugs.webkit.org/show_bug.cgi?id=142595
</span><span class="cx">         Heap::heap(array)-&gt;deprecatedReportExtraMemory((capacity - m_reportedCapacity) * (sizeof(unsigned) + sizeof(WriteBarrier&lt;Unknown&gt;)));
</span></span></pre>
</div>
</div>

</body>
</html>