CVE-2026-76045
Overview
Background
- `OVR_multiview2`
- a WebGL 2 extension, implemented in Blink in
ovr_multiview_2.cc, that exposesframebufferTextureMultiviewOVRso a range of layers of a 2D array texture can be attached to a framebuffer for multi-view rendering. - Framebuffer attachment tracking
- Blink’s WebGL layer keeps its own record of which texture is attached to which attachment point of the bound framebuffer, separate from the GPU-process state, and relies on that record to keep attached objects valid.
- `ValidateFramebufferFuncParameters`
- an existing
WebGL2RenderingContextBasehelper used byframebufferTexture2Dand similar calls to check thattargetis a valid framebuffer binding point andattachmentis a valid attachment enum before any state is changed.
Root Cause Analysis
The vulnerable path is the WebGL entry point for framebufferTextureMultiviewOVR in third_party/blink/renderer/modules/webgl/ovr_multiview_2.cc. The hunk header does not name the enclosing function, but the error strings indicate it is the extension’s framebufferTextureMultiviewOVR implementation. Before the patch, that function validated the texture layer range (ValidateTexFuncLayer) and the mip level (ValidateTexFuncLevel), but passed the caller-supplied target and attachment through without checking them. The commit message says this “could lead to resource tracking issues if an out-of-range value was given”. In other words, Blink’s attachment bookkeeping could be updated using an attachment or target value that the normal framebufferTexture2D path would reject, which leaves Blink’s view of which texture is attached out of step with reality. The advisory classes the result as a use-after-free in WebGL. The exact freed object and the dereference site are not in the diff (inferred: a texture or attachment record whose lifetime tracking went wrong because it was recorded under an invalid attachment slot).
The fix adds a call to ValidateFramebufferFuncParameters after the layer check and before the level check. On an invalid target or attachment it returns early, so the tracking code further down the function is never reached with an unvalidated value.
target and attachment that every other framebuffer-attach entry point validates, so bad enums reached Blink’s attachment/resource tracking. The fix reuses the framebufferTexture2D validation (ValidateFramebufferFuncParameters) and returns early before any state is changed.Attack Path
- Obtain the extension
A page creates a WebGL 2 context and enables
OVR_multiview2withgetExtension. This works only where the GPU/driver exposes the extension (inferred: no flag or user gesture is needed where it is supported). - Pass out-of-range enums
The page calls
framebufferTextureMultiviewOVRwith a valid texture, level and layer range but an invalidtargetorattachment. Before the patch, these passed every check the function made. - Corrupt attachment tracking Execution continued past validation into the function’s attachment handling (not shown in the hunk). Blink recorded the texture against an attachment slot or binding that the normal paths would never accept, so its tracking no longer matched the real attachment state (inferred from the commit message).
- Trigger the stale reference Later WebGL operations, such as deleting the texture or framebuffer, or tearing down the context, act on the mismatched tracking state. The result is a use-after-free in the renderer (the specific freed object and sink are inferred and not shown in the patch).
Impact Assessment
OVR_multiview2 is available on the user’s GPU/driver. Memory corruption in the renderer could plausibly lead to code execution inside the sandbox (inferred). The vendor advisory rates this high severity and does not report in-the-wild exploitation; NVD scores it CVSS 8.8.Files Changed
third_party/blink/renderer/modules/webgl/ovr_multiview_2.cc
Audit Directions
- Extension entry points that bypass core validationCheck WebGL and WebGL 2 extension methods in
third_party/blink/renderer/modules/webgl/(for example other*Multiview*, draw-buffer and framebuffer-related extensions). Each should apply the same enum and range validation as the core API it mirrors before touching Blink-side tracking. - Bookkeeping keyed by caller-supplied enumsAudit code that updates Blink-side attachment maps, binding points or object references using a
GLenumtaken from JavaScript. If it does not first confirm the enum belongs to the expected set, a mismatch between Blink and GPU state can lead to stale-pointer bugs. - Validation orderingMake sure every early-return validator runs before the first state mutation in WebGL setter functions, so that rejected calls can never leave partially updated tracking behind.
Patch
From 2b3a653dda35893442fbda35c6d8f89b0c683002 Mon Sep 17 00:00:00 2001 From: Brandon Jones <[email protected]> Date: Tue, 11 Aug 2026 18:38:56 -0700 Subject: [PATCH] Validate framebufferTextureMultiviewOVR target, attachment Previously these were being passed through unvalidated, which could lead to resource tracking issues if an out-of-range value was given. While the extension definitions for this method don't state any explicit validation to perform on these values it should be safe to assume they should be treated like values being passed to framebufferTexture2D, and thus the same validation is reused here. Fixed: 543082390 Change-Id: I63a172b6723e6e5a626d50583668b4dc0ca2a33a Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/8243727 Commit-Queue: Kai Ninomiya <[email protected]> Reviewed-by: Kai Ninomiya <[email protected]> Cr-Commit-Position: refs/heads/main@{#1677709} --- diff --git a/third_party/blink/renderer/modules/webgl/ovr_multiview_2.cc b/third_party/blink/renderer/modules/webgl/ovr_multiview_2.cc index 8af07167..2633c7b 100644 --- a/third_party/blink/renderer/modules/webgl/ovr_multiview_2.cc +++ b/third_party/blink/renderer/modules/webgl/ovr_multiview_2.cc @@ -62,6 +62,11 @@ ->ValidateTexFuncLayer("framebufferTextureMultiviewOVR", textarget, base_view_index + num_views - 1)) return; + if (!static_cast<WebGL2RenderingContextBase*>(scoped.Context()) + ->ValidateFramebufferFuncParameters( + "framebufferTextureMultiviewOVR", target, attachment)) { + return; + } if (!scoped.Context()->ValidateTexFuncLevel( "framebufferTextureMultiviewOVR", textarget, level)) return;
Original Bug Report
WebGL OVR_multiview2 Oilpan Use-After-Free Enables Arbitrary Native Write
WebGL OVR_multiview2 Oilpan Use-After-Poison
As a request of https://issues.chromium.org/issues/543082387#comment4 I have re-opened this issue with a short description along with an ASAN trace.
Please note that this vulnerability is the first part of a working full sandbox-escape exploit chain we are in the process of analyzing and reporting.
I want to affirm our commitment to only report vulnerabilities for which we have strong evidence that they are exploitable. We understand you are dealing with an influx of reports and thank you for your efforts to keep Chrome secure.
Tested Version
- Chromium 151.0.7922.0, Stable-branch ASan revision 1654411 (Linux x86_64).
- Chromium commit: 059c884787b1bc15faa5afe5250f7d3a7e0ef64a.
- Chrome BuildId: 5c8f91628a04675e.
The vulnerable source files are the same in Chrome 151.0.7922.76 and the vulnerability still exists in latest main.
PoC: chromium-ovr-oilpan-asan-use-after-poison.html
Root Cause
OVRMultiview2::framebufferTextureMultiviewOVR fails to validate its
framebuffer attachment enum before inserting it into WebGLFramebuffer’s
Oilpan HeapHashMap. Supplying the reserved hash-table key 0xffffffff
causes Oilpan to skip tracing the attachment’s strong reference. A major
collection poisons the unmarked attachment, and a later removal dereferences
that poisoned object in WebGLFramebuffer::RemoveAttachmentInternal.
Symbolized ASan Report
Symbolized offline using the exact matching build’s bundled llvm-symbolizer
and Chrome ELF. The sandboxed renderer could not launch the external
symbolizer; the original warnings are retained.
=================================================================
==1==ERROR: AddressSanitizer: use-after-poison on address 0x7b9402009020 at pc 0x5642643decc5 bp 0x7ffe83b62cb0 sp 0x7ffe83b62ca8
READ of size 8 at 0x7b9402009020 thread T0 (chrome)
==1==WARNING: invalid path to external symbolizer!
==1==WARNING: Failed to use and restart external symbolizer!
#0 0x5642643decc4 in blink::WebGLFramebuffer::RemoveAttachmentInternal(unsigned int, unsigned int)
third_party/blink/renderer/modules/webgl/webgl_framebuffer.cc:537:24
(/tmp/chromium-asan-1654411/chrome+0x4273bcc4) (BuildId: 5c8f91628a04675e)
#1 0x5642643dc1a7 in blink::WebGLFramebuffer::SetAttachmentInternal(unsigned int, unsigned int, unsigned int, blink::WebGLTexture*, int, int)
third_party/blink/renderer/modules/webgl/webgl_framebuffer.cc:505:3
(/tmp/chromium-asan-1654411/chrome+0x427391a7) (BuildId: 5c8f91628a04675e)
#2 0x5642643dbcd1 in blink::WebGLFramebuffer::SetAttachmentForBoundFramebuffer(unsigned int, unsigned int, unsigned int, blink::WebGLTexture*, int, int, int)
third_party/blink/renderer/modules/webgl/webgl_framebuffer.cc:0:0
(/tmp/chromium-asan-1654411/chrome+0x42738cd1) (BuildId: 5c8f91628a04675e)
#3 0x5642643d07a4 in blink::OVRMultiview2::framebufferTextureMultiviewOVR(unsigned int, unsigned int, blink::WebGLTexture*, int, int, int)
third_party/blink/renderer/modules/webgl/ovr_multiview_2.cc:78:24
(/tmp/chromium-asan-1654411/chrome+0x4272d7a4) (BuildId: 5c8f91628a04675e)
#4 0x5642643d1489 in blink::(anonymous namespace)::v8_ovr_multiview_2::FramebufferTextureMultiviewOVROperationCallback(v8::FunctionCallbackInfo<v8::Value> const&)
gen/third_party/blink/renderer/bindings/modules/v8/v8_ovr_multiview_2.cc:118:17
(/tmp/chromium-asan-1654411/chrome+0x4272e489) (BuildId: 5c8f91628a04675e)
#5 0x56423f354693 in Builtins_CallApiCallbackGeneric
crtstuff.c:0:0
(/tmp/chromium-asan-1654411/chrome+0x1d6b1693) (BuildId: 5c8f91628a04675e)
#6 0x56423f352940 in Builtins_InterpreterEntryTrampoline
crtstuff.c:0:0
(/tmp/chromium-asan-1654411/chrome+0x1d6af940) (BuildId: 5c8f91628a04675e)
#7 0x56423f34f6db in Builtins_JSEntryTrampoline
crtstuff.c:0:0
(/tmp/chromium-asan-1654411/chrome+0x1d6ac6db) (BuildId: 5c8f91628a04675e)
#8 0x56423f34f42a in Builtins_JSEntry
crtstuff.c:0:0
(/tmp/chromium-asan-1654411/chrome+0x1d6ac42a) (BuildId: 5c8f91628a04675e)
#9 0x56423a3358a9 in v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&)
v8/src/execution/execution.cc:475:22
(/tmp/chromium-asan-1654411/chrome+0x186928a9) (BuildId: 5c8f91628a04675e)
#10 0x56423a3341d9 in v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::DirectHandle<v8::internal::Object>, v8::internal::DirectHandle<v8::internal::Object>, v8::base::Vector<v8::internal::DirectHandle<v8::internal::Object> const>)
v8/src/execution/execution.cc:565:10
(/tmp/chromium-asan-1654411/chrome+0x186911d9) (BuildId: 5c8f91628a04675e)
#11 0x56423b076f7e in v8::internal::JSFinalizationRegistry::Cleanup(v8::internal::Isolate*, v8::internal::DirectHandle<v8::internal::JSFinalizationRegistry>)
v8/src/objects/js-weak-refs.cc:77:9
(/tmp/chromium-asan-1654411/chrome+0x193d3f7e) (BuildId: 5c8f91628a04675e)
#12 0x564239f759b5 in v8::internal::InvokeFinalizationRegistryCleanupFromTask(v8::internal::DirectHandle<v8::internal::NativeContext>, v8::internal::DirectHandle<v8::internal::JSFinalizationRegistry>)
v8/src/api/api.cc:12425:3
(/tmp/chromium-asan-1654411/chrome+0x182d29b5) (BuildId: 5c8f91628a04675e)
#13 0x56423a5ad5eb in v8::internal::FinalizationRegistryCleanupTask::RunInternal()
v8/src/heap/finalization-registry-cleanup-task.cc:90:3
(/tmp/chromium-asan-1654411/chrome+0x1890a5eb) (BuildId: 5c8f91628a04675e)
#14 0x564255338aa0 in base::internal::Invoker<base::internal::FunctorTraits<void (v8::Task::*&&)(), std::__Cr::unique_ptr<v8::Task, std::__Cr::default_delete<v8::Task>>&&>, base::internal::BindState<true, true, false, void (v8::Task::*)(), std::__Cr::unique_ptr<v8::Task, std::__Cr::default_delete<v8::Task>>>, void ()>::RunOnce(base::internal::BindStateBase*)
base/functional/bind_internal.h:740:12
(/tmp/chromium-asan-1654411/chrome+0x33695aa0) (BuildId: 5c8f91628a04675e)
#15 0x56424d3d0817 in base::TaskAnnotator::RunTaskImpl(base::PendingTask&)
base/functional/callback.h:155:12
(/tmp/chromium-asan-1654411/chrome+0x2b72d817) (BuildId: 5c8f91628a04675e)
#16 0x56424d43f8b4 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWorkImpl(base::LazyNow*)
base/task/common/task_annotator.h:112:5
(/tmp/chromium-asan-1654411/chrome+0x2b79c8b4) (BuildId: 5c8f91628a04675e)
#17 0x56424d43e645 in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::DoWork()
base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:345:40
(/tmp/chromium-asan-1654411/chrome+0x2b79b645) (BuildId: 5c8f91628a04675e)
#18 0x56424d290555 in base::MessagePumpDefault::Run(base::MessagePump::Delegate*)
base/message_loop/message_pump_default.cc:66:55
(/tmp/chromium-asan-1654411/chrome+0x2b5ed555) (BuildId: 5c8f91628a04675e)
#19 0x56424d44104c in base::sequence_manager::internal::ThreadControllerWithMessagePumpImpl::Run(bool, base::TimeDelta)
base/task/sequence_manager/thread_controller_with_message_pump_impl.cc:660:12
(/tmp/chromium-asan-1654411/chrome+0x2b79e04c) (BuildId: 5c8f91628a04675e)
#20 0x56424d34dbb8 in base::RunLoop::Run(base::Location const&)
base/run_loop.cc:135:14
(/tmp/chromium-asan-1654411/chrome+0x2b6aabb8) (BuildId: 5c8f91628a04675e)
#21 0x564259e8b7db in content::RendererMain(content::MainFunctionParams)
content/renderer/renderer_main.cc:339:16
(/tmp/chromium-asan-1654411/chrome+0x381e87db) (BuildId: 5c8f91628a04675e)
#22 0x56424989df9f in content::RunZygote(content::ContentMainDelegate*)
content/app/content_main_runner_impl.cc:666:14
(/tmp/chromium-asan-1654411/chrome+0x27bfaf9f) (BuildId: 5c8f91628a04675e)
#23 0x56424989f204 in content::RunOtherNamedProcessTypeMain(std::__Cr::basic_string<char, std::__Cr::char_traits<char>, std::__Cr::allocator<char>> const&, content::MainFunctionParams, content::ContentMainDelegate*)
content/app/content_main_runner_impl.cc:773:12
(/tmp/chromium-asan-1654411/chrome+0x27bfc204) (BuildId: 5c8f91628a04675e)
#24 0x5642498a20ed in content::ContentMainRunnerImpl::Run()
content/app/content_main_runner_impl.cc:1165:10
(/tmp/chromium-asan-1654411/chrome+0x27bff0ed) (BuildId: 5c8f91628a04675e)
#25 0x56424989b9af in content::RunContentProcess(content::ContentMainParams, content::ContentMainRunner*)
content/app/content_main.cc:356:36
(/tmp/chromium-asan-1654411/chrome+0x27bf89af) (BuildId: 5c8f91628a04675e)
#26 0x56424989bfac in content::ContentMain(content::ContentMainParams)
content/app/content_main.cc:369:10
(/tmp/chromium-asan-1654411/chrome+0x27bf8fac) (BuildId: 5c8f91628a04675e)
#27 0x56423357c0bf in ChromeMain
chrome/app/chrome_main.cc:221:12
(/tmp/chromium-asan-1654411/chrome+0x118d90bf) (BuildId: 5c8f91628a04675e)
#28 0x7fab070721c9 in __libc_start_call_main
sysdeps/nptl/libc_start_call_main.h:58:16
(/lib/x86_64-linux-gnu/libc.so.6+0x2a1c9) (BuildId: 328820b908de8ea1ef79afa8995e302e819163d7)
#29 0x7fab0707228a in __libc_start_main
csu/libc-start.c:360:3
(/lib/x86_64-linux-gnu/libc.so.6+0x2a28a) (BuildId: 328820b908de8ea1ef79afa8995e302e819163d7)
#30 0x56423349e029 in _start
??:0:0
(/tmp/chromium-asan-1654411/chrome+0x117fb029) (BuildId: 5c8f91628a04675e)
Address 0x7b9402009020 is a wild pointer inside of access range of size 0x000000000008.
SUMMARY: AddressSanitizer: use-after-poison in blink::WebGLFramebuffer::RemoveAttachmentInternal(unsigned int, unsigned int)
third_party/blink/renderer/modules/webgl/webgl_framebuffer.cc:537:24
(/tmp/chromium-asan-1654411/chrome+0x4273bcc4) (BuildId: 5c8f91628a04675e)
Shadow bytes around the buggy address:
0x7b9402008d80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7b9402008e00: 00 00 00 00 00 00 f7 f7 f7 f7 f7 00 00 00 00 00
0x7b9402008e80: 00 00 00 00 00 00 00 00 00 00 00 f7 f7 f7 00 00
0x7b9402008f00: 00 00 00 f7 f7 f7 f7 f7 00 00 00 00 00 00 00 00
0x7b9402008f80: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
=>0x7b9402009000: 00 00 00 00[f7]f7 f7 00 00 00 00 00 00 00 00 00
0x7b9402009080: 00 00 00 00 00 00 00 00 f7 f7 f7 00 00 00 00 00
0x7b9402009100: 00 00 00 00 00 00 00 00 00 00 00 00 f7 f7 f7 00
0x7b9402009180: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7b9402009200: f7 f7 f7 00 00 00 00 00 00 00 00 00 00 00 00 00
0x7b9402009280: 00 00 00 00 f7 f7 f7 00 00 00 00 00 00 00 00 00
Shadow byte legend (one shadow byte represents 8 application bytes):
Addressable: 00
Partially addressable: 01 02 03 04 05 06 07
Heap left redzone: fa
Freed heap region: fd
Stack left redzone: f1
Stack mid redzone: f2
Stack right redzone: f3
Stack after return: f5
Stack use after scope: f8
Global redzone: f9
Global init order: f6
Poisoned by user: f7
Container overflow: fc
Array cookie: ac
Intra object redzone: bb
ASan internal: fe
Left alloca redzone: ca
Right alloca redzone: cb
NOTE: the stack trace above identifies the code that *accessed* the poisoned memory.
HINT: To identify the code that *poisoned* the memory, try the experimental setting ASAN_OPTIONS=poison_history_size=<size>.
==1==ADDITIONAL INFO
==1==Note: Please include this section with the ASan report.
Task trace:
#0 0x56425532c44f in gin::V8ToBaseLocation(v8::SourceLocation const&)
gin/converter.cc:289:10
(/tmp/chromium-asan-1654411/chrome+0x3368944f) (BuildId: 5c8f91628a04675e)
#1 0x5642606e621c in blink::DOMTimer::DOMTimer(blink::ExecutionContext&, blink::ScheduledAction*, base::TimeDelta, bool, blink::probe::AsyncTaskContext::ScanForAds)
third_party/blink/renderer/core/scheduler/dom_timer.cc:348:27
(/tmp/chromium-asan-1654411/chrome+0x3ea4321c) (BuildId: 5c8f91628a04675e)
#2 0x5642606e621c in blink::DOMTimer::DOMTimer(blink::ExecutionContext&, blink::ScheduledAction*, base::TimeDelta, bool, blink::probe::AsyncTaskContext::ScanForAds)
third_party/blink/renderer/core/scheduler/dom_timer.cc:348:27
(/tmp/chromium-asan-1654411/chrome+0x3ea4321c) (BuildId: 5c8f91628a04675e)
#3 0x5642512f7287 in IPC::ChannelAssociatedGroupController::Accept(mojo::Message*)
ipc/ipc_mojo_bootstrap.cc:1138:13
(/tmp/chromium-asan-1654411/chrome+0x2f654287) (BuildId: 5c8f91628a04675e)
Command line: `/proc/self/exe --type=renderer --gpu-client-id=6 --crashpad-handler-pid=2926502 --enable-crash-reporter=, --noerrdialogs --user-data-dir=/tmp/chromium-asan-151-profile-01 --change-stack-guard-on-fork=enable --disable-dev-shm-usage --file-url-path-alias=/gen=/tmp/chromium-asan-1654411/gen --remote-debugging-port=0 --ozone-platform=headless --lang=en-US --num-raster-threads=4 --enable-main-frame-before-activation --renderer-client-id=6 --time-ticks-at-unix-epoch=-1782698642359408 --gpu-channel-request-start-time-ticks=3269794668529 --launch-time-ticks=3269794675661 --shared-files=v8_context_snapshot_data:100 --field-trial-handle=3,i,1506818743404535082,4229230523870192825,262144 --disable-features=PaintHolding --variations-seed-version --pseudonymization-salt-handle=7,i,10557152309683851536,8784820902952286342,4 --trace-process-track-uuid=3190708991934122588`
==1==END OF ADDITIONAL INFO
==1==ABORTING
Disclaimer
This information is being shared by OpenAI solely for the purpose of improving security and reducing potential harm. This information is presented as-is. We make no representations or warranties, express or implied, as to the completeness, accuracy, or fitness for any particular purpose of the information. This includes, without limitation any suggestions or ideas presented on how to remedy or mitigate an identified vulnerability, including whether such suggestions or ideas would be effective and/or could have other negative impacts.
OpenAI disclaims any liability for direct or indirect damages arising from the reliance on, or use, misuse, or interpretation of this information. Any references to third-party systems, services, or entities are included solely for identification purposes and do not imply endorsement, responsibility, or attribution.
Reporting Credit
OpenAI Codex Security (amyb)