Critical watch CVSS 8.8 chrome UAF 📄 Reporter bug report 🔧 Commit mapped

Overview

Critical
Severity
8.8
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in Downloads
DescriptionUse after free in Downloads
ComponentDownloads
Sub-componentiOS
Bug ClassUAF
Action tierwatch — corruption primitive but gated (uncommon) — not default-reachable
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.
Tracker504185107
Fix commit31a488274dd4 (chromium/src) +52/-80
CWECWE-416 (Use-after-free)
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:H/I:H/A:H
CISA KEVNot listed
CreditedGoogle
Reported2026-04-19 (per vendor advisory)
Fix landed2026-04-20 (public git)
Disclosed2026-05-12
Patch gap22 days — fix public in source before the advisory

Background

`DownloadManagerCoordinator`
the iOS coordinator that owns the download UI and tracks the single active web::DownloadTask via its downloadTask property.
`web::DownloadTask`
the model object for an in-flight download, owned elsewhere (the tab helper / web layer), not by the coordinator.
`base::WeakPtr`
a non-owning smart pointer that nulls out automatically when its target is destroyed, letting the holder detect a freed task instead of dereferencing a dangling pointer.
`DownloadManagerTabHelper` delegate callbacks
the downloadManagerTabHelper:did...Download: methods through which the tab helper informs the coordinator of task state changes, including when a task is being replaced or cleaned up.

Root Cause Analysis

DownloadManagerCoordinator.downloadTask was a raw web::DownloadTask* property. The coordinator supports only one task at a time, so when a task was replaced the coordinator kept pointing at — and could later dereference — the previous task after it had been destroyed, a use-after-free; the per-commit message states the coordinator “used to rely on a dangling pointer to the previous download task when it was being replaced by a new task.”

The fix changes the property to base::WeakPtr<web::DownloadTask>, so the download task’s own weak-pointer factory is responsible for nulling the reference when the task is freed, and the tab helper is refactored to always notify its delegate when a task is cleaned up. Every raw-pointer use is updated to .get() at the point of use, and new sinks that dereference the pointer (-decidePolicyForDownload-style path and -tryDownload) gain a CHECK(_downloadTask) so a null/freed task is caught before dereference rather than read as garbage. The numerous DCHECK/DCHECK_EQ assertions become CHECK(..., base::NotFatalUntil::M150) so the invariants are enforced in release through M150. The regression test (DownloadManagerCoordinatorTest) is updated throughout to assign task->GetWeakPtr() to the property, encoding the weak-ownership contract.

Key insight
The core mistake was holding the single active download task as an owning-looking raw pointer that dangled when the task was replaced or destroyed; the fix makes the reference a base::WeakPtr (plus explicit non-null CHECKs before dereference) so a freed task is observed as null rather than used after free.

Attack Path

  1. Start a download Trigger a download so DownloadManagerCoordinator.downloadTask is set to the active web::DownloadTask and the download UI is presented.
  2. Replace or tear down the task Cause the active task to be replaced by a new one (or otherwise cleaned up) so the underlying web::DownloadTask the coordinator points at is destroyed, while the raw pointer property still holds its address.
  3. Re-enter a coordinator method Reach a path that dereferences _downloadTask (e.g., _downloadTask->GetWebState() in the overlay/decision path or tryDownload) after the task has been freed.
  4. Use-after-free The stale raw pointer is dereferenced, reading or using freed web::DownloadTask memory; any further primitive beyond the UAF read is (inferred) and not shown by the diff.

Impact Assessment

An attacker who can drive download start/replace/cleanup timing can cause the coordinator to dereference a freed web::DownloadTask, a use-after-free in the browser (app) process on iOS; consequences beyond the freed-memory access (controlled reuse, further corruption) are (inferred). The vendor advisory rates this critical and classifies it as a use-after-free in Downloads; it is iOS-specific and requires exercising the download-manager UI so that the tracked task is replaced or destroyed while the coordinator still references it. The diff demonstrates the dangling-pointer ownership defect and the added guards, not any escalation to code execution.

Changed Functions

FunctionChangeNotes
if
ios/chrome/browser/download/coordinator/download_manager_coordinator.mm
modified

