Medium watch CVSS 5.3 chrome Input Validation 📄 Reporter bug report 🔧 Commit mapped

Overview

Medium
Severity
5.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactInsufficient validation of untrusted input in GPU
DescriptionInsufficient validation of untrusted input in GPU
ComponentGPU
Sub-componentGPU
Bug ClassInput Validation
Action tierwatch — information disclosure — security-relevant, below the RCE-class bar
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.
Tracker498721671
Fix commita0683f849d4f (chromium/src) +115/-15
CWECWE-20 (Improper input validation)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-04-01 (per vendor advisory)
Fix landed2026-05-15 (public git)
Disclosed2026-06-30
Patch gap46 days — fix public in source before the advisory

Background

`D3DImageBacking`
the Windows GPU-service shared-image backing built on a D3D11 texture, shared across D3D11, D3D11on12/D3D12, and Dawn (WebGPU) devices.
`D3DSharedFence`
a cross-device GPU synchronization fence; a backing waits on pending fences before granting access so producers’ writes are visible and ordered.
`GetPendingWaitFences`
the method that gathers the set of fences an access must wait on, lazily creating and signaling a per-texture-device fence when the backing is used on another device.
External fence injection
UpdateExternalFence lets a caller supply a fence handle for the backing; an invalid handle can make internal wait/signal operations fail.

Root Cause Analysis

GetPendingWaitFences returned a std::vector of fences, and on internal failure paths (failing to create the D3D11 signal fence, failing WaitD3D11 on a write_fence, or failing IncrementAndSignalD3D11) it returned an empty vector {}. Callers (BeginAccess paths for Dawn, D3D11on12/D3D12, and D3D11) could not distinguish “no synchronization required” from “synchronization failed,” so on failure they iterated an empty list and proceeded to grant access as if no waits were needed, skipping required GPU synchronization. This is a value-vs-error conflation: the empty-list sentinel overloaded a legitimate “nothing to wait on” result with a hard failure.

The fix changes the return type to std::optional<std::vector<...>>, returning std::nullopt on genuine failures while returning an empty vector for the legitimate no-fence case; every caller now checks if (!wait_fences) and fails the access (return nullptr / return false). Additionally, the lazy per-device fence is inserted into d3d11_signaled_fence_map_ only after all create/wait/signal steps succeed (via insert_or_assign at the end) instead of eagerly binding a map slot with operator[], avoiding leaving a partial/invalid fence cached in the map.

Key insight
The core mistake was using an empty return vector as both “no synchronization needed” and “synchronization failed,” so callers silently skipped required GPU waits on failure; the fix distinguishes the two with std::optional/std::nullopt and makes callers reject access on nullopt.

Attack Path

  1. Create a shared-image backing A renderer creates a D3DImageBacking from a shared handle (as in the InvalidExternalFence test).
  2. Inject an invalid fence The attacker calls UpdateExternalFence with a bogus fence handle (e.g. a dummy event handle), so later internal wait/signal operations on it will fail.
  3. Begin access The attacker begins access on a representation, driving a BeginAccess path that calls GetPendingWaitFences.
  4. Failure returns empty list (pre-patch) An internal wait/signal step fails; pre-patch GetPendingWaitFences returns {}, which the caller reads as “no fences to wait on” and grants access without synchronizing.
  5. Race / stale data Access proceeds without the required GPU synchronization, producing data races and reads of unsynchronized/stale GPU memory, an information leak (specific leaked contents inferred).

Impact Assessment

An attacker in a compromised renderer who can inject an invalid external fence into a D3DImageBacking and begin access can cause the GPU process to skip required cross-device synchronization, leading to data races and information leaks of unsynchronized GPU memory contents, as the patch description states. Preconditions are Windows with D3D shared images and D3DSharedFence support plus the ability to call UpdateExternalFence. The advisory classifies this as insufficient validation of untrusted input in GPU; it reports no exploited-in-the-wild status, and any escalation beyond the described races/leaks is (inferred).

Changed Functions

FunctionChangeNotes
if
gpu/command_buffer/service/shared_image/d3d_image_backing.cc
modified
for
gpu/command_buffer/service/shared_image/d3d_image_backing.cc
modified
TEST_P
gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc
modified

Files Changed

  • gpu/command_buffer/service/shared_image/d3d_image_backing.cc
  • gpu/command_buffer/service/shared_image/d3d_image_backing.h
  • gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc

Audit Directions

  • Sentinel-value error conflation
    Audit APIs that report failure by returning an empty container, zero, or a default value indistinguishable from a valid “nothing here” result; prefer std::optional/status codes so callers can reject failures.
  • Skipped synchronization on error
    Review GPU access/BeginAccess paths where a failed fence create/wait/signal could let access proceed unsynchronized, especially with externally injected fences.
  • Partial state mutation on failure
    Look for lazy-initialization patterns using map[key] that insert a slot before the value is fully built, leaving invalid entries cached when a later step fails.
From a0683f849d4f6ea24ddfa848d03a9796c6cb5580 Mon Sep 17 00:00:00 2001
From: Sunny Sachanandani <sunnyps@chromium.org>
Date: Fri, 15 May 2026 13:36:29 -0700
Subject: [PATCH] [gpu] Fix GPU synchronization bypass in D3DImageBacking

GetPendingWaitFences returned an empty list of fences if one of the
internal waits failed (e.g., due to an invalid injected fence).
Callers interpreted this as no synchronization being required, leading
to potential data races and information leaks.

This CL changes GetPendingWaitFences to return std::optional and
returns std::nullopt on failure. Callers are updated to fail the
access if GetPendingWaitFences returns std::nullopt.

It also refactors the lazy initialization of the texture device fence
to only insert it into the map when everything succeeds, avoiding
partial state mutation.

Bug: 498721671
Test: D3DImageBackingFactoryTest.InvalidExternalFence
Link: https://chromium-review.googlesource.com/id/I2d3ff59eaa59d7a29e484056be70b26b6a6a6964
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7823350
Reviewed-by: Vasiliy Telezhnikov <vasilyt@chromium.org>
Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1631525}
---

diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
index c9746faba..cf9c985 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.cc
@@ -1138,7 +1138,7 @@
                                                        device, src_texture);
 }
 
