High watch CVSS 4.3 chrome Uninitialized Memory 🔧 Commit mapped

Overview

High
Severity
4.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUninitialized resource in Skia
DescriptionUninitialized resource in Skia
ComponentSkia
Sub-componentGanesh
Bug ClassUninitialized Memory
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.
Tracker553122373
Fix commit4b2fad13ffe4 (skia) +243/-200
CWECWE-908
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:L/I:N/A:N
CISA KEVNot listed
CreditedGoogle
Reported2026-08-26 (per vendor advisory)
Fix landed2026-09-08 (public git)
Disclosed2026-09-15
Patch gap7 days — fix public in source before the advisory

Background

Ganesh
Skia’s OpenGL/Vulkan GPU backend, driven through GrDirectContext, which records and submits drawing to the underlying 3D API.
`flush` / `flushAndSubmit`
GrDirectContext calls that push recorded drawing to the 3D API and (for the submit variant) send it to the GPU.
`GrSemaphoresSubmitted`
the enum (kYes / kNo) the old flush API returned, which conflated “semaphores were submitted” with “the flush succeeded.”
Async read-pixels callback
the ReadPixelsCallback / ReadPixelsContext pair that Device::asyncRescaleAndReadPixels must invoke to hand back GPU-read pixel data, or a null result on failure.

Root Cause Analysis

The old flush surface, GrDirectContext::flush(const GrFlushInfo&) and its overloads, returned only a GrSemaphoresSubmitted value, so a genuine flush failure was indistinguishable from the benign “no semaphores were submitted” case and was silently swallowed as it propagated up the call stack from GrDrawingManager. Because failure was never surfaced, a caller could treat a surface or image as fully rendered when the flush had actually failed, and then consume a resource whose contents were never initialized. Separately, Device::asyncRescaleAndReadPixels and Device::asyncRescaleAndReadPixelsYUV420 took an early return when no direct context was available (if (!dContext)) without ever invoking the caller’s callback, leaving the caller’s expected result uninitialized and its completion contract unfulfilled.

The fix introduces the FlushResult struct, whose fSuccess field defaults to false and is tracked separately from fSubmitted, and plumbs real flush success/failure all the way up to the public API so callers can discard results on failure. It also patches both async read paths to call callback(callbackContext, nullptr) on the failure path so the caller is explicitly told the read produced no data. Together these changes make failure observable rather than masquerading as success.

Key insight
The single core mistake was conflating “semaphores submitted” with “flush succeeded” in one enum return (and, in the async read path, skipping the failure callback entirely), so failure was silently swallowed and callers consumed an uninitialized resource; the fix separates the two concerns via FlushResult.fSuccess and always fires the callback.

Attack Path

  1. Force a Ganesh flush failure Drive GPU rendering through content that causes GrDirectContext::flush (or GrVkSecondaryCBDrawContext::flush) to fail, or reach the Device::asyncRescaleAndReadPixels path when no direct context is available.
  2. Failure is swallowed by the API Under the old contract flush returns GrSemaphoresSubmitted::kNo, which the caller interprets as merely “no semaphores submitted” rather than “flush failed,” so processing continues as though rendering succeeded; on the async path the completion callback is never invoked at all.
  3. Uninitialized resource is treated as valid The surface/image or the never-populated read-pixels result is consumed as if it held valid, fully rendered data even though the underlying work never completed. (inferred)
  4. Uninitialized memory is disclosed The uninitialized contents are read back or presented, leaking process memory to the attacker-controlled context. (inferred)

Impact Assessment

An attacker who can force a flush failure or reach the no-direct-context async read path gains a result built from an uninitialized resource in Skia, which could disclose uninitialized memory contents in the rendering/GPU process (inferred); the precondition is the ability to drive Ganesh-backed rendering, which is reachable from ordinary web content (e.g. canvas/WebGL). The vendor advisory rates this high severity and does not report in-the-wild exploitation; NVD scores it CVSS 4.3. Any escalation to a stronger read/write primitive or code execution is (inferred) and not demonstrated by the patch.

Changed Functions

FunctionChangeNotes
if
src/gpu/ganesh/Device.cpp
modified
if
src/gpu/ganesh/GrDirectContext.cpp
modified

