Medium chrome Logic Error 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactObject lifecycle issue in Dawn
DescriptionObject lifecycle issue in Dawn
ComponentDawn
Bug ClassLogic Error
Tracker501762953
Fix commitc366d82e562f (dawn) +3/-0
CISA KEVNot listed
CreditedGoogle
Disclosed2026-06-02

Changed Functions

FunctionChangeNotes
if
src/dawn/native/vulkan/TextureVk.cpp
modified

Files Changed

  • src/dawn/native/vulkan/TextureVk.cpp
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,
Loading diff…

Original Bug Report

reported by [email protected]

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:

  1. 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.
  2. The attacker uses the WebGPU API to import an external video frame or SharedImage, creating an ExternalVkImageTexture.
  3. The attacker submits a command buffer that uses this texture, causing mExternalSemaphoreHandle to be populated with a valid FD.
  4. The attacker submits another command buffer using the texture. In OnAfterSubmit(), the existing mExternalSemaphoreHandle is closed.
  5. 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.
  6. ExportSemaphore calls vkGetSemaphoreFdKHR. Because the FD limit is exhausted, this fails (e.g., VK_ERROR_OUT_OF_HOST_MEMORY).
  7. DAWN_TRY_ASSIGN returns early, leaving the stale FD value in mExternalSemaphoreHandle.
  8. The error causes device loss. The attacker triggers garbage collection of the texture object, invoking ~ImportedTextureBase().
  9. 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.

View on issue tracker