[PATCH] rust_binder: clear freeze listener on node removal

From: Alice Ryhl

Date: Tue Jun 09 2026 - 07:00:08 EST


Generally userspace is supposed to explicitly clear freeze listeners
before they drop the refcount on the node ref to zero, but there's
nothing forcing that. Currently, in this scenario the freeze listener
remains in the freeze_listeners rbtree and in the remote node's freeze
listener list, even though the ref for which the listener is registered
is gone. This could potentially lead to a memory leak due to a refcount
cycle. Thus, remove the freeze listener in this scenario.

Cc: stable@xxxxxxxxxxxxxxx
Fixes: eafedbc7c050 ("rust_binder: add Rust Binder driver")
Signed-off-by: Alice Ryhl <aliceryhl@xxxxxxxxxx>
---
This series is based on top of:
https://lore.kernel.org/all/20260609-binder-noderefs-spin-v2-0-eafde2ff376c@xxxxxxxxxx/
---
drivers/android/binder/freeze.rs | 11 +++++++++--
drivers/android/binder/node.rs | 6 ++++--
drivers/android/binder/process.rs | 13 +++++++++++--
3 files changed, 24 insertions(+), 6 deletions(-)

diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs
index 20041689e98d..1b49e63723b5 100644
--- a/drivers/android/binder/freeze.rs
+++ b/drivers/android/binder/freeze.rs
@@ -154,10 +154,17 @@ fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> {
}

impl FreezeListener {
- pub(crate) fn on_process_exit(&self, proc: &Arc<Process>) {
+ /// Called when this freeze listener is cleared abnormally.
+ ///
+ /// This occurs either because the process exited or because the process dropped its last
+ /// refcount on the node ref without explicitly removing the freeze listener first.
+ ///
+ /// The returned `KVVec` is just a value that should be dropped outside of the lock.
+ pub(crate) fn on_process_cleanup(&self, proc: &Process) -> KVVec<Arc<Process>> {
if !self.is_clearing {
- self.node.remove_freeze_listener(proc);
+ return self.node.remove_freeze_listener(proc);
}
+ KVVec::new()
}
}

diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs
index fb27674a8c94..79f660071bd6 100644
--- a/drivers/android/binder/node.rs
+++ b/drivers/android/binder/node.rs
@@ -687,11 +687,13 @@ pub(crate) fn resize_for_add_freeze_listener(
Ok(())
}

- pub(crate) fn remove_freeze_listener(&self, p: &Arc<Process>) -> KVVec<Arc<Process>> {
+ pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec<Arc<Process>> {
let mut guard = self.owner.inner.lock();
let inner = self.inner.access_mut(&mut guard);
let len = inner.freeze_list.len();
- inner.freeze_list.retain(|proc| !Arc::ptr_eq(proc, p));
+ inner
+ .freeze_list
+ .retain(|proc| !core::ptr::eq::<Process>(&**proc, p));
if len == inner.freeze_list.len() {
pr_warn!(
"Could not remove freeze listener for {}\n",
diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
index 82c34a93660e..5802fbbaacd3 100644
--- a/drivers/android/binder/process.rs
+++ b/drivers/android/binder/process.rs
@@ -950,6 +950,8 @@ pub(crate) fn update_ref(
// increment references on itself.
let _to_free_by_handle;
let _to_free_by_node;
+ let _to_free_freeze_listener;
+ let _to_free_freeze_listener_cleanup;
let mut refs = self.node_refs.lock();
if let Some(info) = refs.by_handle.get_mut(&handle) {
if info.node_ref().update(inc, strong) {
@@ -965,8 +967,15 @@ pub(crate) fn update_ref(

// SAFETY: We are removing the `NodeRefInfo` from the right node.
unsafe { info.node_ref2().node.remove_node_info(info) };
-
let id = info.node_ref().node.global_id();
+
+ if let Some(freeze) = *info.freeze() {
+ if let Some(fl) = refs.freeze_listeners.remove(&freeze) {
+ _to_free_freeze_listener_cleanup = fl.on_process_cleanup(&self);
+ _to_free_freeze_listener = fl;
+ }
+ }
+
_to_free_by_handle = refs.by_handle.remove_node(&handle);
_to_free_by_node = refs.by_node.remove_node(&id);
refs.handle_is_present.release_id(handle as usize);
@@ -1391,7 +1400,7 @@ fn deferred_release(self: Arc<Self>) {
// Clean up freeze listeners.
let freeze_listeners = take(&mut self.node_refs.lock().freeze_listeners);
for listener in freeze_listeners.values() {
- listener.on_process_exit(&self);
+ listener.on_process_cleanup(&self);
}
drop(freeze_listeners);


---
base-commit: 3bc831df9ee16fceee851872315161377ca1417d
change-id: 20260609-remove-freeze-on-remove-node-30e72ff6b46e
prerequisite-change-id:20260608-binder-noderefs-spin-3a0ec0589043:v2
prerequisite-patch-id: 3a1c4f545b2281e2e91ea0af7fe6c71f5ae0c08e
prerequisite-patch-id: e1ae5f73b329080c0fcea1d2509c085e105f67c3
prerequisite-patch-id: 63f2092c6fd9dd54b7adf93be8d5670d8490f401
prerequisite-patch-id: 5811aeb4ca435a1d4d0bb347d4de9f3b2d91f814
prerequisite-patch-id: 556e45c697f9888c8446245bbcf478422991fddf
prerequisite-patch-id: f567ba2263108b12871013309ae7c482d500eeae

Best regards,
--
Alice Ryhl <aliceryhl@xxxxxxxxxx>