summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHongchan Choi <hongchan@chromium.org>2023-01-25 20:31:15 +0000
committerMichael BrĂ¼ning <michael.bruning@qt.io>2023-03-24 14:04:41 +0000
commitb67e4f21fb9418b2926e0918b4a768bfe7602c92 (patch)
tree061f82273139e3d534c959a9ba363edd8a405d0a
parent348e15090e2a3dac329dc716d8ce28d69ded380a (diff)
downloadqtwebengine-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>
-rw-r--r--chromium/third_party/blink/renderer/modules/webaudio/audio_destination_node.h5
-rw-r--r--chromium/third_party/blink/renderer/modules/webaudio/base_audio_context.cc24
-rw-r--r--chromium/third_party/blink/renderer/modules/webaudio/offline_audio_destination_node.h2
-rw-r--r--chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.cc14
-rw-r--r--chromium/third_party/blink/renderer/modules/webaudio/realtime_audio_destination_node.h1
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_destination.cc23
-rw-r--r--chromium/third_party/blink/renderer/platform/audio/audio_destination.h6
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);