Closed Bug 1745874 (CVE-2022-22737) Opened 1 year ago Closed 11 months ago

Use-after-free of AudioSink::NotifyAudioNeeded

Categories

(Core :: Audio/Video, defect, P1)

defect

Tracking

()

RESOLVED FIXED
97 Branch
Tracking Status
firefox-esr91 96+ fixed
firefox95 --- wontfix
firefox96 + fixed
firefox97 + fixed

People

(Reporter: bo13oy, Assigned: alwu)

Details

(Keywords: csectype-uaf, sec-high, Whiteboard: [reporter-external] [client-bounty-form][adv-main96+][adv-ESR91.5+][sec-survey][post-critsmash-triage])

Attachments

(6 files, 1 obsolete file)

Attached file poc.html

Tested Version: Ubuntu 64-bit memory 8G + linux64-fuzzing-asan-opt(95.0.1 (64-bit)) => linux64-fuzzing-asan-opt

Loading poc.html into the firefox browser to create a new tab(Using the grizzly tool(allow pop-ups)),This poc is not 100% stable, and I can only trigger it in the docker container,if it fails, please repeat more times. the crash report is as follows:

=================================================================
==40332==ERROR: AddressSanitizer: heap-use-after-free on address 0x6180000503f8 at pc 0x7f9161f84be0 bp 0x7f913ee81570 sp 0x7f913ee81568
READ of size 8 at 0x6180000503f8 thread T33 (MediaDe~hine #1)
#0 0x7f9161f84bdf in mozilla::AudioSink::NotifyAudioNeeded() /builds/worker/checkouts/gecko/dom/media/mediasink/AudioSink.cpp:361:10
#1 0x7f9161fa63af in operator() /builds/worker/workspace/obj-build/dist/include/MediaEventSource.h:404:7
#2 0x7f9161fa63af in ApplyWithArgsImpl<(lambda at /builds/worker/workspace/obj-build/dist/include/MediaEventSource.h:403:37)> /builds/worker/workspace/obj-build/dist/include/MediaEventSource.h:214:5
#3 0x7f9161fa63af in mozilla::detail::ListenerImpl<mozilla::AbstractThread, std::enable_if<TakeArgs<void (mozilla::AudioSink::)(RefPtr<mozilla::AudioData> const&)>::value, mozilla::MediaEventListener>::type mozilla::MediaEventSourceImpl<(mozilla::ListenerPolicy)1, RefPtr<mozilla::AudioData> >::ConnectInternal<mozilla::AbstractThread, mozilla::AudioSink, void (mozilla::AudioSink::)(RefPtr<mozilla::AudioData> const&)>(mozilla::AbstractThread*, mozilla::AudioSink*, void (mozilla::AudioSink::)(RefPtr<mozilla::AudioData> const&))::'lambda'(RefPtr<mozilla::AudioData>&&), RefPtr<mozilla::AudioData> >::ApplyWithArgs(RefPtr<mozilla::AudioData>&&) /builds/worker/workspace/obj-build/dist/include/MediaEventSource.h:236:5
#4 0x7f9161b8caee in applyImpl<mozilla::detail::Listener<RefPtr<mozilla::AudioData> >, void (mozilla::detail::Listener<RefPtr<mozilla::AudioData> >::
)(RefPtr<mozilla::AudioData> &&), StoreCopyPassByRRef<RefPtr<mozilla::AudioData> > , 0UL> /builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h:1147:12
#5 0x7f9161b8caee in apply<mozilla::detail::Listener<RefPtr<mozilla::AudioData> >, void (mozilla::detail::Listener<RefPtr<mozilla::AudioData> >::)(RefPtr<mozilla::AudioData> &&)> /builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h:1153:12
#6 0x7f9161b8caee in mozilla::detail::RunnableMethodImpl<mozilla::detail::Listener<RefPtr<mozilla::AudioData> >
, void (mozilla::detail::Listener<RefPtr<mozilla::AudioData> >::)(RefPtr<mozilla::AudioData>&&), true, (mozilla::RunnableKind)0, RefPtr<mozilla::AudioData>&&>::Run() /builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h:1200:13
#7 0x7f915ba95b70 in mozilla::AutoTaskDispatcher::TaskGroupRunnable::Run() /builds/worker/workspace/obj-build/dist/include/mozilla/TaskDispatcher.h:227:35
#8 0x7f915baa2f0d in mozilla::TaskQueue::Runner::Run() /builds/worker/checkouts/gecko/xpcom/threads/TaskQueue.cpp:208:20
#9 0x7f915bacac3b in nsThreadPool::Run() /builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp:305:14
#10 0x7f915babd9f4 in nsThread::ProcessNextEvent(bool, bool
) /builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp:1169:16
#11 0x7f915bac76ec in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp:467:10
#12 0x7f915cf4ee0d in mozilla::ipc::MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate*) /builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp:300:20
#13 0x7f915cdd91f1 in RunInternal /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:331:10
#14 0x7f915cdd91f1 in RunHandler /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:324:3
#15 0x7f915cdd91f1 in MessageLoop::Run() /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:306:3
#16 0x7f915bab6d7b in nsThread::ThreadFunc(void*) /builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp:391:10
#17 0x7f917722a09e in _pt_root /builds/worker/checkouts/gecko/nsprpub/pr/src/pthreads/ptthread.c:201:5
#18 0x7f9178b50608 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x9608)
#19 0x7f9178718292 in __clone (/lib/x86_64-linux-gnu/libc.so.6+0x122292)

