From f3e0b76fc29c4e1ee542f5173a4a631803e69436 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Thu, 19 Feb 2026 11:27:00 +0000 Subject: rust_binder: avoid name mangling for get_work[_local] Currently ps -A shows processes waiting on schedule() in functions with names such as do_epoll_wait, wait_woken, and the impeccably named _RNvMs2_NtCs8QPsHWIn21X_16rust_binder_main6threadNtB5_6Thread8get_work. To improve how ps output looks, give explicit non-mangled names to the functions where Rust Binder calls schedule(), since these are the most likely places to show up on ps output. The name of rust_binder_waitlcl is truncated instead of using _local suffix because rust_binder_wait_local is sufficiently long that ps shows unaligned output. This is intended to be a temporary workaround until we find a better solution. Adding #[export_name] to every Rust function that calls schedule() is not a great long-term solution. Suggested-by: Matthew Maurer Signed-off-by: Alice Ryhl Acked-by: Gary Guo Link: https://patch.msgid.link/20260219-rust-binder-ps-v2-1-773eca09c125@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 3 +++ drivers/android/binder/thread.rs | 6 ++++++ 2 files changed, 9 insertions(+) (limited to 'drivers/android') diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 41de5593197c..f62f626f928e 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1442,6 +1442,9 @@ impl Process { } } + // #[export_name] is a temporary workaround so that ps output does not become unreadable from + // mangled symbol names. + #[export_name = "rust_binder_freeze"] pub(crate) fn ioctl_freeze(&self, info: &BinderFreezeInfo) -> Result { if info.enable == 0 { let msgs = self.prepare_freeze_messages()?; diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 0b62d24b2118..6f197be0fa75 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -513,6 +513,9 @@ impl Thread { /// Attempts to fetch a work item from the thread-local queue. The behaviour if the queue is /// empty depends on `wait`: if it is true, the function waits for some work to be queued (or a /// signal); otherwise it returns indicating that none is available. + // #[export_name] is a temporary workaround so that ps output does not become unreadable from + // mangled symbol names. + #[export_name = "rust_binder_waitlcl"] fn get_work_local(self: &Arc, wait: bool) -> Result>> { { let mut inner = self.inner.lock(); @@ -551,6 +554,9 @@ impl Thread { /// /// This must only be called when the thread is not participating in a transaction chain. If it /// is, the local version (`get_work_local`) should be used instead. + // #[export_name] is a temporary workaround so that ps output does not become unreadable from + // mangled symbol names. + #[export_name = "rust_binder_wait"] fn get_work(self: &Arc, wait: bool) -> Result>> { // Try to get work from the thread's work queue, using only a local lock. { -- cgit v1.2.3 From 65b6721522892a4994472fbac41386c63c769511 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 13 Feb 2026 22:37:30 +0100 Subject: binder: use current_euid() for transaction sender identity Binder currently uses task_euid(proc->tsk) as the transaction sender EUID, where proc->tsk is the main thread of the process that opened /dev/binder. That's not clean; use the subjective EUID of the current task instead. Signed-off-by: Jann Horn Reviewed-by: Alice Ryhl Acked-by: Gary Guo Link: https://patch.msgid.link/20260213-binder-uid-v1-1-7b795ae05523@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/android') diff --git a/drivers/android/binder.c b/drivers/android/binder.c index 21f91d9f2fbc..9e6194224593 100644 --- a/drivers/android/binder.c +++ b/drivers/android/binder.c @@ -3117,7 +3117,7 @@ static void binder_transaction(struct binder_proc *proc, t->start_time = t_start_time; t->from_pid = proc->pid; t->from_tid = thread->pid; - t->sender_euid = task_euid(proc->tsk); + t->sender_euid = current_euid(); t->code = tr->code; t->flags = tr->flags; t->priority = task_nice(current); -- cgit v1.2.3 From d31ed22a0678da8948439c3009b01c4806a677c9 Mon Sep 17 00:00:00 2001 From: Jann Horn Date: Fri, 13 Feb 2026 22:37:31 +0100 Subject: rust_binder: use current_euid() for transaction sender identity Binder currently uses from.process.task.euid() as the transaction sender EUID, where from.process.task is the main thread of the process that opened /dev/binder. That's not clean; use the subjective EUID of the current task instead. Signed-off-by: Jann Horn Reviewed-by: Alice Ryhl Acked-by: Gary Guo Link: https://patch.msgid.link/20260213-binder-uid-v1-2-7b795ae05523@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/transaction.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 75e6f5fbaaae..10af40527ca7 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -107,7 +107,7 @@ impl Transaction { debug_id, target_node: Some(target_node), from_parent, - sender_euid: from.process.task.euid(), + sender_euid: Kuid::current_euid(), from: from.clone(), to, code: trd.code, @@ -147,7 +147,7 @@ impl Transaction { debug_id, target_node: None, from_parent: None, - sender_euid: from.process.task.euid(), + sender_euid: Kuid::current_euid(), from: from.clone(), to, code: trd.code, -- cgit v1.2.3 From 34268365a9e9424e38083c8f318cc34b153dcb07 Mon Sep 17 00:00:00 2001 From: Shivam Kalra Date: Mon, 16 Feb 2026 19:39:57 +0530 Subject: rust_binder: shrink all_procs when deregistering processes When a process is deregistered from the binder context, the all_procs vector may have significant unused capacity. Add logic to shrink the vector using a conservative strategy that prevents shrink-then-regrow oscillation. The shrinking strategy triggers when length drops below 1/4 of capacity, and shrinks to twice the current length rather than to the exact length. This provides hysteresis to avoid repeated reallocations when the process count fluctuates. The shrink operation uses GFP_KERNEL and is allowed to fail gracefully since it is purely an optimization. The vector remains valid and functional even if shrinking fails. Suggested-by: Alice Ryhl Reviewed-by: Alice Ryhl Signed-off-by: Shivam Kalra Acked-by: Danilo Krummrich Link: https://patch.msgid.link/20260216-binder-shrink-vec-v3-v6-3-ece8e8593e53@zohomail.in Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/context.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers/android') diff --git a/drivers/android/binder/context.rs b/drivers/android/binder/context.rs index 9cf437c025a2..ddddb66b3557 100644 --- a/drivers/android/binder/context.rs +++ b/drivers/android/binder/context.rs @@ -94,6 +94,17 @@ impl Context { } let mut manager = self.manager.lock(); manager.all_procs.retain(|p| !Arc::ptr_eq(p, proc)); + + // Shrink the vector if it has significant unused capacity to avoid memory waste, + // but use a conservative strategy to prevent shrink-then-regrow oscillation. + // Only shrink when length drops below 1/4 of capacity, and shrink to twice the length. + let len = manager.all_procs.len(); + let cap = manager.all_procs.capacity(); + if len < cap / 4 { + // Shrink to twice the current length. Ignore allocation failures since this + // is just an optimization; the vector remains valid even if shrinking fails. + let _ = manager.all_procs.shrink_to(len * 2, GFP_KERNEL); + } } pub(crate) fn set_manager_node(&self, node_ref: NodeRef) -> Result { -- cgit v1.2.3 From 5326a18e3e640061ca4b65c1b732feaeace61c39 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Fri, 6 Mar 2026 11:28:46 +0000 Subject: rust_binder: introduce TransactionInfo Rust Binder exposes information about transactions that are sent in various ways: printing to the kernel log, tracepoints, files in binderfs, and the upcoming netlink support. Currently all these mechanisms use disparate ways of obtaining the same information, so let's introduce a single Info struct that collects all the required information in a single place, so that all of these different mechanisms can operate in a more uniform way. For now, the new info struct is only used to replace a few things: * The BinderTransactionDataSg struct that is passed as an argument to several methods is removed as the information is moved into the new info struct and passed down that way. * The oneway spam detection fields on Transaction and Allocation can be removed, as the information can be returned to the caller via the mutable info struct instead. But several other uses of the info struct are planned in follow-up patches. Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260306-transaction-info-v1-1-fda58fca558b@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 3 - drivers/android/binder/error.rs | 10 +- drivers/android/binder/process.rs | 15 ++- drivers/android/binder/thread.rs | 190 +++++++++++++++++++--------------- drivers/android/binder/transaction.rs | 83 +++++++++------ rust/kernel/uaccess.rs | 2 +- 6 files changed, 168 insertions(+), 135 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 7f65a9c3a0e5..97edfb1ff382 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -56,7 +56,6 @@ pub(crate) struct Allocation { pub(crate) process: Arc, allocation_info: Option, free_on_drop: bool, - pub(crate) oneway_spam_detected: bool, #[allow(dead_code)] pub(crate) debug_id: usize, } @@ -68,7 +67,6 @@ impl Allocation { offset: usize, size: usize, ptr: usize, - oneway_spam_detected: bool, ) -> Self { Self { process, @@ -76,7 +74,6 @@ impl Allocation { size, ptr, debug_id, - oneway_spam_detected, allocation_info: None, free_on_drop: true, } diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error.rs index b24497cfa292..45d85d4c2815 100644 --- a/drivers/android/binder/error.rs +++ b/drivers/android/binder/error.rs @@ -13,7 +13,7 @@ pub(crate) type BinderResult = core::result::Result; /// errno. pub(crate) struct BinderError { pub(crate) reply: u32, - source: Option, + pub(crate) source: Option, } impl BinderError { @@ -41,14 +41,6 @@ impl BinderError { pub(crate) fn is_dead(&self) -> bool { self.reply == BR_DEAD_REPLY } - - pub(crate) fn as_errno(&self) -> kernel::ffi::c_int { - self.source.unwrap_or(EINVAL).to_errno() - } - - pub(crate) fn should_pr_warn(&self) -> bool { - self.source.is_some() - } } /// Convert an errno into a `BinderError` and store the errno used to construct it. The errno diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index da81a9569365..ae26fe817794 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -48,6 +48,7 @@ use crate::{ range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs}, stats::BinderStats, thread::{PushWorkRes, Thread}, + transaction::TransactionInfo, BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead, }; @@ -1003,16 +1004,15 @@ impl Process { self: &Arc, debug_id: usize, size: usize, - is_oneway: bool, - from_pid: i32, + info: &mut TransactionInfo, ) -> BinderResult { use kernel::page::PAGE_SIZE; let mut reserve_new_args = ReserveNewArgs { debug_id, size, - is_oneway, - pid: from_pid, + is_oneway: info.is_oneway(), + pid: info.from_pid, ..ReserveNewArgs::default() }; @@ -1028,13 +1028,13 @@ impl Process { reserve_new_args = alloc_request.make_alloc()?; }; + info.oneway_spam_suspect = new_alloc.oneway_spam_detected; let res = Allocation::new( self.clone(), debug_id, new_alloc.offset, size, addr + new_alloc.offset, - new_alloc.oneway_spam_detected, ); // This allocation will be marked as in use until the `Allocation` is used to free it. @@ -1066,7 +1066,7 @@ impl Process { let mapping = inner.mapping.as_mut()?; let offset = ptr.checked_sub(mapping.address)?; let (size, debug_id, odata) = mapping.alloc.reserve_existing(offset).ok()?; - let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr, false); + let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr); if let Some(data) = odata { alloc.set_info(data); } @@ -1414,8 +1414,7 @@ impl Process { .alloc .take_for_each(|offset, size, debug_id, odata| { let ptr = offset + address; - let mut alloc = - Allocation::new(self.clone(), debug_id, offset, size, ptr, false); + let mut alloc = Allocation::new(self.clone(), debug_id, offset, size, ptr); if let Some(data) = odata { alloc.set_info(data); } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 6f283de53213..97a5e4acf64c 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -19,7 +19,7 @@ use kernel::{ sync::poll::{PollCondVar, PollTable}, sync::{aref::ARef, Arc, SpinLock}, task::Task, - uaccess::UserSlice, + uaccess::{UserPtr, UserSlice, UserSliceReader}, uapi, }; @@ -30,7 +30,7 @@ use crate::{ process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, - transaction::Transaction, + transaction::{Transaction, TransactionInfo}, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead, }; @@ -951,13 +951,11 @@ impl Thread { pub(crate) fn copy_transaction_data( &self, to_process: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, debug_id: usize, allow_fds: bool, txn_security_ctx_offset: Option<&mut usize>, ) -> BinderResult { - let trd = &tr.transaction_data; - let is_oneway = trd.flags & TF_ONE_WAY != 0; let mut secctx = if let Some(offset) = txn_security_ctx_offset { let secid = self.process.cred.get_secid(); let ctx = match security::SecurityCtx::from_secid(secid) { @@ -972,10 +970,10 @@ impl Thread { None }; - let data_size = trd.data_size.try_into().map_err(|_| EINVAL)?; + let data_size = info.data_size; let aligned_data_size = ptr_align(data_size).ok_or(EINVAL)?; - let offsets_size: usize = trd.offsets_size.try_into().map_err(|_| EINVAL)?; - let buffers_size: usize = tr.buffers_size.try_into().map_err(|_| EINVAL)?; + let offsets_size = info.offsets_size; + let buffers_size = info.buffers_size; let aligned_secctx_size = match secctx.as_ref() { Some((_offset, ctx)) => ptr_align(ctx.len()).ok_or(EINVAL)?, None => 0, @@ -998,32 +996,25 @@ impl Thread { size_of::(), ); let secctx_off = aligned_data_size + offsets_size + buffers_size; - let mut alloc = - match to_process.buffer_alloc(debug_id, len, is_oneway, self.process.task.pid()) { - Ok(alloc) => alloc, - Err(err) => { - pr_warn!( - "Failed to allocate buffer. len:{}, is_oneway:{}", - len, - is_oneway - ); - return Err(err); - } - }; + let mut alloc = match to_process.buffer_alloc(debug_id, len, info) { + Ok(alloc) => alloc, + Err(err) => { + pr_warn!( + "Failed to allocate buffer. len:{}, is_oneway:{}", + len, + info.is_oneway(), + ); + return Err(err); + } + }; - // SAFETY: This accesses a union field, but it's okay because the field's type is valid for - // all bit-patterns. - let trd_data_ptr = unsafe { &trd.data.ptr }; - let mut buffer_reader = - UserSlice::new(UserPtr::from_addr(trd_data_ptr.buffer as _), data_size).reader(); + let mut buffer_reader = UserSlice::new(info.data_ptr, data_size).reader(); let mut end_of_previous_object = 0; let mut sg_state = None; // Copy offsets if there are any. if offsets_size > 0 { - let mut offsets_reader = - UserSlice::new(UserPtr::from_addr(trd_data_ptr.offsets as _), offsets_size) - .reader(); + let mut offsets_reader = UserSlice::new(info.offsets_ptr, offsets_size).reader(); let offsets_start = aligned_data_size; let offsets_end = aligned_data_size + offsets_size; @@ -1198,37 +1189,92 @@ impl Thread { } } - fn transaction(self: &Arc, tr: &BinderTransactionDataSg, inner: T) - where - T: FnOnce(&Arc, &BinderTransactionDataSg) -> BinderResult, - { - if let Err(err) = inner(self, tr) { - if err.should_pr_warn() { - let mut ee = self.inner.lock().extended_error; - ee.command = err.reply; - ee.param = err.as_errno(); - pr_warn!( - "Transaction failed: {:?} my_pid:{}", - err, - self.process.pid_in_current_ns() - ); + // No inlining avoids allocating stack space for `BinderTransactionData` for the entire + // duration of `transaction()`. + #[inline(never)] + fn read_transaction_info( + &self, + cmd: u32, + reader: &mut UserSliceReader, + info: &mut TransactionInfo, + ) -> Result<()> { + let td = match cmd { + BC_TRANSACTION | BC_REPLY => { + reader.read::()?.with_buffers_size(0) + } + BC_TRANSACTION_SG | BC_REPLY_SG => reader.read::()?, + _ => return Err(EINVAL), + }; + + // SAFETY: Above `read` call initializes all bytes, so this union read is ok. + let trd_data_ptr = unsafe { &td.transaction_data.data.ptr }; + + info.is_reply = matches!(cmd, BC_REPLY | BC_REPLY_SG); + info.from_pid = self.process.task.pid(); + info.from_tid = self.id; + info.code = td.transaction_data.code; + info.flags = td.transaction_data.flags; + info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize); + info.data_size = td.transaction_data.data_size as usize; + info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize); + info.offsets_size = td.transaction_data.offsets_size as usize; + info.buffers_size = td.buffers_size as usize; + // SAFETY: Above `read` call initializes all bytes, so this union read is ok. + info.target_handle = unsafe { td.transaction_data.target.handle }; + Ok(()) + } + + #[inline(never)] + fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Result<()> { + let mut info = TransactionInfo::zeroed(); + self.read_transaction_info(cmd, reader, &mut info)?; + + let ret = if info.is_reply { + self.reply_inner(&mut info) + } else if info.is_oneway() { + self.oneway_transaction_inner(&mut info) + } else { + self.transaction_inner(&mut info) + }; + + if let Err(err) = ret { + if err.reply != BR_TRANSACTION_COMPLETE { + info.reply = err.reply; } self.push_return_work(err.reply); + if let Some(source) = &err.source { + info.errno = source.to_errno(); + info.reply = err.reply; + + { + let mut ee = self.inner.lock().extended_error; + ee.command = err.reply; + ee.param = source.to_errno(); + } + + pr_warn!( + "{}:{} transaction to {} failed: {source:?}", + info.from_pid, + info.from_tid, + info.to_pid + ); + } } + + Ok(()) } - fn transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { - // SAFETY: Handle's type has no invalid bit patterns. - let handle = unsafe { tr.transaction_data.target.handle }; - let node_ref = self.process.get_transaction_node(handle)?; + fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { + let node_ref = self.process.get_transaction_node(info.target_handle)?; + info.to_pid = node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?; // TODO: We need to ensure that there isn't a pending transaction in the work queue. How // could this happen? let top = self.top_of_transaction_stack()?; let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; let completion = list_completion.clone_arc(); - let transaction = Transaction::new(node_ref, top, self, tr)?; + let transaction = Transaction::new(node_ref, top, self, info)?; // Check that the transaction stack hasn't changed while the lock was released, then update // it with the new transaction. @@ -1244,7 +1290,7 @@ impl Thread { inner.push_work_deferred(list_completion); } - if let Err(e) = transaction.submit() { + if let Err(e) = transaction.submit(info) { completion.skip(); // Define `transaction` first to drop it after `inner`. let transaction; @@ -1257,18 +1303,21 @@ impl Thread { } } - fn reply_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { + fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { let orig = self.inner.lock().pop_transaction_to_reply(self)?; if !orig.from.is_current_transaction(&orig) { return Err(EINVAL.into()); } + info.to_tid = orig.from.id; + info.to_pid = orig.from.process.task.pid(); + // We need to complete the transaction even if we cannot complete building the reply. let out = (|| -> BinderResult<_> { let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?; let process = orig.from.process.clone(); let allow_fds = orig.flags & TF_ACCEPT_FDS != 0; - let reply = Transaction::new_reply(self, process, tr, allow_fds)?; + let reply = Transaction::new_reply(self, process, info, allow_fds)?; self.inner.lock().push_work(completion); orig.from.deliver_reply(Ok(reply), &orig); Ok(()) @@ -1289,16 +1338,12 @@ impl Thread { out } - fn oneway_transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -> BinderResult { - // SAFETY: The `handle` field is valid for all possible byte values, so reading from the - // union is okay. - let handle = unsafe { tr.transaction_data.target.handle }; - let node_ref = self.process.get_transaction_node(handle)?; + fn oneway_transaction_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { + let node_ref = self.process.get_transaction_node(info.target_handle)?; + info.to_pid = node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?; - let transaction = Transaction::new(node_ref, None, self, tr)?; - let code = if self.process.is_oneway_spam_detection_enabled() - && transaction.oneway_spam_detected - { + let transaction = Transaction::new(node_ref, None, self, info)?; + let code = if self.process.is_oneway_spam_detection_enabled() && info.oneway_spam_suspect { BR_ONEWAY_SPAM_SUSPECT } else { BR_TRANSACTION_COMPLETE @@ -1306,7 +1351,7 @@ impl Thread { let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?; let completion = list_completion.clone_arc(); self.inner.lock().push_work(list_completion); - match transaction.submit() { + match transaction.submit(info) { Ok(()) => Ok(()), Err(err) => { completion.skip(); @@ -1327,29 +1372,8 @@ impl Thread { GLOBAL_STATS.inc_bc(cmd); self.process.stats.inc_bc(cmd); match cmd { - BC_TRANSACTION => { - let tr = reader.read::()?.with_buffers_size(0); - if tr.transaction_data.flags & TF_ONE_WAY != 0 { - self.transaction(&tr, Self::oneway_transaction_inner); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_TRANSACTION_SG => { - let tr = reader.read::()?; - if tr.transaction_data.flags & TF_ONE_WAY != 0 { - self.transaction(&tr, Self::oneway_transaction_inner); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_REPLY => { - let tr = reader.read::()?.with_buffers_size(0); - self.transaction(&tr, Self::reply_inner) - } - BC_REPLY_SG => { - let tr = reader.read::()?; - self.transaction(&tr, Self::reply_inner) + BC_TRANSACTION | BC_TRANSACTION_SG | BC_REPLY | BC_REPLY_SG => { + self.transaction(cmd, &mut reader)?; } BC_FREE_BUFFER => { let buffer = self.process.buffer_get(reader.read()?); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 10af40527ca7..5dff3d655c4d 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -8,7 +8,7 @@ use kernel::{ seq_print, sync::atomic::{ordering::Relaxed, Atomic}, sync::{Arc, SpinLock}, - task::Kuid, + task::{Kuid, Pid}, time::{Instant, Monotonic}, types::ScopeGuard, }; @@ -24,6 +24,33 @@ use crate::{ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead, }; +#[derive(Zeroable)] +pub(crate) struct TransactionInfo { + pub(crate) from_pid: Pid, + pub(crate) from_tid: Pid, + pub(crate) to_pid: Pid, + pub(crate) to_tid: Pid, + pub(crate) code: u32, + pub(crate) flags: u32, + pub(crate) data_ptr: UserPtr, + pub(crate) data_size: usize, + pub(crate) offsets_ptr: UserPtr, + pub(crate) offsets_size: usize, + pub(crate) buffers_size: usize, + pub(crate) target_handle: u32, + pub(crate) errno: i32, + pub(crate) reply: u32, + pub(crate) oneway_spam_suspect: bool, + pub(crate) is_reply: bool, +} + +impl TransactionInfo { + #[inline] + pub(crate) fn is_oneway(&self) -> bool { + self.flags & TF_ONE_WAY != 0 + } +} + use core::mem::offset_of; use kernel::bindings::rb_transaction_layout; pub(crate) const TRANSACTION_LAYOUT: rb_transaction_layout = rb_transaction_layout { @@ -52,7 +79,6 @@ pub(crate) struct Transaction { data_address: usize, sender_euid: Kuid, txn_security_ctx_off: Option, - pub(crate) oneway_spam_detected: bool, start_time: Instant, } @@ -65,17 +91,16 @@ impl Transaction { node_ref: NodeRef, from_parent: Option>, from: &Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, ) -> BinderResult> { let debug_id = super::next_debug_id(); - let trd = &tr.transaction_data; let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0; let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0; let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None }; let to = node_ref.node.owner.clone(); let mut alloc = match from.copy_transaction_data( to.clone(), - tr, + info, debug_id, allow_fds, txn_security_ctx_off.as_mut(), @@ -88,15 +113,14 @@ impl Transaction { return Err(err); } }; - let oneway_spam_detected = alloc.oneway_spam_detected; - if trd.flags & TF_ONE_WAY != 0 { + if info.is_oneway() { if from_parent.is_some() { pr_warn!("Oneway transaction should not be in a transaction stack."); return Err(EINVAL.into()); } alloc.set_info_oneway_node(node_ref.node.clone()); } - if trd.flags & TF_CLEAR_BUF != 0 { + if info.flags & TF_CLEAR_BUF != 0 { alloc.set_info_clear_on_drop(); } let target_node = node_ref.node.clone(); @@ -110,15 +134,14 @@ impl Transaction { sender_euid: Kuid::current_euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"), is_outstanding: Atomic::new(false), txn_security_ctx_off, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -126,21 +149,19 @@ impl Transaction { pub(crate) fn new_reply( from: &Arc, to: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, allow_fds: bool, ) -> BinderResult> { let debug_id = super::next_debug_id(); - let trd = &tr.transaction_data; - let mut alloc = match from.copy_transaction_data(to.clone(), tr, debug_id, allow_fds, None) - { - Ok(alloc) => alloc, - Err(err) => { - pr_warn!("Failure in copy_transaction_data: {:?}", err); - return Err(err); - } - }; - let oneway_spam_detected = alloc.oneway_spam_detected; - if trd.flags & TF_CLEAR_BUF != 0 { + let mut alloc = + match from.copy_transaction_data(to.clone(), info, debug_id, allow_fds, None) { + Ok(alloc) => alloc, + Err(err) => { + pr_warn!("Failure in copy_transaction_data: {:?}", err); + return Err(err); + } + }; + if info.flags & TF_CLEAR_BUF != 0 { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { @@ -150,15 +171,14 @@ impl Transaction { sender_euid: Kuid::current_euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address: alloc.ptr, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"), is_outstanding: Atomic::new(false), txn_security_ctx_off: None, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -248,7 +268,7 @@ impl Transaction { /// stack, otherwise uses the destination process. /// /// Not used for replies. - pub(crate) fn submit(self: DLArc) -> BinderResult { + pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) -> BinderResult { // Defined before `process_inner` so that the destructor runs after releasing the lock. let mut _t_outdated; @@ -298,6 +318,7 @@ impl Transaction { } let res = if let Some(thread) = self.find_target_thread() { + info.to_tid = thread.id; crate::trace::trace_transaction(false, &self, Some(&thread.task)); match thread.push_work(self) { PushWorkRes::Ok => Ok(()), diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs index f989539a31b4..984c3ec03a7b 100644 --- a/rust/kernel/uaccess.rs +++ b/rust/kernel/uaccess.rs @@ -19,7 +19,7 @@ use core::mem::{size_of, MaybeUninit}; /// /// This is the Rust equivalent to C pointers tagged with `__user`. #[repr(transparent)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Zeroable)] pub struct UserPtr(*mut c_void); impl UserPtr { -- cgit v1.2.3 From ed72cfffc491c88996addd387586234dd8141ee4 Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 24 Mar 2026 20:02:37 +0000 Subject: rust_binder: make use of == for Task Now that we have implemented the == operator for Task, replace the two raw pointer comparisons in Binder with the == operator. Reviewed-by: Gary Guo Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260324-close-fd-check-current-v3-3-b94274bedac7@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index ae26fe817794..312e5c3f14cd 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -682,7 +682,7 @@ impl Process { fn get_current_thread(self: ArcBorrow<'_, Self>) -> Result> { let id = { let current = kernel::current!(); - if !core::ptr::eq(current.group_leader(), &*self.task) { + if self.task != current.group_leader() { pr_err!("get_current_thread was called from the wrong process."); return Err(EINVAL); } @@ -1672,7 +1672,7 @@ impl Process { vma: &mm::virt::VmaNew, ) -> Result { // We don't allow mmap to be used in a different process. - if !core::ptr::eq(kernel::current!().group_leader(), &*this.task) { + if this.task != kernel::current!().group_leader() { return Err(EINVAL); } if vma.start() == 0 { -- cgit v1.2.3 From fc74559e2dd4405492102ada28afa60d012a662f Mon Sep 17 00:00:00 2001 From: Alice Ryhl Date: Tue, 24 Mar 2026 20:02:38 +0000 Subject: rust_binder: check current before closing fds This list gets populated once the transaction is delivered to the target process, at which point it's not touched again except in BC_FREE_BUFFER and process exit, so if the list has been populated then this code should not run in the context of the wrong userspace process. However, why tempt fate? The function itself can run in the context of both the sender and receiver, and if someone can engineer a scenario where it runs in the sender and this list is non-empty (or future Rust Binder changes make such a scenario possible), then that'd be a problem because we'd be closing random unrelated fds in the wrong process. Note that on process exit, the == comparison may actually fail because it's called from a kthread. The fd closing code is a no-op on kthreads, so there is no actual behavior different though. Suggested-by: Jann Horn Signed-off-by: Alice Ryhl Link: https://patch.msgid.link/20260324-close-fd-check-current-v3-4-b94274bedac7@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 97edfb1ff382..0dc4f364d86d 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -257,19 +257,22 @@ impl Drop for Allocation { } } - for &fd in &info.file_list.close_on_free { - let closer = match DeferredFdCloser::new(GFP_KERNEL) { - Ok(closer) => closer, - Err(kernel::alloc::AllocError) => { - // Ignore allocation failures. - break; - } - }; - - // Here, we ignore errors. The operation can fail if the fd is not valid, or if the - // method is called from a kthread. However, this is always called from a syscall, - // so the latter case cannot happen, and we don't care about the first case. - let _ = closer.close_fd(fd); + if self.process.task == kernel::current!().group_leader() { + for &fd in &info.file_list.close_on_free { + let closer = match DeferredFdCloser::new(GFP_KERNEL) { + Ok(closer) => closer, + Err(kernel::alloc::AllocError) => { + // Ignore allocation failures. + break; + } + }; + + // Here, we ignore errors. The operation can fail if the fd is not valid, or if + // the method is called from a kthread. However, this is always called from a + // syscall, so the latter case cannot happen, and we don't care about the first + // case. + let _ = closer.close_fd(fd); + } } if info.clear_on_free { -- cgit v1.2.3 From f698a253e3936ed516aa234495861eb707d608b9 Mon Sep 17 00:00:00 2001 From: Pedro Montes Alcalde Date: Fri, 27 Mar 2026 22:02:51 -0300 Subject: rust_binder: drop startup init log message The "Loaded Rust Binder." message is logged during normal initialization and does not indicate an error/warning condition. Logging it creates unnecessary noise and is inconsistent with other drivers, so this change fixes that Signed-off-by: Pedro Montes Alcalde Acked-by: Carlos Llamas Acked-by: Alice Ryhl Link: https://patch.msgid.link/20260328010250.249131-2-pedro.montes.alcalde@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_main.rs | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index aa5f2a75adb4..dccb7ba3ffc0 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -292,8 +292,6 @@ impl kernel::Module for BinderModule { // SAFETY: The module initializer never runs twice, so we only call this once. unsafe { crate::context::CONTEXTS.init() }; - pr_warn!("Loaded Rust Binder."); - BINDER_SHRINKER.register(c"android-binder")?; // SAFETY: The module is being loaded, so we can initialize binderfs. -- cgit v1.2.3 From e3007a92332d15b085c5d1157ffeea26f03f0aa6 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:42 +0300 Subject: rust_binder: remove "rust_" prefix from tracepoints Remove the "rust_" prefix as the name is part of the uapi, and userspace expects tracepoints to have the old names. Link: https://github.com/Rust-for-Linux/linux/issues/1226 Suggested-by: Alice Ryhl Reviewed-by: Alice Ryhl Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-1-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.h | 4 ++-- drivers/android/binder/trace.rs | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 8ad785c6bd0f..e3adfb93170d 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -15,7 +15,7 @@ #include -TRACE_EVENT(rust_binder_ioctl, +TRACE_EVENT(binder_ioctl, TP_PROTO(unsigned int cmd, unsigned long arg), TP_ARGS(cmd, arg), @@ -30,7 +30,7 @@ TRACE_EVENT(rust_binder_ioctl, TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) ); -TRACE_EVENT(rust_binder_transaction, +TRACE_EVENT(binder_transaction, TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread), TP_ARGS(reply, t, thread), TP_STRUCT__entry( diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 9839901c7151..d54b18ab71a8 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -10,8 +10,8 @@ use kernel::task::Task; use kernel::tracepoint::declare_trace; declare_trace! { - unsafe fn rust_binder_ioctl(cmd: c_uint, arg: c_ulong); - unsafe fn rust_binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); + unsafe fn binder_ioctl(cmd: c_uint, arg: c_ulong); + unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); } #[inline] @@ -22,7 +22,7 @@ fn raw_transaction(t: &Transaction) -> rust_binder_transaction { #[inline] pub(crate) fn trace_ioctl(cmd: u32, arg: usize) { // SAFETY: Always safe to call. - unsafe { rust_binder_ioctl(cmd, arg as c_ulong) } + unsafe { binder_ioctl(cmd, arg as c_ulong) } } #[inline] @@ -33,5 +33,5 @@ pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Ta }; // SAFETY: The raw transaction is valid for the duration of this call. The thread pointer is // valid or null. - unsafe { rust_binder_transaction(reply, raw_transaction(t), thread) } + unsafe { binder_transaction(reply, raw_transaction(t), thread) } } -- cgit v1.2.3 From be3953bb2655fe4571a1b2cd1705bcc5a241a58e Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:43 +0300 Subject: rust_binder: add ioctl/read/write done tracepoints Add Rust Binder tracepoints declarations for `ioctl_done`, `read_done` and `write_done`. Additionally, wire in the new tracepoints into the corresponding Binder call sites. Note that the new tracepoints report final errno-style return values, matching the existing C model for operation completion. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-2-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/process.rs | 7 +++++-- drivers/android/binder/rust_binder_events.h | 21 ++++++++++++++++++++ drivers/android/binder/thread.rs | 2 ++ drivers/android/binder/trace.rs | 30 ++++++++++++++++++++++++++++- 4 files changed, 57 insertions(+), 3 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs index 312e5c3f14cd..820cbd541435 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -1659,11 +1659,14 @@ impl Process { const _IOC_READ_WRITE: u32 = _IOC_READ | _IOC_WRITE; - match _IOC_DIR(cmd) { + let res = match _IOC_DIR(cmd) { _IOC_WRITE => Self::ioctl_write_only(this, file, cmd, &mut user_slice.reader()), _IOC_READ_WRITE => Self::ioctl_write_read(this, file, cmd, user_slice), _ => Err(EINVAL), - } + }; + + crate::trace::trace_ioctl_done(res); + res } pub(crate) fn mmap( diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index e3adfb93170d..4fda8576c01f 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -30,6 +30,27 @@ TRACE_EVENT(binder_ioctl, TP_printk("cmd=0x%x arg=0x%lx", __entry->cmd, __entry->arg) ); +DECLARE_EVENT_CLASS(binder_function_return_class, + TP_PROTO(int ret), + TP_ARGS(ret), + TP_STRUCT__entry( + __field(int, ret) + ), + TP_fast_assign( + __entry->ret = ret; + ), + TP_printk("ret=%d", __entry->ret) +); + +#define DEFINE_RBINDER_FUNCTION_RETURN_EVENT(name) \ +DEFINE_EVENT(binder_function_return_class, name, \ + TP_PROTO(int ret), \ + TP_ARGS(ret)) + +DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); +DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_read_done); +DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_write_done); + TRACE_EVENT(binder_transaction, TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread), TP_ARGS(reply, t, thread), diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 97a5e4acf64c..138c45cecfa0 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1507,6 +1507,7 @@ impl Thread { let mut ret = Ok(()); if req.write_size > 0 { ret = self.write(&mut req); + crate::trace::trace_write_done(ret); if let Err(err) = ret { pr_warn!( "Write failure {:?} in pid:{}", @@ -1523,6 +1524,7 @@ impl Thread { // Go through the work queue. if req.read_size > 0 { ret = self.read(&mut req, wait); + crate::trace::trace_read_done(ret); if ret.is_err() && ret != Err(EINTR) { pr_warn!( "Read failure {:?} in pid:{}", diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index d54b18ab71a8..3b0458e2738c 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -5,12 +5,16 @@ use crate::transaction::Transaction; use kernel::bindings::{rust_binder_transaction, task_struct}; -use kernel::ffi::{c_uint, c_ulong}; +use kernel::error::Result; +use kernel::ffi::{c_int, c_uint, c_ulong}; use kernel::task::Task; use kernel::tracepoint::declare_trace; declare_trace! { unsafe fn binder_ioctl(cmd: c_uint, arg: c_ulong); + unsafe fn binder_ioctl_done(ret: c_int); + unsafe fn binder_read_done(ret: c_int); + unsafe fn binder_write_done(ret: c_int); unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); } @@ -19,12 +23,36 @@ fn raw_transaction(t: &Transaction) -> rust_binder_transaction { t as *const Transaction as rust_binder_transaction } +#[inline] +fn to_errno(ret: Result) -> i32 { + match ret { + Ok(()) => 0, + Err(err) => err.to_errno(), + } +} + #[inline] pub(crate) fn trace_ioctl(cmd: u32, arg: usize) { // SAFETY: Always safe to call. unsafe { binder_ioctl(cmd, arg as c_ulong) } } +#[inline] +pub(crate) fn trace_ioctl_done(ret: Result) { + // SAFETY: Always safe to call. + unsafe { binder_ioctl_done(to_errno(ret)) } +} +#[inline] +pub(crate) fn trace_read_done(ret: Result) { + // SAFETY: Always safe to call. + unsafe { binder_read_done(to_errno(ret)) } +} +#[inline] +pub(crate) fn trace_write_done(ret: Result) { + // SAFETY: Always safe to call. + unsafe { binder_write_done(to_errno(ret)) } +} + #[inline] pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Task>) { let thread = match thread { -- cgit v1.2.3 From 2335167a614eceb506453dc27cc99a186b6eca76 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:44 +0300 Subject: rust_binder: add `wait_for_work` tracepoint Add the Rust Binder `wait_for_work` tracepoint declaration and wire it into the thread read path before selecting the work source. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-3-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.h | 18 ++++++++++++++++++ drivers/android/binder/thread.rs | 11 +++++++++-- drivers/android/binder/trace.rs | 7 +++++++ 3 files changed, 34 insertions(+), 2 deletions(-) (limited to 'drivers/android') diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 4fda8576c01f..62b587c7c494 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -51,6 +51,24 @@ DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_ioctl_done); DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_read_done); DEFINE_RBINDER_FUNCTION_RETURN_EVENT(binder_write_done); +TRACE_EVENT(binder_wait_for_work, + TP_PROTO(bool proc_work, bool transaction_stack, bool thread_todo), + TP_ARGS(proc_work, transaction_stack, thread_todo), + TP_STRUCT__entry( + __field(bool, proc_work) + __field(bool, transaction_stack) + __field(bool, thread_todo) + ), + TP_fast_assign( + __entry->proc_work = proc_work; + __entry->transaction_stack = transaction_stack; + __entry->thread_todo = thread_todo; + ), + TP_printk("proc_work=%d transaction_stack=%d thread_todo=%d", + __entry->proc_work, __entry->transaction_stack, + __entry->thread_todo) +); + TRACE_EVENT(binder_transaction, TP_PROTO(bool reply, rust_binder_transaction t, struct task_struct *thread), TP_ARGS(reply, t, thread), diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 138c45cecfa0..62e293e1a4b8 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1437,11 +1437,18 @@ impl Thread { UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(), self, ); - let (in_pool, use_proc_queue) = { + let (in_pool, has_transaction, thread_todo, use_proc_queue) = { let inner = self.inner.lock(); - (inner.is_looper(), inner.should_use_process_work_queue()) + ( + inner.is_looper(), + inner.current_transaction.is_some(), + !inner.work_list.is_empty(), + inner.should_use_process_work_queue(), + ) }; + crate::trace::trace_wait_for_work(use_proc_queue, has_transaction, thread_todo); + let getter = if use_proc_queue { Self::get_work } else { diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 3b0458e2738c..1f62b2276740 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -15,6 +15,7 @@ declare_trace! { unsafe fn binder_ioctl_done(ret: c_int); unsafe fn binder_read_done(ret: c_int); unsafe fn binder_write_done(ret: c_int); + unsafe fn binder_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool); unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); } @@ -53,6 +54,12 @@ pub(crate) fn trace_write_done(ret: Result) { unsafe { binder_write_done(to_errno(ret)) } } +#[inline] +pub(crate) fn trace_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool) { + // SAFETY: Always safe to call. + unsafe { binder_wait_for_work(proc_work, transaction_stack, thread_todo) } +} + #[inline] pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Task>) { let thread = match thread { -- cgit v1.2.3 From caf3719f335dac62e5626baadb66836907176337 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:45 +0300 Subject: rust_binder: add `transaction_received` tracepoint Add Rust Binder `transaction_received` tracepoint decalaration and wire in the corresponding trace call when a transaction work item is accepted for execution. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-4-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.h | 12 ++++++++++++ drivers/android/binder/trace.rs | 7 +++++++ drivers/android/binder/transaction.rs | 2 ++ 3 files changed, 21 insertions(+) (limited to 'drivers/android') diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 62b587c7c494..8a0b72bf0255 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -99,6 +99,18 @@ TRACE_EVENT(binder_transaction, __entry->reply, __entry->flags, __entry->code) ); +TRACE_EVENT(binder_transaction_received, + TP_PROTO(rust_binder_transaction t), + TP_ARGS(t), + TP_STRUCT__entry( + __field(int, debug_id) + ), + TP_fast_assign( + __entry->debug_id = rust_binder_transaction_debug_id(t); + ), + TP_printk("transaction=%d", __entry->debug_id) +); + #endif /* _RUST_BINDER_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index 1f62b2276740..d96afdb79c65 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -17,6 +17,7 @@ declare_trace! { unsafe fn binder_write_done(ret: c_int); unsafe fn binder_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool); unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); + unsafe fn binder_transaction_received(t: rust_binder_transaction); } #[inline] @@ -70,3 +71,9 @@ pub(crate) fn trace_transaction(reply: bool, t: &Transaction, thread: Option<&Ta // valid or null. unsafe { binder_transaction(reply, raw_transaction(t), thread) } } + +#[inline] +pub(crate) fn trace_transaction_received(t: &Transaction) { + // SAFETY: The raw transaction is valid for the duration of this call. + unsafe { binder_transaction_received(raw_transaction(t)) } +} diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index 5dff3d655c4d..47d5e4d88b07 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -451,6 +451,8 @@ impl DeliverToRead for Transaction { self.drop_outstanding_txn(); + crate::trace::trace_transaction_received(&self); + // When this is not a reply and not a oneway transaction, update `current_transaction`. If // it's a reply, `current_transaction` has already been updated appropriately. if self.target_node.is_some() && tr_sec.transaction_data.flags & TF_ONE_WAY == 0 { -- cgit v1.2.3 From 25917c05ab477bf60f8cf09acb1e9e89de3df61f Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:46 +0300 Subject: rust_binder: add fd translation tracepoints Add Rust Binder tracepoint declarations for both `transaction_fd_send` and `transaction_fd_recv`. Also, wire in the corresponding trace calls where fd objects are serialised/deserialised. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-5-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/allocation.rs | 1 + drivers/android/binder/rust_binder.h | 5 +++++ drivers/android/binder/rust_binder_events.h | 34 +++++++++++++++++++++++++++++ drivers/android/binder/thread.rs | 1 + drivers/android/binder/trace.rs | 13 +++++++++++ 5 files changed, 54 insertions(+) (limited to 'drivers/android') diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs index 0dc4f364d86d..0cab959e4b7e 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -205,6 +205,7 @@ impl Allocation { let res = FileDescriptorReservation::get_unused_fd_flags(bindings::O_CLOEXEC)?; let fd = res.reserved_fd(); self.write::(file_info.buffer_offset, &fd)?; + crate::trace::trace_transaction_fd_recv(self.debug_id, fd, file_info.buffer_offset); reservations.push( Reservation { diff --git a/drivers/android/binder/rust_binder.h b/drivers/android/binder/rust_binder.h index d2284726c025..3936b9d0b8cd 100644 --- a/drivers/android/binder/rust_binder.h +++ b/drivers/android/binder/rust_binder.h @@ -99,4 +99,9 @@ static inline size_t rust_binder_node_debug_id(rust_binder_node t) return *(size_t *) (t + RUST_BINDER_LAYOUT.n.debug_id); } +static inline binder_uintptr_t rust_binder_node_ptr(rust_binder_node t) +{ + return *(binder_uintptr_t *) (t + RUST_BINDER_LAYOUT.n.ptr); +} + #endif diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 8a0b72bf0255..572a4bf7d1d0 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -111,6 +111,40 @@ TRACE_EVENT(binder_transaction_received, TP_printk("transaction=%d", __entry->debug_id) ); +TRACE_EVENT(binder_transaction_fd_send, + TP_PROTO(int t_debug_id, int fd, size_t offset), + TP_ARGS(t_debug_id, fd, offset), + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, fd) + __field(size_t, offset) + ), + TP_fast_assign( + __entry->debug_id = t_debug_id; + __entry->fd = fd; + __entry->offset = offset; + ), + TP_printk("transaction=%d src_fd=%d offset=%zu", + __entry->debug_id, __entry->fd, __entry->offset) +); + +TRACE_EVENT(binder_transaction_fd_recv, + TP_PROTO(int t_debug_id, int fd, size_t offset), + TP_ARGS(t_debug_id, fd, offset), + TP_STRUCT__entry( + __field(int, debug_id) + __field(int, fd) + __field(size_t, offset) + ), + TP_fast_assign( + __entry->debug_id = t_debug_id; + __entry->fd = fd; + __entry->offset = offset; + ), + TP_printk("transaction=%d dest_fd=%d offset=%zu", + __entry->debug_id, __entry->fd, __entry->offset) +); + #endif /* _RUST_BINDER_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 62e293e1a4b8..1feac87026e6 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -712,6 +712,7 @@ impl Thread { core::mem::offset_of!(uapi::binder_fd_object, __bindgen_anon_1.fd); let field_offset = offset + FD_FIELD_OFFSET; + crate::trace::trace_transaction_fd_send(view.alloc.debug_id, fd, field_offset); view.alloc.info_add_fd(file, field_offset, false)?; } diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index d96afdb79c65..c6f39d83314e 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -18,6 +18,8 @@ declare_trace! { unsafe fn binder_wait_for_work(proc_work: bool, transaction_stack: bool, thread_todo: bool); unsafe fn binder_transaction(reply: bool, t: rust_binder_transaction, thread: *mut task_struct); unsafe fn binder_transaction_received(t: rust_binder_transaction); + unsafe fn binder_transaction_fd_send(t_debug_id: c_int, fd: c_int, offset: usize); + unsafe fn binder_transaction_fd_recv(t_debug_id: c_int, fd: c_int, offset: usize); } #[inline] @@ -77,3 +79,14 @@ pub(crate) fn trace_transaction_received(t: &Transaction) { // SAFETY: The raw transaction is valid for the duration of this call. unsafe { binder_transaction_received(raw_transaction(t)) } } + +#[inline] +pub(crate) fn trace_transaction_fd_send(t_debug_id: usize, fd: u32, offset: usize) { + // SAFETY: This function is always safe to call. + unsafe { binder_transaction_fd_send(t_debug_id as c_int, fd as c_int, offset) } +} +#[inline] +pub(crate) fn trace_transaction_fd_recv(t_debug_id: usize, fd: u32, offset: usize) { + // SAFETY: This function is always safe to call. + unsafe { binder_transaction_fd_recv(t_debug_id as c_int, fd as c_int, offset) } +} -- cgit v1.2.3 From 8f3481028b05e3418b6fe7119428ab44a5b2eb20 Mon Sep 17 00:00:00 2001 From: Mohamad Alsadhan Date: Tue, 17 Mar 2026 17:49:47 +0300 Subject: rust_binder: add `command`/`return` tracepoints Add Rust Binder `command` and `return` tracepoint declarations and wire them in where BC commands are parsed and BR return codes are emitted to userspace. Signed-off-by: Mohamad Alsadhan Link: https://patch.msgid.link/20260317-rust-binder-trace-v3-6-6fae4fbcf637@sdhn.cc Signed-off-by: Greg Kroah-Hartman --- drivers/android/binder/rust_binder_events.h | 32 +++++++++++++++++++++++++++++ drivers/android/binder/rust_binder_main.rs | 1 + drivers/android/binder/thread.rs | 1 + drivers/android/binder/trace.rs | 13 ++++++++++++ 4 files changed, 47 insertions(+) (limited to 'drivers/android') diff --git a/drivers/android/binder/rust_binder_events.h b/drivers/android/binder/rust_binder_events.h index 572a4bf7d1d0..1a446787c8b3 100644 --- a/drivers/android/binder/rust_binder_events.h +++ b/drivers/android/binder/rust_binder_events.h @@ -145,6 +145,38 @@ TRACE_EVENT(binder_transaction_fd_recv, __entry->debug_id, __entry->fd, __entry->offset) ); +TRACE_EVENT(binder_command, + TP_PROTO(uint32_t cmd), + TP_ARGS(cmd), + TP_STRUCT__entry( + __field(uint32_t, cmd) + ), + TP_fast_assign( + __entry->cmd = cmd; + ), + TP_printk("cmd=0x%x %s", + __entry->cmd, + _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_command_strings) ? + binder_command_strings[_IOC_NR(__entry->cmd)] : + "unknown") +); + +TRACE_EVENT(binder_return, + TP_PROTO(uint32_t cmd), + TP_ARGS(cmd), + TP_STRUCT__entry( + __field(uint32_t, cmd) + ), + TP_fast_assign( + __entry->cmd = cmd; + ), + TP_printk("cmd=0x%x %s", + __entry->cmd, + _IOC_NR(__entry->cmd) < ARRAY_SIZE(binder_return_strings) ? + binder_return_strings[_IOC_NR(__entry->cmd)] : + "unknown") +); + #endif /* _RUST_BINDER_TRACE_H */ /* This part must be outside protection */ diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index dccb7ba3ffc0..bd26b61b2192 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -116,6 +116,7 @@ impl<'a> BinderReturnWriter<'a> { /// Write a return code back to user space. /// Should be a `BR_` constant from [`defs`] e.g. [`defs::BR_TRANSACTION_COMPLETE`]. fn write_code(&mut self, code: u32) -> Result { + crate::trace::trace_return(code); stats::GLOBAL_STATS.inc_br(code); self.thread.process.stats.inc_br(code); self.writer.write(&code) diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 1feac87026e6..97d5f31e8fe3 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1370,6 +1370,7 @@ impl Thread { while reader.len() >= size_of::() && self.inner.lock().return_work.is_unused() { let before = reader.len(); let cmd = reader.read::()?; + crate::trace::trace_command(cmd); GLOBAL_STATS.inc_bc(cmd); self.process.stats.inc_bc(cmd); match cmd { diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs index c6f39d83314e..5539672d7285 100644 --- a/drivers/android/binder/trace.rs +++ b/drivers/android/binder/trace.rs @@ -20,6 +20,8 @@ declare_trace! { unsafe fn binder_transaction_received(t: rust_binder_transaction); unsafe fn binder_transaction_fd_send(t_debug_id: c_int, fd: c_int, offset: usize); unsafe fn binder_transaction_fd_recv(t_debug_id: c_int, fd: c_int, offset: usize); + unsafe fn binder_command(cmd: u32); + unsafe fn binder_return(ret: u32); } #[inline] @@ -90,3 +92,14 @@ pub(crate) fn trace_transaction_fd_recv(t_debug_id: usize, fd: u32, offset: usiz // SAFETY: This function is always safe to call. unsafe { binder_transaction_fd_recv(t_debug_id as c_int, fd as c_int, offset) } } + +#[inline] +pub(crate) fn trace_command(cmd: u32) { + // SAFETY: This function is always safe to call. + unsafe { binder_command(cmd) } +} +#[inline] +pub(crate) fn trace_return(ret: u32) { + // SAFETY: This function is always safe to call. + unsafe { binder_return(ret) } +} -- cgit v1.2.3