Bug 52732

Summary: [CSS Gradients] Crash due to out-of-memory with repeating-linear-gradient and latter stop positioned before former
Product: WebKit Reporter: Rafał Chłodnicki <rchl2k>
Component: CSSAssignee: Simon Fraser (smfr) <simon.fraser>
Status: RESOLVED FIXED    
Severity: Critical CC: abarth, aroben, eric, simon.fraser, webkit.review.bot
Priority: P2 Keywords: InRadar
Version: 528+ (Nightly build)   
Hardware: All   
OS: All   
Attachments:
Description Flags
Testcase
none
Patch aroben: review+

Description Rafał Chłodnicki 2011-01-19 12:24:43 PST
-webkit-repeating-linear-gradient(red, green 0)
or
-webkit-repeating-linear-gradient(red 0, green 0)
or
-webkit-repeating-linear-gradient(red 10%, green 9%)
or
-webkit-repeating-linear-gradient(red 110%, green)

freezes browser.
Comment 1 Simon Fraser (smfr) 2011-01-19 12:32:29 PST
Created attachment 79466 [details]
Testcase
Comment 2 Simon Fraser (smfr) 2011-01-19 12:32:42 PST
Testcase works fine on Mac.
Comment 3 Rafał Chłodnicki 2011-01-19 12:35:45 PST
I've actually tested Chrome (10.0.634.0). Maybe it doesn't have latest fixes yet...
Comment 4 Adam Roben (:aroben) 2011-01-19 12:39:32 PST
This crashes on Windows due to a failed allocation. Here's the backtrace:

 	JavaScriptCore.dll!WTF::fastMalloc(unsigned int n=379677936)  Line 260	C++
 	WebKit.dll!WTF::VectorBufferBase<WebCore::GradientStop>::allocateBuffer(unsigned int newCapacity=23729871)  Line 287 + 0xc bytes	C++
 	WebKit.dll!WTF::Vector<WebCore::GradientStop,0>::reserveCapacity(unsigned int newCapacity=23729871)  Line 872	C++
 	WebKit.dll!WTF::Vector<WebCore::GradientStop,0>::expandCapacity(unsigned int newMinCapacity=18983897)  Line 789	C++
 	WebKit.dll!WTF::Vector<WebCore::GradientStop,0>::expandCapacity(unsigned int newMinCapacity=18983897, const WebCore::GradientStop * ptr=0x0012e8c8)  Line 796	C++
 	WebKit.dll!WTF::Vector<WebCore::GradientStop,0>::append<WebCore::GradientStop>(const WebCore::GradientStop & val={...})  Line 967 + 0x18 bytes	C++
