Medium chrome UAF 🔧 Commit mapped

Overview

Medium
Severity
CVSS
No
Exploited ITW
Fixed
Fix Status
ImpactUse after free in TopChrome
DescriptionUse after free in TopChrome
ComponentTopChrome
Bug ClassUAF
Tracker497487462
Fix commit53245ff272a9 (chromium/src) +14/-18
CISA KEVNot listed
CreditedGoogle
Disclosed2026-05-05

Changed Functions

FunctionChangeNotes
if
chrome/browser/ui/toasts/toast_view_browsertest.cc
modified

Files Changed

  • chrome/browser/ui/toasts/toast_controller.cc
  • chrome/browser/ui/toasts/toast_view.cc
  • chrome/browser/ui/toasts/toast_view.h
  • chrome/browser/ui/toasts/toast_view_browsertest.cc
From 53245ff272a9b4118ed8129092263b23e012fdc3 Mon Sep 17 00:00:00 2001
From: Alison Gale <agale@chromium.org>
Date: Fri, 03 Apr 2026 17:47:23 -0700
Subject: [PATCH] Prevent UAF by storing ImageModel rather than raw_ptr


Bug: 497487462
Change-Id: Ieb551cfcda46428c1569c355b1645b5cfa69cc56
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/7729341
Reviewed-by: Steven Luong <stluong@chromium.org>
Commit-Queue: Alison Gale <agale@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1610045}
---

diff --git a/chrome/browser/ui/toasts/toast_controller.cc b/chrome/browser/ui/toasts/toast_controller.cc
index e8ee971a..1b41d0c8 100644
--- a/chrome/browser/ui/toasts/toast_controller.cc
+++ b/chrome/browser/ui/toasts/toast_controller.cc
@@ -302,10 +302,6 @@
     return;
   }
 
-  const ui::ImageModel* image_override = params.image_override.has_value()
-                                             ? &params.image_override.value()
-                                             : nullptr;
-
   const std::u16string body_string =
       params.body_string_override.has_value()
           ? params.body_string_override.value()
@@ -313,7 +309,7 @@
                          params.body_string_replacement_params,
                          params.body_string_cardinality_param);
   auto toast_view = std::make_unique<toasts::ToastView>(
-      anchor_view, body_string, spec->icon(), image_override,
+      anchor_view, body_string, spec->icon(), params.image_override,
       ShouldRenderToastOverWebContents(),
       base::BindRepeating(&RecordToastDismissReason, params.toast_id));
 
diff --git a/chrome/browser/ui/toasts/toast_view.cc b/chrome/browser/ui/toasts/toast_view.cc
index c43d4907..3d5ca1c4 100644
--- a/chrome/browser/ui/toasts/toast_view.cc
+++ b/chrome/browser/ui/toasts/toast_view.cc
@@ -56,9 +56,9 @@
   return transform;
 }
 
-bool IsCompatibleImageSize(const ui::ImageModel* image) {
+bool IsCompatibleImageSize(const ui::ImageModel& image) {
   const auto intended_size = toasts::ToastView::GetIconSize();
-  const auto image_size = image->Size();
+  const auto image_size = image.Size();
   return image_size.width() == intended_size &&
          image_size.height() == intended_size;
 }
@@ -106,14 +106,14 @@
     views::View* anchor_view,
     const std::u16string& toast_text,
     const gfx::VectorIcon& icon,
-    const ui::ImageModel* image_override,
+    std::optional<ui::ImageModel> image_override,
     bool render_toast_over_web_contents,
     base::RepeatingCallback<void(ToastCloseReason)> toast_close_callback)
     : BubbleDialogDelegateView(anchor_view, views::BubbleBorder::NONE),
       AnimationDelegateViews(this),
       toast_text_(toast_text),
       icon_(icon),
