diff --git a/chrome/browser/ui/tabs/tab_strip_model.cc b/chrome/browser/ui/tabs/tab_strip_model.cc index b43ada223b1be..fac459ef9a85d 100644 --- a/chrome/browser/ui/tabs/tab_strip_model.cc +++ b/chrome/browser/ui/tabs/tab_strip_model.cc @@ -4593,7 +4593,11 @@ void TabStripModel::MoveTabToIndexImpl( CHECK_LT(initial_index, count()); CHECK_LT(final_index, count()); - tabs::TabInterface* const tab = GetTabAtIndex(initial_index); + // Mitigation: keep `tab` under BackupRefPtr protection for the entire + // function so a re-entrant CloseWebContentsAt() during the kMoved observer + // notification cannot turn the later `tab->IsPinned()` dereference into an + // exploitable UAF (memory stays quarantined while this raw_ptr is live). + const raw_ptr tab = GetTabAtIndex(initial_index); const bool initial_pinned_state = tab->IsPinned(); const std::optional initial_group = tab->GetGroup(); @@ -4641,6 +4645,12 @@ void TabStripModel::MoveTabToIndexImpl( SendMoveNotificationForTab(initial_index, final_index, tab, selection); } + // Mitigation: CloseWebContentsAt()/CloseTabs() lack a ReentrancyCheck, so an + // observer could have synchronously closed `tab` during the notification + // above. CHECK so this becomes a safe crash instead of a use-after-free on + // the dereferences below. + CHECK_NE(GetIndexOfTab(tab), kNoTab); + if (move_within_split) { NotifySplitTabContentsUpdated( tab->GetSplit().value(), initial_split_tabs,