[PATCH v2] rust_binder: clear freeze listener on node removal
From: Alice Ryhl
Date: Mon Jun 15 2026 - 09:20:22 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/20260615-binder-noderefs-spin-v3-0-3235f5a3e0a0@xxxxxxxxxx/
---
Changes in v2:
- Rebase on v3 of parent series.
- Link to v1: https://lore.kernel.org/r/20260609-remove-freeze-on-remove-node-v1-1-f67f3b9bfeb8@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 2aef4f62cd11..5256f305e456 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 53fc8ba42e86..abcc979dceed 100644
--- a/drivers/android/binder/node.rs
+++ b/drivers/android/binder/node.rs
@@ -679,11 +679,13 @@ pub(crate) fn add_freeze_listener(
Ok(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:v3
prerequisite-patch-id: 4984d542e1da65da603c302f0bccb423867f71c2
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>