Files Changed

  • ios/chrome/browser/download/coordinator/download_manager_coordinator.h
  • ios/chrome/browser/download/coordinator/download_manager_coordinator.mm

Audit Directions

  • Raw pointers to externally-owned model objects
    Audit iOS coordinators/mediators holding web::DownloadTask*, WebState*, etc. as owning-looking raw properties; such single-slot “current item” pointers dangle when the item is replaced.
  • Replace-without-notify
    Review tab-helper/delegate relationships where an object can be swapped out without the holder being told the old one is being cleaned up; ensure a cleanup notification always fires.
  • Dereference without liveness check
    Flag ptr->Method() on cached model pointers that lack a preceding null/WeakPtr check, especially in callbacks that can run after teardown.
From 31a488274dd4b1f524f3dd1de814cc669b1164a4 Mon Sep 17 00:00:00 2001
From: Quentin Pubert <qpubert@google.com>
Date: Mon, 20 Apr 2026 08:48:22 -0700
Subject: [PATCH] [iOS] Replace DownloadTask* with WeakPtr in DownloadManagerCoordinator

This CL addresses a potential use-after-free by replacing the property

```
@property(nonatomic) web::DownloadTask* downloadTask;
```

in DownloadManagerCoordinator with instead the following

```
@property(nonatomic, assign)
base::WeakPtr<web::DownloadTask> downloadTask;
```

so that the weak pointer factory of DownloadTask will now be responsible
for cleaning up `downloadTask`. Since the coordinator used to rely on a
dangling pointer to the previous download task when it was being
replaced by a new task, the tab helper is also refactored so it always
informs its delegate (the coordinator) when the task is cleaned up.
`ScheduleTaskDestruction` is removed since cleanup only needs to be
postponed when it is done from the download task observer method.

