rust_binder: track caller location in BinderError

Record the caller's location in BinderError using #[track_caller] and a
new ErrorLocation wrapper around &'static Location<'static> across all
BinderError constructors and From implementations. ErrorLocation
implements Display by stripping the directory prefix so locations are
formatted as filename.rs:line (e.g. process.rs:422).

In Context::get_manager_node and Process::buffer_alloc, avoid passing
BinderError::new_dead() or BinderError::from() as function pointers to
Option::ok_or_else() and Result::map_err() so that #[track_caller]
captures the call site in rust_binder correctly.

Include the error location in the FailedTransaction debug print and
prepare for recording it in the binder transaction log. Example output:

  rust_binder: 840:4583 transaction async to 7656:0 failed ESRCH, code 1 size 428-16 line thread.rs:711

Assisted-by: LLM
Signed-off-by: Alice Ryhl <aliceryhl@google.com>
Link: https://patch.msgid.link/20260930-binder-transaction-log-v2-2-8a15e9b8cae3@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Alice Ryhl 2026-09-30 13:56:37 +00:00 committed by Greg Kroah-Hartman
parent 32d0ed324f
commit 831f1ddd64
4 changed files with 50 additions and 7 deletions

View File

@ -134,13 +134,13 @@ pub(crate) fn unset_manager_node(&self) {
}
pub(crate) fn get_manager_node(&self, strong: bool) -> Result<NodeRef, BinderError> {
self.manager
Ok(self
.manager
.lock()
.node
.as_ref()
.ok_or_else(BinderError::new_dead)?
.clone(strong)
.map_err(BinderError::from)
.ok_or_else(|| BinderError::new_dead())?
.clone(strong)?)
}
pub(crate) fn for_each_proc<F>(&self, mut func: F)

View File

@ -2,6 +2,7 @@
// Copyright (C) 2025 Google LLC.
use core::panic::Location;
use kernel::fmt;
use kernel::prelude::*;
@ -9,32 +10,65 @@
pub(crate) type BinderResult<T = ()> = core::result::Result<T, BinderError>;
/// Wraps a `&'static Location` to format it as `filename.rs:line` without the directory prefix.
#[derive(Copy, Clone)]
#[repr(transparent)]
pub(crate) struct ErrorLocation(&'static Location<'static>);
// SAFETY: `ErrorLocation` is `repr(transparent)` over a shared reference, which is part of the
// option layout optimization guarantee, so all zeroes is a valid representation for `None`.
unsafe impl pin_init::ZeroableOption for ErrorLocation {}
impl ErrorLocation {
#[track_caller]
pub(crate) const fn caller() -> Self {
Self(Location::caller())
}
}
impl fmt::Display for ErrorLocation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let file = match self.0.file().rsplit_once('/') {
Some((_, file)) => file,
None => self.0.file(),
};
write!(f, "{}:{}", file, self.0.line())
}
}
/// An error that will be returned to userspace via the `BINDER_WRITE_READ` ioctl rather than via
/// errno.
pub(crate) struct BinderError {
pub(crate) reply: u32,
pub(crate) source: Option<Error>,
pub(crate) line: ErrorLocation,
}
impl BinderError {
#[track_caller]
pub(crate) fn new_dead() -> Self {
Self {
reply: BR_DEAD_REPLY,
source: None,
line: ErrorLocation::caller(),
}
}
#[track_caller]
pub(crate) fn new_frozen() -> Self {
Self {
reply: BR_FROZEN_REPLY,
source: None,
line: ErrorLocation::caller(),
}
}
#[track_caller]
pub(crate) fn new_frozen_oneway() -> Self {
Self {
reply: BR_TRANSACTION_PENDING_FROZEN,
source: None,
line: ErrorLocation::caller(),
}
}
@ -46,25 +80,30 @@ pub(crate) fn is_dead(&self) -> bool {
/// Convert an errno into a `BinderError` and store the errno used to construct it. The errno
/// should be stored as the thread's extended error when given to userspace.
impl From<Error> for BinderError {
#[track_caller]
fn from(source: Error) -> Self {
Self {
reply: BR_FAILED_REPLY,
source: Some(source),
line: ErrorLocation::caller(),
}
}
}
impl From<kernel::fs::file::BadFdError> for BinderError {
#[track_caller]
fn from(source: kernel::fs::file::BadFdError) -> Self {
BinderError::from(Error::from(source))
}
}
impl From<kernel::alloc::AllocError> for BinderError {
#[track_caller]
fn from(_: kernel::alloc::AllocError) -> Self {
Self {
reply: BR_FAILED_REPLY,
source: Some(ENOMEM),
line: ErrorLocation::caller(),
}
}
}

View File

@ -1071,7 +1071,10 @@ pub(crate) fn buffer_alloc(
let (new_alloc, addr) = loop {
let mut inner = self.inner.lock();
let mapping = inner.mapping.as_mut().ok_or_else(BinderError::new_dead)?;
let mapping = inner
.mapping
.as_mut()
.ok_or_else(|| BinderError::new_dead())?;
let alloc_request = match mapping.alloc.reserve_new(reserve_new_args)? {
ReserveNew::Success(new_alloc) => break (new_alloc, mapping.address),
ReserveNew::NeedAlloc(request) => request,

View File

@ -1340,7 +1340,7 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu
binder_debug!(
FailedTransaction,
"transaction {} to {}:{} failed {:?}, code {} size {}-{}",
"transaction {} to {}:{} failed {:?}, code {} size {}-{} line {}",
if info.is_reply {
"reply"
} else if info.is_oneway() {
@ -1353,7 +1353,8 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu
err,
info.code,
info.data_size,
info.offsets_size
info.offsets_size,
err.line
);
}
}