CVE-2026-6358
Overview
Background
- Dangling pointer / use-after-free (UAF)
- a pointer that still refers to memory whose object has been freed or relocated, so dereferencing it reads or writes invalid memory.
- `std::vector` reallocation
- when a
std::vectorgrows past its capacity,emplace_back/push_backmoves its storage to a new buffer, invalidating every pointer, reference, and iterator into the old elements. - `OpenXrSpatialFrameworkManager`
- the Chrome OpenXR device-layer component that builds an
XrSpatialContextCreateInfoEXTfrom the capability configurations requested for a WebXR spatial session.
Root Cause Analysis
The context-creation routine in openxr_spatial_framework_manager.cc built two parallel vectors in a single loop: it emplace_backed an OpenXrSpatialCapabilityConfigurationBase into capability_configs and then immediately pushed capability_configs.back().GetAsBaseHeader() — a raw XrSpatialCapabilityConfigurationBaseHeaderEXT* pointing into that vector’s storage — into capability_config_ptrs. Because capability_configs had no reserved capacity, a later emplace_back could reallocate the vector’s backing buffer, leaving every previously collected pointer in capability_config_ptrs dangling at a freed address. Those stale pointers were then handed to the OpenXR runtime through XrSpatialContextCreateInfoEXT, i.e. the code read/passed freed memory.
The fix reserves the final capacity up front with capability_configs.reserve(capability_configuration.size()) and splits the work into two loops: the first fully populates capability_configs (no further reallocation possible after the reserve), and the second collects the element pointers only once all elements are stable.
std::vector elements while that same vector was still being grown, so reallocation invalidated them; the fix reserves exact capacity and defers pointer collection to a second loop after the vector is final.Attack Path
- Start a spatial WebXR session
Web content requests a WebXR session whose configuration maps to multiple OpenXR spatial capabilities, so
capability_configurationholds more than one entry. - Grow the configs vector
OpenXrSpatialFrameworkManagerloops over the entries,emplace_backing each into the zero-reservedcapability_configsvector and recording a pointer to.back()incapability_config_ptrs. - Trigger reallocation
a subsequent
emplace_backexceeds capacity and relocatescapability_configs, leaving the earlier pointers incapability_config_ptrsdangling (inferred: whether this fires depends on how many capabilities are requested). - Use freed memory
the dangling pointers are passed into
XrSpatialContextCreateInfoEXTand consumed when creating the spatial context, reading freed heap memory (inferred downstream consequences).
Impact Assessment
(inferred).Changed Functions
| Function | Change | Notes |
|---|---|---|
fordevice/vr/openxr/openxr_spatial_framework_manager.cc |
modified |
Files Changed
AUTHORSdevice/vr/openxr/openxr_spatial_framework_manager.cc
Audit Directions
- Pointer-into-container captured mid-growthsearch for code that stores
.back(),&vec[i], orvec.data()-derived pointers/iterators inside a loop that continues topush_back/emplace_back/insertinto the same container. - Parallel vectors built in lockstepflag patterns where one vector holds objects and a second holds pointers/references into the first; the pointer vector must be filled only after the object vector is fully sized (reserve or two-phase build).
- FFI structs holding borrowed pointersaudit OpenXR/graphics
*CreateInfostructures that take arrays of raw pointers into local containers, ensuring the backing storage outlives the call and is never relocated between collection and use.
Patch
From b173791bf4026a6bb43124f7c5f46cfa4539c014 Mon Sep 17 00:00:00 2001 From: p0-tato <[email protected]> Date: Mon, 13 Apr 2026 14:50:07 -0700 Subject: [PATCH] Fix dangling pointers in OpenXrSpatialFrameworkManager Pointers to vector elements were collected during emplace_back, which invalidates them on reallocation. Split into two loops and reserve the correct capacity. Bug: 497724498 Change-Id: I204534bc1bd1522fe03db86f03c2c3e0d285631c Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7735242 Commit-Queue: Brian Sheedy <[email protected]> Reviewed-by: Brian Sheedy <[email protected]> Reviewed-by: Brandon Jones <[email protected]> Cr-Commit-Position: refs/heads/main@{#1613990} --- diff --git a/AUTHORS b/AUTHORS index 44daf5c..675d2c1 100644 --- a/AUTHORS +++ b/AUTHORS @@ -738,6 +738,7 @@ Jihun Brent Kim <[email protected]> Jihwan Marc Kim <[email protected]> Jihye Hyun <[email protected]> +Jihyeon Jeong <[email protected]> Jihyeon Lee <[email protected]> Jim Wu <[email protected]> Jin Yang <[email protected]> diff --git a/device/vr/openxr/openxr_spatial_framework_manager.cc b/device/vr/openxr/openxr_spatial_framework_manager.cc index 2fd3609..b6f82c5 100644 --- a/device/vr/openxr/openxr_spatial_framework_manager.cc +++ b/device/vr/openxr/openxr_spatial_framework_manager.cc @@ -74,12 +74,15 @@ // to help abstract some of the details of creating the child structs, even // though at present we only have a configuration base. std::vector<OpenXrSpatialCapabilityConfigurationBase> capability_configs; - std::vector<XrSpatialCapabilityConfigurationBaseHeaderEXT*> - capability_config_ptrs; + capability_configs.reserve(capability_configuration.size()); for (auto& [capability, components] : capability_configuration) { capability_configs.emplace_back(capability, components); - capability_config_ptrs.push_back( - capability_configs.back().GetAsBaseHeader()); + } + + std::vector<XrSpatialCapabilityConfigurationBaseHeaderEXT*> + capability_config_ptrs; + for (auto& config : capability_configs) { + capability_config_ptrs.push_back(config.GetAsBaseHeader()); } XrSpatialContextCreateInfoEXT create_info = {