Critical watch ✔︎ verified CVSS 8.3 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

Critical
Severity
8.3
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in WebShare
DescriptionUse after free in WebShare
ComponentWebShare
Sub-componentWebShare
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable — factors set by independent review by AI
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.
Independent review by AIConfirmed
Reviewer notes: Non-atomic BlobDataItem refcount and COM-thread release come only from the unverified report; diff comment confirms only the FileWriterDelegate->BlobReader->BlobDataSnapshot IO-thread requirement. Attack step 3 states off-thread release as fact without (inferred).
A second, independent AI reviewer (not a human) re-derived this analysis from the fixing commit and the vendor advisory, looking for errors.
Tracker516496659
Fix commit8badafd07b12 (chromium/src) +19/-3
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-05-25 (per vendor advisory)
Fix landed2026-06-08 (public git)
Disclosed2026-06-16
Patch gap8 days — fix public in source before the advisory

Background

WebShare (Windows)
Chrome’s browser-process implementation of the Web Share API that streams shared file data to a target app via Windows Runtime (WRL) COM interfaces.
`OutputStreamWriteOperation`
a ref-counted browser-process object (chrome/browser/webshare/win/share_operation.cc) that drives writing a shared blob to a Windows IOutputStream.
`DeleteOnIOThread`
a base::RefCountedThreadSafe deletion-trait that posts the object’s destructor to the content::BrowserThread::IO sequence instead of destroying it wherever the last reference drops.
`FileWriterDelegate` / `BlobReader` / `BlobDataSnapshot`
the IO-thread-affine ownership chain held by OutputStreamWriteOperation; per the fix’s destructor comment, BlobDataSnapshot must be destroyed on the IO thread; per the reporter, it holds non-atomically ref-counted BlobDataItems.

Root Cause Analysis

Re-analyzed on 2026-10-03 against the actual fix commit 8badafd07b12. An earlier analysis was withdrawn because it had been written against a commit that was not the security fix (d04b3a27cd55).

On the IO thread, OutputStreamWriteOperation::BlobDataHandleReady builds a FileWriterDelegate and starts it with a reader created from the blob handle (share_operation.cc:303-310), establishing the ownership chain OutputStreamWriteOperation → FileWriterDelegate → BlobReader → BlobDataSnapshot → scoped_refptr<BlobDataItem>. Pre-fix, the class derived only from base::RefCountedThreadSafe<OutputStreamWriteOperation> (share_operation.cc:233-234) and used ~OutputStreamWriteOperation() = default (share_operation.cc:263), so the object was destroyed on whatever thread released its last reference. The invariant violated is that BlobDataSnapshot must be torn down on the IO thread (stated in the fix’s destructor comment; the reporter adds that it releases non-atomically ref-counted BlobDataItems); because a scoped_refptr<OutputStreamWriteOperation> is held by a Windows COM delegate that Windows can release on an arbitrary thread-pool/COM thread (per the reporter; the off-thread release itself is not shown in the material), the destructor and its synchronous teardown of the IO-thread-only members could run off-sequence.

The fix (commit 8badafd07b12649b98bb1db5cfae908a7e4c376c) changes the base class to base::RefCountedThreadSafe<OutputStreamWriteOperation, content::BrowserThread::DeleteOnIOThread> and adds the matching friend declarations, so the final release re-posts destruction to the IO thread; it also replaces the defaulted destructor with one asserting DCHECK_CURRENTLY_ON(content::BrowserThread::IO) to catch any future off-thread teardown. This restores the IO-thread-only destruction invariant for the whole object and its owned children.

Key insight
The object transitively owned IO-thread-only blob resources (a BlobDataSnapshot, per the fix’s comment) but could be destroyed on any thread; specifying the DeleteOnIOThread trait forces its destructor (and that teardown) back onto the IO thread.

Attack Path

  1. Initiate a Web Share with a file A renderer-driven Web Share operation streams a blob-backed file to a Windows share target, causing the browser to create OutputStreamWriteOperation and its IO-thread blob-reader chain (share_operation.cc:303-310).
  2. Windows holds the sole reference off-thread The scoped_refptr<OutputStreamWriteOperation> is captured by a WRL COM delegate owned by Windows, so the last reference lives outside Chrome’s IO sequence (reporter’s claim; not shown in the material, (inferred)).
  3. Off-thread destruction When the share target finishes or aborts, Windows releases the delegate on an arbitrary thread, running ~OutputStreamWriteOperation and synchronously destroying FileWriterDelegate/BlobReader/BlobDataSnapshot off the IO thread (reporter’s claim; the off-thread release is not shown in the material, (inferred)).
  4. Refcount race / UAF Per the reporter, non-atomic BlobDataItem refcount decrements race with IO-thread users, potentially freeing an item prematurely and yielding a use-after-free in the unsandboxed browser process (inferred).