0x6180000503f8 is located 888 bytes inside of 896-byte region [0x618000050080,0x618000050400)
freed by thread T33 (MediaDe~hine #1) here:
#0 0x563dfcba4cb2 in __interceptor_free /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:111:3
#1 0x7f9161f88e9e in operator() /builds/worker/workspace/obj-build/dist/include/mozilla/UniquePtr.h:463:5
#2 0x7f9161f88e9e in reset /builds/worker/workspace/obj-build/dist/include/mozilla/UniquePtr.h:305:7
#3 0x7f9161f88e9e in ~UniquePtr /builds/worker/workspace/obj-build/dist/include/mozilla/UniquePtr.h:253:18
#4 0x7f9161f88e9e in mozilla::AudioSinkWrapper::~AudioSinkWrapper() /builds/worker/checkouts/gecko/dom/media/mediasink/AudioSinkWrapper.cpp:17:37
#5 0x7f9161f88fdd in mozilla::AudioSinkWrapper::~AudioSinkWrapper() /builds/worker/checkouts/gecko/dom/media/mediasink/AudioSinkWrapper.cpp:17:37
#6 0x7f915c92d09f in mozilla::MozPromise<bool, nsresult, false>::ThenValueBase::ResolveOrRejectRunnable::Run() /builds/worker/workspace/obj-build/dist/include/mozilla/MozPromise.h:487:21
#7 0x7f915ba95b70 in mozilla::AutoTaskDispatcher::TaskGroupRunnable::Run() /builds/worker/workspace/obj-build/dist/include/mozilla/TaskDispatcher.h:227:35
#8 0x7f915baa2f0d in mozilla::TaskQueue::Runner::Run() /builds/worker/checkouts/gecko/xpcom/threads/TaskQueue.cpp:208:20
#9 0x7f915bacac3b in nsThreadPool::Run() /builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp:305:14
#10 0x7f915babd9f4 in nsThread::ProcessNextEvent(bool, bool*) /builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp:1169:16
#11 0x7f915bac76ec in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp:467:10
#12 0x7f915cf4ee0d in mozilla::ipc::MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate*) /builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp:300:20
#13 0x7f915cdd91f1 in RunInternal /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:331:10
#14 0x7f915cdd91f1 in RunHandler /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:324:3
#15 0x7f915cdd91f1 in MessageLoop::Run() /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:306:3
#16 0x7f915bab6d7b in nsThread::ThreadFunc(void*) /builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp:391:10
#17 0x7f917722a09e in _pt_root /builds/worker/checkouts/gecko/nsprpub/pr/src/pthreads/ptthread.c:201:5
#18 0x7f9178b50608 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x9608)

previously allocated by thread T33 (MediaDe~hine #1) here:
#0 0x563dfcba4f1d in __interceptor_malloc /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_malloc_linux.cpp:129:3
#1 0x563dfcbdfb8d in moz_xmalloc /builds/worker/checkouts/gecko/memory/mozalloc/mozalloc.cpp:52:15
#2 0x7f9161bc2a7d in operator new /builds/worker/workspace/obj-build/dist/include/mozilla/cxxalloc.h:33:10
#3 0x7f9161bc2a7d in operator() /builds/worker/checkouts/gecko/dom/media/MediaDecoderStateMachine.cpp:2871:9
#4 0x7f9161bc2a7d in mozilla::AudioSinkWrapper::CreatorImpl<mozilla::MediaDecoderStateMachine::CreateAudioSink()::$_31>::Create(mozilla::media::TimeUnit const&) /builds/worker/checkouts/gecko/dom/media/mediasink/AudioSinkWrapper.h:42:14
#5 0x7f9161f89bd2 in mozilla::AudioSinkWrapper::Start(mozilla::media::TimeUnit const&, mozilla::MediaInfo const&) /builds/worker/checkouts/gecko/dom/media/mediasink/AudioSinkWrapper.cpp:174:30
#6 0x7f9161f9b01f in mozilla::VideoSink::Start(mozilla::media::TimeUnit const&, mozilla::MediaInfo const&) /builds/worker/checkouts/gecko/dom/media/mediasink/VideoSink.cpp:219:29
#7 0x7f9161a322be in mozilla::MediaDecoderStateMachine::StartMediaSink() /builds/worker/checkouts/gecko/dom/media/MediaDecoderStateMachine.cpp:3460:29
#8 0x7f9161a1c504 in mozilla::MediaDecoderStateMachine::MaybeStartPlayback() /builds/worker/checkouts/gecko/dom/media/MediaDecoderStateMachine.cpp:3052:3
#9 0x7f9161a3a864 in mozilla::MediaDecoderStateMachine::ResumeMediaSink() /builds/worker/checkouts/gecko/dom/media/MediaDecoderStateMachine.cpp:3930:3
#10 0x7f9161bdcedd in applyImpl<mozilla::MediaDecoderStateMachine, void (mozilla::MediaDecoderStateMachine::)()> /builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h:1147:12
#11 0x7f9161bdcedd in apply<mozilla::MediaDecoderStateMachine, void (mozilla::MediaDecoderStateMachine::
)()> /builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h:1153:12
#12 0x7f9161bdcedd in mozilla::detail::RunnableMethodImpl<mozilla::MediaDecoderStateMachine*, void (mozilla::MediaDecoderStateMachine::)(), true, (mozilla::RunnableKind)0>::Run() /builds/worker/workspace/obj-build/dist/include/nsThreadUtils.h:1200:13
#13 0x7f915ba95b70 in mozilla::AutoTaskDispatcher::TaskGroupRunnable::Run() /builds/worker/workspace/obj-build/dist/include/mozilla/TaskDispatcher.h:227:35
#14 0x7f915baa2f0d in mozilla::TaskQueue::Runner::Run() /builds/worker/checkouts/gecko/xpcom/threads/TaskQueue.cpp:208:20
#15 0x7f915bacac3b in nsThreadPool::Run() /builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp:305:14
#16 0x7f915babd9f4 in nsThread::ProcessNextEvent(bool, bool
) /builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp:1169:16
#17 0x7f915bac76ec in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp:467:10
#18 0x7f915cf4ee0d in mozilla::ipc::MessagePumpForNonMainThreads::Run(base::MessagePump::Delegate*) /builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp:300:20
#19 0x7f915cdd91f1 in RunInternal /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:331:10
#20 0x7f915cdd91f1 in RunHandler /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:324:3
#21 0x7f915cdd91f1 in MessageLoop::Run() /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:306:3
#22 0x7f915bab6d7b in nsThread::ThreadFunc(void*) /builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp:391:10
#23 0x7f917722a09e in _pt_root /builds/worker/checkouts/gecko/nsprpub/pr/src/pthreads/ptthread.c:201:5
#24 0x7f9178b50608 in start_thread (/lib/x86_64-linux-gnu/libpthread.so.0+0x9608)

