High watch CVSS 4.3 chrome Integer Overflow 🔧 Commit mapped

Overview

High
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInteger overflow in Compositing
DescriptionInteger overflow in Compositing
ComponentCompositing
Bug ClassInteger Overflow
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.
Tracker556260782
Fix commit863daaa953d8 (chromium/src) +12/-8
CWECWE-190 (Integer overflow)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-09-02 (per vendor advisory)
Fix landed2026-09-08 (public git)
Disclosed2026-09-15
Patch gap7 days — fix public in source before the advisory

Background

Transfer cache
a GPU-service-side cache that holds serialized resources (such as decoded images) uploaded by a renderer/client, keyed by a numeric entry ID.
`ClientImageTransferCacheEntry`
the client-side object representing an image entry to be uploaded; each instance takes an ID at construction that the GPU service later uses to look the entry up.
`base::AtomicSequenceNumber` / `base::AtomicSequenceNumberT<T>`
a thread-safe monotonically increasing counter; GetNext() returns the current value and increments, and the templated form lets the counter be a uint32_t instead of a signed int.
ID wrap around
when a fixed-width counter exceeds its maximum representable value it silently rolls back to zero, so subsequent allocations reuse IDs that are already live.

Root Cause Analysis

Every ClientImageTransferCacheEntry obtained its ID from the static ClientImageTransferCacheEntry::GetNextId(), which simply returned s_next_id_.GetNext() on a shared base::AtomicSequenceNumber with no upper-bound check. Because the GPU service transfer cache reuses one ID namespace for all clients in the same process, a client that forces enough entry allocations can drive the counter past its maximum, at which point GetNext() wraps and begins handing out IDs that are still in use — violating the invariant that each live cache entry has a unique key.

The fix introduces GetNextCacheEntryId(), which draws from an explicit base::AtomicSequenceNumberT<uint32_t> and executes CHECK_NE(id, std::numeric_limits<uint32_t>::max()) before returning, so the process deterministically crashes at the boundary instead of silently reissuing a duplicate ID. Both ClientImageTransferCacheEntry constructors were repointed from GetNextId() to GetNextCacheEntryId(), and switching the counter to uint32_t also makes the full 32-bit range usable rather than the signed int range. The change is a TIGHTENING: it adds a guard that previously did not exist and converts a silent ID-collision condition into a controlled abort.

Key insight
The single mistake was generating cache-entry IDs from an unbounded, wrap-capable counter shared across all clients, so exhausting the counter produced colliding IDs; the fix adds a CHECK that aborts before wrap around can occur, keeping every issued ID unique.

Attack Path

  1. Drive entry allocation A compromised or malicious renderer repeatedly creates image transfer cache entries, each of which calls GetNextId() and consumes one ID from the shared sequence.
  2. Exhaust the ID space By allocating enough entries the client advances the counter through its entire range so that GetNext() wraps back toward previously issued values (the CL notes this is not reachable through normal Chrome usage).
  3. Force ID collision The wrapped counter hands a new entry an ID that is already bound to a live entry in the process-wide transfer cache namespace, breaking the one-ID-per-entry invariant.
  4. Confuse cache lookups (inferred) With two live entries sharing an ID, GPU-service lookups may resolve to the wrong entry, potentially causing aliasing or type/size confusion between cache entries. (inferred)
  5. Escalate to memory corruption (inferred) Such a mismatch in the GPU process could be leveraged toward out-of-bounds access or corruption, though the diff itself only demonstrates the duplicate-ID condition. (inferred)

Impact Assessment

An attacker who can allocate transfer cache entries (e.g., from a renderer) could exhaust the shared ID counter and obtain colliding IDs in the GPU process, undermining the uniqueness the cache relies on; any resulting memory-safety consequence occurs in the GPU service context (inferred). The precondition is the ability to force an extremely large number of entry allocations, which the commit states does not happen under normal use. The vendor advisory rates this high severity and does not report in-the-wild exploitation; NVD scores it CVSS 4.3.

Changed Functions

FunctionChangeNotes
image_
cc/paint/image_transfer_cache_entry.cc
modified
gainmap_info_
cc/paint/image_transfer_cache_entry.cc
modified

Files Changed

  • gpu/command_buffer/service/shared_context_state.cc
  • cc/paint/image_transfer_cache_entry.h
  • cc/paint/image_transfer_cache_entry.cc

Audit Directions

  • Unbounded ID/handle counters
    Look for base::AtomicSequenceNumber/GetNext() (or similar monotonic counters) used to mint IDs or handles without a wrap-around or exhaustion check, especially where IDs key a shared lookup structure.
  • Cross-client shared namespaces
    Audit resource pools in the GPU service and other privileged processes where one ID/key space is shared across multiple clients, since a single client can then affect keys other clients rely on.
  • Signed-to-full-range mismatches
    Review counters typed as signed int but consumed as uint32_t (or vice versa), where the assumed range and the wrap point diverge and collisions or overflow can go unnoticed.
