High patch-now CVSS 8.8 chrome OOB 🔧 Commit mapped

Overview

High
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactOut of bounds write in WebAudio
DescriptionOut of bounds write in WebAudio
ComponentWebAudio
Bug ClassOOB
Action tierpatch-now — corruption primitive, web-reachable in default config (unverified)
Computed by PatchGap's deterministic exploitability ruler from the fixing commit. patch-now = RCE-class & attacker-reachable in a default config, or exploited in the wild; watch = real security signal below that bar; noise = no security relevance.
Tracker499565267
Fix commit5c37ebf85514 (chromium/src) +19/-5
CWECWE-787 (Out-of-bounds write)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedBrendan Dolan-Gavitt, XBOW
Reported2026-04-06 · bounty $7000 (per vendor advisory)
Fix landed2026-04-15 (public git)
Disclosed2026-05-12
Patch gap27 days — fix public in source before the advisory

Background

Denormal (subnormal) floats
Very-small IEEE-754 values whose handling can be toggled to flush-to-zero; WebAudio disables them on audio threads for performance, which changes CPU/FPU state and JIT-generated code assumptions.
`DenormalModifier::DisableDenormals`
The platform helper (in denormal_disabler.h) that sets the thread’s flush-to-zero/denormals-off flag.
`WorkerBackingThread`
The Blink object wrapping a non-main worker thread and owning its v8::Isolate, initialized in WorkerBackingThread::InitializeOnBackingThread.
`AudioWorkletGlobalScope`
The V8 global scope for an AudioWorklet, previously the place where DisableDenormals was called, i.e. after the isolate already existed.

Root Cause Analysis

Denormals were being disabled inside the AudioWorkletGlobalScope constructor, which runs after the V8 isolate for the AudioWorklet thread has already been created and initialized. That left a window in which the isolate’s internal state and any generated code were set up under a different denormal-flag state than the global scope later ran with, producing an inconsistency between the isolate and the scope.

The patch moves the decision to thread-creation time: WorkerBackingThread records is_denormal_disabled_thread_ from IsDenormalDisabledThreadType(params.thread_type) for the three audio-worklet thread types, and InitializeOnBackingThread now calls DenormalModifier::DisableDenormals() before V8PerIsolateData::Initialize. This restores the invariant that the denormal flag is consistent across isolate creation and the global scope, and the old call site in AudioWorkletGlobalScope is removed. The advisory classifies the resulting defect as an out-of-bounds write in WebAudio; the diff itself only shows the flag-ordering correction, so the precise OOB-write sink is not visible in the hunks shown.

Key insight
The single mistake was disabling denormals too late — after the isolate was already initialized — so the isolate and its generated code assumed a different FPU state than the running scope; the fix hoists the flag change to before V8PerIsolateData::Initialize so both agree.

Attack Path

  1. Create an AudioWorklet A page spins up an AudioWorklet, causing Blink to create a WorkerBackingThread of an audio-worklet ThreadType and initialize a V8 isolate on it.
  2. Inconsistent FPU/flag state at isolate init Pre-patch, the isolate and its generated code were initialized while denormals were still enabled, then denormals were disabled later in AudioWorkletGlobalScope, leaving the isolate’s assumptions out of sync with the scope’s actual flag state.
  3. Trigger the inconsistency in generated code Audio-processing JavaScript/generated code runs under the mismatched denormal state. (inferred: how the mismatch is driven to a specific miscomputation is not shown in the diff)
  4. Out-of-bounds write The advisory reports this leads to an out-of-bounds write in WebAudio; the exact sink, offset, and object are not present in the shown diff and are therefore (inferred).

Impact Assessment

An attacker-controlled page using an AudioWorklet could reach an out-of-bounds write in the WebAudio code running in the renderer process, per the advisory. The vendor advisory rates this high severity; any further escalation (memory corruption to code execution) beyond the OOB write the advisory names is (inferred) and not demonstrated by the diff. The precondition is the ability to create and run an AudioWorklet, which is available to ordinary web content.

Changed Functions

FunctionChangeNotes
if
third_party/blink/renderer/core/workers/worker_backing_thread.cc
modified

Files Changed

  • third_party/blink/renderer/core/workers/worker_backing_thread.cc
  • third_party/blink/renderer/core/workers/worker_backing_thread.h
  • third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc

Audit Directions

  • Order of state changes vs. isolate/JIT setup
    Audit any per-thread CPU/FPU/mode flag (denormals, rounding mode, SIMD flags) set after V8PerIsolateData::Initialize; such state must be fixed before the isolate and its generated code are created.
  • Thread-type-specific configuration
    Review other ThreadType-conditional setup that happens in a global-scope constructor rather than at WorkerBackingThread creation, since late configuration can diverge from isolate assumptions.
  • WebAudio numeric-state assumptions
    Examine WebAudio buffer-size / index computations that depend on floating-point results, where a flushed-vs-unflushed denormal difference could perturb a length or offset used for memory access.
From 5c37ebf855147997e7cbc84d8ef67ab142cc78b4 Mon Sep 17 00:00:00 2001
From: Michael Wilson <mjwilson@chromium.org>
Date: Wed, 15 Apr 2026 03:37:44 -0700
Subject: [PATCH] Disable denormals before isolate creation for AudioWorklet threads

This is to ensure consistency in the denormal flag state between the
isolate and the global scope.

Bug: 499565267
Change-Id: Iec7735bd6bc543da2ad08aea2f7bc716265dc54d
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7744866
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Commit-Queue: Michael Wilson <mjwilson@chromium.org>
Reviewed-by: Hongchan Choi <hongchan@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1615043}
---

diff --git a/third_party/blink/renderer/core/workers/worker_backing_thread.cc b/third_party/blink/renderer/core/workers/worker_backing_thread.cc
index 38d221a6..5997bed3 100644
--- a/third_party/blink/renderer/core/workers/worker_backing_thread.cc
+++ b/third_party/blink/renderer/core/workers/worker_backing_thread.cc
@@ -18,6 +18,7 @@
 #include "third_party/blink/renderer/bindings/core/v8/v8_initializer.h"
 #include "third_party/blink/renderer/core/inspector/worker_thread_debugger.h"
 #include "third_party/blink/renderer/core/workers/worker_backing_thread_startup_data.h"
+#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
 #include "third_party/blink/renderer/platform/heap/thread_state.h"
 #include "third_party/blink/renderer/platform/runtime_enabled_features.h"
 #include "third_party/blink/renderer/platform/scheduler/public/main_thread.h"
@@ -91,6 +92,14 @@
   ForegroundedIsolates().erase(isolate);
 }
 
+bool IsDenormalDisabledThreadType(ThreadType type) {
+  // Disable denormals on WebAudio threads for performance reasons.  See:
+  // https://esdiscuss.org/topic/float-denormal-issue-in-javascript-processor-node-in-web-audio-api
+  return type == ThreadType::kOfflineAudioWorkletThread ||
+         type == ThreadType::kRealtimeAudioWorkletThread ||
+         type == ThreadType::kSemiRealtimeAudioWorkletThread;
+}
+
 }  // namespace
 
 // Wrapper functions defined in third_party/blink/public/web/blink.h
@@ -119,7 +128,9 @@
 
 WorkerBackingThread::WorkerBackingThread(const ThreadCreationParams& params)
     : backing_thread_(blink::NonMainThread::CreateThread(
-          ThreadCreationParams(params).SetSupportsGC(true))) {}
+          ThreadCreationParams(params).SetSupportsGC(true))),
+      is_denormal_disabled_thread_(
+          IsDenormalDisabledThreadType(params.thread_type)) {}
 
 WorkerBackingThread::~WorkerBackingThread() = default;
 
@@ -127,6 +138,12 @@
     const WorkerBackingThreadStartupData& startup_data) {
   DCHECK(backing_thread_->IsCurrentThread());
 
+  // Denormals must be disabled before the V8 isolate is initialized so that the
+  // isolate's internal state and generated code respect the flag.
+  if (is_denormal_disabled_thread_) {
+    DenormalModifier::DisableDenormals();
+  }
+
   DCHECK(!isolate_);
   ThreadScheduler* scheduler = BackingThread().Scheduler();
   isolate_ = V8PerIsolateData::Initialize(
diff --git a/third_party/blink/renderer/core/workers/worker_backing_thread.h b/third_party/blink/renderer/core/workers/worker_backing_thread.h
index 445aad71..4c5f3cc7 100644
--- a/third_party/blink/renderer/core/workers/worker_backing_thread.h
+++ b/third_party/blink/renderer/core/workers/worker_backing_thread.h
@@ -56,6 +56,7 @@
  private:
   std::unique_ptr<blink::NonMainThread> backing_thread_;
   v8::Isolate* isolate_ = nullptr;
+  const bool is_denormal_disabled_thread_;
 };
 
 }  // namespace blink
diff --git a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
index b258751..e3c9d55 100644
--- a/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
+++ b/third_party/blink/renderer/modules/webaudio/audio_worklet_global_scope.cc
@@ -23,7 +23,6 @@
 #include "third_party/blink/renderer/modules/webaudio/audio_worklet_processor.h"
 #include "third_party/blink/renderer/modules/webaudio/audio_worklet_processor_definition.h"
 #include "third_party/blink/renderer/modules/webaudio/cross_thread_audio_worklet_processor_info.h"
-#include "third_party/blink/renderer/platform/audio/denormal_disabler.h"
 #include "third_party/blink/renderer/platform/bindings/callback_method_retriever.h"
 #include "third_party/blink/renderer/platform/heap/garbage_collected.h"
 #include "third_party/blink/renderer/platform/wtf/text/strcat.h"
@@ -36,9 +35,6 @@
     : WorkletGlobalScope(std::move(creation_params),
                          thread->GetWorkerReportingProxy(),
                          thread) {
-  // Disable denormals for performance.
-  DenormalModifier::DisableDenormals();
-
   // Audio is prone to jank introduced by e.g. the garbage collector. Workers
   // are generally put in a background mode (as they are non-visible). Audio is
   // an exception here, requiring low-latency behavior similar to any visible
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.