[PATCH v4 4/4] rust_binder: report netlink transactions

Alice Ryhl
2026-05-04T09:05:14Z

From: Carlos Llamas <cmllamas@google.com>

The Android Binder driver supports a netlink API that reports
transaction *failures* to a userspace daemon. This allows devices to
monitor processes with many failed transactions so that it can e.g. kill
misbehaving apps.

One very important thing that this monitors is when many oneway messages
are sent to a frozen process, so there is special handling to ensure
this scenario is surfaced over netlink.

Signed-off-by: Carlos Llamas <cmllamas@google.com>
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
---
drivers/android/binder/rust_binder_main.rs | 1 -
drivers/android/binder/thread.rs | 10 ++++++++
drivers/android/binder/transaction.rs | 40 ++++++++++++++++++++++++++++++
3 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index 2cb9f4897ad7..bbef68993b8d 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -38,7 +38,6 @@
mod deferred_close;
mod defs;
mod error;
-#[allow(dead_code)]
mod netlink;
mod node;
mod page_range;
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index 97d5f31e8fe3..aa4e93a877ac 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -1263,6 +1263,15 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu
}
}

+ if info.oneway_spam_suspect {
+ // If this is both a oneway spam suspect and a failure, we report it twice. This is
+ // useful in case the transaction failed with BR_TRANSACTION_PENDING_FROZEN.
+ info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx);
+ }
+ if info.reply != 0 {
+ info.report_netlink(info.reply, &self.process.ctx);
+ }
+
Ok(())
}

@@ -1332,6 +1341,7 @@ fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
);
let reply = Err(BR_FAILED_REPLY);
orig.from.deliver_reply(reply, &orig);
+ info.reply = BR_FAILED_REPLY;
err.reply = BR_TRANSACTION_COMPLETE;
err
});
diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
index 47d5e4d88b07..3fa7091ed8a6 100644
--- a/drivers/android/binder/transaction.rs
+++ b/drivers/android/binder/transaction.rs
@@ -3,6 +3,7 @@
// Copyright (C) 2025 Google LLC.

use kernel::{
+ netlink::GENLMSG_DEFAULT_SIZE,
prelude::*,
seq_file::SeqFile,
seq_print,
@@ -17,6 +18,7 @@
allocation::{Allocation, TranslatedFds},
defs::*,
error::{BinderError, BinderResult},
+ netlink::Report,
node::{Node, NodeRef},
process::{Process, ProcessInner},
ptr_align,
@@ -49,6 +51,44 @@ impl TransactionInfo {
pub(crate) fn is_oneway(&self) -> bool {
self.flags & TF_ONE_WAY != 0
}
+
+ pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) {
+ if let Err(err) = self.report_netlink_inner(reply, ctx) {
+ pr_warn!(
+ "{}:{} netlink report failed: {err:?}\n",
+ self.from_pid,
+ self.from_tid
+ );
+ }
+ }
+
+ fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> kernel::error::Result {
+ if !Report::has_listeners() {
+ return Ok(());
+ }
+ let mut report = Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KERNEL)?;
+
+ report.error(reply)?;
+ report.context(&ctx.name)?;
+ report.from_pid(self.from_pid as u32)?;
+ report.from_tid(self.from_tid as u32)?;
+ if self.to_pid != 0 {
+ report.to_pid(self.to_pid as u32)?;
+ }
+ if self.to_tid != 0 {
+ report.to_tid(self.to_tid as u32)?;
+ }
+
+ if self.is_reply {
+ report.is_reply()?;
+ }
+ report.flags(self.flags)?;
+ report.code(self.code)?;
+ report.data_size(self.data_size as u32)?;
+
+ report.multicast(0, GFP_KERNEL)?;
+ Ok(())
+ }
}

use core::mem::offset_of;

--
2.54.0.545.g6539524ca2-goog