diff --git a/patches/chromium/.patches b/patches/chromium/.patches index 980b0f0c7b9a6..85840d1cf7a0f 100644 --- a/patches/chromium/.patches +++ b/patches/chromium/.patches @@ -220,3 +220,5 @@ ozone_nexus_fix_isvisible_to_account_for_window_opacity.patch ozone_nexus_transfer_keyboard_focus_to_next_window_on_destroy.patch ozone_nexus_assign_virtualkeyboard_role_to_overlay_windows_os-20973.patch brightsign_fix_semi-transparent_window_opacity_not_rendering_on.patch +add_removetrap_api_os-21158.patch +fix_mojo_trap_leak_os-21158.patch diff --git a/patches/chromium/add_removetrap_api_os-21158.patch b/patches/chromium/add_removetrap_api_os-21158.patch new file mode 100644 index 0000000000000..9f856c0449ad9 --- /dev/null +++ b/patches/chromium/add_removetrap_api_os-21158.patch @@ -0,0 +1,141 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Geoffrey Ostosh +Date: Thu, 28 May 2026 18:34:17 -0500 +Subject: Add RemoveTrap API OS-21158 + +The ipcz library exposes no removal entry point for traps installed +via Trap(); once registered on the Router's TrapSet they persist for +the lifetime of the portal. Add a RemoveTrap function to ipcz's +public API that dispatches a synthetic IPCZ_TRAP_REMOVED event so any +pending handler sees a clean teardown, then erases the entry from +the Router's TrapSet. + +diff --git a/third_party/ipcz/include/ipcz/ipcz.h b/third_party/ipcz/include/ipcz/ipcz.h +index b2072b92c9b262222e6bdcdfba7cdeaef93df410..9673cf696e1631c9810bab1817ca1f53715fd2a4 100644 +--- a/third_party/ipcz/include/ipcz/ipcz.h ++++ b/third_party/ipcz/include/ipcz/ipcz.h +@@ -1606,6 +1606,11 @@ struct IPCZ_ALIGN(8) IpczAPI { + IpczTrapConditionFlags* satisfied_condition_flags, // out + struct IpczPortalStatus* status); // out + ++ IpczResult (IPCZ_API* RemoveTrap)(IpczHandle portal, ++ uintptr_t context, ++ uint32_t flags, ++ const void* options); ++ + // Reject() + // ======== + // +diff --git a/third_party/ipcz/src/api.cc b/third_party/ipcz/src/api.cc +index b54bee2c9087e958fa37f6e783a90d2ca0801d4b..d9f58d71f3cd6bcf4e10fb7b38b84439242ea21e 100644 +--- a/third_party/ipcz/src/api.cc ++++ b/third_party/ipcz/src/api.cc +@@ -276,6 +276,18 @@ IpczResult Trap(IpczHandle portal_handle, + status); + } + ++IpczResult RemoveTrap(IpczHandle portal_handle, ++ uintptr_t context, ++ uint32_t flags, ++ const void* options) { ++ ipcz::Router* router = ipcz::Router::FromHandle(portal_handle); ++ if (!router) { ++ return IPCZ_RESULT_INVALID_ARGUMENT; ++ } ++ router->RemoveTrap(context); ++ return IPCZ_RESULT_OK; ++} ++ + IpczResult Reject(IpczHandle parcel_handle, + uintptr_t context, + uint32_t flags, +@@ -360,6 +372,7 @@ constexpr IpczAPI kCurrentAPI = { + BeginGet, + EndGet, + Trap, ++ RemoveTrap, + Reject, + Box, + Unbox, +diff --git a/third_party/ipcz/src/ipcz/router.cc b/third_party/ipcz/src/ipcz/router.cc +index af70af0c0b72ab028463d836ec14561878f406db..2513529bf186eea8ad7fca0d8ff0b8457bbefcd5 100644 +--- a/third_party/ipcz/src/ipcz/router.cc ++++ b/third_party/ipcz/src/ipcz/router.cc +@@ -674,6 +674,15 @@ IpczResult Router::Trap(const IpczTrapConditions& conditions, + inbound_parcels_, satisfied_condition_flags, status); + } + ++void Router::RemoveTrap(uintptr_t context) { ++ const OperationContext op_context{OperationContext::kAPICall}; ++ TrapEventDispatcher dispatcher; ++ { ++ absl::MutexLock lock(&mutex_); ++ traps_.Remove(context, op_context, dispatcher); ++ } ++} ++ + IpczResult Router::MergeRoute(const Ref& other) { + if (HasLocalPeer(*other) || other == this) { + return IPCZ_RESULT_INVALID_ARGUMENT; +diff --git a/third_party/ipcz/src/ipcz/router.h b/third_party/ipcz/src/ipcz/router.h +index 1a60e847cc68f58a1c24f03e653d9b06f595c67e..0ca24a49f838317ee44cd8377d3ef719820fa76a 100644 +--- a/third_party/ipcz/src/ipcz/router.h ++++ b/third_party/ipcz/src/ipcz/router.h +@@ -169,6 +169,8 @@ class Router : public APIObjectImpl { + IpczTrapConditionFlags* satisfied_condition_flags, + IpczPortalStatus* status); + ++ void RemoveTrap(uintptr_t context); ++ + // Attempts to merge this Router's route with the route terminated by `other`. + // Both `other` and this Router must be terminal routers on their own separate + // routes, and neither Router must have transmitted or retreived any parcels +diff --git a/third_party/ipcz/src/ipcz/trap_set.cc b/third_party/ipcz/src/ipcz/trap_set.cc +index 161ae7d3c7f04233cdc728062d8963821b0032c0..4997c7fedf6260301bc882ec54ac515c21e12d3f 100644 +--- a/third_party/ipcz/src/ipcz/trap_set.cc ++++ b/third_party/ipcz/src/ipcz/trap_set.cc +@@ -98,6 +98,29 @@ void TrapSet::RemoveAll(const OperationContext& context, + traps_.clear(); + } + ++bool TrapSet::Remove(uintptr_t context_to_remove, ++ const OperationContext& context, ++ TrapEventDispatcher& dispatcher) { ++ IpczTrapConditionFlags flags = IPCZ_TRAP_REMOVED; ++ if (context.is_api_call()) { ++ flags |= IPCZ_TRAP_WITHIN_API_CALL; ++ } ++ const IpczPortalStatus status{ ++ .size = sizeof(status), ++ .flags = IPCZ_NO_FLAGS, ++ .num_local_parcels = 0, ++ .num_local_bytes = 0, ++ }; ++ for (auto it = traps_.begin(); it != traps_.end(); ++it) { ++ if (it->context == context_to_remove) { ++ dispatcher.DeferEvent(it->handler, it->context, flags, status); ++ traps_.erase(it); ++ return true; ++ } ++ } ++ return false; ++} ++ + IpczTrapConditionFlags TrapSet::GetSatisfiedConditionsForUpdate( + const IpczTrapConditions& conditions, + IpczPortalStatusFlags status_flags, +diff --git a/third_party/ipcz/src/ipcz/trap_set.h b/third_party/ipcz/src/ipcz/trap_set.h +index 054607cb866981aa702e03f28502685e3c45c805..57f747557f2c04ae1ac94fb99fcb03087a884dbc 100644 +--- a/third_party/ipcz/src/ipcz/trap_set.h ++++ b/third_party/ipcz/src/ipcz/trap_set.h +@@ -73,6 +73,10 @@ class TrapSet { + void RemoveAll(const OperationContext& context, + TrapEventDispatcher& dispatcher); + ++ bool Remove(uintptr_t context_to_remove, ++ const OperationContext& op_context, ++ TrapEventDispatcher& dispatcher); ++ + private: + struct Trap { + Trap(IpczTrapConditions conditions, diff --git a/patches/chromium/fix_mojo_trap_leak_os-21158.patch b/patches/chromium/fix_mojo_trap_leak_os-21158.patch new file mode 100644 index 0000000000000..388245942f2fd --- /dev/null +++ b/patches/chromium/fix_mojo_trap_leak_os-21158.patch @@ -0,0 +1,45 @@ +From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 +From: Geoffrey Ostosh +Date: Thu, 28 May 2026 18:48:36 -0500 +Subject: Fix mojo trap leak OS-21158 + +MojoTrap::RemoveTrigger and MojoTrap::Close erase entries from their +local triggers_ map but leave the corresponding ipcz traps installed +on the Router's TrapSet, causing them to accumulate across +BrowserWindow create/destroy cycles. Call the new ipcz RemoveTrap API +from both entry points to release the underlying trap. + +diff --git a/mojo/core/ipcz_driver/mojo_trap.cc b/mojo/core/ipcz_driver/mojo_trap.cc +index 7a5765a74a7f64e584b0a080e659c88c019adcbb..d5052e2d4c81963b9bc08dcf0a4761758446b02c 100644 +--- a/mojo/core/ipcz_driver/mojo_trap.cc ++++ b/mojo/core/ipcz_driver/mojo_trap.cc +@@ -277,12 +277,16 @@ MojoResult MojoTrap::RemoveTrigger(uintptr_t trigger_context) { + if (it == triggers_.end()) { + return MOJO_RESULT_NOT_FOUND; + } +- + scoped_refptr trigger = std::move(it->second); + trigger->armed = false; + triggers_.erase(it); + next_trigger_ = triggers_.begin(); + DispatchOrQueueTriggerRemoval(*trigger); ++ { ++ base::AutoUnlock unlock(lock_); ++ GetIpczAPI().RemoveTrap(trigger->handle, trigger->ipcz_context(), ++ IPCZ_NO_FLAGS, nullptr); ++ } + return MOJO_RESULT_OK; + } + +@@ -379,6 +383,11 @@ void MojoTrap::Close() { + + DCHECK(!trigger->removed); + DispatchOrQueueTriggerRemoval(*trigger); ++ { ++ base::AutoUnlock unlock(lock_); ++ GetIpczAPI().RemoveTrap(trigger->handle, trigger->ipcz_context(), ++ IPCZ_NO_FLAGS, nullptr); ++ } + } + } +