Thread T33 (MediaDe~hine #1) created by T0 (Web Content) here:
#0 0x563dfcb8f61c in __interceptor_pthread_create /builds/worker/fetches/llvm-project/llvm/projects/compiler-rt/lib/asan/asan_interceptors.cpp:207:3
#1 0x7f917721a124 in _PR_CreateThread /builds/worker/checkouts/gecko/nsprpub/pr/src/pthreads/ptthread.c:458:14
#2 0x7f917720b3ce in PR_CreateThread /builds/worker/checkouts/gecko/nsprpub/pr/src/pthreads/ptthread.c:533:12
#3 0x7f915bab9bcd in nsThread::Init(nsTSubstring<char> const&) /builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp:607:18
#4 0x7f915bac59cf in nsThreadManager::NewNamedThread(nsTSubstring<char> const&, unsigned int, nsIThread**) /builds/worker/checkouts/gecko/xpcom/threads/nsThreadManager.cpp:581:12
#5 0x7f915bacfc31 in NS_NewNamedThread(nsTSubstring<char> const&, nsIThread**, already_AddRefed<nsIRunnable>, unsigned int) /builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp:163:57
#6 0x7f915bac9859 in NS_NewNamedThread /builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp:155:10
#7 0x7f915bac9859 in nsThreadPool::PutEvent(already_AddRefed<nsIRunnable>, unsigned int) /builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp:120:17
#8 0x7f915bacbae9 in nsThreadPool::Dispatch(already_AddRefed<nsIRunnable>, unsigned int) /builds/worker/checkouts/gecko/xpcom/threads/nsThreadPool.cpp:357:5
#9 0x7f915baa0d23 in mozilla::TaskQueue::DispatchLocked(nsCOMPtr<nsIRunnable>&, unsigned int, mozilla::AbstractThread::DispatchReason) /builds/worker/checkouts/gecko/xpcom/threads/TaskQueue.cpp:68:26
#10 0x7f915bad3933 in mozilla::TaskQueue::Dispatch(already_AddRefed<nsIRunnable>, mozilla::AbstractThread::DispatchReason) /builds/worker/workspace/obj-build/dist/include/mozilla/TaskQueue.h:87:14
#11 0x7f915ba95755 in mozilla::AutoTaskDispatcher::DispatchTaskGroup(mozilla::UniquePtr<mozilla::AutoTaskDispatcher::PerThreadTaskGroup, mozilla::DefaultDelete<mozilla::AutoTaskDispatcher::PerThreadTaskGroup> >) /builds/worker/workspace/obj-build/dist/include/mozilla/TaskDispatcher.h:275:20
#12 0x7f915ba94b36 in mozilla::AutoTaskDispatcher::~AutoTaskDispatcher() /builds/worker/workspace/obj-build/dist/include/mozilla/TaskDispatcher.h:121:7
#13 0x7f915ba96ad9 in reset /builds/worker/workspace/obj-build/dist/include/mozilla/Maybe.h:639:19
#14 0x7f915ba96ad9 in mozilla::XPCOMThreadWrapper::MaybeFireTailDispatcher() /builds/worker/checkouts/gecko/xpcom/threads/AbstractThread.cpp:195:23
#15 0x7f915ba92fac in AfterProcessNextEvent /builds/worker/checkouts/gecko/xpcom/threads/AbstractThread.cpp:133:5
#16 0x7f915ba92fac in non-virtual thunk to mozilla::XPCOMThreadWrapper::AfterProcessNextEvent(nsIThreadInternal*, bool) /builds/worker/checkouts/gecko/xpcom/threads/AbstractThread.cpp
#17 0x7f915babd537 in nsThread::ProcessNextEvent(bool, bool*) /builds/worker/checkouts/gecko/xpcom/threads/nsThread.cpp:1199:3
#18 0x7f915bac76ec in NS_ProcessNextEvent(nsIThread*, bool) /builds/worker/checkouts/gecko/xpcom/threads/nsThreadUtils.cpp:467:10
#19 0x7f915cf4d84f in mozilla::ipc::MessagePump::Run(base::MessagePump::Delegate*) /builds/worker/checkouts/gecko/ipc/glue/MessagePump.cpp:85:21
#20 0x7f915cdd91f1 in RunInternal /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:331:10
#21 0x7f915cdd91f1 in RunHandler /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:324:3
#22 0x7f915cdd91f1 in MessageLoop::Run() /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:306:3
#23 0x7f91635d3567 in nsBaseAppShell::Run() /builds/worker/checkouts/gecko/widget/nsBaseAppShell.cpp:137:27
#24 0x7f916772a5df in XRE_RunAppShell() /builds/worker/checkouts/gecko/toolkit/xre/nsEmbedFunctions.cpp:917:20
#25 0x7f915cdd91f1 in RunInternal /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:331:10
#26 0x7f915cdd91f1 in RunHandler /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:324:3
#27 0x7f915cdd91f1 in MessageLoop::Run() /builds/worker/checkouts/gecko/ipc/chromium/src/base/message_loop.cc:306:3
#28 0x7f9167729851 in XRE_InitChildProcess(int, char**, XREChildData const*) /builds/worker/checkouts/gecko/toolkit/xre/nsEmbedFunctions.cpp:749:34
#29 0x563dfcbd987d in content_process_main(mozilla::Bootstrap*, int, char**) /builds/worker/checkouts/gecko/browser/app/../../ipc/contentproc/plugin-container.cpp:57:28
#30 0x563dfcbd9ca8 in main /builds/worker/checkouts/gecko/browser/app/nsBrowserApp.cpp:327:18
#31 0x7f917861d0b2 in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x270b2)

SUMMARY: AddressSanitizer: heap-use-after-free /builds/worker/checkouts/gecko/dom/media/mediasink/AudioSink.cpp:361:10 in mozilla::AudioSink::NotifyAudioNeeded()
Shadow bytes around the buggy address:
0x0c3080002020: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c3080002030: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c3080002040: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c3080002050: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
0x0c3080002060: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd
=>0x0c3080002070: fd fd fd fd fd fd fd fd fd fd fd fd fd fd fd[fd]
0x0c3080002080: fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa fa
0x0c3080002090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c30800020a0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c30800020b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x0c30800020c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
==40332==ABORTING

This vuln is discovered by bo13oy of Cyber Kunlun Lab.

Thanks.

Flags: sec-bounty?
Attached image reproduce.gif
Group: firefox-core-security → core-security
Type: task → defect
Component: Security → Audio/Video
Product: Firefox → Core
Group: core-security → media-core-security

What is the build ID for the testing Firefox you were running on? I couldn't reprodue this error (but I encountered another SEGV) while testing that poc file on the latest central by using following commands.

python3 -m fuzzfetch --central --asan --fuzzing -n firefox
python3 -m grizzly.replay ./firefox/firefox ~/Downloads/poc.html -l ~/poc-testing

Thank you.

Assignee: nobody → alwu
Flags: needinfo?(bo13oy)

Vendor=Mozilla
Name=Firefox
RemotingName=firefox
CodeName=Nightly
Version=95.0.1
BuildID=20211213184707
SourceStamp=e1e02ca86a8e08d28a750053f51cc30ed144fbb8
ID={ec8030f7-c20a-464f-9b0e-13a3a9e97384}

/usr/bin/python3 -m grizzly ../browsers/firefox/firefox sample -p ../browsers/prefs.js ->
/home/code/browsers/firefox/firefox -no-remote -profile /tmp/ffprof_d_obgvku http://127.0.0.1:32029/poc.html

I feel that this is a conditional competition vulnerability,The conditions for triggering this vulnerability are demanding, and as you can see from my reproduction video(reproduce.gif), it needs to be based on a very poorly performing environment in order to trigger the vulnerability.

Flags: needinfo?(bo13oy)
Attached file prefs.js

Set severity to P3 is because this UAF isn't easy to reproduce, per comment 3, it seems only being able to reproduce on poorly performing environment. I couldn't reproduce this UAF on both central and the build 20211213184707, which is the one used by reporter.

I suspect this issue is caused by here. The following is the possible situation.

(1) Shutdown the media sink #1
When we finish decoding the first frame, we would suspend the decoder which causes the media sink gets shutdown.

(2) Media starts playing, then creating another new media sink #2
In POC file, audio.play().then(function(){}); will trigger decoder to start playing, and then the play state of decoder will be mirrored to MDSM. As the play state is playing, it's possible to trigger starting media sink and then create a new media sink.

(3) Resume media sink, which causes incorrectly discard media sink #2 and create another sink #3
We would resume the decoder when media element starts playing. Notice that, although this happen before mDecoder->Play() (that is the step (2) to change play state), the mirror is done by a direct task, so even if it's called later, it might still be possible to be processed before the resume task (just a regular task).

Therefore, if (2) runs before (3), ResumeMediaSink() will just create another new sink, and the old one won't be stopped properly.

Severity: -- → S3
Priority: -- → P1

After an offline discussion with bryce, I will provide more details for how those tasks get dispatched.

Let's assuming that now the task queue (MDSM thread) is still running another task group runnable A, and the script is calling audio.play() on the main thread. Then in HTMLMediaElement::PlayInternal(), these two functions will be called, the first one will eventually queue a regular task on the task queue, and the second one will change the canonical play state which queues a state-change task on the task queue.

Because the task queue supports tail dispatching, all related tasks will be stored in its task group. Inside a task group, the task will be seperate into three different types of tasks: StateChangeTask (state-mirroring), DirectTask (state-watching) and RegularTask. Those tasks will be run in a different priorities.

Now back to the crash, after calling decoder.resume(), it would queue a regular task into the task group and the task queue (MDSM) will look like this

[task group A (running)] [task group B : resuming media sink (waiting)]

What I guess is that, on a super slow machine, task A takes longer time to run, and decoder.play() would be called before the task A finsihes, which would queue a state-change into task group B. Then as state-change task would be run before regular task, resuming media sink would run later and cause recreating the media sink.

Comment on attachment 9255824 [details]
Bug 1745874 - only recreate and start media sink when it's not running.

Security Approval Request

  • How easily could an exploit be constructed based on the patch?: It seems difficult to use this patch to construct the exploit because it involves many layers of event dispatching.
  • Do comments in the patch, the check-in comment, or tests included in the patch paint a bulls-eye on the security problem?: No
  • Which older supported branches are affected by this flaw?: 96
  • If not all supported branches, which bug introduced the flaw?: Bug 1571513
  • Do you have backports for the affected branches?: Yes
  • If not, how different, hard to create, and risky will they be?:
  • How likely is this patch to cause regressions; how much testing does it need?: Low risk to cause a regression, because we already have a debug assertion to prevent this thing happen. Now we just change the assertion to real conditional check to further prevent this situation happens. This patch doesn't introduce any new functionality.
Attachment #9255824 - Flags: sec-approval?

Comment on attachment 9255824 [details]
Bug 1745874 - only recreate and start media sink when it's not running.

approved to land and request uplift

Attachment #9255824 - Flags: sec-approval? → sec-approval+

Comment on attachment 9255824 [details]
Bug 1745874 - only recreate and start media sink when it's not running.

Per this process I would like to request a uplift and land them as close as possible. However, this patch won't work on Release, I would need to update another verison of patch in order to avoid the merging conflict.

Beta/Release Uplift Approval Request

  • User impact if declined: Firefox would crash and the exploit might be used for attacking
  • Is this code covered by automated tests?: No
  • Has the fix been verified in Nightly?: No
  • Needs manual test from QE?: No
  • If yes, steps to reproduce: No
  • List of other uplifts needed: None
  • Risk to taking this patch: Low
  • Why is the change risky/not risky? (and alternatives if risky): This change is to add a new conditional check to avoid triggering the UAF, it doesn't introduce any new behavior.
  • String changes made/needed: no
Attachment #9255824 - Flags: approval-mozilla-beta?

Approval Request Comment
[Feature/Bug causing the regression]: bug 1571513
[User impact if declined]: Firefox would crash and the exploit might be used for attacking
[Is this code covered by automated tests?]: no
[Has the fix been verified in Nightly?]: no
[Needs manual test from QE? If yes, steps to reproduce]: no
[List of other uplifts needed for the feature/fix]: no
[Is the change risky?]: no
[Why is the change risky/not risky?]: This change is to add a new conditional check to avoid triggering the UAF, it doesn't introduce any new behavior.
[String changes made/needed]: no

[Approval Request Comment]
If this is not a sec:{high,crit} bug, please state case for ESR consideration:
User impact if declined: Firefox would crash and the exploit might be used for attacking
Fix Landed on Version: 97
Risk to taking this patch (and alternatives if risky): low risk, this change is to add a new conditional check to avoid triggering the UAF, it doesn't introduce any new behavior.

See https://wiki.mozilla.org/Release_Management/ESR_Landing_Process for more info.

Attachment #9257377 - Flags: approval-mozilla-release?
Attachment #9257377 - Flags: approval-mozilla-esr91?

Comment on attachment 9255824 [details]
Bug 1745874 - only recreate and start media sink when it's not running.

96 was already merged to release today as we're now in RC week.

Attachment #9255824 - Flags: approval-mozilla-beta? → approval-mozilla-release?
Attachment #9257377 - Flags: approval-mozilla-release?
Group: media-core-security → core-security-release
Status: NEW → RESOLVED
Closed: 11 months ago
Resolution: --- → FIXED
Target Milestone: --- → 97 Branch

Comment on attachment 9255824 [details]
Bug 1745874 - only recreate and start media sink when it's not running.

Approved for 96.0rc2

Attachment #9255824 - Flags: approval-mozilla-release? → approval-mozilla-release+
Whiteboard: [reporter-external] [client-bounty-form] [verif?] → [reporter-external] [client-bounty-form][adv-main96+][adv-ESR91.5+]
Attached file advisory.txt (obsolete) —
Comment on attachment 9257644 [details]
advisory.txt

>Race condition when playing audio files
>bo13oy of Cyber Kunlun Lab
>
>Constructing audio sinks could lead to a race condition when playing audio files and closing windows. This can lead to a use-after-free causing a potentially exploitable crash.
Attached file advisory.txt

Thanks.

Attachment #9257644 - Attachment is obsolete: true

Comment on attachment 9257377 [details]
Fix-for-release-version

Approved for 91.5esr.

Attachment #9257377 - Flags: approval-mozilla-esr91? → approval-mozilla-esr91+

As part of a security bug pattern analysis, we are requesting your help with a high level analysis of this bug. It is our hope to develop static analysis (or potentially runtime/dynamic analysis) in the future to identify classes of bugs.

Please visit this google form to reply.

Flags: needinfo?(alwu)
Whiteboard: [reporter-external] [client-bounty-form][adv-main96+][adv-ESR91.5+] → [reporter-external] [client-bounty-form][adv-main96+][adv-ESR91.5+][sec-survey]
Flags: needinfo?(alwu)
Alias: CVE-2022-22737
Flags: sec-bounty? → sec-bounty+
Flags: qe-verify-
Whiteboard: [reporter-external] [client-bounty-form][adv-main96+][adv-ESR91.5+][sec-survey] → [reporter-external] [client-bounty-form][adv-main96+][adv-ESR91.5+][sec-survey][post-critsmash-triage]
Group: core-security-release
You need to log in before you can comment on or make changes to this bug.