>	WebKit.dll!WebCore::CSSGradientValue::addStops(WebCore::Gradient * gradient=0x06eb8b38, WebCore::RenderObject * renderer=0x05c0e7bc, WebCore::RenderStyle * rootStyle=0x06917ae0, float maxLengthForRepeat=1.0000000)  Line 280	C++
 	WebKit.dll!WebCore::CSSLinearGradientValue::createGradient(WebCore::RenderObject * renderer=0x05c0e7bc, const WebCore::IntSize & size={...})  Line 532	C++
 	WebKit.dll!WebCore::CSSGradientValue::image(WebCore::RenderObject * renderer=0x05c0e7bc, const WebCore::IntSize & size={...})  Line 60 + 0x1f bytes	C++
 	WebKit.dll!WebCore::StyleGeneratedImage::image(WebCore::RenderObject * renderer=0x05c0e7bc, const WebCore::IntSize & size={...})  Line 77 + 0x1d bytes	C++
 	WebKit.dll!WebCore::RenderBoxModelObject::paintFillLayerExtended(const WebCore::PaintInfo & paintInfo={...}, const WebCore::Color & c={...}, const WebCore::FillLayer * bgLayer=0x06e37a80, int tx=8, int ty=8, int w=100, int h=100, WebCore::InlineFlowBox * box=0x00000000, WebCore::CompositeOperator op=CompositeSourceOver, WebCore::RenderObject * backgroundObject=0x00000000)  Line 695 + 0x23 bytes	C++
 	WebKit.dll!WebCore::RenderBox::paintFillLayer(const WebCore::PaintInfo & paintInfo={...}, const WebCore::Color & c={...}, const WebCore::FillLayer * fillLayer=0x06e37a80, int tx=8, int ty=8, int width=100, int height=100, WebCore::CompositeOperator op=CompositeSourceOver, WebCore::RenderObject * backgroundObject=0x00000000)  Line 943	C++
 	WebKit.dll!WebCore::RenderBox::paintFillLayers(const WebCore::PaintInfo & paintInfo={...}, const WebCore::Color & c={...}, const WebCore::FillLayer * fillLayer=0x06e37a80, int tx=8, int ty=8, int width=100, int height=100, WebCore::CompositeOperator op=CompositeSourceOver, WebCore::RenderObject * backgroundObject=0x00000000)  Line 938	C++
 	WebKit.dll!WebCore::RenderBox::paintBoxDecorationsWithSize(WebCore::PaintInfo & paintInfo={...}, int tx=8, int ty=8, int width=100, int height=100)  Line 821	C++
 	WebKit.dll!WebCore::RenderBox::paintBoxDecorations(WebCore::PaintInfo & paintInfo={...}, int tx=8, int ty=8)  Line 800	C++
 	WebKit.dll!WebCore::RenderBlock::paintObject(WebCore::PaintInfo & paintInfo={...}, int tx=8, int ty=8)  Line 2396 + 0x1e bytes	C++
 	WebKit.dll!WebCore::RenderBlock::paint(WebCore::PaintInfo & paintInfo={...}, int tx=8, int ty=8)  Line 2205 + 0x1e bytes	C++
 	WebKit.dll!WebCore::RenderBlock::paintChildren(WebCore::PaintInfo & paintInfo={...}, int tx=8, int ty=8)  Line 2357 + 0x28 bytes	C++
 	WebKit.dll!WebCore::RenderBlock::paintContents(WebCore::PaintInfo & paintInfo={...}, int tx=8, int ty=8)  Line 2317	C++
 	WebKit.dll!WebCore::RenderBlock::paintObject(WebCore::PaintInfo & paintInfo={...}, int tx=8, int ty=8)  Line 2429	C++
 	WebKit.dll!WebCore::RenderBlock::paint(WebCore::PaintInfo & paintInfo={...}, int tx=8, int ty=8)  Line 2205 + 0x1e bytes	C++
 	WebKit.dll!WebCore::RenderBlock::paintChildren(WebCore::PaintInfo & paintInfo={...}, int tx=0, int ty=0)  Line 2357 + 0x28 bytes	C++
 	WebKit.dll!WebCore::RenderBlock::paintContents(WebCore::PaintInfo & paintInfo={...}, int tx=0, int ty=0)  Line 2317	C++
 	WebKit.dll!WebCore::RenderBlock::paintObject(WebCore::PaintInfo & paintInfo={...}, int tx=0, int ty=0)  Line 2429	C++
 	WebKit.dll!WebCore::RenderBlock::paint(WebCore::PaintInfo & paintInfo={...}, int tx=0, int ty=0)  Line 2205 + 0x1e bytes	C++
 	WebKit.dll!WebCore::RenderLayer::paintLayer(WebCore::RenderLayer * rootLayer=0x05c3cc14, WebCore::GraphicsContext * p=0x0012fb38, const WebCore::IntRect & paintDirtyRect={...}, unsigned int paintBehavior=0, WebCore::RenderObject * paintingRoot=0x00000000, WTF::HashMap<WebCore::OverlapTestRequestClient *,WebCore::IntRect,WTF::PtrHash<WebCore::OverlapTestRequestClient *>,WTF::HashTraits<WebCore::OverlapTestRequestClient *>,WTF::HashTraits<WebCore::IntRect> > * overlapTestRequests=0x0012f968, unsigned int paintFlags=0)  Line 2506 + 0x3b bytes	C++
 	WebKit.dll!WebCore::RenderLayer::paintList(WTF::Vector<WebCore::RenderLayer *,0> * list=[1](0x023f8dd4 {m_renderer=0x067cc784 m_parent=0x05c3cc14 m_previous=0x00000000 ...}), WebCore::RenderLayer * rootLayer=0x05c3cc14, WebCore::GraphicsContext * p=0x0012fb38, const WebCore::IntRect & paintDirtyRect={...}, unsigned int paintBehavior=0, WebCore::RenderObject * paintingRoot=0x00000000, WTF::HashMap<WebCore::OverlapTestRequestClient *,WebCore::IntRect,WTF::PtrHash<WebCore::OverlapTestRequestClient *>,WTF::HashTraits<WebCore::OverlapTestRequestClient *>,WTF::HashTraits<WebCore::IntRect> > * overlapTestRequests=0x0012f968, unsigned int paintFlags=0)  Line 2566	C++
 	WebKit.dll!WebCore::RenderLayer::paintLayer(WebCore::RenderLayer * rootLayer=0x05c3cc14, WebCore::GraphicsContext * p=0x0012fb38, const WebCore::IntRect & paintDirtyRect={...}, unsigned int paintBehavior=0, WebCore::RenderObject * paintingRoot=0x00000000, WTF::HashMap<WebCore::OverlapTestRequestClient *,WebCore::IntRect,WTF::PtrHash<WebCore::OverlapTestRequestClient *>,WTF::HashTraits<WebCore::OverlapTestRequestClient *>,WTF::HashTraits<WebCore::IntRect> > * overlapTestRequests=0x0012f968, unsigned int paintFlags=0)  Line 2535	C++
 	WebKit.dll!WebCore::RenderLayer::paint(WebCore::GraphicsContext * p=0x0012fb38, const WebCore::IntRect & damageRect={...}, unsigned int paintBehavior=0, WebCore::RenderObject * paintingRoot=0x00000000)  Line 2318	C++
 	WebKit.dll!WebCore::FrameView::paintContents(WebCore::GraphicsContext * p=0x0012fb38, const WebCore::IntRect & rect={...})  Line 2186	C++
 	WebKit.dll!WebCore::ScrollView::paint(WebCore::GraphicsContext * context=0x0012fb38, const WebCore::IntRect & rect={...})  Line 895 + 0x1a bytes	C++

