diff options
author | Hongchan Choi <hongchan@chromium.org> | 2023-01-25 20:31:15 +0000 |
---|---|---|
committer | Michael BrĂ¼ning <michael.bruning@qt.io> | 2023-03-24 14:04:41 +0000 |
commit | b67e4f21fb9418b2926e0918b4a768bfe7602c92 (patch) | |
tree | 061f82273139e3d534c959a9ba363edd8a405d0a | |
parent | 348e15090e2a3dac329dc716d8ce28d69ded380a (diff) | |
download | qtwebengine-chromium-b67e4f21fb9418b2926e0918b4a768bfe7602c92.tar.gz |
[Backport] CVE-2023-1222: Heap buffer overflow in Web Audio API
Manual backport of patch originallt reviewed on
https://chromium-review.googlesource.com/c/chromium/src/+/4150813:
Handle a transitory state of context/destination correctly for AudioWorklet operation
When the context resumes from a suspended state, it is possible for
the internal (destination) and the external (context) state to be
different in a rare case. This allows the non-worklet thread to
touch the worklet-related objects, which can causes invalid access
to the V8-managed memory space.
This CL adds a check; if the context state is suspended it swaps
the task runner right away without waiting until a resume() promise
is resolved.
Bug: 1403515
Test: The provided repro case doesn't crash ASAN anymore.
Change-Id: Ic2ea7b0337c444b7dc7d9d8b7195ed3e9ac3955f
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/4150813
Reviewed-by: Michael Wilson <mjwilson@chromium.org>
Commit-Queue: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1096948}
Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/468224
Reviewed-by: Michal Klocek <michal.klocek@qt.io>
7 files changed, 61 insertions, 14 deletions
diff --git a/chromium/third_party/blink/renderer/modules/webaudio/audio_destination_node.h b/chromium/third_party/blink/renderer/modules/webaudio/audio_destination_node.h index 07bb092a02c..48d482e3e4b 100644 --- a/chromium/third_party/blink/renderer/modules/webaudio/audio_destination_node.h +++ b/chromium/third_party/blink/renderer/modules/webaudio/audio_destination_node.h @@ -56,6 +56,11 @@ class AudioDestinationHandler : public AudioHandler { // restart of the context. virtual void RestartRendering() = 0; + // The worklet thread change can happen when a context/destination is + // suspended. In that case, we prepare the worklet operation but do not start + // running. + virtual void PrepareTaskRunnerForWorklet() = 0; + size_t CurrentSampleFrame() const { return current_sample_frame_.load(std::memory_order_acquire); } diff --git a/chromium/third_party/blink/renderer/modules/webaudio/base_audio_context.cc b/chromium/third_party/blink/renderer/modules/webaudio/base_audio_context.cc index 63fbe739139..c870d439ed7 100644 --- a/chromium/third_party/blink/renderer/modules/webaudio/base_audio_context.cc +++ b/chromium/third_party/blink/renderer/modules/webaudio/base_audio_context.cc @@ -862,12 +862,24 @@ void BaseAudioContext::NotifyWorkletIsReady() { audioWorklet()->GetMessagingProxy()->GetBackingWorkerThread(); } - // If the context is running, restart the destination to switch the render - // thread with the worklet thread. When the context is suspended, the next - // resume() call will start rendering with the worklet thread. - // Note that restarting can happen right after the context construction. - if (ContextState() == kRunning) { - destination()->GetAudioDestinationHandler().RestartRendering(); + switch (ContextState()) { + case kRunning: + // If the context is running, restart the destination to switch the render + // thread with the worklet thread right away. + destination()->GetAudioDestinationHandler().RestartRendering(); + break; + case kSuspended: + // For the suspended context, the destination will use the worklet task + // runner for rendering. This also prevents the regular audio thread from + // touching worklet-related objects by blocking an invalid transitory + // state where the context state is suspended and the destination state is + // running. See: crbug.com/1403515 + destination()->GetAudioDestinationHandler().PrepareTaskRunnerForWorklet(); + break; + case kClosed: + // When the context is closed, no preparation for the worklet operations + // is necessary. + return; } } diff --git a/chromium/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h b/chromium/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h index 0aa8af05110..5dbaeba0d25 100644 --- a/chromium/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h +++ b/chromium/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h @@ -66,6 +66,8 @@ class OfflineAudioDestinationHandler final : public AudioDestinationHandler { void Pause() override; void Resume() override; uint32_t MaxChannelCount() const override; + void PrepareTaskRunnerForWorklet() override {} + void RestartRendering() override; diff --git a/chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.cc b/chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.cc index b5207388e96..dac3e1df7aa 100644 --- a/chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.cc +++ b/chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.cc @@ -366,4 +366,16 @@ RealtimeAudioDestinationNode* RealtimeAudioDestinationNode::Create( *context, latency_hint, sample_rate); } -} // namespace blink +void RealtimeAudioDestinationHandler::PrepareTaskRunnerForWorklet() { + DCHECK(IsMainThread()); + DCHECK_EQ(Context()->ContextState(), BaseAudioContext::kSuspended); + DCHECK(Context()->audioWorklet()); + DCHECK(Context()->audioWorklet()->IsReady()); + + platform_destination_->SetWorkletTaskRunner( + Context()->audioWorklet()->GetMessagingProxy() + ->GetBackingWorkerThread() + ->GetTaskRunner(TaskType::kInternalMediaRealTime)); +} + +} // namespace blink diff --git a/chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.h b/chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.h index 0d736c1963e..2b72e6a1753 100644 --- a/chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.h +++ b/chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.h @@ -69,6 +69,7 @@ class RealtimeAudioDestinationHandler final void RestartRendering() override; uint32_t MaxChannelCount() const override; double SampleRate() const override; + void PrepareTaskRunnerForWorklet() override; // For AudioIOCallback. This is invoked by the platform audio destination to // get the next render quantum into |destination_bus| and update diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc b/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc index 642122e137a..8391ae03509 100644 --- a/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc +++ b/chromium/third_party/blink/renderer/platform/audio/audio_destination.cc @@ -326,10 +326,25 @@ void AudioDestination::Start() { SetDeviceState(DeviceState::kRunning); } +void AudioDestination::SetWorkletTaskRunner( + scoped_refptr<base::SingleThreadTaskRunner> worklet_task_runner) { + DCHECK(IsMainThread()); + TRACE_EVENT0("webaudio", "AudioDestination::SetWorkletTaskRunner"); + + if (worklet_task_runner_) { + DCHECK_EQ(worklet_task_runner_, worklet_task_runner); + return; + } + + // The dual-thread rendering kicks off, so update the earmark frames + // accordingly. + fifo_->SetEarmarkFrames(callback_buffer_size_); + worklet_task_runner_ = std::move(worklet_task_runner); +} + void AudioDestination::StartWithWorkletTaskRunner( scoped_refptr<base::SingleThreadTaskRunner> worklet_task_runner) { DCHECK(IsMainThread()); - DCHECK_EQ(worklet_task_runner_, nullptr); TRACE_EVENT0("webaudio", "AudioDestination::StartWithWorkletTaskRunner"); SendLogMessage(String::Format("%s", __func__)); @@ -337,11 +352,7 @@ void AudioDestination::StartWithWorkletTaskRunner( return; } - // The dual-thread rendering kicks off, so updates the earmark frames - // accordingly. - fifo_->SetEarmarkFrames(callback_buffer_size_); - - worklet_task_runner_ = std::move(worklet_task_runner); + SetWorkletTaskRunner(worklet_task_runner); web_audio_device_->Start(); SetDeviceState(DeviceState::kRunning); } diff --git a/chromium/third_party/blink/renderer/platform/audio/audio_destination.h b/chromium/third_party/blink/renderer/platform/audio/audio_destination.h index aa2d66e98d5..edbb91af382 100644 --- a/chromium/third_party/blink/renderer/platform/audio/audio_destination.h +++ b/chromium/third_party/blink/renderer/platform/audio/audio_destination.h @@ -112,7 +112,11 @@ class PLATFORM_EXPORT AudioDestination virtual void Pause(); virtual void Resume(); - // Starts the destination with the AudioWorklet support. + // Sets the destination for worklet operation, but does not start rendering. + void SetWorkletTaskRunner( + scoped_refptr<base::SingleThreadTaskRunner> worklet_task_runner); + + // Starts rendering in the AudioWorklet mode. void StartWithWorkletTaskRunner( scoped_refptr<base::SingleThreadTaskRunner> worklet_task_runner); |