Bug: 504185107
Change-Id: If35e2ec631e34cfedb52cf025f5a30548f3dcb7e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7776871
Reviewed-by: Ewann Pellé <ewannpv@chromium.org>
Commit-Queue: Quentin Pubert <qpubert@google.com>
Auto-Submit: Quentin Pubert <qpubert@google.com>
Cr-Commit-Position: refs/heads/main@{#1617516}
---

diff --git a/ios/chrome/browser/download/coordinator/download_manager_coordinator.h b/ios/chrome/browser/download/coordinator/download_manager_coordinator.h
index 97193f3..4930be61 100644
--- a/ios/chrome/browser/download/coordinator/download_manager_coordinator.h
+++ b/ios/chrome/browser/download/coordinator/download_manager_coordinator.h
@@ -5,6 +5,7 @@
 #ifndef IOS_CHROME_BROWSER_DOWNLOAD_COORDINATOR_DOWNLOAD_MANAGER_COORDINATOR_H_
 #define IOS_CHROME_BROWSER_DOWNLOAD_COORDINATOR_DOWNLOAD_MANAGER_COORDINATOR_H_
 
+#import "base/memory/weak_ptr.h"
 #import "ios/chrome/browser/download/model/download_manager_tab_helper_delegate.h"
 #import "ios/chrome/browser/shared/coordinator/chrome_coordinator/chrome_coordinator.h"
 
@@ -26,7 +27,7 @@
 
 // Download Manager supports only one download task at a time. Set to null when
 // stop method is called.
-@property(nonatomic) web::DownloadTask* downloadTask;
+@property(nonatomic, assign) base::WeakPtr<web::DownloadTask> downloadTask;
 
 // Underlying UIViewController presented by this coordinator.
 @property(nonatomic, readonly) UIViewController* viewController;
diff --git a/ios/chrome/browser/download/coordinator/download_manager_coordinator.mm b/ios/chrome/browser/download/coordinator/download_manager_coordinator.mm
index f465478..efd25076 100644
--- a/ios/chrome/browser/download/coordinator/download_manager_coordinator.mm
+++ b/ios/chrome/browser/download/coordinator/download_manager_coordinator.mm
@@ -19,6 +19,7 @@
 #import "base/metrics/histogram_functions.h"
 #import "base/metrics/user_metrics.h"
 #import "base/metrics/user_metrics_action.h"
+#import "base/not_fatal_until.h"
 #import "base/strings/stringprintf.h"
 #import "base/strings/sys_string_conversions.h"
 #import "base/strings/utf_string_conversions.h"
@@ -106,7 +107,7 @@
 @implementation DownloadManagerCoordinator
 
 - (void)dealloc {
-  DCHECK(_stopped);
+  CHECK(_stopped, base::NotFatalUntil::M150);
 }
 
 - (void)start {
@@ -115,8 +116,8 @@
 
 // Similar to start but can be called after pause.
 - (void)restart {
-  DCHECK(self.presenter);
-  DCHECK(self.browser);
+  CHECK(self.presenter, base::NotFatalUntil::M150);
+  CHECK(self.browser, base::NotFatalUntil::M150);
   if (IsGeminiCopresenceEnabled()) {
     _geminiHandler =
         HandlerForProtocol(self.browser->GetCommandDispatcher(), BWGCommands);
@@ -159,7 +160,7 @@
         DownloadRecordServiceFactory::GetForProfile(profile));
   }
 
-  _mediator.SetDownloadTask(_downloadTask);
+  _mediator.SetDownloadTask(_downloadTask.get());
   _mediator.SetConsumer(_viewController);
   if (base::FeatureList::IsEnabled(kIOSDownloadNoUIUpdateInBackground)) {
     _mediator.StartObservingNotifications();
@@ -236,7 +237,7 @@
   }
 
   BOOL replacingExistingDownload = _downloadTask ? YES : NO;
-  _downloadTask = download;
+  _downloadTask = download->GetWeakPtr();
 
   if (web::GetWebClient()->EnableFullscreenAPI()) {
     // Exit fullscreen since download UI will be behind fullscreen mode.
@@ -245,7 +246,7 @@
   }
 
   if (replacingExistingDownload) {
-    _mediator.SetDownloadTask(_downloadTask);
+    _mediator.SetDownloadTask(_downloadTask.get());
   } else {
     self.animatesPresentation = YES;
     [self restart];
@@ -285,7 +286,7 @@
 - (void)downloadManagerTabHelper:(DownloadManagerTabHelper*)tabHelper
                  didHideDownload:(web::DownloadTask*)download
                         animated:(BOOL)animated {
-  DCHECK_EQ(_downloadTask, download);
+  CHECK_EQ(_downloadTask.get(), download, base::NotFatalUntil::M150);
   self.animatesPresentation = animated;
   [self stop];
   self.animatesPresentation = YES;
@@ -294,8 +295,8 @@
 - (void)downloadManagerTabHelper:(DownloadManagerTabHelper*)tabHelper
                  didShowDownload:(web::DownloadTask*)download
                         animated:(BOOL)animated {
-  DCHECK_NE(_downloadTask, download);
-  _downloadTask = download;
+  CHECK_NE(_downloadTask.get(), download, base::NotFatalUntil::M150);
+  _downloadTask = download->GetWeakPtr();
   self.animatesPresentation = animated;
   [self start];
   self.animatesPresentation = YES;
@@ -309,7 +310,7 @@
     // observer is called.
     return;
   }
-  DCHECK_EQ(_downloadTask, download);
+  CHECK_EQ(_downloadTask.get(), download, base::NotFatalUntil::M150);
   self.animatesPresentation = NO;
   [self pause];
   self.animatesPresentation = YES;
@@ -329,7 +330,7 @@
 
 - (void)downloadManagerTabHelper:(DownloadManagerTabHelper*)tabHelper
             wantsToStartDownload:(web::DownloadTask*)download {
-  DCHECK_EQ(_downloadTask, download);
+  CHECK_EQ(_downloadTask.get(), download, base::NotFatalUntil::M150);
   [self tryDownload];
 }
 
@@ -344,11 +345,11 @@
 }
 
 - (void)containedPresenterDidPresent:(id<ContainedPresenter>)presenter {
-  DCHECK(presenter == self.presenter);
+  CHECK_EQ(presenter, self.presenter, base::NotFatalUntil::M150);
 }
 
 - (void)containedPresenterDidDismiss:(id<ContainedPresenter>)presenter {
-  DCHECK(presenter == self.presenter);
+  CHECK_EQ(presenter, self.presenter, base::NotFatalUntil::M150);
   // The view controller may not be dealloced immediately.
   presenter.presentedViewController = nil;
   if (_restartPending) {
@@ -394,7 +395,8 @@
         }
       }));
 
-  web::WebState* webState = self.downloadTask->GetWebState();
+  CHECK(_downloadTask);
+  web::WebState* webState = _downloadTask->GetWebState();
   OverlayRequestQueue::FromWebState(webState, OverlayModality::kWebContentArea)
       ->AddRequest(std::move(request));
 }
