Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in WebGL
DescriptionUse after free in WebGL
ComponentWebGL
Bug ClassUAF
Tracker499365904
Fix commitdd6b2a8de1b4 (chromium/src) +3/-1
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Files Changed

  • third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
From dd6b2a8de1b4a30b28865c3e27255e2db98492e3 Mon Sep 17 00:00:00 2001
From: Andrey Kosyakov <caseq@chromium.org>
Date: Tue, 14 Apr 2026 12:50:14 -0700
Subject: [PATCH] Perform detach check when processing PassAsSpan arguments of NADC calls too

Bug: 499365904
Change-Id: I957efef97bee1417b7e13d2c360180a68028bfbc
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7746207
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Commit-Queue: Andrey Kosyakov <caseq@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1614674}
---

diff --git a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
index de414fa..046be14 100644
--- a/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
+++ b/third_party/blink/renderer/bindings/scripts/bind_gen/interface.py
@@ -2488,7 +2488,9 @@
     body.register_code_symbol(
         S(
             "kPerformDetachCheckFlag",
-            "constexpr auto kPerformDetachCheckFlag = PassAsSpanMarkerBase::Flags::kNone;"
+            # TODO(caseq): figure out if it makes sense to skip it when we can.
+            # See https://crbug.com/499365904 for details.
+            "constexpr auto kPerformDetachCheckFlag = PassAsSpanMarkerBase::Flags::kPerformDetachCheck;"
         ))
 
     bind_callback_local_vars(body, cg_context)
Loading diff…

Original Bug Report

reported by rj...@google.com

Use-After-Free Read in WebGL multi-draw via ArrayBuffer detachment

Flapjack, 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 without the security team.

Overview: A potential Use-After-Free (UAF) read exists in WebGL multi-draw extensions due to bypassed ArrayBuffer detachment checks in generated NoAllocDirectCall (NADC) stubs. An attacker can pass a malicious sequence to execute JavaScript during argument conversion, detaching an earlier argument’s buffer and passing a dangling pointer to the GPU.

Affected files:

  • third_party/blink/renderer/modules/webgl/webgl_multi_draw.idl
  • third_party/blink/renderer/modules/webgl/webgl_multi_draw_instanced_base_vertex_base_instance.idl
  • third_party/blink/renderer/modules/webgl/webgl_multi_draw.cc
  • third_party/blink/renderer/modules/webgl/webgl_multi_draw_instanced_base_vertex_base_instance.cc

Estimated timestamp from git blame: 2026-03-18

Summary

A potential Use-After-Free (UAF) read vulnerability exists in WebGL multi-draw extensions (e.g., WEBGL_multi_draw). The issue stems from an incorrect assumption in Blink’s bindings generator for functions annotated with [NoAllocDirectCall] (NADC). The generator disables ArrayBuffer detachment checks for [PassAsSpan] arguments, incorrectly assuming JavaScript cannot execute during the argument conversion phase. However, because the IDL types allow sequences, converting a later argument can invoke a JavaScript iterator, detaching the buffer of an earlier argument and leading to a UAF read when the WebGL implementation executes.

Technical Details

  1. Methods like multiDrawArraysWEBGL use the [NoAllocDirectCall] attribute for optimization and accept Int32List arguments (firstsList, countsList), which are defined as [PassAsSpan] (Int32Array or sequence<GLint>).
  2. In third_party/blink/renderer/bindings/scripts/bind_gen/interface.py, the NADC stub generator hardcodes constexpr auto kPerformDetachCheckFlag = PassAsSpanMarkerBase::Flags::kNone;. This completely compiles out the WasDetached() check inside ByteSpanWithInlineStorage::as_span().
  3. The NADC signature takes v8::Local<v8::Value> for these arguments. When NativeValueTraits<PassAsSpan<...>>::ArgumentValue converts the first argument (firstsList), it extracts the raw pointer to the ArrayBuffer backing store.
  4. When converting the second argument (countsList), if the attacker provides a sequence (an iterable object), the fallback path NativeValueTraits<IDLSequence<...>>::ArgumentValue is hit. This iterates the sequence using V8 APIs, executing synchronous JavaScript (e.g., a custom iterator).
  5. If this JavaScript detaches the ArrayBuffer of firstsList (e.g., via postMessage), the backing store is immediately freed.
  6. Because the detachment check is disabled (kPerformDetachCheckFlag == kNone), the dangling raw pointer is cast to a base::span and passed into the Blink implementation.
  7. Inside gpu/command_buffer/client/transfer_buffer_cmd_copy_helpers.h, a memcpy reads from this dangling pointer into a shared memory transfer buffer, resulting in a UAF Read.

MiraclePtr (BackupRefPtr) is explicitly disabled for the ArrayBuffer partition, meaning it offers no protection against this UAF.

Potential Steps to Trigger

Note: These are suggested steps; a working Proof of Concept has not yet been executed by our tooling.

  1. An attacker sets up a WebGL context and enables the WEBGL_multi_draw extension.
  2. The attacker creates an Int32Array (backed by an ArrayBuffer) for the firstsList argument.
  3. The attacker creates a custom JavaScript iterable object (e.g., an array with a modified Symbol.iterator) for the countsList argument.
  4. Inside the custom iterator, the attacker places code to call postMessage() or transfer() on the ArrayBuffer created in Step 2, detaching it.
  5. The attacker calls ext.multiDrawArraysWEBGL(mode, firstsList, 0, countsList, 0, drawcount).
  6. The Fast API NADC stub begins processing, extracts the pointer for firstsList, then evaluates countsList. The malicious iterator executes, freeing firstsList’s memory.
  7. The GPU command buffer copies the memory from the dangling pointer, leaking heap memory from the ArrayBuffer partition.

Affected Methods

  • multiDrawArraysWEBGL
  • multiDrawElementsWEBGL
  • multiDrawArraysInstancedWEBGL
  • multiDrawElementsInstancedWEBGL
  • multiDrawArraysInstancedBaseInstanceWEBGL
  • multiDrawElementsInstancedBaseVertexBaseInstanceWEBGL

Suggested Fix

Update third_party/blink/renderer/bindings/scripts/bind_gen/interface.py. Do not force kPerformDetachCheckFlag to kNone for NADC stubs when processing PassAsSpan arguments. The detachment check is extremely cheap (a boolean flag check) and must be maintained as long as sequence fallback conversion (which can execute JS) is possible in Fast API calls.

Alternatively, enforce that [PassAsSpan] arguments on [NoAllocDirectCall] methods strictly accept TypedArray objects and reject sequences prior to evaluation.

Evaluated with Chrome root at commit: 09ec9e7cc4d24823d20b6d37cf3d282734f6bf0f


Results so far have been promising, but there can be wrong deductions. If this proves to be a false positive, please close as WAI; 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