Changeset 42896 in webkit


Ignore:
Timestamp:
Apr 27, 2009 9:57:13 AM (15 years ago)
Author:
ap@webkit.org
Message:

Reviewed by Darin Adler.

https://bugs.webkit.org/show_bug.cgi?id=25399
<rdar://problem/6633943> REGRESSION: Many crashes reported accessing Lexis/Nexis database,
beneath WebCore::Cache::evict

The crash happened because a cached resource handle was removed from a document's cached
resources map twice recursively, so a destructor was called for a value in a deleted bucket.
The first call was from Cache::evict, and when destroying CachedResourceHandle destroyed
CachedResource, DocLoader::removeCachedResource() was called again, with HashMap being in
an inconsistent state.

I couldn't fully reconstruct the loading sequence to make a test.

  • loader/Cache.cpp: (WebCore::Cache::revalidateResource): Assert that the resource being revalidated is in cache (it makes no sense to revalidate one that isn't). (WebCore::Cache::evict): Don't remove the resource from document's map. Removing a resource from the cache in no way implies that documents no longer use the old version. This fixes the crash, and also fixes many cases of resource content being unavailable in Web Inspector.
  • loader/CachedResource.h: (WebCore::CachedResource::setInCache): When bringing a revalidated resource back to cache, reset m_isBeingRevalidated to maintain the invariant of resources being revalidated never being in cache. This fixes another assertion I saw on LexisNexis search: in rare cases, switchClientsToRevalidatedResource() results in the same resource being requested again, but we were only enforcing CachedResource invariants after calling this function. (WebCore::CachedResource::unregisterHandle): Assert that the counter doesn't underflow.
  • loader/DocLoader.cpp: (WebCore::DocLoader::removeCachedResource): Assert that the passed resource is removed, not some other resource that happens to have the same URL (this used to fail on LexisNexis search before this patch).
  • loader/ImageDocument.cpp: (WebCore::ImageTokenizer::write): Replaced ASSERT_NOT_REACHED with notImplemented(). This method can be legally called via document.write(), and should work. LexisNexis takes this code path, but apparently has a fallback for Safari, so it doesn't affect site functionality.
  • loader/CachedResource.cpp: (WebCore::CachedResource::clearResourceToRevalidate): Don't assert that m_resourceToRevalidate is being revalidated - this may no longer be true, because we now reset this member in CachedResource::setInCache().
Location:
trunk/WebCore
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/WebCore/ChangeLog

    r42895 r42896  
     12009-04-27  Alexey Proskuryakov  <ap@webkit.org>
     2
     3        Reviewed by Darin Adler.
     4
     5        https://bugs.webkit.org/show_bug.cgi?id=25399
     6        <rdar://problem/6633943> REGRESSION: Many crashes reported accessing Lexis/Nexis database,
     7        beneath WebCore::Cache::evict
     8
     9        The crash happened because a cached resource handle was removed from a document's cached
     10        resources map twice recursively, so a destructor was called for a value in a deleted bucket.
     11        The first call was from Cache::evict, and when destroying CachedResourceHandle destroyed
     12        CachedResource, DocLoader::removeCachedResource() was called again, with HashMap being in
     13        an inconsistent state.
     14
     15        I couldn't fully reconstruct the loading sequence to make a test.
     16
     17        * loader/Cache.cpp:
     18        (WebCore::Cache::revalidateResource): Assert that the resource being revalidated is in cache
     19        (it makes no sense to revalidate one that isn't).
     20        (WebCore::Cache::evict): Don't remove the resource from document's map. Removing a resource
     21        from the cache in no way implies that documents no longer use the old version. This fixes the
     22        crash, and also fixes many cases of resource content being unavailable in Web Inspector.
     23
     24        * loader/CachedResource.h:
     25        (WebCore::CachedResource::setInCache): When bringing a revalidated resource back to cache,
     26        reset m_isBeingRevalidated to maintain the invariant of resources being revalidated never
     27        being in cache. This fixes another assertion I saw on LexisNexis search: in rare cases,
     28        switchClientsToRevalidatedResource() results in the same resource being requested again,
     29        but we were only enforcing CachedResource invariants after calling this function.
     30        (WebCore::CachedResource::unregisterHandle): Assert that the counter doesn't underflow.
     31
     32        * loader/DocLoader.cpp: (WebCore::DocLoader::removeCachedResource): Assert that the passed
     33        resource is removed, not some other resource that happens to have the same URL (this used to
     34        fail on LexisNexis search before this patch).
     35
     36        * loader/ImageDocument.cpp: (WebCore::ImageTokenizer::write): Replaced ASSERT_NOT_REACHED
     37        with notImplemented(). This method can be legally called via document.write(), and should
     38        work. LexisNexis takes this code path, but apparently has a fallback for Safari, so it
     39        doesn't affect site functionality.
     40
     41        * loader/CachedResource.cpp:
     42        (WebCore::CachedResource::clearResourceToRevalidate): Don't assert that m_resourceToRevalidate
     43        is being revalidated - this may no longer be true, because we now reset this member in
     44        CachedResource::setInCache().
     45
    1462009-04-27  Dan Bernstein  <mitz@apple.com>
    247
  • trunk/WebCore/loader/Cache.cpp

    r41430 r42896  
    182182{
    183183    ASSERT(resource);
     184    ASSERT(resource->inCache());
    184185    ASSERT(!disabled());
    185186    if (resource->resourceToRevalidate())
     
    391392    // who needed a fresh copy for a reload. See <http://bugs.webkit.org/show_bug.cgi?id=12479#c6>.
    392393    if (resource->inCache()) {
    393         if (!resource->isCacheValidator()) {
    394             // Notify all doc loaders that might be observing this object still that it has been
    395             // extracted from the set of resources.
    396             // No need to do this for cache validator resources, they are replaced automatically by using CachedResourceHandles.
    397             HashSet<DocLoader*>::iterator end = m_docLoaders.end();
    398             for (HashSet<DocLoader*>::iterator itr = m_docLoaders.begin(); itr != end; ++itr)
    399                 (*itr)->removeCachedResource(resource);
    400         }
    401        
    402394        // Remove from the resource map.
    403395        m_resources.remove(resource->url());
  • trunk/WebCore/loader/CachedResource.cpp

    r41092 r42896  
    259259{
    260260    ASSERT(m_resourceToRevalidate);
    261     ASSERT(m_resourceToRevalidate->m_isBeingRevalidated);
    262261    m_resourceToRevalidate->m_isBeingRevalidated = false;
    263262    m_resourceToRevalidate->deleteIfPossible();
     
    270269{
    271270    ASSERT(m_resourceToRevalidate);
     271    ASSERT(m_resourceToRevalidate->inCache());
    272272    ASSERT(!inCache());
    273273
  • trunk/WebCore/loader/CachedResource.h

    r42179 r42896  
    128128    // while still being referenced. This means the object should delete itself
    129129    // if the number of clients observing it ever drops to 0.
    130     void setInCache(bool b) { m_inCache = b; }
     130    // The resource can be brought back to cache after successful revalidation.
     131    void setInCache(bool b) { m_inCache = b; if (b) m_isBeingRevalidated = false; }
    131132    bool inCache() const { return m_inCache; }
    132133   
     
    164165   
    165166    void registerHandle(CachedResourceHandleBase* h) { ++m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.add(h); }
    166     void unregisterHandle(CachedResourceHandleBase* h) { --m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.remove(h); if (!m_handleCount) deleteIfPossible(); }
     167    void unregisterHandle(CachedResourceHandleBase* h) { ASSERT(m_handleCount > 0); --m_handleCount; if (m_resourceToRevalidate) m_handlesToRevalidate.remove(h); if (!m_handleCount) deleteIfPossible(); }
    167168   
    168169    bool canUseCacheValidator() const;
  • trunk/WebCore/loader/DocLoader.cpp

    r42084 r42896  
    277277void DocLoader::removeCachedResource(CachedResource* resource) const
    278278{
     279#ifndef NDEBUG
     280    DocumentResourceMap::iterator it = m_documentResources.find(resource->url());
     281    if (it != m_documentResources.end())
     282        ASSERT(it->second.get() == resource);
     283#endif
    279284    m_documentResources.remove(resource->url());
    280285}
  • trunk/WebCore/loader/ImageDocument.cpp

    r42446 r42896  
    3838#include "LocalizedStrings.h"
    3939#include "MouseEvent.h"
     40#include "NotImplemented.h"
    4041#include "Page.h"
    4142#include "SegmentedString.h"
     
    9495void ImageTokenizer::write(const SegmentedString&, bool)
    9596{
    96     ASSERT_NOT_REACHED();
     97    // <https://bugs.webkit.org/show_bug.cgi?id=25397>: JS code can always call document.write, we need to handle it.
     98    notImplemented();
    9799}
    98100
Note: See TracChangeset for help on using the changeset viewer.