Impact Assessment

The flaw is a threading/lifetime violation that, per the reporter, can corrupt non-atomically ref-counted blob objects in the unsandboxed browser process, leading to a potential use-after-free. Preconditions are a Windows platform and a Web Share file-stream operation whose COM delegate is released off the IO thread. The advisory rates it critical with no in-the-wild exploitation reported; memory-corruption-to-code-execution escalation is not demonstrated by the material (inferred).

Changed Functions

FunctionChangeNotes
OutputStreamWriteOperation
chrome/browser/webshare/win/share_operation.cc
modified

Files Changed

  • chrome/browser/webshare/win/share_operation.cc

Audit Directions

  • Thread-affine ownership under foreign callbacks
    Flag RefCountedThreadSafe objects whose references are handed to OS/COM/third-party callbacks that may release on arbitrary threads, and confirm they specify a DeleteOn*Thread trait when they own sequence-restricted members.
  • Non-atomic refcounts off-sequence
    Audit chains that reach base::RefCounted (non-atomic) members such as BlobDataItem to ensure their release always happens on the owning sequence.
  • Destructor DCHECKs
    Treat a destructor that must run on a specific thread as requiring an explicit DCHECK_CURRENTLY_ON(...) so off-sequence teardown is caught by existing tests rather than shipping silently.
diff --git a/chrome/browser/webshare/win/share_operation.cc b/chrome/browser/webshare/win/share_operation.cc
index 6769897..6d0b7548 100644
--- a/chrome/browser/webshare/win/share_operation.cc
+++ b/chrome/browser/webshare/win/share_operation.cc
@@ -231,7 +231,9 @@
 
 // Represents an ongoing operation of writing to an IOutputStream.
 class OutputStreamWriteOperation
