High chrome Type Confusion 📄 Reporter bug report 🔧 Commit mapped

Overview

High
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactType Confusion in Bindings
DescriptionType Confusion in Bindings
ComponentBindings
Bug ClassType Confusion
Tracker513773313
Fix commitd4db042c2c7c (chromium/src) +43/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-08

Files Changed

  • third_party/blink/renderer/bindings/core/v8/native_value_traits_buffer_sources.cc
  • third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/allow-shared.https.html
From d4db042c2c7c5ad02a39576cf139425e4dc7208a Mon Sep 17 00:00:00 2001
From: Andrey Kosyakov <[email protected]>
Date: Fri, 22 May 2026 10:31:55 -0700
Subject: [PATCH] bindings: add checks for whether underlying array buffer is shared when converting array views

Fixed: 513773313
Change-Id: Ie845dfd6edef361715654644be65c2e4720795f6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7869251
Commit-Queue: Andrey Kosyakov <[email protected]>
Reviewed-by: Nate Chapin <[email protected]>
Cr-Commit-Position: refs/heads/main@{#1635059}
---

diff --git a/third_party/blink/renderer/bindings/core/v8/native_value_traits_buffer_sources.cc b/third_party/blink/renderer/bindings/core/v8/native_value_traits_buffer_sources.cc
index b8bc386..31ebea6 100644
--- a/third_party/blink/renderer/bindings/core/v8/native_value_traits_buffer_sources.cc
+++ b/third_party/blink/renderer/bindings/core/v8/native_value_traits_buffer_sources.cc
@@ -268,7 +268,10 @@
 template <typename T>
 struct RecipeTrait<NotShared<T>> : public RecipeTrait<T> {
   static NotShared<T> NullValue() { return NotShared<T>(); }
-  static NotShared<T> ToReturnType(T* buffer) { return NotShared<T>(buffer); }
+  static NotShared<T> ToReturnType(T* buffer) {
+    CHECK(!buffer->IsShared());
+    return NotShared<T>(buffer);
+  }
 };
 
 template <typename T>
@@ -350,6 +353,11 @@
   v8::Local<typename Trait::V8ViewType> v8_view =
       value.As<typename Trait::V8ViewType>();
   if (auto* blink_view = ToScriptWrappable<DOMViewType>(isolate, v8_view)) {
+    if constexpr (!allow_shared) {
+      if (blink_view->IsShared()) [[unlikely]] {
+        return nullptr;
+      }
+    }
     return blink_view;
   }
 
@@ -385,6 +393,11 @@
   v8::Local<v8::ArrayBufferView> v8_view = value.As<v8::ArrayBufferView>();
   if (auto* blink_view =
           ToScriptWrappable<DOMArrayBufferView>(isolate, v8_view)) {
+    if constexpr (!allow_shared) {
+      if (blink_view->IsShared()) [[unlikely]] {
+        return nullptr;
+      }
+    }
     return blink_view;
   }
 
diff --git a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/allow-shared.https.html b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/allow-shared.https.html
new file mode 100644
index 0000000..b4cd1e3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/allow-shared.https.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebIDL [AllowShared] semantics</title>
+<link rel="help" href="https://webidl.spec.whatwg.org/#AllowShared">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/sab.js"></script>
+<script>
+  "use strict";
+
+  test(t => {
+    // Create a standard SharedArrayBuffer (safe even without COOP/COEP headers)
+    const sab = createBuffer('SharedArrayBuffer', 128);
+    const arr = new Uint8Array(sab);
+    arr.fill(42);
+    assert_equals(sab.constructor.name, 'SharedArrayBuffer');
+    new ImageDecoder({
+      data: arr,
+      // We don't really need encoding to succeed, just for argument conversion before the native call.
+      type: "invalid"
+    });
+
+    assert_throws_js(TypeError, () => {
+      new MIDIMessageEvent('message', { data: arr });
+    }, "An attempt to pass a view backed by a shared array buffer should throw");
+
+  }, "APIs without [AllowShared] do not accept SharedArrayBuffer-backed views with pre-existing wrapper");
+
+</script>
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/allow-shared.https.html b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/allow-shared.https.html
new file mode 100644
index 0000000..b4cd1e3
--- /dev/null
+++ b/third_party/blink/web_tests/external/wpt/webidl/ecmascript-binding/allow-shared.https.html
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<meta charset="utf-8">
+<title>WebIDL [AllowShared] semantics</title>
+<link rel="help" href="https://webidl.spec.whatwg.org/#AllowShared">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/common/sab.js"></script>
+<script>
+  "use strict";
+
+  test(t => {
+    // Create a standard SharedArrayBuffer (safe even without COOP/COEP headers)
+    const sab = createBuffer('SharedArrayBuffer', 128);
+    const arr = new Uint8Array(sab);
+    arr.fill(42);
+    assert_equals(sab.constructor.name, 'SharedArrayBuffer');
+    new ImageDecoder({
+      data: arr,
+      // We don't really need encoding to succeed, just for argument conversion before the native call.
+      type: "invalid"
+    });
+
+    assert_throws_js(TypeError, () => {
+      new MIDIMessageEvent('message', { data: arr });
+    }, "An attempt to pass a view backed by a shared array buffer should throw");
+
+  }, "APIs without [AllowShared] do not accept SharedArrayBuffer-backed views with pre-existing wrapper");
+
+</script>
Loading diff…

Original Bug Report

reported by [email protected]

Sibling-Class Type Confusion in Blink ArrayBufferView Binding Conversion

Project Fortify, an experimental security project, has identified the following potential security issue. If you’re a feature owner CC-ed on this bug, please do your best to review these reports. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A potential vulnerability exists in Blink’s binding layer where cached SharedArrayBuffer-backed views can bypass sharedness restrictions when converted to NotShared types. This leads to a sibling-class type confusion between DOMSharedArrayBuffer and DOMArrayBuffer. Due to object size differences, this can result in out-of-bounds heap memory access in the renderer process.

Affected files:

  • third_party/blink/renderer/bindings/core/v8/native_value_traits_buffer_sources.cc
  • third_party/blink/renderer/core/typed_arrays/dom_array_buffer_view.h
  • third_party/blink/renderer/core/typed_arrays/array_buffer_view_helpers.h
  • third_party/blink/renderer/core/typed_arrays/dom_array_buffer.cc
  • third_party/blink/renderer/core/streams/readable_byte_stream_controller.cc

Estimated timestamp from git blame: Unknown (Google3 checkout)

Summary

An issue has been identified in Blink’s WebIDL binding implementation for ArrayBufferView types. The conversion logic in third_party/blink/renderer/bindings/core/v8/native_value_traits_buffer_sources.cc potentially returns a cached wrapper for a JavaScript ArrayBufferView without re-verifying whether the underlying buffer is shared. This can allow a view backed by a SharedArrayBuffer (SAB) to bypass the non-shared requirement of NotShared<T> parameters, leading to a type confusion between DOMSharedArrayBuffer and DOMArrayBuffer sibling classes.

Root Cause Analysis

The vulnerability resides in the ToDOMViewType template function (and related ToDOMArrayBufferView) within native_value_traits_buffer_sources.cc. When converting a V8 value, the function first checks if a Blink wrapper is already associated with the V8 object using ToScriptWrappable:

// third_party/blink/renderer/bindings/core/v8/native_value_traits_buffer_sources.cc
template <typename DOMViewType, bool allow_shared>
DOMViewType* ToDOMViewType(v8::Isolate* isolate, v8::Local<v8::Value> value) {
  // ...
  if (auto* blink_view = ToScriptWrappable<DOMViewType>(isolate, v8_view)) {
    return blink_view; // Potential bypass: returns cached wrapper without checking allow_shared
  }
  // ...
}

If the same V8 ArrayBufferView was previously passed to an API that allows shared buffers (e.g., WebGL), a DOMArrayBufferView wrapper is created and cached. If that same view is subsequently passed to an API expecting a NotShared<T> type (where allow_shared is false), the logic incorrectly returns the cached shared wrapper.

While the NotShared<T> wrapper in C++ includes a DCHECK to verify that the buffer is not shared, this check is disabled in production (Release) builds. Consequently, the C++ implementation receives a pointer to a shared view where a non-shared view was expected.

Impact

The bypass leads to a sibling-class type confusion where a DOMSharedArrayBuffer is cast to a DOMArrayBuffer. These classes have different sizes on 64-bit systems:

  • DOMSharedArrayBuffer: ~40 bytes
  • DOMArrayBuffer: ~56 bytes

When Blink code treats the confused object as a DOMArrayBuffer, it may access members like detach_key_ (offset 40) or has_non_main_world_wrappers_ (offset 48), resulting in out-of-bounds (OOB) reads and writes on the Oilpan heap. For example, ReadableByteStreamController::enqueue triggers a transfer operation that ultimately leads to ForArrayBuffersInAllWorlds, which performs an OOB read of the has_non_main_world_wrappers_ flag. Such OOB accesses can be leveraged for information leaks or further memory corruption.

Potential Reproduction Steps (Suggested)

Note: These steps are based on static analysis and represent a potential attack vector; they have not been verified on a running system.

  1. Establish a Cross-Origin Isolated environment to enable SharedArrayBuffer support.
  2. Create a SharedArrayBuffer and a corresponding Uint8Array view v.
  3. Call a Blink API that opts into [AllowShared], such as WebGLRenderingContext.bufferData, passing the view v. This ensures a C++ wrapper is cached.
  4. Call an API that expects a non-shared view, such as ReadableByteStreamController.enqueue(v).
  5. In a Release build, the conversion bypasses the non-sharedness check, leading to type confusion and potential memory corruption when the controller attempts to process or transfer the buffer.

Suggested Fix

Modify ToDOMViewType and ToDOMArrayBufferView in native_value_traits_buffer_sources.cc to validate the sharedness of a cached wrapper against the allow_shared requirement before returning it:

  if (auto* blink_view = ToScriptWrappable<DOMViewType>(isolate, v8_view)) {
    if (!allow_shared && blink_view->IsShared()) {
      return nullptr;
    }
    return blink_view;
  }

Evaluated with Chrome root at commit: 1a8d40fc44df2088d5945c0bf53584038aa1614a


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

Data from false positives will be used to improve accuracy over time. And please feel free to reach out to me directly if you have concerns or feedback on the project.

View on issue tracker