Chrome · TopChrome
CVE-2026-7970
UAF in TopChrome
Overview
Medium
Severity
—
CVSS
No
Exploited ITW
Fixed
Fix Status
Changed Functions
| Function | Change | Notes |
|---|---|---|
ifchrome/browser/ui/toasts/toast_view_browsertest.cc |
modified |
Files Changed
chrome/browser/ui/toasts/toast_controller.ccchrome/browser/ui/toasts/toast_view.ccchrome/browser/ui/toasts/toast_view.hchrome/browser/ui/toasts/toast_view_browsertest.cc
Patch
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()
- ? ¶ms.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.
References
On This Page