@@ -413,7 +415,7 @@
   }
   id<SaveToDriveCommands> saveToDriveHandler = HandlerForProtocol(
       self.browser->GetCommandDispatcher(), SaveToDriveCommands);
-  [saveToDriveHandler showSaveToDriveForDownload:_downloadTask];
+  [saveToDriveHandler showSaveToDriveForDownload:_downloadTask.get()];
 }
 
 - (void)downloadManagerViewControllerDidRetry:(UIViewController*)controller {
@@ -519,6 +521,7 @@
 // Attempts to start the current download task, either for the first time or
 // after one or several previously failed attempts.
 - (void)tryDownload {
+  CHECK(_downloadTask);
   DownloadManagerTabHelper* tabHelper =
       DownloadManagerTabHelper::FromWebState(_downloadTask->GetWebState());
   if (_downloadTask->GetErrorCode() != net::OK) {
@@ -528,7 +531,7 @@
         base::UserMetricsAction("IOSDownloadStartDownloadToDrive"));
   } else {
     base::RecordAction(base::UserMetricsAction("IOSDownloadStartDownload"));
-    _unopenedDownloads.Add(_downloadTask);
+    _unopenedDownloads.Add(_downloadTask.get());
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/ios/chrome/browser/download/coordinator/download_manager_coordinator_unittest.mm b/ios/chrome/browser/download/coordinator/download_manager_coordinator_unittest.mm
index ee7094c..60dc2356 100644
--- a/ios/chrome/browser/download/coordinator/download_manager_coordinator_unittest.mm
+++ b/ios/chrome/browser/download/coordinator/download_manager_coordinator_unittest.mm
@@ -152,7 +152,7 @@
 // DownloadManagerViewController is propertly configured and presented.
 TEST_F(DownloadManagerCoordinatorTest, Start) {
   auto task = CreateTestTask();
-  coordinator_.downloadTask = task.get();
+  coordinator_.downloadTask = task->GetWeakPtr();
   [coordinator_ start];
 
   // By default coordinator presents without animation.
@@ -184,7 +184,7 @@
 // stale raw pointer).
 TEST_F(DownloadManagerCoordinatorTest, Stop) {
   auto task = CreateTestTask();
-  coordinator_.downloadTask = task.get();
+  coordinator_.downloadTask = task->GetWeakPtr();
   [coordinator_ start];
   @autoreleasepool {
     // Calling -stop will retain and autorelease coordinator_. task_environment_
@@ -204,7 +204,7 @@
   auto task = CreateTestTask();
   web::FakeDownloadTask* task_ptr = task.get();
   tab_helper()->SetCurrentDownload(std::move(task));
-  coordinator_.downloadTask = task_ptr;
+  coordinator_.downloadTask = task_ptr->GetWeakPtr();
   [coordinator_ start];
 
   EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
@@ -250,7 +250,7 @@
                        webStateIsVisible:YES];
 
   // Verify that coordinator's properties are set up.
-  EXPECT_EQ(task.get(), coordinator_.downloadTask);
+  EXPECT_EQ(task.get(), coordinator_.downloadTask.get());
   EXPECT_TRUE(coordinator_.animatesPresentation);
 
   // First presentation of Download Manager UI should be animated.
@@ -402,7 +402,7 @@
 // cancelled.
 TEST_F(DownloadManagerCoordinatorTest, Close) {
   auto task = CreateTestTask();
-  coordinator_.downloadTask = task.get();
+  coordinator_.downloadTask = task->GetWeakPtr();
   [coordinator_ start];
 
   EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
@@ -439,7 +439,7 @@
   auto task = CreateTestTask();
   web::FakeDownloadTask* task_ptr = task.get();
   tab_helper()->SetCurrentDownload(std::move(task));
-  coordinator_.downloadTask = task_ptr;
+  coordinator_.downloadTask = task_ptr->GetWeakPtr();
   [coordinator_ start];
 
   EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
@@ -526,7 +526,7 @@
   auto task = CreateTestTask();
   web::FakeDownloadTask* task_ptr = task.get();
   tab_helper()->SetCurrentDownload(std::move(task));
-  coordinator_.downloadTask = task_ptr;
+  coordinator_.downloadTask = task_ptr->GetWeakPtr();
   [coordinator_ start];
 
   EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
@@ -569,7 +569,7 @@
   auto task = CreateTestTask();
   web::DownloadTask* task_ptr = task.get();
   tab_helper()->SetCurrentDownload(std::move(task));
-  coordinator_.downloadTask = task_ptr;
+  coordinator_.downloadTask = task_ptr->GetWeakPtr();
   auto web_state = std::make_unique<web::FakeWebState>();
   browser_->GetWebStateList()->InsertWebState(std::move(web_state));
   [coordinator_ start];
@@ -621,7 +621,7 @@
 TEST_F(DownloadManagerCoordinatorTest, CloseInProgressDownload) {
   auto task = CreateTestTask();
   task->Start(base::FilePath());
-  coordinator_.downloadTask = task.get();
+  coordinator_.downloadTask = task->GetWeakPtr();
   [coordinator_ start];
 
   EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
@@ -679,7 +679,7 @@
 // Coordinator should present the confirmation dialog.
 TEST_F(DownloadManagerCoordinatorTest, DecidePolicyForDownload) {
   auto task = CreateTestTask();
-  coordinator_.downloadTask = task.get();
+  coordinator_.downloadTask = task->GetWeakPtr();
 
   OverlayRequestQueue* queue = OverlayRequestQueue::FromWebState(
       web_state_.get(), OverlayModality::kWebContentArea);
@@ -766,7 +766,7 @@
   auto task = CreateTestTask();
   web::FakeDownloadTask* task_ptr = task.get();
   tab_helper()->SetCurrentDownload(std::move(task));
-  coordinator_.downloadTask = task_ptr;
+  coordinator_.downloadTask = task_ptr->GetWeakPtr();
   [coordinator_ start];
 
   DownloadManagerViewController* viewController =
@@ -806,7 +806,7 @@
   auto task = CreateTestTask();
   web::FakeDownloadTask* task_ptr = task.get();
   tab_helper()->SetCurrentDownload(std::move(task));
-  coordinator_.downloadTask = task_ptr;
+  coordinator_.downloadTask = task_ptr->GetWeakPtr();
   [coordinator_ start];
 
   // First download is a failure.
@@ -862,7 +862,7 @@
   auto task = CreateTestTask();
   web::FakeDownloadTask* task_ptr = task.get();
   tab_helper()->SetCurrentDownload(std::move(task));
-  coordinator_.downloadTask = task_ptr;
+  coordinator_.downloadTask = task_ptr->GetWeakPtr();
   [coordinator_ start];
 
   // Start and immediately fail the download.
@@ -899,7 +899,7 @@
   auto task = CreateTestTask();
   web::FakeDownloadTask* task_ptr = task.get();
   tab_helper()->SetCurrentDownload(std::move(task));
-  coordinator_.downloadTask = task_ptr;
+  coordinator_.downloadTask = task_ptr->GetWeakPtr();
   [coordinator_ start];
 
   EXPECT_EQ(1U, base_view_controller_.childViewControllers.count);
@@ -936,7 +936,7 @@
 // started and nil when stopped.
 TEST_F(DownloadManagerCoordinatorTest, ViewController) {
   auto task = CreateTestTask();
-  coordinator_.downloadTask = task.get();
+  coordinator_.downloadTask = task->GetWeakPtr();
   ASSERT_FALSE(coordinator_.viewController);
   [coordinator_ start];
Loading diff…

Original Bug Report

reported by vm...@google.com

Potential Use-After-Free of web::DownloadTask in iOS DownloadManagerCoordinator

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. Please see go/chrome-ai-generated-security-bugs-faq for more information.

Overview: A potential Use-After-Free vulnerability exists in iOS Chrome’s Download Manager due to a race condition during the dismissal animation of the download UI. A sequence of downloading, closing the UI, downloading again, and closing the tab can result in a dangling raw pointer that bypasses MiraclePtr and is accessed virtually.

Affected files:

  • ios/chrome/browser/download/coordinator/download_manager_coordinator.mm
  • ios/chrome/browser/download/model/download_manager_tab_helper.mm
  • ios/chrome/browser/download/coordinator/download_manager_mediator.mm
  • ios/chrome/browser/presenters/ui_bundled/vertical_animation_container.mm

Estimated timestamp from git blame: 2025-04-22

Technical Details

A potential Use-After-Free (UAF) vulnerability exists in DownloadManagerCoordinator on iOS. The coordinator manages the lifecycle of the download UI and holds a bare Objective-C pointer to the active web::DownloadTask in the _downloadTask ivar. This pointer is not protected by MiraclePtr (base::raw_ptr).

The vulnerability is triggered by a race condition during the dismissal of the download manager UI:

  1. UI Dismissal Initiated: When a user taps ‘Close’ on the download bar, DownloadManagerCoordinator calls -pause. This sets _stopped = YES, clears _downloadTask, and initiates a 200ms UIView animation in VerticalAnimationContainer to dismiss the UI.

  2. New Download Arrival: If a new download (D2) arrives while the dismissal animation is still running, didCreateDownload: is called. It updates the raw _downloadTask ivar to point to D2 and calls -restart. Because _stopped is YES and the presenter is still displaying the view controller (as the animation is not finished), -restart sets _restartPending = YES and returns early. Crucially, it does not call _mediator.SetDownloadTask(D2). The mediator (which uses raw_ptr<web::DownloadTask>) never receives a pointer to D2.

  3. Tab Closure: If the tab is closed (e.g., via window.close()) during this same animation window, the WebState detachment process eventually calls DownloadManagerCoordinator::pause. However, because _stopped is already YES, pause returns early (ios/chrome/browser/download/coordinator/download_manager_coordinator.mm:186) without clearing the now-populated _downloadTask or the _restartPending flag.

  4. Task Destruction: When the WebState is destroyed, DownloadManagerTabHelper::WebStateDestroyed clears its std::unique_ptr<web::DownloadTask>, which destroys D2. Since no raw_ptr ever held a reference to D2, the object’s BackupRefPtr refcount drops to zero, and the memory is unquarantined.

  5. Use-After-Free: When the 200ms dismissal animation completes, containedPresenterDidDismiss: is called. Seeing _restartPending is YES, it calls -restart, which now proceeds to call _mediator.SetDownloadTask(_downloadTask). This method performs a virtual call (AddObserver) on the freed _downloadTask object through its vtable pointer.

Impact

This vulnerability could potentially allow for Remote Code Execution (RCE) in the browser process of iOS Chrome. An attacker who can time a tab closure within the 200ms window after a user closes the download bar can trigger this UAF. By reclaiming the freed memory with controlled data (e.g., via heap spraying during the animation window), the attacker might gain control of the vtable and redirect execution in the unsandboxed browser process.

Potential Reproduction Steps

  1. Host an attacker-controlled page (T0) that opens a popup (T1).
  2. In T1, trigger a fast download (D1) to show the download bar.
  3. Prompt the user to tap the ‘X’ button to close the download bar for D1.
  4. In T1, set up a script that triggers a second download (D2) and calls window.close() within the 200ms animation window following the ‘X’ tap.
  5. Upon completion of the animation, the browser will attempt to use the freed web::DownloadTask object, leading to a crash or code execution.

Suggested Fix

  1. Convert the _downloadTask ivar in DownloadManagerCoordinator from a bare pointer to a base::raw_ptr<web::DownloadTask> or base::WeakPtr<web::DownloadTask> to ensure it is protected by MiraclePtr or safely nullified.
  2. In DownloadManagerCoordinator::pause, do not return early if _stopped is already YES. Ensure _downloadTask and _restartPending are cleared regardless of the _stopped state to prevent dangling pointers and stale state.

Evaluated with Chrome root at commit: 7353d249d9cacf9c7218e1d7b8a39cf39c72d646


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