Files Changed

  • include/gpu/ganesh/SkSurfaceGanesh.h
  • src/gpu/ganesh/vk/GrVkSecondaryCBDrawContext.cpp
  • src/gpu/ganesh/SurfaceContext.cpp
  • src/gpu/ganesh/GrDrawingManager.cpp
  • src/gpu/ganesh/surface/SkSurface_Ganesh.cpp
  • src/gpu/ganesh/image/SkImage_GaneshFactories.cpp
  • src/gpu/ganesh/GrDirectContextPriv.h
  • src/gpu/ganesh/image/SkImage_GaneshFactories_Android.cpp
  • include/private/chromium/GrVkSecondaryCBDrawContext.h
  • relnotes/flush-error.md
  • src/gpu/ganesh/image/SkImage_GaneshBase.h
  • src/gpu/ganesh/GrGpu.h
  • src/gpu/ganesh/image/SkImage_Ganesh.h
  • src/gpu/ganesh/image/SkImage_GaneshYUVA.h
  • src/gpu/ganesh/GrGpu.cpp
  • include/gpu/ganesh/GrDirectContext.h
  • src/gpu/ganesh/GrDirectContext.cpp
  • src/gpu/ganesh/image/SkImage_Ganesh.cpp
  • src/gpu/ganesh/GrDrawingManager.h
  • tests/DefaultPathRendererTest.cpp
  • src/gpu/ganesh/image/SkImage_GaneshYUVA.cpp
  • src/gpu/ganesh/GrDirectContextPriv.cpp
  • tests/SurfaceSemaphoreTest.cpp
  • src/gpu/ganesh/Device.cpp

Audit Directions

  • Status enums that conflate concerns
    Audit APIs whose single enum return value encodes two orthogonal outcomes (here, semaphore submission vs. flush success), since callers routinely treat the benign value as success and swallow real failures.
  • Guard clauses that skip completion callbacks
    Review early-return patterns (if (!ptr) return;) that bail before invoking a required completion callback, which leaves the caller’s result buffer or promise uninitialized.
  • Async readback and flush paths
    Examine other async...ReadPixels and flush/submit code paths across Ganesh (e.g. SurfaceContext, GrDrawingManager, image/surface factories) for failure branches that do not propagate failure or fulfil their callback contract.
From 4b2fad13ffe43c3099bcf6d9c432bed53a4cfadc Mon Sep 17 00:00:00 2001
From: Robert Phillips <robertphillips@google.com>
Date: Tue, 08 Sep 2026 11:51:48 -0400
Subject: [PATCH] [ganesh] Add a new GrDirectContext::FlushResult and plumb flush failure up the call stack

The plumbing of flush failure up the call stack, from the DrawingManager, fused semaphore submission and flush failure. This CL separates out the two concerns and plumbs it all the way up to the API.

Bug: b/553122373
Bug: b/540021213
Change-Id: I55d2c3685fcf755e2997a46d9de78e447ccf9db3
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/1348276
Commit-Queue: Robert Phillips <robertphillips@google.com>
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
---
diff --git a/include/gpu/ganesh/GrDirectContext.h b/include/gpu/ganesh/GrDirectContext.h
index 9dc6f62..8ccf5a4 100644
--- a/include/gpu/ganesh/GrDirectContext.h
+++ b/include/gpu/ganesh/GrDirectContext.h
@@ -320,14 +320,34 @@
     bool wait(int numSemaphores, const GrBackendSemaphore* waitSemaphores,
               bool deleteSemaphoresAfterWait = true);
 
+    /*
+     * Result of a flush call. A flush can be successful w/ or w/o any semaphores being flushed.
+     * In some circumstances an unsuccessful flush can still have flushed the semaphores but
+     * the rendering results should be discarded.
+     */
+    struct FlushResult {
+        // This implicit cast is temporary and recreates the success/semaphore-submission
+        // conflation of the old API. This can be removed when all clients have been
+        // updated to the new return value.
+        operator GrSemaphoresSubmitted() const {
+            return fSuccess ? GrSemaphoresSubmitted::kYes : GrSemaphoresSubmitted::kNo;
+        }
+
+        bool fSuccess = false;                    // Did the flush succeed
+        // Were any semaphores submitted during the flush process. Will be kNo if no
+        // semaphores were specified.
+        GrSemaphoresSubmitted fSubmitted = GrSemaphoresSubmitted::kNo;
+    };
+
     /**
      * Call to ensure all drawing to the context has been flushed and submitted to the underlying 3D
      * API. This is equivalent to calling GrContext::flush with a default GrFlushInfo followed by
      * GrContext::submit(sync).
      */
