Changeset 123856 in webkit


Ignore:
Timestamp:
Jul 27, 2012 2:37:51 AM (12 years ago)
Author:
abarth@webkit.org
Message:

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):

Location:
trunk/Source
Files:
12 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r123853 r123856  
     12012-07-27  Adam Barth  <abarth@webkit.org>
     2
     3        Add a Setting to expose quantized, rate-limited MemoryInfo values
     4        https://bugs.webkit.org/show_bug.cgi?id=80444
     5
     6        Reviewed by Eric Seidel.
     7
     8        We do not currently expose real MemoryInfo objects to the web unless
     9        the user opts in because we're worried that this memory information
     10        could be used in side-channel attacks.
     11
     12        We've gotten feedback from a number of web app developers that this
     13        information is very useful in tracking the performance of their
     14        applications.  These developers use the setting in their testing labs
     15        and regression harnesses to catch memory leaks and regressiosn early in
     16        their development cycle.
     17
     18        Some of these developers have experimented with enabling this feature
     19        within their enterprise and have found the memory data from the field
     20        extremely useful in tracking down memory issues that slip through their
     21        testing.
     22
     23        Based on this experience, they've asked whether we can enable this
     24        functionality on a wider scale so they catch even more problems
     25        including problems that don't manifest within their enterprise.
     26        Because we're still worried about side-channel attacks, we don't want
     27        to expose the raw data, so we've talked with these folks in more detail
     28        to understand what information they find most valuable.
     29
     30        This patch is the result of those discussions.  In particular, this
     31        patch adds an option to expose quantized and rate-limited memory
     32        information to web pages.  Web pages can only learn new data every 20
     33        minutes, which helps mitigate attacks where the attacker compares two
     34        or readings to extract side-channel information.  The patch also only
     35        reports 100 distinct memory values, which (combined with the rate
     36        limts) makes it difficult for attackers to learn about small changes in
     37        memory use.
     38
     39        * page/MemoryInfo.cpp:
     40        (WebCore):
     41        (HeapSizeCache):
     42        (WebCore::HeapSizeCache::HeapSizeCache):
     43        (WebCore::HeapSizeCache::getCachedHeapSize):
     44        (WebCore::HeapSizeCache::maybeUpdate):
     45        (WebCore::HeapSizeCache::update):
     46        (WebCore::HeapSizeCache::quantize):
     47        (WebCore::MemoryInfo::MemoryInfo):
     48        * page/Settings.cpp:
     49        (WebCore::Settings::Settings):
     50        * page/Settings.h:
     51        (WebCore::Settings::setQuantizedMemoryInfoEnabled):
     52        (WebCore::Settings::quantizedMemoryInfoEnabled):
     53        (Settings):
     54
    1552012-07-27  Vsevolod Vlasov  <vsevik@chromium.org>
    256
  • trunk/Source/WebCore/bindings/js/ScriptGCEvent.cpp

    r95901 r123856  
    4444using namespace JSC;
    4545
    46 void ScriptGCEvent::getHeapSize(size_t& usedHeapSize, size_t& totalHeapSize, size_t& heapSizeLimit)
     46void ScriptGCEvent::getHeapSize(HeapInfo& info)
    4747{
    4848    JSGlobalData* globalData = JSDOMWindow::commonJSGlobalData();
    49     totalHeapSize = globalData->heap.capacity();
    50     usedHeapSize = globalData->heap.size();
    51     heapSizeLimit = 0;
     49    info.totalJSHeapSize = globalData->heap.capacity();
     50    info.usedJSHeapSize = globalData->heap.size();
     51    info.jsHeapSizeLimit = 0;
    5252}
    5353
  • trunk/Source/WebCore/bindings/js/ScriptGCEvent.h

    r95901 r123856  
    3838class ScriptGCEventListener;
    3939
     40struct HeapInfo {
     41    HeapInfo()
     42        : usedJSHeapSize(0)
     43        , totalJSHeapSize(0)
     44        , jsHeapSizeLimit(0)
     45    {
     46    }
     47
     48    size_t usedJSHeapSize;
     49    size_t totalJSHeapSize;
     50    size_t jsHeapSizeLimit;
     51};
     52
    4053class ScriptGCEvent
    4154{
     
    4356    static void addEventListener(ScriptGCEventListener*) { }
    4457    static void removeEventListener(ScriptGCEventListener*) { }
    45     static void getHeapSize(size_t& usedHeapSize, size_t& totalHeapSize, size_t& heapSizeLimit);
     58    static void getHeapSize(HeapInfo&);
    4659};
    4760
  • trunk/Source/WebCore/bindings/v8/ScriptGCEvent.cpp

    r116103 r123856  
    7272}
    7373
    74 void ScriptGCEvent::getHeapSize(size_t& usedHeapSize, size_t& totalHeapSize, size_t& heapSizeLimit)
     74void ScriptGCEvent::getHeapSize(HeapInfo& info)
    7575{
    7676    v8::HeapStatistics heapStatistics;
    7777    v8::V8::GetHeapStatistics(&heapStatistics);
    78     usedHeapSize = heapStatistics.used_heap_size();
    79     totalHeapSize = heapStatistics.total_heap_size();
    80     heapSizeLimit = heapStatistics.heap_size_limit();
     78    info.usedJSHeapSize = heapStatistics.used_heap_size();
     79    info.totalJSHeapSize = heapStatistics.total_heap_size();
     80    info.jsHeapSizeLimit = heapStatistics.heap_size_limit();
    8181}
    8282
  • trunk/Source/WebCore/bindings/v8/ScriptGCEvent.h

    r116103 r123856  
    4141class ScriptGCEventListener;
    4242
     43struct HeapInfo {
     44    HeapInfo()
     45        : usedJSHeapSize(0)
     46        , totalJSHeapSize(0)
     47        , jsHeapSizeLimit(0)
     48    {
     49    }
     50
     51    size_t usedJSHeapSize;
     52    size_t totalJSHeapSize;
     53    size_t jsHeapSizeLimit;
     54};
     55
    4356class ScriptGCEvent
    4457{
     
    4659    static void addEventListener(ScriptGCEventListener*);
    4760    static void removeEventListener(ScriptGCEventListener*);
    48     static void getHeapSize(size_t&, size_t&, size_t&);
     61    static void getHeapSize(HeapInfo&);
     62
    4963private:
    5064    static void gcEpilogueCallback(v8::GCType type, v8::GCCallbackFlags flags);
  • trunk/Source/WebCore/inspector/InspectorMemoryAgent.cpp

    r123744 r123856  
    418418static PassRefPtr<InspectorMemoryBlock> jsHeapInfo()
    419419{
    420     size_t usedJSHeapSize;
    421     size_t totalJSHeapSize;
    422     size_t jsHeapSizeLimit;
    423     ScriptGCEvent::getHeapSize(usedJSHeapSize, totalJSHeapSize, jsHeapSizeLimit);
     420    HeapInfo info;
     421    ScriptGCEvent::getHeapSize(info);
    424422
    425423    RefPtr<InspectorMemoryBlock> jsHeapAllocated = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapAllocated);
    426     jsHeapAllocated->setSize(totalJSHeapSize);
     424    jsHeapAllocated->setSize(static_cast<int>(info.totalJSHeapSize));
    427425
    428426    RefPtr<TypeBuilder::Array<InspectorMemoryBlock> > children = TypeBuilder::Array<InspectorMemoryBlock>::create();
    429427    RefPtr<InspectorMemoryBlock> jsHeapUsed = InspectorMemoryBlock::create().setName(MemoryBlockName::jsHeapUsed);
    430     jsHeapUsed->setSize(usedJSHeapSize);
     428    jsHeapUsed->setSize(static_cast<int>(info.usedJSHeapSize));
    431429    children->addItem(jsHeapUsed);
    432430
  • trunk/Source/WebCore/inspector/InspectorTimelineAgent.cpp

    r122312 r123856  
    468468void InspectorTimelineAgent::setHeapSizeStatistics(InspectorObject* record)
    469469{
    470     size_t usedHeapSize = 0;
    471     size_t totalHeapSize = 0;
    472     size_t heapSizeLimit = 0;
    473     ScriptGCEvent::getHeapSize(usedHeapSize, totalHeapSize, heapSizeLimit);
    474     record->setNumber("usedHeapSize", usedHeapSize);
    475     record->setNumber("totalHeapSize", totalHeapSize);
     470    HeapInfo info;
     471    ScriptGCEvent::getHeapSize(info);
     472    record->setNumber("usedHeapSize", info.usedJSHeapSize);
     473    record->setNumber("totalHeapSize", info.totalJSHeapSize);
    476474
    477475    if (m_state->getBoolean(TimelineAgentState::includeMemoryDetails)) {
  • trunk/Source/WebCore/page/MemoryInfo.cpp

    r95901 r123856  
    3535#include "ScriptGCEvent.h"
    3636#include "Settings.h"
     37#include <limits>
     38#include <wtf/CurrentTime.h>
     39#include <wtf/MainThread.h>
    3740
    3841namespace WebCore {
    3942
     43#if ENABLE(INSPECTOR)
     44
     45class HeapSizeCache {
     46    WTF_MAKE_NONCOPYABLE(HeapSizeCache);
     47public:
     48    HeapSizeCache()
     49        : m_lastUpdateTime(0)
     50    {
     51    }
     52
     53    void getCachedHeapSize(HeapInfo& info)
     54    {
     55        maybeUpdate();
     56        info = m_info;
     57    }
     58
     59private:
     60    void maybeUpdate()
     61    {
     62        // We rate-limit queries to once every twenty minutes to make it more difficult
     63        // for attackers to compare memory usage before and after some event.
     64        const double TwentyMinutesInSeconds = 20 * 60;
     65
     66        double now = monotonicallyIncreasingTime();
     67        if (now - m_lastUpdateTime >= TwentyMinutesInSeconds) {
     68            update();
     69            m_lastUpdateTime = now;
     70        }
     71    }
     72
     73    void update()
     74    {
     75        ScriptGCEvent::getHeapSize(m_info);
     76        m_info.usedJSHeapSize = quantizeMemorySize(m_info.usedJSHeapSize);
     77        m_info.totalJSHeapSize = quantizeMemorySize(m_info.totalJSHeapSize);
     78        m_info.jsHeapSizeLimit = quantizeMemorySize(m_info.jsHeapSizeLimit);
     79    }
     80
     81    double m_lastUpdateTime;
     82
     83    HeapInfo m_info;
     84};
     85
     86// We quantize the sizes to make it more difficult for an attacker to see precise
     87// impact of operations on memory. The values are used for performance tuning,
     88// and hence don't need to be as refined when the value is large, so we threshold
     89// at a list of exponentially separated buckets.
     90size_t quantizeMemorySize(size_t size)
     91{
     92    const int numberOfBuckets = 100;
     93    DEFINE_STATIC_LOCAL(Vector<size_t>, bucketSizeList, ());
     94
     95    ASSERT(isMainThread());
     96    if (bucketSizeList.isEmpty()) {
     97        bucketSizeList.resize(numberOfBuckets);
     98
     99        float sizeOfNextBucket = 10000000.0; // First bucket size is roughly 10M.
     100        const float largestBucketSize = 4000000000.0; // Roughly 4GB.
     101        // We scale with the Nth root of the ratio, so that we use all the bucktes.
     102        const float scalingFactor = exp(log(largestBucketSize / sizeOfNextBucket) / numberOfBuckets);
     103
     104        size_t nextPowerOfTen = static_cast<size_t>(pow(10, floor(log10(sizeOfNextBucket)) + 1) + 0.5);
     105        size_t granularity = nextPowerOfTen / 1000; // We want 3 signficant digits.
     106
     107        for (int i = 0; i < numberOfBuckets; ++i) {
     108            size_t currentBucketSize = static_cast<size_t>(sizeOfNextBucket);
     109            bucketSizeList[i] = currentBucketSize - (currentBucketSize % granularity);
     110
     111            sizeOfNextBucket *= scalingFactor;
     112            if (sizeOfNextBucket >= nextPowerOfTen) {
     113                if (std::numeric_limits<size_t>::max() / 10 <= nextPowerOfTen)
     114                    nextPowerOfTen = std::numeric_limits<size_t>::max();                       
     115                else {
     116                    nextPowerOfTen *= 10;
     117                    granularity *= 10;
     118                }
     119            }
     120
     121            // Watch out for overflow, if the range is too large for size_t.
     122            if (i > 0 && bucketSizeList[i] < bucketSizeList[i - 1])
     123                bucketSizeList[i] = std::numeric_limits<size_t>::max();
     124        }
     125    }
     126
     127    for (int i = 0; i < numberOfBuckets; ++i) {
     128        if (size <= bucketSizeList[i])
     129            return bucketSizeList[i];
     130    }
     131
     132    return bucketSizeList[numberOfBuckets - 1];
     133}
     134
     135#endif
     136
    40137MemoryInfo::MemoryInfo(Frame* frame)
    41         : m_totalJSHeapSize(0),
    42           m_usedJSHeapSize(0),
    43           m_jsHeapSizeLimit(0)
    44138{
    45     if (frame && frame->settings() && frame->settings()->memoryInfoEnabled()) {
     139    if (!frame || !frame->settings())
     140        return;
     141
    46142#if ENABLE(INSPECTOR)
    47         ScriptGCEvent::getHeapSize(m_usedJSHeapSize, m_totalJSHeapSize, m_jsHeapSizeLimit);
     143    if (frame->settings()->memoryInfoEnabled())
     144        ScriptGCEvent::getHeapSize(m_info);
     145    else if (true || frame->settings()->quantizedMemoryInfoEnabled()) {
     146        DEFINE_STATIC_LOCAL(HeapSizeCache, heapSizeCache, ());
     147        heapSizeCache.getCachedHeapSize(m_info);
     148    }
    48149#endif
    49     }
    50150}
    51151
  • trunk/Source/WebCore/page/MemoryInfo.h

    r123451 r123856  
    3232#define MemoryInfo_h
    3333
     34#include "ScriptGCEvent.h"
    3435#include <wtf/PassRefPtr.h>
    3536#include <wtf/RefCounted.h>
     
    4344    static PassRefPtr<MemoryInfo> create(Frame* frame) { return adoptRef(new MemoryInfo(frame)); }
    4445
    45     size_t totalJSHeapSize() const { return m_totalJSHeapSize; }
    46     size_t usedJSHeapSize() const { return m_usedJSHeapSize; }
    47     size_t jsHeapSizeLimit() const { return m_jsHeapSizeLimit; }
     46    size_t totalJSHeapSize() const { return m_info.totalJSHeapSize; }
     47    size_t usedJSHeapSize() const { return m_info.usedJSHeapSize; }
     48    size_t jsHeapSizeLimit() const { return m_info.jsHeapSizeLimit; }
    4849
    4950private:
    5051    explicit MemoryInfo(Frame*);
    5152
    52     size_t m_totalJSHeapSize;
    53     size_t m_usedJSHeapSize;
    54     size_t m_jsHeapSizeLimit;
     53    HeapInfo m_info;
    5554};
     55
     56size_t quantizeMemorySize(size_t);
    5657
    5758} // namespace WebCore
  • trunk/Source/WebCore/page/Settings.cpp

    r123775 r123856  
    243243#endif
    244244    , m_memoryInfoEnabled(false)
     245    , m_quantizedMemoryInfoEnabled(false)
    245246    , m_interactiveFormValidation(false)
    246247    , m_usePreHTML5ParserQuirks(false)
  • trunk/Source/WebCore/page/Settings.h

    r123775 r123856  
    453453        void setMemoryInfoEnabled(bool flag) { m_memoryInfoEnabled = flag; }
    454454        bool memoryInfoEnabled() const { return m_memoryInfoEnabled; }
     455
     456        void setQuantizedMemoryInfoEnabled(bool flag) { m_quantizedMemoryInfoEnabled = flag; }
     457        bool quantizedMemoryInfoEnabled() const { return m_quantizedMemoryInfoEnabled; }
    455458
    456459        // This setting will be removed when an HTML5 compatibility issue is
     
    732735        bool m_fullScreenAPIEnabled : 1;
    733736#endif
    734         bool m_asynchronousSpellCheckingEnabled: 1;
    735         bool m_unifiedTextCheckerEnabled: 1;
    736         bool m_memoryInfoEnabled: 1;
    737         bool m_interactiveFormValidation: 1;
    738         bool m_usePreHTML5ParserQuirks: 1;
     737        bool m_asynchronousSpellCheckingEnabled : 1;
     738        bool m_unifiedTextCheckerEnabled : 1;
     739        bool m_memoryInfoEnabled : 1;
     740        bool m_quantizedMemoryInfoEnabled : 1;
     741        bool m_interactiveFormValidation : 1;
     742        bool m_usePreHTML5ParserQuirks : 1;
    739743        bool m_hyperlinkAuditingEnabled : 1;
    740744        bool m_crossOriginCheckInGetMatchedCSSRulesDisabled : 1;
  • trunk/Source/WebKit/chromium/WebKit.gypi

    r123065 r123856  
    126126            'tests/LocalizedDateICUTest.cpp',
    127127            'tests/LocalizedNumberICUTest.cpp',
     128            'tests/MemoryInfo.cpp',
    128129            'tests/MockCCQuadCuller.h',
    129130            'tests/OpaqueRectTrackingContentLayerDelegateTest.cpp',
  • trunk/Source/WebKit/chromium/tests/MemoryInfo.cpp

    r123854 r123856  
    11/*
    2  * Copyright (C) 2010 Google Inc. All rights reserved.
     2 * Copyright (C) 2012 Google Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3030
    3131#include "config.h"
     32
    3233#include "MemoryInfo.h"
    3334
    34 #include "Frame.h"
    35 #include "ScriptGCEvent.h"
    36 #include "Settings.h"
     35#include <gtest/gtest.h>
    3736
    38 namespace WebCore {
     37using namespace WebCore;
    3938
    40 MemoryInfo::MemoryInfo(Frame* frame)
    41         : m_totalJSHeapSize(0),
    42           m_usedJSHeapSize(0),
    43           m_jsHeapSizeLimit(0)
     39namespace {
     40
     41TEST(MemoryInfo, quantizeMemorySize)
    4442{
    45     if (frame && frame->settings() && frame->settings()->memoryInfoEnabled()) {
    46 #if ENABLE(INSPECTOR)
    47         ScriptGCEvent::getHeapSize(m_usedJSHeapSize, m_totalJSHeapSize, m_jsHeapSizeLimit);
    48 #endif
    49     }
     43    EXPECT_EQ(10000000u, quantizeMemorySize(1024));
     44    EXPECT_EQ(10000000u, quantizeMemorySize(1024 * 1024));
     45    EXPECT_EQ(410000000u, quantizeMemorySize(389472983));
     46    EXPECT_EQ(39600000u, quantizeMemorySize(38947298));
     47    EXPECT_EQ(29400000u, quantizeMemorySize(28947298));
     48    EXPECT_EQ(19300000u, quantizeMemorySize(18947298));
     49    EXPECT_EQ(14300000u, quantizeMemorySize(13947298));
     50    EXPECT_EQ(10000000u, quantizeMemorySize(3894729));
     51    EXPECT_EQ(10000000u, quantizeMemorySize(389472));
     52    EXPECT_EQ(10000000u, quantizeMemorySize(38947));
     53    EXPECT_EQ(10000000u, quantizeMemorySize(3894));
     54    EXPECT_EQ(10000000u, quantizeMemorySize(389));
     55    EXPECT_EQ(10000000u, quantizeMemorySize(38));
     56    EXPECT_EQ(10000000u, quantizeMemorySize(3));
     57    EXPECT_EQ(10000000u, quantizeMemorySize(1));
     58    EXPECT_EQ(10000000u, quantizeMemorySize(0));
    5059}
    5160
    52 } // namespace WebCore
     61} // namespace
Note: See TracChangeset for help on using the changeset viewer.