CVE-2026-11152
Overview
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifsrc/dawn/native/vulkan/TextureVk.cpp |
modified |
Files Changed
src/dawn/native/vulkan/TextureVk.cpp
Patch
From c366d82e562f70c9cc726ed589c4c3eb0d339e45 Mon Sep 17 00:00:00 2001 From: Brandon Jones <[email protected]> Date: Wed, 15 Apr 2026 18:12:25 -0700 Subject: [PATCH] Vulkan: Prevent double-closing stale FD Sets the external semephore handle for Vulkan's ImportedTextureBase to null immediately after the handle is closed so there's no chance that the handle is left with a stale value that might be closed a second time if the subsequent ExportSemephore() call fails. Bug: 501762953 Fixed: 501762953 Change-Id: I1044d86da0534830f19764c3dd34f0b3cc972fa7 Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/302861 Auto-Submit: Brandon Jones <[email protected]> Reviewed-by: Loko Kung <[email protected]> Reviewed-by: Kai Ninomiya <[email protected]> Commit-Queue: Brandon Jones <[email protected]> --- diff --git a/src/dawn/native/vulkan/TextureVk.cpp b/src/dawn/native/vulkan/TextureVk.cpp index f5ec1cf..6dfc2c0 100644 --- a/src/dawn/native/vulkan/TextureVk.cpp +++ b/src/dawn/native/vulkan/TextureVk.cpp @@ -1852,6 +1852,9 @@ // The submit succeeded, we can replace the previous external semaphore with the pending one. if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) { device->GetExternalSemaphoreService()->CloseHandle(mExternalSemaphoreHandle); + // Set the handle to null after it's been closed so that we don't accidentally attempt to + // use or close it again if the ExportSemaphore() below fails. + mExternalSemaphoreHandle = kNullExternalSemaphoreHandle; } DAWN_TRY_ASSIGN(mExternalSemaphoreHandle,
Original Bug Report
Potential stale FD double-close in Dawn ImportedTextureBase
Project Fortify, 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 Chrome Security team.
Overview: In Dawn’s Vulkan backend, ImportedTextureBase::OnAfterSubmit closes an external semaphore handle but does not immediately clear the member variable. If the subsequent semaphore export fails, the variable retains the stale file descriptor, which is closed again during object destruction, potentially causing file descriptor confusion in the GPU process.
Affected files:
third_party/dawn/src/dawn/native/vulkan/TextureVk.cpp
Estimated timestamp from git blame: 2025-02-25
Vulnerability Details
A potential stale-FD double-close vulnerability exists in Dawn’s Vulkan backend within the ImportedTextureBase class. The issue arises when an external semaphore handle is closed but not nullified, followed by a failing operation that prevents the handle from being updated.
In third_party/dawn/src/dawn/native/vulkan/TextureVk.cpp, the function ImportedTextureBase::OnAfterSubmit() handles the rotation of external semaphores after a Vulkan queue submission:
MaybeError ImportedTextureBase::OnAfterSubmit() {
Device* device = ToBackend(GetDevice());
// The submit succeeded, we can replace the previous external semaphore with the pending one.
if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
device->GetExternalSemaphoreService()->CloseHandle(mExternalSemaphoreHandle);
}
DAWN_TRY_ASSIGN(mExternalSemaphoreHandle,
device->GetExternalSemaphoreService()->ExportSemaphore(mPendingSemaphore));
// ...
}
At line 1854, CloseHandle(mExternalSemaphoreHandle) is called (which maps to close(fd) on POSIX platforms), but mExternalSemaphoreHandle is not reset to kNullExternalSemaphoreHandle.
At line 1857, the DAWN_TRY_ASSIGN macro is used to export a new semaphore and assign it to the handle. If ExportSemaphore returns an error, DAWN_TRY_ASSIGN returns early from the function before the assignment occurs. Consequently, mExternalSemaphoreHandle retains the integer value of the file descriptor that was just closed.
Although the error triggers a Dawn device loss, the texture object remains alive until its reference count drops to zero. When the object is eventually destroyed, the destructor ~ImportedTextureBase() executes:
ImportedTextureBase::~ImportedTextureBase() {
if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
ToBackend(GetDevice())
->GetExternalSemaphoreService()
->CloseHandle(mExternalSemaphoreHandle);
mExternalSemaphoreHandle = kNullExternalSemaphoreHandle;
}
}
Since mExternalSemaphoreHandle still holds the old, now-stale FD value, a second close() is performed.
Potential Attack Scenario
An attacker could potentially exploit this by deliberately exhausting the GPU process’s file descriptor limit:
- The attacker creates a Web page that spawns multiple Web Workers to continuously allocate resources that consume file descriptors in the GPU process (e.g., WebGL textures backed by shared memory), pushing the process near its FD limit.
- The attacker uses the WebGPU API to import an external video frame or SharedImage, creating an
ExternalVkImageTexture. - The attacker submits a command buffer that uses this texture, causing
mExternalSemaphoreHandleto be populated with a valid FD. - The attacker submits another command buffer using the texture. In
OnAfterSubmit(), the existingmExternalSemaphoreHandleis closed. - During the brief window after the close, one of the attacker’s background workers successfully allocates a new FD (e.g., a Mojo data pipe). The OS assigns it the exact FD number just released, exhausting the FD limit again.
ExportSemaphorecallsvkGetSemaphoreFdKHR. Because the FD limit is exhausted, this fails (e.g.,VK_ERROR_OUT_OF_HOST_MEMORY).DAWN_TRY_ASSIGNreturns early, leaving the stale FD value inmExternalSemaphoreHandle.- The error causes device loss. The attacker triggers garbage collection of the texture object, invoking
~ImportedTextureBase(). - The destructor calls
CloseHandle()on the stale FD, inadvertently closing the unrelated FD allocated by the background worker in step 5.
This leads to FD-aliasing and potential FD type-confusion, which could be exploited to disrupt the GPU process or potentially achieve a sandbox escape or remote code execution by manipulating sensitive kernel objects associated with the reused FD.
Note: These are suggested steps based on static analysis; we do not currently have a working proof of concept to demonstrate this exploit end-to-end.
Suggested Fix
Clear the handle immediately after closing it in ImportedTextureBase::OnAfterSubmit():
if (mExternalSemaphoreHandle != kNullExternalSemaphoreHandle) {
device->GetExternalSemaphoreService()->CloseHandle(mExternalSemaphoreHandle);
mExternalSemaphoreHandle = kNullExternalSemaphoreHandle; // ADD THIS LINE
}
Evaluated with Chrome root at commit: 096fc8fdbfacf2546485756d03f160a3d04fcc9b
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.