-      image_override_(image_override),
+      image_override_(std::move(image_override)),
       render_toast_over_web_contents_(render_toast_over_web_contents),
       toast_close_callback_(std::move(toast_close_callback)) {
   SetBackgroundColor(ui::kColorToastBackgroundProminent);
@@ -424,8 +424,9 @@
 void ToastView::OnThemeChanged() {
   BubbleDialogDelegateView::OnThemeChanged();
   const auto* color_provider = GetColorProvider();
-  if (image_override_ != nullptr && IsCompatibleImageSize(image_override_)) {
-    icon_view_->SetImage(*image_override_);
+  if (image_override_.has_value() &&
+      IsCompatibleImageSize(image_override_.value())) {
+    icon_view_->SetImage(image_override_.value());
   } else {
     icon_view_->SetImage(ui::ImageModel::FromVectorIcon(
         *icon_, color_provider->GetColor(ui::kColorToastForeground),
diff --git a/chrome/browser/ui/toasts/toast_view.h b/chrome/browser/ui/toasts/toast_view.h
index 0f5074b6..3f11d0a 100644
--- a/chrome/browser/ui/toasts/toast_view.h
+++ b/chrome/browser/ui/toasts/toast_view.h
@@ -60,7 +60,7 @@
       views::View* anchor_view,
       const std::u16string& toast_text,
       const gfx::VectorIcon& icon,
-      const ui::ImageModel* image_override,
+      std::optional<ui::ImageModel> image_override,
       bool should_hide_ui_for_fullscreen,
       base::RepeatingCallback<void(ToastCloseReason)> on_toast_close_callback);
   ~ToastView() override;
@@ -124,7 +124,7 @@
 
   const std::u16string toast_text_;
   const raw_ref<const gfx::VectorIcon> icon_;
-  const raw_ptr<const ui::ImageModel> image_override_;
+  const std::optional<ui::ImageModel> image_override_;
   bool render_toast_over_web_contents_;
   bool has_close_button_ = false;
   bool has_action_button_ = false;
diff --git a/chrome/browser/ui/toasts/toast_view_browsertest.cc b/chrome/browser/ui/toasts/toast_view_browsertest.cc
index d290db84..ab106f90 100644
--- a/chrome/browser/ui/toasts/toast_view_browsertest.cc
+++ b/chrome/browser/ui/toasts/toast_view_browsertest.cc
@@ -52,16 +52,16 @@
     if (!options_.text.empty()) {
       toast_text = options_.text;
     }
+    std::optional<ui::ImageModel> image_override;
     if (options_.add_image_override) {
       int size = toasts::ToastView::GetIconSize();
-      image_override_ =
-          std::make_unique<ui::ImageModel>(ui::ImageModel::FromImage(
-              gfx::test::CreateImage(size, size, 0xff0000)));
+      image_override = ui::ImageModel::FromImage(
+          gfx::test::CreateImage(size, size, 0xff0000));
     }
     std::unique_ptr<toasts::ToastView> toast =
         std::make_unique<toasts::ToastView>(
             anchor_view_, toast_text, vector_icons::kLinkIcon,
-            image_override_.get(), false, base::DoNothing());
+            std::move(image_override), false, base::DoNothing());
     if (options_.add_close_button) {
       toast->AddCloseButton(base::DoNothing());
     }
@@ -99,7 +99,6 @@
 
  private:
   raw_ptr<views::View> anchor_view_;
-  std::unique_ptr<ui::ImageModel> image_override_;
   raw_ptr<toasts::ToastView> toast_;
   raw_ptr<views::Widget> widget_;
   ToastOptions options_;
Loading diff…

Regression Test / PoC

shipped with the fix
diff --git a/chrome/browser/ui/toasts/toast_view_browsertest.cc b/chrome/browser/ui/toasts/toast_view_browsertest.cc
index d290db84..ab106f90 100644
--- a/chrome/browser/ui/toasts/toast_view_browsertest.cc
+++ b/chrome/browser/ui/toasts/toast_view_browsertest.cc
@@ -52,16 +52,16 @@
     if (!options_.text.empty()) {
       toast_text = options_.text;
     }
+    std::optional<ui::ImageModel> image_override;
     if (options_.add_image_override) {
       int size = toasts::ToastView::GetIconSize();
-      image_override_ =
-          std::make_unique<ui::ImageModel>(ui::ImageModel::FromImage(
-              gfx::test::CreateImage(size, size, 0xff0000)));
+      image_override = ui::ImageModel::FromImage(
+          gfx::test::CreateImage(size, size, 0xff0000));
     }
     std::unique_ptr<toasts::ToastView> toast =
         std::make_unique<toasts::ToastView>(
             anchor_view_, toast_text, vector_icons::kLinkIcon,
-            image_override_.get(), false, base::DoNothing());
+            std::move(image_override), false, base::DoNothing());
     if (options_.add_close_button) {
       toast->AddCloseButton(base::DoNothing());
     }
@@ -99,7 +99,6 @@
 
  private:
   raw_ptr<views::View> anchor_view_;
-  std::unique_ptr<ui::ImageModel> image_override_;
   raw_ptr<toasts::ToastView> toast_;
   raw_ptr<views::Widget> widget_;
   ToastOptions options_;
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.