RESOLVED FIXED 174168
REGRESSION(r208511): RenderImageResourceStyleImage should not assume image() won't return null if its m_cachedImage is valid
https://bugs.webkit.org/show_bug.cgi?id=174168
Summary REGRESSION(r208511): RenderImageResourceStyleImage should not assume image() ...
Said Abou-Hallawa
Reported 2017-07-05 12:09:52 PDT
In the <http://trac.webkit.org/changeset/208511>, RenderImageResource::shutdown() was changed from: void RenderImageResource::shutdown() { ASSERT(m_renderer); if (m_cachedImage) m_cachedImage->removeClient(*m_renderer); } to be: void RenderImageResource::shutdown() { ASSERT(m_renderer); if (m_cachedImage) { image()->stopAnimation(); m_cachedImage->removeClient(*m_renderer); } } To check for m_cachedImage and then assume image() won't be null makes sense because the implementation of RenderImageResource::image() is the following: RefPtr<Image> RenderImageResource::image(const IntSize&) const { return m_cachedImage ? m_cachedImage->imageForRenderer(m_renderer) : &Image::nullImage(); } The problem was the same change was copied as is to RenderImageResourceStyleImage::shutdown(). So it was changed from: void RenderImageResourceStyleImage::shutdown() { ASSERT(m_renderer); m_styleImage->removeClient(m_renderer); m_cachedImage = nullptr; } to be: void RenderImageResourceStyleImage::shutdown() { ASSERT(m_renderer); m_styleImage->removeClient(m_renderer); if (m_cachedImage) { image()->stopAnimation(); m_cachedImage = nullptr; } } This change was wrong because RenderImageResourceStyleImage::image() can return null even if m_cachedImage is not null. Here is the implementation of RenderImageResourceStyleImage::image(): RefPtr<Image> RenderImageResourceStyleImage::image(const IntSize& size) const { // Generated content may trigger calls to image() while we're still pending, don't assert but gracefully exit. if (m_styleImage->isPending()) return nullptr; return m_styleImage->image(m_renderer, size); } Notice StyleCachedImage::isPending can return true if the StyleCachedImage::load() has not been called or it was constructed with CSSImageValue which does not have a valid CachedImage. This led to the following crash: [ 1] 0x000000018b1f0773 WebCore`WebCore::RenderImageResourceStyleImage::shutdown() [inlined] WebCore::RenderImageResourceStyleImage::image(WebCore::IntSize const&) const + 63 at RenderImageResourceStyleImage.cpp:71:26 [ 1] 0x000000018b1f0734 WebCore`WebCore::RenderImageResourceStyleImage::shutdown() + 60 at RenderImageResourceStyleImage.cpp:61 [ 2] 0x000000018b1eef5b WebCore`WebCore::RenderImage::willBeDestroyed() + 31 at RenderImage.cpp:151:21 [ 3] 0x000000018a4b2d2b WebCore`WebCore::RenderObject::destroyAndCleanupAnonymousWrappers() [inlined] WebCore::RenderObject::destroy() + 39 at RenderObject.cpp:1551:5 [ 3] 0x000000018a4b2d04 WebCore`WebCore::RenderObject::destroyAndCleanupAnonymousWrappers() + 224 at RenderObject.cpp:1516 [ 4] 0x000000018b2a3617 WebCore`WebCore::RenderTreeUpdater::tearDownRenderers(WebCore::Element&, WebCore::RenderTreeUpdater::TeardownType) [inlined] WebCore::RenderTreeUpdater::tearDownRenderers(WebCore::Element&, WebCore::RenderTreeUpdater::TeardownType)::$_2::operator()(unsigned int) const + 87 at RenderTreeUpdater.cpp:562:27 [ 4] 0x000000018b2a35c0 WebCore`WebCore::RenderTreeUpdater::tearDownRenderers(WebCore::Element&, WebCore::RenderTreeUpdater::TeardownType) + 1144 at RenderTreeUpdater.cpp:584 [ 5] 0x000000018b2a2adf WebCore`WebCore::RenderTreeUpdater::updateElementRenderer(WebCore::Element&, WebCore::Style::ElementUpdate const&) + 395 at RenderTreeUpdater.cpp:268:9 [ 6] 0x000000018b2a1ddf WebCore`WebCore::RenderTreeUpdater::updateRenderTree(WebCore::ContainerNode&) + 647 at RenderTreeUpdater.cpp:178:9 [ 7] 0x000000018b2a1adb WebCore`WebCore::RenderTreeUpdater::commit(std::__1::unique_ptr<WebCore::Style::Update const, std::__1::default_delete<WebCore::Style::Update const> >) + 563 at RenderTreeUpdater.cpp:125:9 [ 8] 0x000000018a7fda6b WebCore`WebCore::Document::resolveStyle(WebCore::Document::ResolveStyleType) + 587 at Document.cpp:1825:21 [ 9] 0x000000018a454a57 WebCore`WebCore::ThreadTimers::sharedTimerFiredInternal() + 171 at ThreadTimers.cpp:118:16 [ 10] 0x000000018a454997 WebCore`WebCore::timerFired(__CFRunLoopTimer*, void*) + 27 at MainThreadSharedTimerCF.cpp:74:40
Attachments
Patch (6.98 KB, patch)
2017-07-05 12:22 PDT, Said Abou-Hallawa
no flags
Patch (3.34 KB, patch)
2017-07-05 14:02 PDT, Said Abou-Hallawa
no flags
Patch (2.15 KB, patch)
2017-07-05 16:33 PDT, Said Abou-Hallawa
no flags
Said Abou-Hallawa
Comment 1 2017-07-05 12:21:11 PDT
Said Abou-Hallawa
Comment 2 2017-07-05 12:22:21 PDT
Simon Fraser (smfr)
Comment 3 2017-07-05 13:26:05 PDT
Comment on attachment 314640 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=314640&action=review > Source/WebCore/rendering/style/StyleImage.h:75 > + virtual bool isCachedImage() const { return false; } > + virtual bool isGeneratedImage() const { return false; } > > protected: > - StyleImage() > - : m_isCachedImage(false) > - , m_isGeneratedImage(false) > - { > - } > - bool m_isCachedImage : 1; > - bool m_isGeneratedImage : 1; > + StyleImage() { } Why did this code change? We normally use this technique when performance matters, to avoid virtual function calls.
Said Abou-Hallawa
Comment 4 2017-07-05 14:02:41 PDT
Simon Fraser (smfr)
Comment 5 2017-07-05 14:41:51 PDT
Comment on attachment 314653 [details] Patch View in context: https://bugs.webkit.org/attachment.cgi?id=314653&action=review > Source/WebCore/ChangeLog:14 > + can return true and hence, RenderImageResourceStyleImage::image() will return > + a null pointer. But you are making it return the nullImage(), so are there other ways that null can get returned?
Said Abou-Hallawa
Comment 6 2017-07-05 16:33:31 PDT
Simon Fraser (smfr)
Comment 7 2017-07-05 16:40:13 PDT
Comment on attachment 314666 [details] Patch Is this testable?
Said Abou-Hallawa
Comment 8 2017-07-06 10:54:30 PDT
(In reply to Simon Fraser (smfr) from comment #7) > Comment on attachment 314666 [details] > Patch > > Is this testable? I tried but I could not find a way to reproduce the crash without the patch. It looks like the scenario is the following. A content data of type image is defined for an element, for example: <style> div::before { content: url(some-url); } </style> And before resolving the style and before loading the pending images, i.e. before calling StyleCachedImage::load(), the renderer get deleted, i.e. RenderImageResourceStyleImage::shutdown() gets called. In this caseRenderImageResourceStyleImage::image() will return nullptr and the crash happens. But this timing is can't be controlled by script.
WebKit Commit Bot
Comment 9 2017-07-06 11:22:47 PDT
Comment on attachment 314666 [details] Patch Clearing flags on attachment: 314666 Committed r219205: <http://trac.webkit.org/changeset/219205>
WebKit Commit Bot
Comment 10 2017-07-06 11:22:49 PDT
All reviewed patches have been landed. Closing bug.
Note You need to log in before you can comment on or make changes to this bug.