At the point when we crashed, the stops Vector in CSSGradientValue::addStops had 19 million items.
Comment 5 Adam Roben (:aroben) 2011-01-19 12:44:09 PST
When we enter this bit of code:

        // Work forwards from the end, adding stops until we get one after 1.
        float lastOffset = stops[stops.size() - 1].offset;
        if (lastOffset < maxExtent) {
            float currOffset = lastOffset;
            size_t srcStopOrdinal = 0;

            while (true) {
                GradientStop newStop = stops[srcStopOrdinal];
                newStop.offset = currOffset;
                stops.append(newStop);
                if (currOffset > maxExtent)
                    break;
                if (srcStopOrdinal < originalNumStops - 1)
                    currOffset += stops[originalFirstStopIndex + srcStopOrdinal + 1].offset - stops[originalFirstStopIndex + srcStopOrdinal].offset;
                srcStopOrdinal = (srcStopOrdinal + 1) % originalNumStops;
            }
        }

stops has two items, both of which have offset 0. maxExtent is 1. So we basically keep doing 0 += 0, and then if (0 > 1) break;
Comment 6 Simon Fraser (smfr) 2011-01-19 12:46:38 PST
<rdar://problem/8886781>
Comment 7 Simon Fraser (smfr) 2011-01-19 13:19:06 PST
Created attachment 79472 [details]
Patch
Comment 8 Adam Roben (:aroben) 2011-01-19 13:21:09 PST
Comment on attachment 79472 [details]
Patch

View in context: https://bugs.webkit.org/attachment.cgi?id=79472&action=review

> Source/WebCore/css/CSSGradientValue.cpp:240
> +        float gradientRange = stops[numStops - 1].offset - stops[0].offset;
> +        if (!gradientRange) {
> +            stops[0].offset = 0;
> +            stops[0].color = stops[numStops - 1].color;
> +            stops.shrink(1);
> +            numStops = 1;

I think using .first() and .last() would be clearer than [0] and [numStops - 1].
Comment 9 Simon Fraser (smfr) 2011-01-19 13:29:14 PST
http://trac.webkit.org/changeset/76154
Comment 10 WebKit Review Bot 2011-01-19 14:41:01 PST
http://trac.webkit.org/changeset/76154 might have broken GTK Linux 32-bit Release
The following tests are not passing:
fast/forms/input-text-scroll-left-on-blur.html
fast/forms/plaintext-mode-2.html