Closed Bug 1879651 Opened 9 months ago Closed 8 months ago

crash in [@ mozilla::dom::FlipYDataSourceSurface]

Categories

(Core :: Graphics: Canvas2D, defect, P3)

defect

Tracking

()

RESOLVED FIXED
125 Branch
Tracking Status
firefox-esr115 --- wontfix
firefox123 --- wontfix
firefox124 --- wontfix
firefox125 --- fixed

People

(Reporter: tsmith, Assigned: lsalzman)

References

(Blocks 1 open bug)

Details

(Keywords: crash, pernosco, testcase, Whiteboard: [bugmon:bisected,confirmed])

Crash Data

Attachments

(2 files)

Attached file testcase.html

Found while fuzzing m-c 20240209-b17a56f7d0a8 (--enable-address-sanitizer --enable-fuzzing)

To reproduce via Grizzly Replay:

$ pip install fuzzfetch grizzly-framework
$ python -m fuzzfetch -a --fuzzing -n firefox
$ python -m grizzly.replay.bugzilla ./firefox/firefox <bugid>
==240681==ERROR: AddressSanitizer: SEGV on unknown address 0x7fd27783f000 (pc 0x7fd2941a30ab bp 0x7ffc1a738c30 sp 0x7ffc1a738a80 T0)
==240681==The signal is caused by a WRITE memory access.
    #0 0x7fd2941a30ab in swap<unsigned char> /builds/worker/fetches/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/move.h:194:11
    #1 0x7fd2941a30ab in iter_swap<unsigned char *, unsigned char *> /builds/worker/fetches/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_algobase.h:148:7
    #2 0x7fd2941a30ab in swap_ranges<unsigned char *, unsigned char *> /builds/worker/fetches/sysroot-x86_64-linux-gnu/usr/lib/gcc/x86_64-linux-gnu/8/../../../../include/c++/8/bits/stl_algobase.h:177:2
    #3 0x7fd2941a30ab in mozilla::dom::FlipYDataSourceSurface(mozilla::gfx::DataSourceSurface*) /builds/worker/checkouts/gecko/dom/canvas/ImageBitmap.cpp:375:5
    #4 0x7fd29419fad6 in mozilla::dom::ImageBitmap::CreateImageBitmapInternal(nsIGlobalObject*, mozilla::gfx::SourceSurface*, mozilla::Maybe<mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits>> const&, mozilla::dom::ImageBitmapOptions const&, bool, bool, bool, gfxAlphaType, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/canvas/ImageBitmap.cpp:1073:15
    #5 0x7fd2941aa45d in mozilla::dom::ImageBitmap::CreateInternal(nsIGlobalObject*, mozilla::dom::CanvasRenderingContext2D&, mozilla::Maybe<mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits>> const&, mozilla::dom::ImageBitmapOptions const&, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/canvas/ImageBitmap.cpp:1435:10
    #6 0x7fd2941adaff in mozilla::dom::ImageBitmap::Create(nsIGlobalObject*, mozilla::dom::HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrVideoFrameOrBlobOrCanvasRenderingContext2DOrImageData const&, mozilla::Maybe<mozilla::gfx::IntRectTyped<mozilla::gfx::UnknownUnits>> const&, mozilla::dom::ImageBitmapOptions const&, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/canvas/ImageBitmap.cpp:1832:19
    #7 0x7fd2913e5acb in nsGlobalWindowInner::CreateImageBitmap(mozilla::dom::HTMLImageElementOrSVGImageElementOrHTMLCanvasElementOrHTMLVideoElementOrOffscreenCanvasOrImageBitmapOrVideoFrameOrBlobOrCanvasRenderingContext2DOrImageData const&, mozilla::dom::ImageBitmapOptions const&, mozilla::ErrorResult&) /builds/worker/checkouts/gecko/dom/base/nsGlobalWindowInner.cpp:7368:10
    #8 0x7fd29339af7e in createImageBitmap /builds/worker/workspace/obj-build/dom/bindings/./WindowBinding.cpp:19051:64
    #9 0x7fd29339af7e in mozilla::dom::Window_Binding::createImageBitmap_promiseWrapper(JSContext*, JS::Handle<JSObject*>, void*, JSJitMethodCallArgs const&) /builds/worker/workspace/obj-build/dom/bindings/./WindowBinding.cpp:19117:13
    #10 0x7fd293e3e3e5 in bool mozilla::dom::binding_detail::GenericMethod<mozilla::dom::binding_detail::MaybeCrossOriginObjectThisPolicy, mozilla::dom::binding_detail::ConvertExceptionsToPromises>(JSContext*, unsigned int, JS::Value*) /builds/worker/checkouts/gecko/dom/bindings/BindingUtils.cpp:3258:13
    #11 0x7fd29e089c95 in CallJSNative /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:480:13
    #12 0x7fd29e089c95 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:574:12
    #13 0x7fd29e0af095 in InternalCall /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:641:10
    #14 0x7fd29e0af095 in CallFromStack /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:646:10
    #15 0x7fd29e0af095 in js::Interpret(JSContext*, js::RunState&) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:3061:16
    #16 0x7fd29e088a17 in MaybeEnterInterpreterTrampoline /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:394:10
    #17 0x7fd29e088a17 in js::RunScript(JSContext*, js::RunState&) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:452:13
    #18 0x7fd29e089dfe in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:606:13
    #19 0x7fd29e08bd86 in InternalCall /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:641:10
    #20 0x7fd29e08bd86 in js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>, js::CallReason) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:673:8
    #21 0x7fd29e67b213 in js::CallSelfHostedFunction(JSContext*, JS::Handle<js::PropertyName*>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>) /builds/worker/checkouts/gecko/js/src/vm/SelfHosting.cpp:1585:10
    #22 0x7fd29e1ed8bf in AsyncFunctionResume(JSContext*, JS::Handle<js::AsyncFunctionGeneratorObject*>, ResumeKind, JS::Handle<JS::Value>) /builds/worker/checkouts/gecko/js/src/vm/AsyncFunction.cpp:151:8
    #23 0x7fd29e55ba3f in AsyncFunctionPromiseReactionJob /builds/worker/checkouts/gecko/js/src/builtin/Promise.cpp:2127:12
    #24 0x7fd29e55ba3f in PromiseReactionJob(JSContext*, unsigned int, JS::Value*) /builds/worker/checkouts/gecko/js/src/builtin/Promise.cpp:2190:12
    #25 0x7fd29e089c95 in CallJSNative /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:480:13
    #26 0x7fd29e089c95 in js::InternalCallOrConstruct(JSContext*, JS::CallArgs const&, js::MaybeConstruct, js::CallReason) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:574:12
    #27 0x7fd29e08bd86 in InternalCall /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:641:10
    #28 0x7fd29e08bd86 in js::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, js::AnyInvokeArgs const&, JS::MutableHandle<JS::Value>, js::CallReason) /builds/worker/checkouts/gecko/js/src/vm/Interpreter.cpp:673:8
    #29 0x7fd29e249f8b in JS::Call(JSContext*, JS::Handle<JS::Value>, JS::Handle<JS::Value>, JS::HandleValueArray const&, JS::MutableHandle<JS::Value>) /builds/worker/checkouts/gecko/js/src/vm/CallAndConstruct.cpp:119:10
    #30 0x7fd292635cd5 in mozilla::dom::PromiseJobCallback::Call(mozilla::dom::BindingCallContext&, JS::Handle<JS::Value>, mozilla::ErrorResult&) /builds/worker/workspace/obj-build/dom/bindings/./PromiseBinding.cpp:83:8
    #31 0x7fd28d55c83a in Call /builds/worker/workspace/obj-build/dist/include/mozilla/dom/PromiseBinding.h:198:12
    #32 0x7fd28d55c83a in Call /builds/worker/workspace/obj-build/dist/include/mozilla/dom/PromiseBinding.h:211:12
    #33 0x7fd28d55c83a in mozilla::PromiseJobRunnable::Run(mozilla::AutoSlowOperation&) /builds/worker/checkouts/gecko/xpcom/base/CycleCollectedJSContext.cpp:210:18
    #34 0x7fd28d5332de in mozilla::CycleCollectedJSContext::PerformMicroTaskCheckPoint(bool) /builds/worker/checkouts/gecko/xpcom/base/CycleCollectedJSContext.cpp:712:17
    #35 0x7fd28d5345af in mozilla::CycleCollectedJSContext::AfterProcessTask(unsigned int) /builds/worker/checkouts/gecko/xpcom/base/CycleCollectedJSContext.cpp:499:3
    #36 0x7fd28f806bac in XPCJSContext::AfterProcessTask(unsigned int) /builds/worker/checkouts/gecko/js/xpconnect/src/XPCJSContext.cpp:1444:28
    #37 0x7fd28d7dbbd9 in nsThread::ProcessNextEvent(bool, bool*) /builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp:1237:24
    #38 0x7fd28d7e8d8a in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp:480:10
    #39 0x7fd28f48c2c3 in mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) /builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp:107:5
    #40 0x7fd28f2b4a4a in RunInternal /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:370:10
    #41 0x7fd28f2b4a4a in RunHandler /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:363:3
    #42 0x7fd28f2b4a4a in MessageLoop::Run() /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:345:3
    #43 0x7fd298c5d7e9 in nsBaseAppShell::Run() /builds/worker/checkouts/gecko/widget/nsBaseAppShell.cpp:148:27
    #44 0x7fd298e63ff2 in nsAppShell::Run() /builds/worker/checkouts/gecko/widget/gtk/nsAppShell.cpp:470:33
    #45 0x7fd29dc37b3e in XRE_RunAppShell() /builds/worker/checkouts/gecko/toolkit/xre/nsEmbedFunctions.cpp:721:20
    #46 0x7fd28f2b4a4a in RunInternal /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:370:10
    #47 0x7fd28f2b4a4a in RunHandler /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:363:3
    #48 0x7fd28f2b4a4a in MessageLoop::Run() /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:345:3
    #49 0x7fd29dc370e3 in XRE_InitChildProcess(int, char**, XREChildData const*) /builds/worker/checkouts/gecko/toolkit/xre/nsEmbedFunctions.cpp:656:34
    #50 0x55640b24a53c in content_process_main /builds/worker/checkouts/gecko/browser/app/../../ipc/contentproc/plugin-container.cpp:57:28
    #51 0x55640b24a53c in main /builds/worker/checkouts/gecko/browser/app/nsBrowserApp.cpp:375:18
    #52 0x7fd2b5e29d8f in __libc_start_call_main csu/../sysdeps/nptl/libc_start_call_main.h:58:16
    #53 0x7fd2b5e29e3f in __libc_start_main csu/../csu/libc-start.c:392:3
    #54 0x55640b16e848 in _start (/home/user/workspace/browsers/m-c-20240209114116-fuzzing-asan-opt/firefox+0xdc848) (BuildId: d7e2b3c0365615acb02f090fdc8c595f41e0ee44)