-    void flushAndSubmit(GrSyncCpu sync = GrSyncCpu::kNo) {
-        this->flush(GrFlushInfo());
-        this->submit(sync);
+    FlushResult flushAndSubmit(GrSyncCpu sync = GrSyncCpu::kNo) {
+        FlushResult result = this->flush(GrFlushInfo());
+        result.fSuccess &= this->submit(sync);
+        return result;
     }
 
     /**
@@ -341,22 +361,22 @@
      *         buffer or encoder objects. However, these objects are not sent to the gpu until a
      *         submission occurs.
      *
-     * If the return is GrSemaphoresSubmitted::kYes, only initialized GrBackendSemaphores will be
-     * submitted to the gpu during the next submit call (it is possible Skia failed to create a
-     * subset of the semaphores). The client should not wait on these semaphores until after submit
-     * has been called, and must keep them alive until then. If this call returns
-     * GrSemaphoresSubmitted::kNo, the GPU backend will not submit any semaphores to be signaled on
-     * the GPU. Thus the client should not have the GPU wait on any of the semaphores passed in with
-     * the GrFlushInfo. Regardless of whether semaphores were submitted to the GPU or not, the
-     * client is still responsible for deleting any initialized semaphores.
+     * If FlushResult.fSubmitted is GrSemaphoresSubmitted::kYes, only initialized
+     * GrBackendSemaphores will be submitted to the gpu during the next submit call (it is possible
+     * Skia failed to create a subset of the semaphores). The client should not wait on these
+     * semaphores until after submit has been called, and must keep them alive until then. If
+     * fSubmitted is GrSemaphoresSubmitted::kNo, the GPU backend will not submit any semaphores
+     * to be signaled on the GPU. Thus the client should not have the GPU wait on any of the
+     * semaphores passed in with the GrFlushInfo. Regardless of whether semaphores were submitted
+     * to the GPU or not, the client is still responsible for deleting any initialized semaphores.
      * Regardless of semaphore submission the context will still be flushed. It should be
-     * emphasized that a return value of GrSemaphoresSubmitted::kNo does not mean the flush did not
-     * happen. It simply means there were no semaphores submitted to the GPU. A caller should only
-     * take this as a failure if they passed in semaphores to be submitted.
+     * emphasized that a fSubmitted value of GrSemaphoresSubmitted::kNo does not mean the flush
+     * did not happen. It simply means there were no semaphores submitted to the GPU. A caller
+     * should only take this as a failure if they passed in semaphores to be submitted.
      */
-    GrSemaphoresSubmitted flush(const GrFlushInfo& info);
+    FlushResult flush(const GrFlushInfo& info);
 
-    void flush() { this->flush(GrFlushInfo()); }
+    void flush() { (void) this->flush(GrFlushInfo()); }
 
     /** Flushes any pending uses of texture-backed images in the GPU backend. If the image is not
      *  texture-backed (including promise texture images) or if the GrDirectContext does not
@@ -367,13 +387,18 @@
      *  @param image    the non-null image to flush.
      *  @param info     flush options
      */
-    GrSemaphoresSubmitted flush(const sk_sp<const SkImage>& image, const GrFlushInfo& info);
-    void flush(const sk_sp<const SkImage>& image);
+    FlushResult flush(const sk_sp<const SkImage>& image, const GrFlushInfo& info);
+
+    void flush(const sk_sp<const SkImage>& image) { (void) this->flush(image, {}); }
 
     /** Version of flush() that uses a default GrFlushInfo. Also submits the flushed work to the
-     *   GPU.
+     *  GPU.
      */
-    void flushAndSubmit(const sk_sp<const SkImage>& image);
+    FlushResult flushAndSubmit(const sk_sp<const SkImage>& image) {
+        FlushResult result = this->flush(image, {});
+        result.fSuccess &= this->submit();
+        return result;
+    }
 
     /** Issues pending SkSurface commands to the GPU-backed API objects and resolves any SkSurface
      *  MSAA. A call to GrDirectContext::submit is always required to ensure work is actually sent
@@ -400,19 +425,20 @@
      *  The GrFlushInfo describes additional options to flush. Please see documentation at
      *  GrFlushInfo for more info.
      *
-     *  If the return is GrSemaphoresSubmitted::kYes, only initialized GrBackendSemaphores will be
-     *  submitted to the gpu during the next submit call (it is possible Skia failed to create a
-     *  subset of the semaphores). The client should not wait on these semaphores until after submit
-     *  has been called, but must keep them alive until then. If a submit flag was passed in with
-     *  the flush these valid semaphores can we waited on immediately. If this call returns
-     *  GrSemaphoresSubmitted::kNo, the GPU backend will not submit any semaphores to be signaled on
-     *  the GPU. Thus the client should not have the GPU wait on any of the semaphores passed in
-     *  with the GrFlushInfo. Regardless of whether semaphores were submitted to the GPU or not, the
-     *  client is still responsible for deleting any initialized semaphores.
-     *  Regardless of semaphore submission the context will still be flushed. It should be
-     *  emphasized that a return value of GrSemaphoresSubmitted::kNo does not mean the flush did not
-     *  happen. It simply means there were no semaphores submitted to the GPU. A caller should only
-     *  take this as a failure if they passed in semaphores to be submitted.
+     *  If FlushResult.fSubmitted is GrSemaphoresSubmitted::kYes, only initialized
+     *  GrBackendSemaphores will be submitted to the gpu during the next submit call (it is
+     *  possible Skia failed to create a subset of the semaphores). The client should not wait on
+     *  these semaphores until after submit has been called, but must keep them alive until then.
+     *  If a submit flag was passed in with the flush these valid semaphores can we waited on
+     *  immediately. If this fSubmitted is GrSemaphoresSubmitted::kNo, the GPU backend will not
+     *  submit any semaphores to be signaled on the GPU. Thus the client should not have the GPU
+     *  wait on any of the semaphores passed in with the GrFlushInfo. Regardless of whether
+     *  semaphores were submitted to the GPU or not, the client is still responsible for deleting
+     *  any initialized semaphores. Regardless of semaphore submission the context will still be
+     *  flushed. It should be emphasized that a fSubmitted value of GrSemaphoresSubmitted::kNo
+     *  does not mean the flush did not happen. It simply means there were no semaphores submitted
+     *  to the GPU. A caller should only take this as a failure if they passed in semaphores to
+     *  be submitted.
      *
      *  Pending surface commands are flushed regardless of the return result.
      *
@@ -420,9 +446,9 @@
      *  @param access  type of access the call will do on the backend object after flush
      *  @param info    flush options
      */
-    GrSemaphoresSubmitted flush(SkSurface* surface,
-                                SkSurfaces::BackendSurfaceAccess access,
-                                const GrFlushInfo& info);
+    FlushResult flush(SkSurface* surface,
+                      SkSurfaces::BackendSurfaceAccess access,
+                      const GrFlushInfo& info);
 
     /**
      *  Same as above except:
@@ -443,9 +469,16 @@
      *  @param info     flush options
      *  @param newState optional state change request after flush
      */
-    GrSemaphoresSubmitted flush(SkSurface* surface,
-                                const GrFlushInfo& info,
-                                const skgpu::MutableTextureState* newState = nullptr);
+    FlushResult flush(SkSurface* surface,
+                      const GrFlushInfo& info,
+                      const skgpu::MutableTextureState* newState = nullptr);
+
+    /**
+     * Flushes the given surface with the default GrFlushInfo.
+     *
+     *  Has no effect on a CPU-backed surface.
+     */
+    void flush(SkSurface* surface) { (void) this->flush(surface, GrFlushInfo(), nullptr); }
 
     /** Call to ensure all reads/writes of the surface have been issued to the underlying 3D API.
      *  Skia will correctly order its own draws and pixel operations. This must to be used to ensure
@@ -455,14 +488,7 @@
      *
      *  Has no effect on a CPU-backed surface.
      */
-    void flushAndSubmit(SkSurface* surface, GrSyncCpu sync = GrSyncCpu::kNo);
-
-    /**
-     * Flushes the given surface with the default GrFlushInfo.
-     *
-     *  Has no effect on a CPU-backed surface.
-     */
-    void flush(SkSurface* surface);
+    FlushResult flushAndSubmit(SkSurface* surface, GrSyncCpu sync = GrSyncCpu::kNo);
 
     /**
      * Submit outstanding work to the gpu from all previously un-submitted flushes. The return
diff --git a/include/gpu/ganesh/SkSurfaceGanesh.h b/include/gpu/ganesh/SkSurfaceGanesh.h
index 3021bca..5ec91ea 100644
--- a/include/gpu/ganesh/SkSurfaceGanesh.h
+++ b/include/gpu/ganesh/SkSurfaceGanesh.h
@@ -11,6 +11,7 @@
 #include "include/core/SkImageInfo.h"
 #include "include/core/SkRefCnt.h"
 #include "include/core/SkSurface.h"
+#include "include/gpu/ganesh/GrDirectContext.h"
 #include "include/gpu/ganesh/GrTypes.h"
 #include "include/private/SkAPI.h"
 
@@ -209,10 +210,12 @@
 // Clients should strive to call GrDirectContext::flush directly. However, there exist some
 // places where the GrDirectContext is hard to find, these helpers allow for the flushing of the
 // provided surface. This is a no-op if the surface is nullptr or not GPU backed.
-SK_API GrSemaphoresSubmitted Flush(sk_sp<SkSurface>);
-SK_API GrSemaphoresSubmitted Flush(SkSurface*);
-SK_API void FlushAndSubmit(sk_sp<SkSurface>);
-SK_API void FlushAndSubmit(SkSurface*);
+SK_API GrDirectContext::FlushResult Flush(sk_sp<SkSurface>);
+SK_API GrDirectContext::FlushResult Flush(SkSurface*);
+
+SK_API GrDirectContext::FlushResult FlushAndSubmit(sk_sp<SkSurface>);
+SK_API GrDirectContext::FlushResult FlushAndSubmit(SkSurface*);
+
 }  // namespace skgpu::ganesh
 
 #endif
diff --git a/include/private/chromium/GrVkSecondaryCBDrawContext.h b/include/private/chromium/GrVkSecondaryCBDrawContext.h
index 80bed15..ede4db8 100644
--- a/include/private/chromium/GrVkSecondaryCBDrawContext.h
+++ b/include/private/chromium/GrVkSecondaryCBDrawContext.h
@@ -11,6 +11,7 @@
 #include "include/core/SkRefCnt.h"
 #include "include/core/SkSurfaceProps.h"
 #include "include/core/SkTypes.h"
+#include "include/gpu/ganesh/GrDirectContext.h"
 
 #include <memory>
 
@@ -72,7 +73,7 @@
 
     // Records all the draws to the imported secondary command buffer and sets any dependent
     // offscreen draws to the GPU.
-    void flush();
+    GrDirectContext::FlushResult flush();
 
     /** Inserts a list of GPU semaphores that Skia will have the driver wait on before executing
         commands for this secondary CB. The wait semaphores will get added to the VkCommandBuffer
diff --git a/relnotes/flush-error.md b/relnotes/flush-error.md
new file mode 100644
index 0000000..2f436c3
--- /dev/null
+++ b/relnotes/flush-error.md
@@ -0,0 +1 @@
+The error handling associated with Ganesh's GrDirectContext flushing has been updated. Instead of returning a GrSemaphoresSubmitted value which conflates semaphore submission and flush success, a new FlushResult struct is returned - which explicitly separates the two concerns. FlushResult has an implicit cast which allows clients to still use the old GrSemaphoresSubmitted-based API.
diff --git a/src/gpu/ganesh/Device.cpp b/src/gpu/ganesh/Device.cpp
index f30d578..d61e230 100644
--- a/src/gpu/ganesh/Device.cpp
+++ b/src/gpu/ganesh/Device.cpp
@@ -1274,15 +1274,16 @@
                                        RescaleGamma rescaleGamma,
                                        RescaleMode rescaleMode,
                                        ReadPixelsCallback callback,
-                                       ReadPixelsContext context) {
+                                       ReadPixelsContext callbackContext) {
     auto* sdc = fSurfaceDrawContext.get();
     // Context TODO: Elevate direct context requirement to public API.
     auto dContext = sdc->recordingContext()->asDirectContext();
     if (!dContext) {
+        callback(callbackContext, nullptr);
         return;
     }
     sdc->asyncRescaleAndReadPixels(dContext, info, srcRect, rescaleGamma, rescaleMode, callback,
-                                   context);
+                                   callbackContext);
 }
 
 void Device::asyncRescaleAndReadPixelsYUV420(SkYUVColorSpace yuvColorSpace,
@@ -1293,11 +1294,12 @@
                                              RescaleGamma rescaleGamma,
                                              RescaleMode rescaleMode,
                                              ReadPixelsCallback callback,
-                                             ReadPixelsContext context) {
+                                             ReadPixelsContext callbackContext) {
     auto* sdc = fSurfaceDrawContext.get();
     // Context TODO: Elevate direct context requirement to public API.
     auto dContext = sdc->recordingContext()->asDirectContext();
     if (!dContext) {
+        callback(callbackContext, nullptr);
         return;
     }
     sdc->asyncRescaleAndReadPixelsYUV420(dContext,
@@ -1309,7 +1311,7 @@
                                          rescaleGamma,
                                          rescaleMode,
                                          callback,
-                                         context);
+                                         callbackContext);
 }
 
 ///////////////////////////////////////////////////////////////////////////////
diff --git a/src/gpu/ganesh/GrDirectContext.cpp b/src/gpu/ganesh/GrDirectContext.cpp
index 7f02376..bac84f3 100644
--- a/src/gpu/ganesh/GrDirectContext.cpp
+++ b/src/gpu/ganesh/GrDirectContext.cpp
@@ -223,7 +223,8 @@
         return;
     }
 
-    this->flushAndSubmit();
+    // TODO: bubble this error up to the client
+    (void) this->flushAndSubmit();
 #if !defined(SK_ENABLE_OPTIMIZE_SIZE)
     if (fSmallPathAtlasMgr) {
         fSmallPathAtlasMgr->reset();
@@ -432,7 +433,7 @@
     return this->caps()->supportedGpuStats();
 }
 
-GrSemaphoresSubmitted GrDirectContext::flush(const GrFlushInfo& info) {
+GrDirectContext::FlushResult GrDirectContext::flush(const GrFlushInfo& info) {
     ASSERT_SINGLE_OWNER
     if (this->abandoned()) {
         if (info.fFinishedProc) {
@@ -441,7 +442,7 @@
         if (info.fSubmittedProc) {
             info.fSubmittedProc(info.fSubmittedContext, false);
         }
-        return GrSemaphoresSubmitted::kNo;
+        return {false, GrSemaphoresSubmitted::kNo};
     }
 
     return this->drawingManager()->flushSurfaces(
@@ -463,37 +464,28 @@
     return result;
 }
 
-GrSemaphoresSubmitted GrDirectContext::flush(const sk_sp<const SkImage>& image,
-                                             const GrFlushInfo& flushInfo) {
+GrDirectContext::FlushResult GrDirectContext::flush(const sk_sp<const SkImage>& image,
+                                                    const GrFlushInfo& flushInfo) {
     if (!image) {
-        return GrSemaphoresSubmitted::kNo;
+        return {false, GrSemaphoresSubmitted::kNo};
     }
     auto ib = as_IB(image);
     if (!ib->isGaneshBacked()) {
-        return GrSemaphoresSubmitted::kNo;
+        return {false, GrSemaphoresSubmitted::kNo};
     }
     auto igb = static_cast<const SkImage_GaneshBase*>(image.get());
     return igb->flush(this, flushInfo);
 }
 
-void GrDirectContext::flush(const sk_sp<const SkImage>& image) {
-    this->flush(image, {});
-}
-
-void GrDirectContext::flushAndSubmit(const sk_sp<const SkImage>& image) {
-    this->flush(image, {});
-    this->submit();
-}
-
-GrSemaphoresSubmitted GrDirectContext::flush(SkSurface* surface,
-                                             SkSurfaces::BackendSurfaceAccess access,
-                                             const GrFlushInfo& info) {
+GrDirectContext::FlushResult GrDirectContext::flush(SkSurface* surface,
+                                                    SkSurfaces::BackendSurfaceAccess access,
+                                                    const GrFlushInfo& info) {
     if (!surface) {
-        return GrSemaphoresSubmitted::kNo;
+        return {false, GrSemaphoresSubmitted::kNo};
     }
     auto sb = asSB(surface);
     if (!sb->isGaneshBacked()) {
-        return GrSemaphoresSubmitted::kNo;
+        return {false, GrSemaphoresSubmitted::kNo};
     }
 
     auto gs = static_cast<SkSurface_Ganesh*>(surface);
@@ -503,15 +495,15 @@
     return this->priv().flushSurface(rtp, access, info, nullptr);
 }
 
-GrSemaphoresSubmitted GrDirectContext::flush(SkSurface* surface,
-                                             const GrFlushInfo& info,
-                                             const skgpu::MutableTextureState* newState) {
+GrDirectContext::FlushResult GrDirectContext::flush(SkSurface* surface,
+                                                    const GrFlushInfo& info,
+                                                    const skgpu::MutableTextureState* newState) {
     if (!surface) {
-        return GrSemaphoresSubmitted::kNo;
+        return {false, GrSemaphoresSubmitted::kNo};
     }
     auto sb = asSB(surface);
     if (!sb->isGaneshBacked()) {
-        return GrSemaphoresSubmitted::kNo;
+        return {false, GrSemaphoresSubmitted::kNo};
     }
 
... (file diff truncated)
Loading diff…

Original Bug Report

The reporter's bug is still restricted on the tracker. Chrome de-restricts security bugs ~30–90 days after the fix ships; a later run will backfill it here.