-std::vector<scoped_refptr<gfx::D3DSharedFence>>
+std::optional<std::vector<scoped_refptr<gfx::D3DSharedFence>>>
 D3DImageBacking::GetPendingWaitFences(
     const Microsoft::WRL::ComPtr<ID3D11Device>& wait_d3d11_device,
     const wgpu::Device& wait_dawn_device,
@@ -1148,33 +1148,40 @@
   // (i.e. scanout cases) means we always need to check for the presence of the
   // availability fence.
   if (!use_cross_device_fence_synchronization() && !dcomp_texture_) {
-    return {};
+    return std::vector<scoped_refptr<gfx::D3DSharedFence>>{};
   }
 
   // Lazily create and signal the D3D11 fence on the texture's original device
   // if not present and we're using the backing on another device.
-  auto& texture_device_fence = d3d11_signaled_fence_map_[texture_d3d11_device_];
+  auto it = d3d11_signaled_fence_map_.find(texture_d3d11_device_);
+  scoped_refptr<gfx::D3DSharedFence> texture_device_fence =
+      it != d3d11_signaled_fence_map_.end() ? it->second : nullptr;
+
   if (wait_d3d11_device != texture_d3d11_device_ && !texture_device_fence) {
     texture_device_fence =
         gfx::D3DSharedFence::CreateForD3D11(texture_d3d11_device_);
     if (!texture_device_fence) {
       LOG(ERROR) << "Failed to retrieve D3D11 signal fence";
-      return {};
+      return std::nullopt;
     }
     // Make D3D11 device wait for |write_fences_| since we'll replace it below.
     for (auto& fence : write_fences_) {
       if (!fence->WaitD3D11(texture_d3d11_device_)) {
         LOG(ERROR) << "Failed to wait for write fence";
-        return {};
+        return std::nullopt;
       }
     }
     if (!texture_device_fence->IncrementAndSignalD3D11()) {
       LOG(ERROR) << "Failed to signal D3D11 signal fence";
-      return {};
+      return std::nullopt;
     }
     // Store it in |write_fences_| so it's waited on for all subsequent access.
     write_fences_.clear();
     write_fences_.insert(texture_device_fence);
+
+    // Insert in the map only when everything succeeds.
+    d3d11_signaled_fence_map_.insert_or_assign(texture_d3d11_device_,
+                                               std::move(texture_device_fence));
   }
 
   // TODO(crbug.com/335003893): Investigate how to avoid passing any fences back
@@ -1299,11 +1306,15 @@
   CHECK(shared_texture_memory);
 
   // Defer clearing fences until later to handle Dawn failure to import texture.
-  std::vector<scoped_refptr<gfx::D3DSharedFence>> wait_fences =
+  auto wait_fences =
       GetPendingWaitFences(dawn_d3d11_device, device, write_access);
+  if (!wait_fences) {
+    LOG(ERROR) << "Failed to get pending wait fences";
+    return nullptr;
+  }
   std::vector<wgpu::SharedFence> shared_fences;
   std::vector<uint64_t> signaled_values;
-  for (auto& wait_fence : wait_fences) {
+  for (auto& wait_fence : *wait_fences) {
     // TODO(crbug.com/335003893): Look into caching the wgpu::SharedFence object
     // in gfx::D3DSharedFence.
     shared_fences.push_back(CreateDawnSharedFence(device, wait_fence));
@@ -1548,10 +1559,13 @@
   auto unwrapped_d3d12_resource = EnsureD3D12Resource();
 
   // Defer clearing fences until later to handle failure to synchronize.
-  std::vector<scoped_refptr<gfx::D3DSharedFence>> wait_fences =
-      GetPendingWaitFences(d3d11on12_device, /*dawn_device=*/nullptr,
-                           write_access);
-  for (auto& wait_fence : wait_fences) {
+  auto wait_fences = GetPendingWaitFences(
+      d3d11on12_device, /*dawn_device=*/nullptr, write_access);
+  if (!wait_fences) {
+    LOG(ERROR) << "Failed to get pending wait fences";
+    return false;
+  }
+  for (auto& wait_fence : *wait_fences) {
     if (!wait_fence->WaitD3D11(d3d11on12_device)) {
       LOG(ERROR) << "Failed to wait for fence";
       return false;
@@ -1605,9 +1619,13 @@
   }
 
   // Defer clearing fences until later to handle D3D11 failure to synchronize.
-  std::vector<scoped_refptr<gfx::D3DSharedFence>> wait_fences =
+  auto wait_fences =
       GetPendingWaitFences(d3d11_device, /*dawn_device=*/nullptr, write_access);
-  for (auto& wait_fence : wait_fences) {
+  if (!wait_fences) {
+    LOG(ERROR) << "Failed to get pending wait fences";
+    return false;
+  }
+  for (auto& wait_fence : *wait_fences) {
     if (!wait_fence->WaitD3D11(d3d11_device)) {
       LOG(ERROR) << "Failed to wait for fence";
       return false;
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing.h b/gpu/command_buffer/service/shared_image/d3d_image_backing.h
index 58f5987..4d69281a 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing.h
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing.h
@@ -343,7 +343,8 @@
   // no-op. Similarly, |wait_dawn_device| can be provided to skip over waits on
   // fences previously signaled on the same Dawn device which are cached in
   // |dawn_signaled_fence_map_|.
-  std::vector<scoped_refptr<gfx::D3DSharedFence>> GetPendingWaitFences(
+  std::optional<std::vector<scoped_refptr<gfx::D3DSharedFence>>>
+  GetPendingWaitFences(
       const Microsoft::WRL::ComPtr<ID3D11Device>& wait_d3d11_device,
       const wgpu::Device& wait_dawn_device,
       bool write_access) EXCLUSIVE_LOCKS_REQUIRED(lock_);
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc
index de85bff..235b4e45 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc
@@ -1044,6 +1044,87 @@
   RunCreateSharedImageFromHandleTest(DXGI_FORMAT_R8G8B8A8_TYPELESS);
 }
 
+TEST_P(D3DImageBackingFactoryTest, InvalidExternalFence) {
+  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
+      shared_image_factory_->GetDeviceForTesting();
+  if (!gfx::D3DSharedFence::IsSupported(d3d11_device.Get())) {
+    GTEST_SKIP();
+  }
+
+  const auto format = viz::SinglePlaneFormat::kRGBA_8888;
+  const gfx::Size size(1, 1);
+  const auto color_space = gfx::ColorSpace::CreateSRGB();
+  const gpu::SharedImageUsageSet usage =
+      SHARED_IMAGE_USAGE_GLES2_READ | SHARED_IMAGE_USAGE_DISPLAY_READ;
+
+  D3D11_TEXTURE2D_DESC desc;
+  desc.Width = size.width();
+  desc.Height = size.height();
+  desc.MipLevels = 1;
+  desc.ArraySize = 1;
+  desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+  desc.SampleDesc.Count = 1;
+  desc.SampleDesc.Quality = 0;
+  desc.Usage = D3D11_USAGE_DEFAULT;
+  desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
+  desc.CPUAccessFlags = 0;
+  desc.MiscFlags =
+      D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
+  HRESULT hr = d3d11_device->CreateTexture2D(&desc, nullptr, &d3d11_texture);
+  ASSERT_EQ(hr, S_OK);
+
+  Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
+  hr = d3d11_texture.As(&dxgi_resource);
+  ASSERT_EQ(hr, S_OK);
+
+  HANDLE shared_handle;
+  hr = dxgi_resource->CreateSharedHandle(
+      nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
+      &shared_handle);
+  ASSERT_EQ(hr, S_OK);
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc
index de85bff..235b4e45 100644
--- a/gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc
+++ b/gpu/command_buffer/service/shared_image/d3d_image_backing_factory_unittest.cc
@@ -1044,6 +1044,87 @@
   RunCreateSharedImageFromHandleTest(DXGI_FORMAT_R8G8B8A8_TYPELESS);
 }
 
+TEST_P(D3DImageBackingFactoryTest, InvalidExternalFence) {
+  Microsoft::WRL::ComPtr<ID3D11Device> d3d11_device =
+      shared_image_factory_->GetDeviceForTesting();
+  if (!gfx::D3DSharedFence::IsSupported(d3d11_device.Get())) {
+    GTEST_SKIP();
+  }
+
+  const auto format = viz::SinglePlaneFormat::kRGBA_8888;
+  const gfx::Size size(1, 1);
+  const auto color_space = gfx::ColorSpace::CreateSRGB();
+  const gpu::SharedImageUsageSet usage =
+      SHARED_IMAGE_USAGE_GLES2_READ | SHARED_IMAGE_USAGE_DISPLAY_READ;
+
+  D3D11_TEXTURE2D_DESC desc;
+  desc.Width = size.width();
+  desc.Height = size.height();
+  desc.MipLevels = 1;
+  desc.ArraySize = 1;
+  desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
+  desc.SampleDesc.Count = 1;
+  desc.SampleDesc.Quality = 0;
+  desc.Usage = D3D11_USAGE_DEFAULT;
+  desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
+  desc.CPUAccessFlags = 0;
+  desc.MiscFlags =
+      D3D11_RESOURCE_MISC_SHARED_NTHANDLE | D3D11_RESOURCE_MISC_SHARED;
+  Microsoft::WRL::ComPtr<ID3D11Texture2D> d3d11_texture;
+  HRESULT hr = d3d11_device->CreateTexture2D(&desc, nullptr, &d3d11_texture);
+  ASSERT_EQ(hr, S_OK);
+
+  Microsoft::WRL::ComPtr<IDXGIResource1> dxgi_resource;
+  hr = d3d11_texture.As(&dxgi_resource);
+  ASSERT_EQ(hr, S_OK);
+
+  HANDLE shared_handle;
+  hr = dxgi_resource->CreateSharedHandle(
+      nullptr, DXGI_SHARED_RESOURCE_READ | DXGI_SHARED_RESOURCE_WRITE, nullptr,
+      &shared_handle);
+  ASSERT_EQ(hr, S_OK);
+
+  gfx::GpuMemoryBufferHandle gmb_handle{
+      gfx::DXGIHandle(base::win::ScopedHandle(shared_handle))};
+
+  auto mailbox = Mailbox::Generate();
+  auto backing = shared_image_factory_->CreateSharedImage(
+      mailbox,
+      SharedImageInfo(format, size, color_space, kTopLeft_GrSurfaceOrigin,
+                      kPremul_SkAlphaType, usage, "TestLabel"),
+      /*is_thread_safe=*/false, std::move(gmb_handle));
+  ASSERT_NE(backing, nullptr);
+
+  D3DImageBacking* d3d_backing = static_cast<D3DImageBacking*>(backing.get());
+
+  // Create a dummy event handle to use as an invalid fence handle.
+  HANDLE dummy_handle = ::CreateEvent(nullptr, FALSE, FALSE, nullptr);
+  ASSERT_NE(dummy_handle, nullptr);
+  auto invalid_fence = gfx::D3DSharedFence::CreateFromScopedHandle(
+      base::win::ScopedHandle(dummy_handle), gfx::DXGIHandleToken());
+
+  // Inject the invalid fence.
+  d3d_backing->UpdateExternalFence(invalid_fence);
+
+  std::unique_ptr<SharedImageRepresentationFactoryRef> factory_ref =
+      shared_image_manager_.Register(std::move(backing),
+                                     memory_type_tracker_.get());
+
+  // Produce a representation and try to BeginAccess.
+  auto gl_representation =
+      shared_image_representation_factory_->ProduceGLTexturePassthrough(
+          mailbox);
+  ASSERT_TRUE(gl_representation);
+
+  // BeginAccess should fail because GetPendingWaitFences will fail on the
+  // invalid fence.
+  std::unique_ptr<GLTexturePassthroughImageRepresentation::ScopedAccess>
+      scoped_access = gl_representation->BeginScopedAccess(
+          GL_SHARED_IMAGE_ACCESS_MODE_READ_CHROMIUM,
+          SharedImageRepresentation::AllowUnclearedAccess::kYes);
+  EXPECT_FALSE(scoped_access);
+}
+
 // Tests that writing to a Skia representation of a D3DImageBacking created
 // from a shared handle is reflected in a second backing created from the
 // same handle.
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential GPU sync bypass in D3DImageBacking leading to info leak

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 security team.

Overview: A compromised renderer can inject an invalid Windows NT handle as a DXGI fence, causing GetPendingWaitFences to fail and return an empty wait list. This bypasses GPU synchronization during cross-device access, allowing WebGPU commands to execute concurrently with initialization. This potential race condition can expose uninitialized, pooled GPU memory.

Affected files:

  • gpu/command_buffer/service/shared_image/d3d_image_backing.cc

Estimated timestamp from git blame: 2025-05-12

Vulnerability Details

In gpu/command_buffer/service/shared_image/d3d_image_backing.cc, the GetPendingWaitFences method is responsible for returning a list of fences that must be waited on before a SharedImage can be safely accessed on a different device (e.g., transitioning from D3D11 to Dawn/D3D12).

During a cross-device access, GetPendingWaitFences attempts a lazy initialization of a D3D11 fence (lines 1027-1048). As part of this, it iterates over all current write_fences_ and calls WaitD3D11 on each. If WaitD3D11 fails (for instance, because the fence wraps an invalid or non-D3D Windows NT handle), the function logs an error and returns an empty std::vector ({}) at line 1038.

The callers of this function, such as BeginAccessDawn, interpret an empty vector to mean that no synchronization is required. BeginAccessDawn passes fenceCount = 0 to Dawn, executing the GPU commands immediately. Furthermore, BeginAccessDawn subsequently calls BeginAccessCommon(write_access), which unconditionally clears both write_fences_ and read_fences_ (lines 1735-1736). This permanently discards legitimate synchronization primitives.

Potential Attack Steps

Note: These are suggested steps to trigger the vulnerability, as our automated tooling does not currently run or verify Proof of Concept code.

  1. Compromise Renderer: An attacker gains arbitrary code execution within the sandboxed renderer process.
  2. Create SharedImage: The renderer requests the creation of a SharedImage with usages that require cross-device synchronization (e.g., SHARED_IMAGE_USAGE_WEBGPU_READ). This texture is backed by newly allocated or pooled D3D memory, which may contain sensitive cross-origin data.
  3. Inject Invalid Fence: The renderer sends a mojom::DeferredSharedImageRequest::Tag::kRegisterDxgiFence IPC message to the GPU process. The attacker supplies an arbitrary, non-D3D Windows NT handle (e.g., a standard Event handle) wrapped as a GpuFenceHandle.
  4. Update Fence: SharedImageStub::OnRegisterDxgiFence wraps this handle without validating it’s a D3D fence. The renderer then sends a kUpdateDxgiFence IPC, causing the invalid fence to be inserted into the backing’s write_fences_ set.
  5. Trigger Cross-Device Access: The renderer initiates a WebGPU read access to the SharedImage (Dawn D3D12 backend). This calls BeginAccessDawn in the GPU process.
  6. Bypass Synchronization: BeginAccessDawn calls GetPendingWaitFences. The function attempts to wait on the attacker’s invalid fence via WaitD3D11. OpenSharedFence fails on the invalid handle, causing GetPendingWaitFences to return an empty vector.
  7. Data Race and Info Leak: Dawn is instructed to proceed with 0 wait fences. The WebGPU read command executes on the GPU concurrently with, or before, the D3D11 clear command that was meant to zero-initialize the texture. The attacker reads the uninitialized, pooled GPU memory into an attacker-controlled buffer, resulting in a cross-origin information leak.

Suggested Fix

  1. Safe Failure Handling: Modify GetPendingWaitFences and its callers (like BeginAccessDawn and BeginAccessD3D11) to treat synchronization failures as fatal for the access. If GetPendingWaitFences encounters an error, it should explicitly indicate a failure (e.g., returning std::nullopt instead of an empty vector), and the caller should abort the access (returning nullptr or false) instead of proceeding with no wait fences.
  2. Input Validation: Add validation to SharedImageStub::OnRegisterDxgiFence or D3DSharedFence::CreateFromScopedHandle to ensure that handles passed over IPC from the renderer are actually valid D3D shared fences before they are accepted and stored.

Evaluated with Chrome root at commit: ff3d2b74fa39431785bd60e51463b08fcc71ee33


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