-    : public base::RefCountedThreadSafe<OutputStreamWriteOperation> {
+    : public base::RefCountedThreadSafe<
+          OutputStreamWriteOperation,
+          content::BrowserThread::DeleteOnIOThread> {
  public:
   OutputStreamWriteOperation(
       content::BrowserContext::BlobContextGetter blob_context_getter,
@@ -258,9 +260,23 @@
   }
 
  private:
-  friend class base::RefCountedThreadSafe<OutputStreamWriteOperation>;
+  friend class base::RefCountedThreadSafe<
+      OutputStreamWriteOperation,
+      content::BrowserThread::DeleteOnIOThread>;
+  friend struct content::BrowserThread::DeleteOnThread<
+      content::BrowserThread::IO>;
+  friend class base::DeleteHelper<OutputStreamWriteOperation>;
 
-  ~OutputStreamWriteOperation() = default;
+  ~OutputStreamWriteOperation() {
+    // Though at the time of this writing the only thing this class owns that
+    // needs to be destroyed on the IO thread is the FileWriterDelegate (because
+    // it owns a BlobReader, which owns a BlobDataSnapshot, which must be
+    // destroyed on the IO thread), this class is largely IO-centric, so to
+    // protect against future changes that may modify what needs to be destroyed
+    // on the IO thread we broadly enforce the class as a whole is always
+    // destroyed there.
+    DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
+  }
 
   void WriteStreamOnIOThread() {
     DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential UAF in browser process via off-thread destruction of OutputStreamWriteOperation

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. Please see https://chromium.googlesource.com/chromium/src/+/main/docs/security/ai-generated-security-bugs-faq.md for more information.

Overview: A potential Use-After-Free (UAF) vulnerability exists in the Windows-specific Web Share implementation. OutputStreamWriteOperation transitively owns resources that must only be destroyed on the IO thread. When its final reference is released on an arbitrary thread by Windows COM delegates, off-sequence destruction of non-atomic ref-counted blob items can lead to a race condition and subsequent memory corruption in the unsandboxed browser process.

Affected files:

  • chrome/browser/webshare/win/share_operation.cc
  • storage/browser/blob/blob_data_snapshot.h
  • storage/browser/blob/blob_data_item.h

Estimated timestamp from git blame: 2020-10-28

Root Cause Analysis

In chrome/browser/webshare/win/share_operation.cc, ShareOperation::PutShareContentInDataPackage creates an OutputStreamWriteOperation for each shared file and registers an IStreamedFileDataRequestedHandler COM delegate callback:

auto operation = base::MakeRefCounted<OutputStreamWriteOperation>(...);
...
auto raw_data_requested_callback =
    Callback<IStreamedFileDataRequestedHandler>(
        [operation](IOutputStream* stream) -> HRESULT {
          operation->WriteStream(stream,
                                 base::DoNothingWithBoundArgs(operation));
          return S_OK;
        });

The scoped_refptr<OutputStreamWriteOperation> is captured by the Windows Runtime (WRL) Callback object, which is passed to Windows via IStorageFileStatics::CreateStreamedFileAsync. Once the registration loop terminates, Windows retains the sole strong reference to this COM delegate.

When Windows eventually releases the delegate (e.g., when the target application releases the streamed StorageFile), the destruction callback runs on an arbitrary Windows thread-pool or COM thread. As a result, the ~OutputStreamWriteOperation destructor executes off-sequence on a non-IO thread.

Thread-Safety Violation on Blob Resources

During its execution, OutputStreamWriteOperation populates the following members on the IO thread:

writer_delegate_ = std::make_unique<storage::FileWriterDelegate>(
    std::make_unique<DataWriterFileStreamWriter>(...),
    storage::FlushPolicy::FLUSH_ON_COMPLETION);
writer_delegate_->Start(
    blob_handle_->CreateReader(),
    base::BindRepeating(&OutputStreamWriteOperation::OnFileWritten,
                        weak_factory_.GetWeakPtr()));

This sets up a ownership and destruction chain:

  1. OutputStreamWriteOperation owns a FileWriterDelegate (storage/browser/file_system/file_writer_delegate.h).
  2. FileWriterDelegate owns a BlobReader (storage/browser/blob/blob_reader.h).
  3. BlobReader owns a BlobDataSnapshot (storage/browser/blob/blob_reader.h).
  4. BlobDataSnapshot holds a list of scoped_refptr<BlobDataItem> objects.

BlobDataSnapshot explicitly documents that it must be deleted on the IO thread. BlobDataItem uses non-atomic (non-thread-safe) reference counting:

class BlobDataItem : public base::RefCounted<BlobDataItem> { ... };

When ~OutputStreamWriteOperation executes on a non-IO thread, it triggers the synchronous destruction of FileWriterDelegate, BlobReader, and BlobDataSnapshot, performing non-atomic Release() decrements on BlobDataItem objects from an arbitrary background thread.

Potential Steps to Trigger

Because this is based on static code analysis, the following are potential steps that an attacker operating from a compromised renderer might use to trigger the race condition:

  1. The renderer initiates a Web Share operation containing a file associated with a specific Blob.
  2. Once the share target begins streaming the file data, the browser instantiates the IO-thread resources.
  3. The renderer concurrently and rapidly drives reference-count mutations on the same underlying Blob (e.g., via rapid URL.createObjectURL / URL.revokeObjectURL calls) to perform AddRef() and Release() operations on the IO thread.
  4. When the file transfer finishes or is aborted, Windows releases the COM delegate off-thread, running ~BlobDataSnapshot and decrementing the non-atomic refcount of BlobDataItem concurrently.
  5. The non-atomic race causes a lost reference increment/decrement, leading to a premature delete of a BlobDataItem while a reference is still held in the registry.
  6. A subsequent access to this item on the IO thread triggers a Use-After-Free (UAF) in the unsandboxed browser process.

Suggested Fix

To resolve this issue, ensure that OutputStreamWriteOperation and its thread-affine members are safely destroyed on the correct sequence. Because OutputStreamWriteOperation inherits from RefCountedThreadSafe, a custom deleter can be specified to force destruction on the IO thread:

class OutputStreamWriteOperation
    : public base::RefCountedThreadSafe<OutputStreamWriteOperation,
                                        content::BrowserThread::DeleteOnIOThread> {
  // ...
};

Alternatively, specify a custom task runner when releasing sequence-affine members (like writer_delegate_ and blob_handle_) or post a task to delete them on the IO thread.

Evaluated with Chrome root at commit: a2bea94528f4bd6cc57739c43fa3bb890b8367d3


Results so far have been promising, but there can be wrong deductions. Feel free to adjust as follows:

  • If you are familiar with the severity guidelines, you may adjust the severity.
  • If this is a false positive, and there’s no work to be done, please close as WAI.
  • If there is work to do here but not a vulnerability, please change the issue type to Task/Bug/FR.

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
Uploaded PoC files