High watch CVSS 4.3 chrome OOB 🔧 Commit mapped

Overview

High
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactHeap buffer overflow in GPU
DescriptionHeap buffer overflow in GPU
ComponentGPU
Bug ClassOOB
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
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.
Tracker498706958
Fix commit397b3fab53f0 (chromium/src) +3/-3
CWECWE-122 (Heap buffer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-04-01 (per vendor advisory)
Fix landed2026-04-02 (public git)
Disclosed2026-05-12
Patch gap40 days — fix public in source before the advisory

Background

3D compressed textures
GPU textures with width, height, and depth dimensions stored in a compressed format; a full level spans depth 2D layers.
`GetCompressedTexSizeInBytes`
A helper that computes the byte size required for a compressed texture region given width, height, depth, and format.
`base::HeapArray<uint8_t>::Uninit`
Allocates an uninitialized heap buffer of the requested byte count, used here to hold cleared/decompressed pixel data.
GLES2 decoder
The GPU-process command decoder (gles2_cmd_decoder.cc) that services GL commands from the renderer, including clearing and decompressing compressed texture levels.

Root Cause Analysis

Two code paths that process 3D compressed textures sized their buffers using only width and height, ignoring the depth dimension. The clear path called GetCompressedTexSizeInBytes("ClearCompressedTextureLevel3D", width, height, 1, format, &bytes_required, ...) with a hardcoded depth of 1, so bytes_required covered only a single layer even when the texture had many depth layers. The decompression path allocated decompressed_data as output_pixel_size * width * height, again omitting depth. Because the subsequent clear/decompress operations write all depth layers into these under-sized buffers, writes past the allocation occur, a heap buffer overflow.

The fix passes the real depth into GetCompressedTexSizeInBytes and multiplies the decompression allocation by depth (output_pixel_size * width * height * depth), so each buffer is large enough to cover every specified layer.

Key insight
The single mistake was computing per-level buffer sizes with depth fixed at 1 (or omitted) while the operation actually writes all depth layers; the fix threads the true depth into both the size computation and the allocation.

Attack Path

  1. Specify a multi-layer 3D compressed texture A renderer issues GL commands creating/updating a compressed 3D texture with depth > 1.
  2. Trigger clear or decompress The renderer drives a path that clears an uninitialized compressed 3D texture level or decompresses it, invoking the affected decoder code.
  3. Under-allocate the buffer (pre-patch) The size/allocation uses depth 1 (clear) or omits depth (decompress), so the heap buffer is sized for a single layer.
  4. Overflow the heap The operation writes all depth layers into the single-layer-sized buffer, writing beyond the allocation (heap buffer overflow). The attacker-controlled dimensions govern how far the overflow extends (specific corruption targets are not shown by the diff and are (inferred)).

Impact Assessment

A renderer able to issue GL commands for a 3D compressed texture with depth > 1 can cause a heap buffer overflow in the GPU process by driving the clear or decompress path with under-sized buffers; the precondition is control over the texture’s depth and the ability to trigger clearing/decompression. The vendor advisory rates this high severity and describes it as a heap buffer overflow in GPU. Exploitation beyond the out-of-bounds write demonstrated by the under-allocation (e.g. controlled corruption or code execution) is not shown by the diff and is (inferred).

Files Changed

  • gpu/command_buffer/service/gles2_cmd_decoder.cc

Audit Directions

  • Dropped dimensions in size math
    Audit texture/buffer size and allocation computations for 3D/array resources where depth (or array layer count) is hardcoded to 1 or omitted while the write loop covers all layers.
  • Allocation vs. write-extent mismatch
    Compare each HeapArray/buffer allocation against the full extent of the subsequent write loop, especially where one uses a helper and the other open-codes the size.
  • Attacker-controlled dimension multipliers
    Review GPU command handlers where renderer-supplied width/height/depth feed allocation sizes, checking for both missing factors and multiplication overflow.
From 397b3fab53f0e040db3f9ac9af5733c524fd772a Mon Sep 17 00:00:00 2001
From: Brandon Jones <bajones@chromium.org>
Date: Thu, 02 Apr 2026 13:04:33 -0700
Subject: [PATCH] Use depth when updating 3D compressed textures

The depth parameter was accidentally not considered in every case when
clearing or decompressing 3D compressed textures. This change ensures
that the memory allocated for clearing or decompressing is large enough
to cover all specified depth layers.

Bug: 498706958
Change-Id: Ic7cae42cf9adee07dda8754e2184a3ee988c1d09
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7727007
Reviewed-by: Kenneth Russell <kbr@chromium.org>
Reviewed-by: Kai Ninomiya <kainino@chromium.org>
Commit-Queue: Brandon Jones <bajones@chromium.org>
Commit-Queue: Kenneth Russell <kbr@chromium.org>
Auto-Submit: Brandon Jones <bajones@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1609454}
---

diff --git a/gpu/command_buffer/service/gles2_cmd_decoder.cc b/gpu/command_buffer/service/gles2_cmd_decoder.cc
index 4fb2911..d55fb20 100644
--- a/gpu/command_buffer/service/gles2_cmd_decoder.cc
+++ b/gpu/command_buffer/service/gles2_cmd_decoder.cc
@@ -12200,7 +12200,7 @@
 
   GLsizei bytes_required = 0;
   if (!GetCompressedTexSizeInBytes("ClearCompressedTextureLevel3D", width,
-                                   height, 1, format, &bytes_required,
+                                   height, depth, format, &bytes_required,
                                    error_state_.get())) {
     return false;
   }
@@ -12518,8 +12518,8 @@
   auto* api = state.api();
   uint32_t output_pixel_size = GLES2Util::ComputeImageGroupSize(
       info.decompressed_format, info.decompressed_type);
-  auto decompressed_data =
-      base::HeapArray<uint8_t>::Uninit(output_pixel_size * width * height);
+  auto decompressed_data = base::HeapArray<uint8_t>::Uninit(
+      output_pixel_size * width * height * depth);
 
   // If a PBO is bound, map it to decompress the data.
   const void* input_data = data;
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.