From 863daaa953d823531569d571cd77da02be50e265 Mon Sep 17 00:00:00 2001
From: kylechar <kylechar@chromium.org>
Date: Tue, 08 Sep 2026 11:01:10 -0700
Subject: [PATCH] Check transfer cache entries for wrap around

The GPU service transfer cache uses the same namespace of ids for every
client in the same process, The ID generation had no protection against
wrap around so we could see duplicate IDs if enough are allocated
maliciously. To prevent ID collisions add a CHECK that ensures wrap
around doesn't happen. We don't expect wrap around to happen through
normal usage of Chrome.

This CL moves the ID generation implementation to source file since it's
only used there. It also generates uint32_t instead of int values so the
full uint32_t ID range is used.

Bug: 556260782
Change-Id: I876c5fdd55185a3a2d290abfdb59cb257f8e1196
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8349499
Commit-Queue: Kyle Charbonneau <kylechar@chromium.org>
Reviewed-by: Vladimir Levin <vmpstr@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1693922}
---
diff --git a/cc/paint/image_transfer_cache_entry.cc b/cc/paint/image_transfer_cache_entry.cc
index 729563a..648e624 100644
--- a/cc/paint/image_transfer_cache_entry.cc
+++ b/cc/paint/image_transfer_cache_entry.cc
@@ -6,9 +6,11 @@
 
 #include <algorithm>
 #include <array>
+#include <limits>
 #include <type_traits>
 #include <utility>
 
+#include "base/atomic_sequence_num.h"
 #include "base/compiler_specific.h"
 #include "base/containers/span.h"
 #include "base/functional/callback_helpers.h"
@@ -434,6 +436,13 @@
   }
 }
 
+uint32_t GetNextCacheEntryId() {
+  static base::AtomicSequenceNumberT<uint32_t> id_sequence;
+  uint32_t id = id_sequence.GetNext();
+  CHECK_NE(id, std::numeric_limits<uint32_t>::max());
+  return id;
+}
+
 }  // namespace
 
 size_t NumberOfPlanesForYUVDecodeFormat(YUVDecodeFormat format) {
@@ -496,7 +505,7 @@
     sk_sp<SkColorSpace> target_color_space)
     : needs_mips_(needs_mips),
       target_color_space_(target_color_space),
-      id_(GetNextId()),
+      id_(GetNextCacheEntryId()),
       image_(image) {
   ComputeSize();
 }
@@ -507,7 +516,7 @@
     const SkGainmapInfo& gainmap_info,
     bool needs_mips)
     : needs_mips_(needs_mips),
-      id_(GetNextId()),
+      id_(GetNextCacheEntryId()),
       image_(image),
       gainmap_image_(gainmap_image),
       gainmap_info_(gainmap_info) {
@@ -516,9 +525,6 @@
 
 ClientImageTransferCacheEntry::~ClientImageTransferCacheEntry() = default;
 
-// static
-base::AtomicSequenceNumber ClientImageTransferCacheEntry::s_next_id_;
-
 uint32_t ClientImageTransferCacheEntry::SerializedSize() const {
   return size_;
 }
diff --git a/cc/paint/image_transfer_cache_entry.h b/cc/paint/image_transfer_cache_entry.h
index 3e00775..14a8efd 100644
--- a/cc/paint/image_transfer_cache_entry.h
+++ b/cc/paint/image_transfer_cache_entry.h
@@ -12,7 +12,6 @@
 #include <optional>
 #include <vector>
 
-#include "base/atomic_sequence_num.h"
 #include "base/containers/span.h"
 #include "base/memory/raw_ptr.h"
 #include "cc/paint/tone_map_util.h"
@@ -96,7 +95,6 @@
   uint32_t SerializedSize() const final;
   bool Serialize(base::span<uint8_t> data) const final;
 
-  static uint32_t GetNextId() { return s_next_id_.GetNext(); }
   bool IsYuv() const {
     return image_.yuv_plane_config != SkYUVAInfo::PlaneConfig::kUnknown;
   }
@@ -109,7 +107,6 @@
   sk_sp<SkColorSpace> target_color_space_;
   const uint32_t id_;
   uint32_t size_ = 0;
-  static base::AtomicSequenceNumber s_next_id_;
 
   Image image_;
 
diff --git a/gpu/command_buffer/service/shared_context_state.cc b/gpu/command_buffer/service/shared_context_state.cc
index d644576..c723443 100644
--- a/gpu/command_buffer/service/shared_context_state.cc
+++ b/gpu/command_buffer/service/shared_context_state.cc
@@ -4,6 +4,7 @@
 
 #include "gpu/command_buffer/service/shared_context_state.h"
 
+#include "base/atomic_sequence_num.h"
 #include "base/compiler_specific.h"
 #include "base/debug/dump_without_crashing.h"
 #include "base/immediate_crash.h"
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.