Flags: in-testsuite?

Testcase crashes using the initial build (mozilla-central 20240209093717-b17a56f7d0a8) but not with tip (mozilla-central 20240209214145-9c7562b79131.)

Unable to bisect testcase (Start build didn't crash!):

Start: b17a56f7d0a828b62b52f572c8237345bf64786f (20240209093717)
End: 9c7562b7913101280cd24aef6e3839c02f297816 (20240209214145)
BuildFlags: BuildFlags(asan=True, tsan=False, debug=False, fuzzing=True, coverage=False, valgrind=False, no_opt=False, fuzzilli=False, nyx=False)

Removing bugmon keyword as no further action possible. Please review the bug and re-add the keyword for further analysis.

Keywords: bugmon
Whiteboard: [bugmon:bisected,confirmed]

I wasn't able to get this to crash in a debug build. Perhaps there are some relevants prefs flipped?

Also bugmon seems a little confused?

A Pernosco session is available here: https://pernos.co/debug/f8vtIYpoc3rKW9Ve9BnDWQ/index.html

Hmm I guess that test is a bit flaky with some builds.

Keywords: pernosco

The code in FlipYDataSourceSurface seems reasonable, maybe some mixup handling the snapshot from the canvas?

Keywords: sec-high
Severity: -- → S2
Assignee: nobody → jimb
Priority: -- → P1

I can reproduce this.

When the crash does occur, the memory at srcBufferPtr is readable, but not writable. I added the following code:

  fprintf(stderr, "JIMB: FlipYDataSourceSurface: %d\n", srcSize.height);
  fprintf(stderr, "first byte: %p  last byte: %p\n",
          srcBufferPtr,
          (srcBufferPtr + stride * srcSize.height - 1));
  fprintf(stderr, "JIMB: read first byte: %d\n", *srcBufferPtr);
  fprintf(stderr, "JIMB: read last byte: %d\n", *(srcBufferPtr + stride * srcSize.height - 1));

  fprintf(stderr, "JIMB: write first byte:\n");
  (*srcBufferPtr)++;
  fprintf(stderr, "JIMB: write last byte:\n");
  (*(srcBufferPtr + stride * srcSize.height - 1))++;

and got the following output:

JIMB: FlipYDataSourceSurface: 150
first byte: 0x7f7af0b10000  last byte: 0x7f7af0b3bf1f
JIMB: read first byte: 0
JIMB: read last byte: 0
JIMB: write first byte:

Program /home/jimb/moz/central/obj-release-debug/dist/bin/firefox (pid = 2378076) received signal 11.

The problem does not reproduce consistently. It crashes in about one run out of three.

[Edit: the below makes it sound as if the DataSourceSurface is created first, and then the page protections get changed. I now believe the order is the opposite: the pages are mapped read-only in response to the SnapshotShmem message, and then the DataSourceSurface is created to point to them. The DataSourceSurface's buffer never dangles: it was read-only from its creation.]

In the Pernosco recording, the data of the DataSourceSurface passed to FlipYDataSourceSurface starts at 0x7f062b9fb000.

This address was previously made read-only by a PCanvas::SnapshotShmem IPDL message, when it calls mmap to map in the shmem handle it received. It passes that mmap call an address of 0, meaning that the kernel may choose any address it likes; it chooses the exact same address the DataSourceSurface was using, 0x7f062b9fb000. It should only do this if that address is unmapped, meaning that the DataSourceSurface's buffer had previously been freed, leaving it dangling.

Tentatively, it seems like, when CanvasChild::GetDataSurface is able to find aTextureId in mTextureInfo, it tries to create a SourceSurfaceRawData that points into that TextureInfo's mSnapshotShmem, which maps its contents read-only. This then runs afoul of ImageBitmap::CreateImageBitmapInternal's expectation that it can call FlipYDataSourceSurface on its contents.

:lsalzman confirms that the memory FlipYDataSourceSurface is trying to write to in the crashes is deliberately mapped read-only, but that that's probably a bug and it needs to be mapped read-write anyway. This means the crash does not indicate a dangling pointer, so this is probably not security-sensitive. Tyler, do you want to re-flag this?

Flags: needinfo?(twsmith)
Group: gfx-core-security
Flags: needinfo?(twsmith)
Keywords: sec-high

Severity and priority were set based on sec high. Adjusting.

Severity: S2 → S3
Priority: P1 → P3
Pushed by lsalzman@mozilla.com: https://hg.mozilla.org/integration/autoland/rev/294b453a50fe Ensure CreateImageBitmapInternal copies DataSourceSurface before modifying it. r=aosmond
Assignee: jimb → lsalzman
Status: NEW → ASSIGNED
Status: ASSIGNED → RESOLVED
Closed: 8 months ago
Resolution: --- → FIXED
Target Milestone: --- → 125 Branch
Crash Signature: [ @ mozilla::dom::FlipYDataSourceSurface ]
Crash Signature: [ @ mozilla::dom::FlipYDataSourceSurface ] → [@ mozilla::dom::FlipYDataSourceSurface ]

The patch landed in nightly and beta is affected.
:lsalzman, is this bug important enough to require an uplift?

  • If yes, please nominate the patch for beta approval.
  • If no, please set status-firefox124 to wontfix.

For more information, please visit BugBot documentation.

Flags: needinfo?(lsalzman)
Flags: needinfo?(lsalzman)
You need to log in before you can comment on or make changes to this bug.

Attachment

General

Created:
Updated:
Size: