Medium firefox Info Disclosure 🔧 Commit mapped

Overview

Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Impactmoderate
DescriptionSide-channel in the Web Audio component
ComponentDOM
Bug ClassInfo Disclosure
Tracker2050380
Fix commit28f8fb9edd74 (firefox) +4/-1
CISA KEVNot listed
CreditedRintaro Kawasugi
Disclosed2026-08-18

Background

`AudioWorkletNode`
a Web Audio node whose audio processing runs as author-supplied JavaScript on a dedicated real-time rendering thread.
`SharedArrayBuffer`
a shared-memory object that multiple agents can read and write concurrently, which is what allowSharedMemoryObjects permits through structured clone.
`JS::CloneDataPolicy`
a policy object passed to structured clone that declares which classes of transferable or shareable objects (including shared memory) may cross an agent boundary.
`IsSharedMemoryAllowed`
a per-global predicate that reports whether the current global is permitted to use shared memory (gated by cross-origin isolation).

Root Cause Analysis

In AudioWorkletNode::Constructor, the structured-clone policy used to pass the node’s options and processor data to the rendering thread unconditionally called cloneDataPolicy.allowSharedMemoryObjects(). This meant a SharedArrayBuffer could be cloned into the AudioWorklet regardless of whether the constructing global was actually allowed to use shared memory. The violated invariant is that shared memory must only be shareable when nsIGlobalObject::IsSharedMemoryAllowed() holds for the current global — the same cross-origin-isolation gate that governs SharedArrayBuffer availability elsewhere.

The fix retrieves the current global via xpc::CurrentNativeGlobal(cx) and only enables shared-memory objects in the clone policy when IsSharedMemoryAllowed() returns true. This closes the path that let non-isolated contexts obtain shared memory, and thus a high-resolution timing side channel, through the Web Audio worklet.

Key insight
The single mistake was unconditionally allowing shared-memory objects in the AudioWorklet clone policy, bypassing the cross-origin-isolation gate; the fix conditions allowSharedMemoryObjects on the current global’s IsSharedMemoryAllowed() state.

Attack Path

  1. Load a non-isolated page An attacker serves a page that is not cross-origin isolated, so SharedArrayBuffer and shared memory should be unavailable to it.
  2. Construct an `AudioWorkletNode` The page creates an AudioWorkletNode, whose constructor builds a clone policy that unconditionally permits shared-memory objects.
  3. Smuggle shared memory into the worklet Shared-memory objects are cloned across the agent boundary into the rendering thread despite the missing isolation gate.
  4. Build a high-resolution timer The attacker uses the smuggled shared memory to construct a fine-grained timing primitive normally denied to non-isolated contexts.
  5. Run a timing side channel The timer is used to mount Spectre-style or other timing-based side-channel measurements.

Impact Assessment

An attacker on a non-cross-origin-isolated web page gains access to shared memory and therefore a high-resolution timing primitive that the isolation policy is meant to withhold. This runs in the content process in the context of the victim page’s Web Audio worklet, and the only precondition is the ability to run script and instantiate an AudioWorkletNode. The gain is an information-disclosure side channel rather than direct memory corruption, consistent with the moderate severity rating.

Files Changed

  • dom/media/webaudio/AudioWorkletNode.cpp

Audit Directions

  • Unconditional `allowSharedMemoryObjects`
    Search other structured-clone sites for allowSharedMemoryObjects() or allowIntraClusterClonableSharedObjects() calls that are not guarded by IsSharedMemoryAllowed().
  • Cross-agent boundaries
    Review worker, worklet, and message-port constructors that clone data to a different thread or agent to ensure they re-check the destination or current global’s shared-memory permission.
  • Isolation-gated capabilities
    Audit features that should be gated by cross-origin isolation (SharedArrayBuffer, high-resolution timers) for paths that expose them without consulting the per-global permission predicate.
diff --git a/dom/media/webaudio/AudioWorkletNode.cpp b/dom/media/webaudio/AudioWorkletNode.cpp
index ba2a5be4e01..2ce03b433c8 100644
--- a/dom/media/webaudio/AudioWorkletNode.cpp
+++ b/dom/media/webaudio/AudioWorkletNode.cpp
@@ -765,7 +765,10 @@ already_AddRefed<AudioWorkletNode> AudioWorkletNode::Constructor(
   // can share memory.
   JS::CloneDataPolicy cloneDataPolicy;
   cloneDataPolicy.allowIntraClusterClonableSharedObjects();
-  cloneDataPolicy.allowSharedMemoryObjects();
+  nsIGlobalObject* currentGlobal = xpc::CurrentNativeGlobal(cx);
+  if (currentGlobal->IsSharedMemoryAllowed()) {
+    cloneDataPolicy.allowSharedMemoryObjects();
+  }
 
   // StructuredCloneHolder does not have a move constructor.  Instead allocate
   // memory so that the pointer can be passed to the rendering thread.
Loading diff…