Bug 222340

Summary: Streamline ImageData size calculations and handle out-of-memory
Product: WebKit Reporter: Darin Adler <darin>
Component: ImagesAssignee: Darin Adler <darin>
Status: RESOLVED FIXED    
Severity: Normal CC: cdumez, changseok, esprehn+autocc, ews-watchlist, ggaren, gyuyoung.kim, webkit-bug-importer
Priority: P2 Keywords: InRadar
Version: WebKit Nightly Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
Patch
ggaren: review+, ews-feeder: commit-queue-
Patch none

Description Darin Adler 2021-02-23 18:42:51 PST
Streamline ImageData size calculations and handle out-of-memory
Comment 1 Darin Adler 2021-02-24 09:57:13 PST
Created attachment 421418 [details]
Patch
Comment 2 Darin Adler 2021-02-24 09:57:47 PST
rdar://72090831
Comment 3 Geoffrey Garen 2021-02-24 10:18:43 PST
Comment on attachment 421418 [details]
Patch

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

r=me

> Source/WebCore/html/ImageData.cpp:69
>  RefPtr<ImageData> ImageData::create(const IntSize& size)
>  {
> -    Checked<int, RecordOverflow> dataSize = 4;
> -    dataSize *= size.width();
> -    dataSize *= size.height();
> +    auto dataSize = ImageData::dataSize(size);
>      if (dataSize.hasOverflowed())
>          return nullptr;
> -
> -    return adoptRef(*new ImageData(size));
> +    return adoptRef(*new ImageData(size, Uint8ClampedArray::createUninitialized(dataSize.unsafeGet())));
>  }

It's kind of funny that this function will safely return null on absurd sizes that overflow, but crash on more modest sizes that trigger allocation failure. Should we crash on overflow too? Or return null on allocation failure instead?
Comment 4 Darin Adler 2021-02-24 10:32:21 PST
Comment on attachment 421418 [details]
Patch

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

>> Source/WebCore/html/ImageData.cpp:69
>>  }
> 
> It's kind of funny that this function will safely return null on absurd sizes that overflow, but crash on more modest sizes that trigger allocation failure. Should we crash on overflow too? Or return null on allocation failure instead?

Those thoughts occurred to me, too. I wasn’t sure enough about where and how this function is used to make a good decision. Let me research that now.
Comment 5 Darin Adler 2021-02-24 11:28:44 PST
Comment on attachment 421418 [details]
Patch

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

>>> Source/WebCore/html/ImageData.cpp:69
>>>  }
>> 
>> It's kind of funny that this function will safely return null on absurd sizes that overflow, but crash on more modest sizes that trigger allocation failure. Should we crash on overflow too? Or return null on allocation failure instead?
> 
> Those thoughts occurred to me, too. I wasn’t sure enough about where and how this function is used to make a good decision. Let me research that now.

Looking into this has led me down a bit of a rabbit hole.

I discovered that CanvasRenderingContext2D calls this in response to calls from JavaScript and thus should be using a create function that can return an exception. That needs to be fixed, which might change the ImageData class interface to add more create functions, or we might just use the JavaScript-friendly one. Maybe it needs a name different from just "create" to make it clear that it tries or to make it clear that it’s for JavaScript-exposed uses.

I’m not sure I should make any behavior change to this function in this patch, though. It seems logical that returning nullptr would be OK, but I am not really sure. One of the uses is in SerializedScriptValue, and I’m not sure are what the implications there; probably fine to fail cleanly if out of memory? It might be indirectly JavaScript-exposed?
Comment 6 Darin Adler 2021-02-24 16:06:17 PST
Comment on attachment 421418 [details]
Patch

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

>>>> Source/WebCore/html/ImageData.cpp:69
>>>>  }
>>> 
>>> It's kind of funny that this function will safely return null on absurd sizes that overflow, but crash on more modest sizes that trigger allocation failure. Should we crash on overflow too? Or return null on allocation failure instead?
>> 
>> Those thoughts occurred to me, too. I wasn’t sure enough about where and how this function is used to make a good decision. Let me research that now.
> 
> Looking into this has led me down a bit of a rabbit hole.
> 
> I discovered that CanvasRenderingContext2D calls this in response to calls from JavaScript and thus should be using a create function that can return an exception. That needs to be fixed, which might change the ImageData class interface to add more create functions, or we might just use the JavaScript-friendly one. Maybe it needs a name different from just "create" to make it clear that it tries or to make it clear that it’s for JavaScript-exposed uses.
> 
> I’m not sure I should make any behavior change to this function in this patch, though. It seems logical that returning nullptr would be OK, but I am not really sure. One of the uses is in SerializedScriptValue, and I’m not sure are what the implications there; probably fine to fail cleanly if out of memory? It might be indirectly JavaScript-exposed?

Turns out that changing behavior here, to return nullptr, fixes a failing regression test. So I’m going to change as you suggested (your second suggestion, return null).

There is still follow up needed to make sure we consistently raise an exception rather than failing silently or returning null in various JavaScript contexts. And I suppose some tiny risk that returning nullptr rather than crashing is worse in some way.
Comment 7 Darin Adler 2021-02-24 16:35:04 PST
Created attachment 421481 [details]
Patch
Comment 8 Darin Adler 2021-02-25 13:10:49 PST
Committed r273505 (234582@main): <https://commits.webkit.org/234582@main>