Diff
Modified: trunk/Source/WebCore/ChangeLog (123855 => 123856)
--- trunk/Source/WebCore/ChangeLog 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/ChangeLog 2012-07-27 09:37:51 UTC (rev 123856)
@@ -1,3 +1,57 @@
+2012-07-27 Adam Barth <[email protected]>
+
+ Add a Setting to expose quantized, rate-limited MemoryInfo values
+ https://bugs.webkit.org/show_bug.cgi?id=80444
+
+ Reviewed by Eric Seidel.
+
+ We do not currently expose real MemoryInfo objects to the web unless
+ the user opts in because we're worried that this memory information
+ could be used in side-channel attacks.
+
+ We've gotten feedback from a number of web app developers that this
+ information is very useful in tracking the performance of their
+ applications. These developers use the setting in their testing labs
+ and regression harnesses to catch memory leaks and regressiosn early in
+ their development cycle.
+
+ Some of these developers have experimented with enabling this feature
+ within their enterprise and have found the memory data from the field
+ extremely useful in tracking down memory issues that slip through their
+ testing.
+
+ Based on this experience, they've asked whether we can enable this
+ functionality on a wider scale so they catch even more problems
+ including problems that don't manifest within their enterprise.
+ Because we're still worried about side-channel attacks, we don't want
+ to expose the raw data, so we've talked with these folks in more detail
+ to understand what information they find most valuable.
+
+ This patch is the result of those discussions. In particular, this
+ patch adds an option to expose quantized and rate-limited memory
+ information to web pages. Web pages can only learn new data every 20
+ minutes, which helps mitigate attacks where the attacker compares two
+ or readings to extract side-channel information. The patch also only
+ reports 100 distinct memory values, which (combined with the rate
+ limts) makes it difficult for attackers to learn about small changes in
+ memory use.
+
+ * page/MemoryInfo.cpp:
+ (WebCore):
+ (HeapSizeCache):
+ (WebCore::HeapSizeCache::HeapSizeCache):
+ (WebCore::HeapSizeCache::getCachedHeapSize):
+ (WebCore::HeapSizeCache::maybeUpdate):
+ (WebCore::HeapSizeCache::update):
+ (WebCore::HeapSizeCache::quantize):
+ (WebCore::MemoryInfo::MemoryInfo):
+ * page/Settings.cpp:
+ (WebCore::Settings::Settings):
+ * page/Settings.h:
+ (WebCore::Settings::setQuantizedMemoryInfoEnabled):
+ (WebCore::Settings::quantizedMemoryInfoEnabled):
+ (Settings):
+
2012-07-27 Vsevolod Vlasov <[email protected]>
Web Inspector: Move formatting support from _javascript_Source to UISourceCode.
Modified: trunk/Source/WebCore/bindings/js/ScriptGCEvent.cpp (123855 => 123856)
--- trunk/Source/WebCore/bindings/js/ScriptGCEvent.cpp 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/bindings/js/ScriptGCEvent.cpp 2012-07-27 09:37:51 UTC (rev 123856)
@@ -43,12 +43,12 @@
using namespace JSC;
-void ScriptGCEvent::getHeapSize(size_t& usedHeapSize, size_t& totalHeapSize, size_t& heapSizeLimit)
+void ScriptGCEvent::getHeapSize(HeapInfo& info)
{
JSGlobalData* globalData = JSDOMWindow::commonJSGlobalData();
- totalHeapSize = globalData->heap.capacity();
- usedHeapSize = globalData->heap.size();
- heapSizeLimit = 0;
+ info.totalJSHeapSize = globalData->heap.capacity();
+ info.usedJSHeapSize = globalData->heap.size();
+ info.jsHeapSizeLimit = 0;
}
} // namespace WebCore
Modified: trunk/Source/WebCore/bindings/js/ScriptGCEvent.h (123855 => 123856)
--- trunk/Source/WebCore/bindings/js/ScriptGCEvent.h 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/bindings/js/ScriptGCEvent.h 2012-07-27 09:37:51 UTC (rev 123856)
@@ -37,12 +37,25 @@
class ScriptGCEventListener;
+struct HeapInfo {
+ HeapInfo()
+ : usedJSHeapSize(0)
+ , totalJSHeapSize(0)
+ , jsHeapSizeLimit(0)
+ {
+ }
+
+ size_t usedJSHeapSize;
+ size_t totalJSHeapSize;
+ size_t jsHeapSizeLimit;
+};
+
class ScriptGCEvent
{
public:
static void addEventListener(ScriptGCEventListener*) { }
static void removeEventListener(ScriptGCEventListener*) { }
- static void getHeapSize(size_t& usedHeapSize, size_t& totalHeapSize, size_t& heapSizeLimit);
+ static void getHeapSize(HeapInfo&);
};
} // namespace WebCore
Modified: trunk/Source/WebCore/bindings/v8/ScriptGCEvent.cpp (123855 => 123856)
--- trunk/Source/WebCore/bindings/v8/ScriptGCEvent.cpp 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/bindings/v8/ScriptGCEvent.cpp 2012-07-27 09:37:51 UTC (rev 123856)
@@ -71,13 +71,13 @@
}
}
-void ScriptGCEvent::getHeapSize(size_t& usedHeapSize, size_t& totalHeapSize, size_t& heapSizeLimit)
+void ScriptGCEvent::getHeapSize(HeapInfo& info)
{
v8::HeapStatistics heapStatistics;
v8::V8::GetHeapStatistics(&heapStatistics);
- usedHeapSize = heapStatistics.used_heap_size();
- totalHeapSize = heapStatistics.total_heap_size();
- heapSizeLimit = heapStatistics.heap_size_limit();
+ info.usedJSHeapSize = heapStatistics.used_heap_size();
+ info.totalJSHeapSize = heapStatistics.total_heap_size();
+ info.jsHeapSizeLimit = heapStatistics.heap_size_limit();
}
size_t ScriptGCEvent::getUsedHeapSize()
Modified: trunk/Source/WebCore/bindings/v8/ScriptGCEvent.h (123855 => 123856)
--- trunk/Source/WebCore/bindings/v8/ScriptGCEvent.h 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/bindings/v8/ScriptGCEvent.h 2012-07-27 09:37:51 UTC (rev 123856)
@@ -40,12 +40,26 @@
class ScriptGCEventListener;
+struct HeapInfo {
+ HeapInfo()
+ : usedJSHeapSize(0)
+ , totalJSHeapSize(0)
+ , jsHeapSizeLimit(0)
+ {
+ }
+
+ size_t usedJSHeapSize;
+ size_t totalJSHeapSize;
+ size_t jsHeapSizeLimit;
+};
+
class ScriptGCEvent
{
public:
static void addEventListener(ScriptGCEventListener*);
static void removeEventListener(ScriptGCEventListener*);
- static void getHeapSize(size_t&, size_t&, size_t&);
+ static void getHeapSize(HeapInfo&);
+
private:
static void gcEpilogueCallback(v8::GCType type, v8::GCCallbackFlags flags);
static void gcPrologueCallback(v8::GCType type, v8::GCCallbackFlags flags);
Modified: trunk/Source/WebCore/inspector/InspectorMemoryAgent.cpp (123855 => 123856)
--- trunk/Source/WebCore/inspector/InspectorMemoryAgent.cpp 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/inspector/InspectorMemoryAgent.cpp 2012-07-27 09:37:51 UTC (rev 123856)
@@ -417,17 +417,15 @@
static PassRefPtr<InspectorMemoryBlock> jsHeapInfo()
{
- size_t usedJSHeapSize;
- size_t totalJSHeapSize;
- size_t jsHeapSizeLimit;
- ScriptGCEvent::getHeapSize(usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit);
+ HeapInfo info;
+ ScriptGCEvent::getHeapSize(info);
RefPtr<InspectorMemoryBlock> jsHeapAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapAllocated);
- jsHeapAllocated->setSize(totalJSHeapSize);
+ jsHeapAllocated->setSize(static_cast<int>(info.totalJSHeapSize));
RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
RefPtr<InspectorMemoryBlock> jsHeapUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapUsed);
- jsHeapUsed->setSize(usedJSHeapSize);
+ jsHeapUsed->setSize(static_cast<int>(info.usedJSHeapSize));
children->addItem(jsHeapUsed);
jsHeapAllocated->setChildren(children);
Modified: trunk/Source/WebCore/inspector/InspectorTimelineAgent.cpp (123855 => 123856)
--- trunk/Source/WebCore/inspector/InspectorTimelineAgent.cpp 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/inspector/InspectorTimelineAgent.cpp 2012-07-27 09:37:51 UTC (rev 123856)
@@ -467,12 +467,10 @@
void InspectorTimelineAgent::setHeapSizeStatistics(InspectorObject* record)
{
- size_t usedHeapSize = 0;
- size_t totalHeapSize = 0;
- size_t heapSizeLimit = 0;
- ScriptGCEvent::getHeapSize(usedHeapSize, totalHeapSize, heapSizeLimit);
- record->setNumber("usedHeapSize", usedHeapSize);
- record->setNumber("totalHeapSize", totalHeapSize);
+ HeapInfo info;
+ ScriptGCEvent::getHeapSize(info);
+ record->setNumber("usedHeapSize", info.usedJSHeapSize);
+ record->setNumber("totalHeapSize", info.totalJSHeapSize);
if (m_state->getBoolean(TimelineAgentState::includeMemoryDetails)) {
RefPtr<InspectorObject> counters = InspectorObject::create();
Modified: trunk/Source/WebCore/page/MemoryInfo.cpp (123855 => 123856)
--- trunk/Source/WebCore/page/MemoryInfo.cpp 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/page/MemoryInfo.cpp 2012-07-27 09:37:51 UTC (rev 123856)
@@ -34,19 +34,119 @@
#include "Frame.h"
#include "ScriptGCEvent.h"
#include "Settings.h"
+#include <limits>
+#include <wtf/CurrentTime.h>
+#include <wtf/MainThread.h>
namespace WebCore {
+#if ENABLE(INSPECTOR)
+
+class HeapSizeCache {
+ WTF_MAKE_NONCOPYABLE(HeapSizeCache);
+public:
+ HeapSizeCache()
+ : m_lastUpdateTime(0)
+ {
+ }
+
+ void getCachedHeapSize(HeapInfo& info)
+ {
+ maybeUpdate();
+ info = m_info;
+ }
+
+private:
+ void maybeUpdate()
+ {
+ // We rate-limit queries to once every twenty minutes to make it more difficult
+ // for attackers to compare memory usage before and after some event.
+ const double TwentyMinutesInSeconds = 20 * 60;
+
+ double now = monotonicallyIncreasingTime();
+ if (now - m_lastUpdateTime >= TwentyMinutesInSeconds) {
+ update();
+ m_lastUpdateTime = now;
+ }
+ }
+
+ void update()
+ {
+ ScriptGCEvent::getHeapSize(m_info);
+ m_info.usedJSHeapSize = quantizeMemorySize(m_info.usedJSHeapSize);
+ m_info.totalJSHeapSize = quantizeMemorySize(m_info.totalJSHeapSize);
+ m_info.jsHeapSizeLimit = quantizeMemorySize(m_info.jsHeapSizeLimit);
+ }
+
+ double m_lastUpdateTime;
+
+ HeapInfo m_info;
+};
+
+// We quantize the sizes to make it more difficult for an attacker to see precise
+// impact of operations on memory. The values are used for performance tuning,
+// and hence don't need to be as refined when the value is large, so we threshold
+// at a list of exponentially separated buckets.
+size_t quantizeMemorySize(size_t size)
+{
+ const int numberOfBuckets = 100;
+ DEFINE_STATIC_LOCAL(Vector<size_t>, bucketSizeList, ());
+
+ ASSERT(isMainThread());
+ if (bucketSizeList.isEmpty()) {
+ bucketSizeList.resize(numberOfBuckets);
+
+ float sizeOfNextBucket = 10000000.0; // First bucket size is roughly 10M.
+ const float largestBucketSize = 4000000000.0; // Roughly 4GB.
+ // We scale with the Nth root of the ratio, so that we use all the bucktes.
+ const float scalingFactor = exp(log(largestBucketSize / sizeOfNextBucket) / numberOfBuckets);
+
+ size_t nextPowerOfTen = static_cast<size_t>(pow(10, floor(log10(sizeOfNextBucket)) + 1) + 0.5);
+ size_t granularity = nextPowerOfTen / 1000; // We want 3 signficant digits.
+
+ for (int i = 0; i < numberOfBuckets; ++i) {
+ size_t currentBucketSize = static_cast<size_t>(sizeOfNextBucket);
+ bucketSizeList[i] = currentBucketSize - (currentBucketSize % granularity);
+
+ sizeOfNextBucket *= scalingFactor;
+ if (sizeOfNextBucket >= nextPowerOfTen) {
+ if (std::numeric_limits<size_t>::max() / 10 <= nextPowerOfTen)
+ nextPowerOfTen = std::numeric_limits<size_t>::max();
+ else {
+ nextPowerOfTen *= 10;
+ granularity *= 10;
+ }
+ }
+
+ // Watch out for overflow, if the range is too large for size_t.
+ if (i > 0 && bucketSizeList[i] < bucketSizeList[i - 1])
+ bucketSizeList[i] = std::numeric_limits<size_t>::max();
+ }
+ }
+
+ for (int i = 0; i < numberOfBuckets; ++i) {
+ if (size <= bucketSizeList[i])
+ return bucketSizeList[i];
+ }
+
+ return bucketSizeList[numberOfBuckets - 1];
+}
+
+#endif
+
MemoryInfo::MemoryInfo(Frame* frame)
- : m_totalJSHeapSize(0),
- m_usedJSHeapSize(0),
- m_jsHeapSizeLimit(0)
{
- if (frame && frame->settings() && frame->settings()->memoryInfoEnabled()) {
+ if (!frame || !frame->settings())
+ return;
+
#if ENABLE(INSPECTOR)
- ScriptGCEvent::getHeapSize(m_usedJSHeapSize, m_totalJSHeapSize, m_jsHeapSizeLimit);
+ if (frame->settings()->memoryInfoEnabled())
+ ScriptGCEvent::getHeapSize(m_info);
+ else if (true || frame->settings()->quantizedMemoryInfoEnabled()) {
+ DEFINE_STATIC_LOCAL(HeapSizeCache, heapSizeCache, ());
+ heapSizeCache.getCachedHeapSize(m_info);
+ }
#endif
- }
}
} // namespace WebCore
Modified: trunk/Source/WebCore/page/MemoryInfo.h (123855 => 123856)
--- trunk/Source/WebCore/page/MemoryInfo.h 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/page/MemoryInfo.h 2012-07-27 09:37:51 UTC (rev 123856)
@@ -31,6 +31,7 @@
#ifndef MemoryInfo_h
#define MemoryInfo_h
+#include "ScriptGCEvent.h"
#include <wtf/PassRefPtr.h>
#include <wtf/RefCounted.h>
@@ -42,18 +43,18 @@
public:
static PassRefPtr<MemoryInfo> create(Frame* frame) { return adoptRef(new MemoryInfo(frame)); }
- size_t totalJSHeapSize() const { return m_totalJSHeapSize; }
- size_t usedJSHeapSize() const { return m_usedJSHeapSize; }
- size_t jsHeapSizeLimit() const { return m_jsHeapSizeLimit; }
+ size_t totalJSHeapSize() const { return m_info.totalJSHeapSize; }
+ size_t usedJSHeapSize() const { return m_info.usedJSHeapSize; }
+ size_t jsHeapSizeLimit() const { return m_info.jsHeapSizeLimit; }
private:
explicit MemoryInfo(Frame*);
- size_t m_totalJSHeapSize;
- size_t m_usedJSHeapSize;
- size_t m_jsHeapSizeLimit;
+ HeapInfo m_info;
};
+size_t quantizeMemorySize(size_t);
+
} // namespace WebCore
#endif // MemoryInfo_h
Modified: trunk/Source/WebCore/page/Settings.cpp (123855 => 123856)
--- trunk/Source/WebCore/page/Settings.cpp 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/page/Settings.cpp 2012-07-27 09:37:51 UTC (rev 123856)
@@ -242,6 +242,7 @@
, m_unifiedTextCheckerEnabled(false)
#endif
, m_memoryInfoEnabled(false)
+ , m_quantizedMemoryInfoEnabled(false)
, m_interactiveFormValidation(false)
, m_usePreHTML5ParserQuirks(false)
, m_hyperlinkAuditingEnabled(false)
Modified: trunk/Source/WebCore/page/Settings.h (123855 => 123856)
--- trunk/Source/WebCore/page/Settings.h 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebCore/page/Settings.h 2012-07-27 09:37:51 UTC (rev 123856)
@@ -453,6 +453,9 @@
void setMemoryInfoEnabled(bool flag) { m_memoryInfoEnabled = flag; }
bool memoryInfoEnabled() const { return m_memoryInfoEnabled; }
+ void setQuantizedMemoryInfoEnabled(bool flag) { m_quantizedMemoryInfoEnabled = flag; }
+ bool quantizedMemoryInfoEnabled() const { return m_quantizedMemoryInfoEnabled; }
+
// This setting will be removed when an HTML5 compatibility issue is
// resolved and WebKit implementation of interactive validation is
// completed. See http://webkit.org/b/40520, http://webkit.org/b/40747,
@@ -731,11 +734,12 @@
#if ENABLE(FULLSCREEN_API)
bool m_fullScreenAPIEnabled : 1;
#endif
- bool m_asynchronousSpellCheckingEnabled: 1;
- bool m_unifiedTextCheckerEnabled: 1;
- bool m_memoryInfoEnabled: 1;
- bool m_interactiveFormValidation: 1;
- bool m_usePreHTML5ParserQuirks: 1;
+ bool m_asynchronousSpellCheckingEnabled : 1;
+ bool m_unifiedTextCheckerEnabled : 1;
+ bool m_memoryInfoEnabled : 1;
+ bool m_quantizedMemoryInfoEnabled : 1;
+ bool m_interactiveFormValidation : 1;
+ bool m_usePreHTML5ParserQuirks : 1;
bool m_hyperlinkAuditingEnabled : 1;
bool m_crossOriginCheckInGetMatchedCSSRulesDisabled : 1;
bool m_forceCompositingMode : 1;
Modified: trunk/Source/WebKit/chromium/WebKit.gypi (123855 => 123856)
--- trunk/Source/WebKit/chromium/WebKit.gypi 2012-07-27 09:36:18 UTC (rev 123855)
+++ trunk/Source/WebKit/chromium/WebKit.gypi 2012-07-27 09:37:51 UTC (rev 123856)
@@ -125,6 +125,7 @@
'tests/ListenerLeakTest.cpp',
'tests/LocalizedDateICUTest.cpp',
'tests/LocalizedNumberICUTest.cpp',
+ 'tests/MemoryInfo.cpp',
'tests/MockCCQuadCuller.h',
'tests/OpaqueRectTrackingContentLayerDelegateTest.cpp',
'tests/OpenTypeVerticalDataTest.cpp',
Copied: trunk/Source/WebKit/chromium/tests/MemoryInfo.cpp (from rev 123854, trunk/Source/WebCore/page/MemoryInfo.cpp) (0 => 123856)
--- trunk/Source/WebKit/chromium/tests/MemoryInfo.cpp (rev 0)
+++ trunk/Source/WebKit/chromium/tests/MemoryInfo.cpp 2012-07-27 09:37:51 UTC (rev 123856)
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2012 Google Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#include "MemoryInfo.h"
+
+#include <gtest/gtest.h>
+
+using namespace WebCore;
+
+namespace {
+
+TEST(MemoryInfo, quantizeMemorySize)
+{
+ EXPECT_EQ(10000000u, quantizeMemorySize(1024));
+ EXPECT_EQ(10000000u, quantizeMemorySize(1024 * 1024));
+ EXPECT_EQ(410000000u, quantizeMemorySize(389472983));
+ EXPECT_EQ(39600000u, quantizeMemorySize(38947298));
+ EXPECT_EQ(29400000u, quantizeMemorySize(28947298));
+ EXPECT_EQ(19300000u, quantizeMemorySize(18947298));
+ EXPECT_EQ(14300000u, quantizeMemorySize(13947298));
+ EXPECT_EQ(10000000u, quantizeMemorySize(3894729));
+ EXPECT_EQ(10000000u, quantizeMemorySize(389472));
+ EXPECT_EQ(10000000u, quantizeMemorySize(38947));
+ EXPECT_EQ(10000000u, quantizeMemorySize(3894));
+ EXPECT_EQ(10000000u, quantizeMemorySize(389));
+ EXPECT_EQ(10000000u, quantizeMemorySize(38));
+ EXPECT_EQ(10000000u, quantizeMemorySize(3));
+ EXPECT_EQ(10000000u, quantizeMemorySize(1));
+ EXPECT_EQ(10000000u, quantizeMemorySize(0));
+}
+
+} // namespace