summaryrefslogtreecommitdiff
path: root/drivers/android
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2026-08-25 09:38:50 -0700
committerLinus Torvalds <torvalds@linux-foundation.org>2026-08-25 09:38:50 -0700
commit93e4b3076b5f2d853462b9777d083c77fc0b7b23 (patch)
tree9e4eb974354d490ba5b74f05dfb1a3f5e4dc5469 /drivers/android
parent5f5ef9c407cfdc524a1aaeb4196d933f61ecf21a (diff)
parent8992f32c57607bdfaf5de2a2cd26b3b71f3a9d55 (diff)
Merge tag 'char-misc-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc
Pull char/misc/IIO/etc driver updates from Greg KH: "Here is the big set of char, misc, iio, counter, fpga, and other small driver subsystems for 7.3-rc1. Overall, due to some driver removals we only added a bit more code than removed, which was a nice change. Highlights in this merge request are: - Loads of IIO driver updates and additions - binder driver updates (more on that below...) - Removal of the SGI XP and GRU drivers as they are not used anymore and turn out to be pretty insecure overall - Removal of the obsolete ibmasm driver as it's not being used anymore - Coresight driver updates and additions - Mei driver udpates - Counter driver updates - FPGA driver updates - ICC driver updates - lots and lots of other tiny driver updates to resolve reported issues All of these have been in linux-next for a while" * tag 'char-misc-7.3-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (513 commits) iio: chemical: atlas-sensor: use iio_trigger_poll_nested() to fix remove UAF iio: adc: pac1921: fix wrong channel used in trigger handler read iio: light: gp2ap002: re-enable irq if runtime suspend fails iio: light: gp2ap002: Fix unbalanced runtime PM on repeated event writes iio: light: apds9306: fix PM reference leak in apds9306_read_data() iio: gyro: mpu3050: fix sign of raw angular velocity readings iio: srf04: fix pm_runtime handling on probe error path iio: adc: ad4080: configure backend data size iio: adc: adi-axi-adc: add data size support for AD408X backend iio: chemical: atlas-sensor: fix PM reference leak in buffer postenable iio: dac: ad5446: fix OF module device table iio: light: opt4001: Fix reversed GENMASK() arguments in fault count mask iio: light: opt4001: Reject integration times with a non-zero seconds part iio: light: opt4001: Fix incompatible pointer type passed to div_u64_rem() iio: light: opt4001: Fix power down clearing bits of the wrong register iio: light: opt4060: Fix incorrect register name in threshold read error message iio: light: opt4060: Fix pointer type passed to div_u64_rem() iio: light: opt4060: Reject integration times with a non-zero seconds part iio: light: ltrf216a: fix runtime PM reference leak in error path iio: pressure: dps310: fix NULL pointer dereference on ACPI probe ...
Diffstat (limited to 'drivers/android')
-rw-r--r--drivers/android/Kconfig2
-rw-r--r--drivers/android/binder/allocation.rs4
-rw-r--r--drivers/android/binder/debug.rs76
-rw-r--r--drivers/android/binder/defs.rs8
-rw-r--r--drivers/android/binder/freeze.rs125
-rw-r--r--drivers/android/binder/netlink.rs117
-rw-r--r--drivers/android/binder/node.rs87
-rw-r--r--drivers/android/binder/node/wrapper.rs2
-rw-r--r--drivers/android/binder/page_range.rs8
-rw-r--r--drivers/android/binder/process.rs226
-rw-r--r--drivers/android/binder/rust_binder_main.rs39
-rw-r--r--drivers/android/binder/rust_binderfs.c3
-rw-r--r--drivers/android/binder/thread.rs296
-rw-r--r--drivers/android/binder/trace.rs4
-rw-r--r--drivers/android/binder/transaction.rs131
15 files changed, 827 insertions, 301 deletions
diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig
index e2e402c9d175..606a9d07f774 100644
--- a/drivers/android/Kconfig
+++ b/drivers/android/Kconfig
@@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC
config ANDROID_BINDER_IPC_RUST
bool "Rust version of Android Binder IPC Driver"
- depends on RUST && MMU && !ANDROID_BINDER_IPC
+ depends on RUST && MMU && NET && !ANDROID_BINDER_IPC
help
This enables the Rust implementation of the Binder driver.
diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/allocation.rs
index ea5846e4da16..165cb797eb1e 100644
--- a/drivers/android/binder/allocation.rs
+++ b/drivers/android/binder/allocation.rs
@@ -384,8 +384,8 @@ impl<'a> AllocationView<'a> {
BINDER_TYPE_WEAK_BINDER
};
newobj.flags = obj.flags;
- newobj.__bindgen_anon_1.binder = ptr as _;
- newobj.cookie = cookie as _;
+ newobj.__bindgen_anon_1.binder = ptr as uapi::binder_uintptr_t;
+ newobj.cookie = cookie as uapi::binder_uintptr_t;
self.write(offset, &newobj)?;
// Increment the user ref count on the node. It will be decremented as part of the
// destruction of the buffer, when we see a binder or weak-binder object.
diff --git a/drivers/android/binder/debug.rs b/drivers/android/binder/debug.rs
new file mode 100644
index 000000000000..824b10c004c3
--- /dev/null
+++ b/drivers/android/binder/debug.rs
@@ -0,0 +1,76 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (C) 2026 Google LLC.
+
+//! Binder debugging helpers.
+
+#![allow(dead_code)]
+
+use kernel::bits::bit_u32;
+use kernel::sync::atomic::Atomic;
+
+kernel::impl_flags!(
+ /// Represents multiple debug mask flags.
+ #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
+ pub struct DebugMasks(u32);
+
+ /// Represents a single debug mask category.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum DebugMask {
+ UserError = bit_u32(0),
+ FailedTransaction = bit_u32(1),
+ DeadTransaction = bit_u32(2),
+ OpenClose = bit_u32(3),
+ DeadBinder = bit_u32(4),
+ DeathNotification = bit_u32(5),
+ ReadWrite = bit_u32(6),
+ UserRefs = bit_u32(7),
+ Threads = bit_u32(8),
+ Transaction = bit_u32(9),
+ TransactionComplete = bit_u32(10),
+ FreeBuffer = bit_u32(11),
+ InternalRefs = bit_u32(12),
+ PriorityCap = bit_u32(13),
+ Spinlocks = bit_u32(14),
+ }
+);
+
+#[no_mangle]
+pub(crate) static rust_binder_debug_mask: Atomic<u32> = Atomic::new(
+ (DebugMask::UserError as u32)
+ | (DebugMask::FailedTransaction as u32)
+ | (DebugMask::DeadTransaction as u32),
+);
+
+/// Checks if the given debug logging category is enabled in the mask.
+pub(crate) fn debug_mask_enabled(mask: DebugMask) -> bool {
+ let current_mask = rust_binder_debug_mask.load(kernel::sync::atomic::Relaxed);
+ DebugMasks(current_mask).contains(mask)
+}
+
+/// Prints a debug log if the specified mask category is enabled.
+#[macro_export]
+macro_rules! binder_debug {
+ // Rule to explicitly specify a PID (used in kworkers).
+ (pid=$pid:expr, $mask:ident, $($arg:tt)*) => {
+ if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) {
+ kernel::pr_info!(
+ "{}: {}\n",
+ $pid,
+ kernel::prelude::fmt!($($arg)*)
+ );
+ }
+ };
+
+ // Default rule (automatically prepends "PID:TID" of the current calling thread).
+ ($mask:ident, $($arg:tt)*) => {
+ if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) {
+ let thread = kernel::current!();
+ kernel::pr_info!(
+ "{}:{} {}\n",
+ thread.tgid(),
+ thread.pid(),
+ kernel::prelude::fmt!($($arg)*)
+ );
+ }
+ };
+}
diff --git a/drivers/android/binder/defs.rs b/drivers/android/binder/defs.rs
index 33f51b4139c7..8ac9bdd7a499 100644
--- a/drivers/android/binder/defs.rs
+++ b/drivers/android/binder/defs.rs
@@ -4,6 +4,8 @@
use core::mem::MaybeUninit;
use core::ops::{Deref, DerefMut};
+use core::ptr;
+
use kernel::{
transmute::{AsBytes, FromBytes},
uapi::{self, *},
@@ -146,7 +148,7 @@ decl_wrapper!(ExtendedError, uapi::binder_extended_error);
impl BinderVersion {
pub(crate) fn current() -> Self {
Self(MaybeUninit::new(uapi::binder_version {
- protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as _,
+ protocol_version: BINDER_CURRENT_PROTOCOL_VERSION as i32,
}))
}
}
@@ -165,8 +167,8 @@ impl BinderTransactionDataSecctx {
pub(crate) fn tr_data(&mut self) -> &mut BinderTransactionData {
// SAFETY: Transparent wrapper is safe to transmute.
unsafe {
- &mut *(&mut self.transaction_data as *mut uapi::binder_transaction_data
- as *mut BinderTransactionData)
+ &mut *(ptr::from_mut::<uapi::binder_transaction_data>(&mut self.transaction_data)
+ .cast::<BinderTransactionData>())
}
}
}
diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs
index f4df14568b25..66912b4cb527 100644
--- a/drivers/android/binder/freeze.rs
+++ b/drivers/android/binder/freeze.rs
@@ -60,6 +60,7 @@ type UninitFM = UniqueArc<core::mem::MaybeUninit<DTRWrap<FreezeMessage>>>;
/// Represents a notification that the freeze state has changed.
pub(crate) struct FreezeMessage {
cookie: FreezeCookie,
+ pid: i32,
}
kernel::list::impl_list_arc_safe! {
@@ -73,8 +74,8 @@ impl FreezeMessage {
UniqueArc::new_uninit(flags)
}
- fn init(ua: UninitFM, cookie: FreezeCookie) -> DLArc<FreezeMessage> {
- match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie })) {
+ fn init(ua: UninitFM, cookie: FreezeCookie, pid: i32) -> DLArc<FreezeMessage> {
+ match ua.pin_init_with(DTRWrap::new(FreezeMessage { cookie, pid })) {
Ok(msg) => ListArc::from(msg),
Err(err) => match err {},
}
@@ -127,7 +128,7 @@ impl DeliverToRead for FreezeMessage {
}
let mut state_info = BinderFrozenStateInfo::default();
- state_info.is_frozen = is_frozen as u32;
+ state_info.is_frozen = u32::from(is_frozen);
state_info.cookie = freeze.cookie.0;
freeze.is_pending = true;
freeze.last_is_frozen = Some(is_frozen);
@@ -140,7 +141,14 @@ impl DeliverToRead for FreezeMessage {
}
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ binder_debug!(
+ pid = self.pid,
+ DeadTransaction,
+ "undelivered freeze notification, {:016x}",
+ self.cookie.0
+ );
+ }
fn should_sync_wakeup(&self) -> bool {
false
@@ -180,36 +188,61 @@ impl Process {
let msg = FreezeMessage::new(GFP_KERNEL)?;
let alloc = RBTreeNodeReservation::new(GFP_KERNEL)?;
+ let mut afl_vec_alloc = KVVec::new();
+ let mut info;
+ let mut freeze_entry;
let mut node_refs_guard = self.node_refs.lock();
- let node_refs = &mut *node_refs_guard;
- let Some(info) = node_refs.by_handle.get_mut(&handle) else {
- pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n", handle);
- return Err(EINVAL);
- };
- if info.freeze().is_some() {
- pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION already set\n");
- return Err(EINVAL);
- }
- let node_ref = info.node_ref();
- let freeze_entry = node_refs.freeze_listeners.entry(cookie);
-
- if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
- if !dupe.get().allow_duplicate(&node_ref.node) {
- pr_warn!("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n");
+ loop {
+ let node_refs = &mut *node_refs_guard;
+ info = match node_refs.by_handle.get_mut(&handle) {
+ Some(info) => info,
+ None => {
+ binder_debug!(
+ UserError,
+ "BC_REQUEST_FREEZE_NOTIFICATION invalid ref {handle}"
+ );
+ return Err(EINVAL);
+ }
+ };
+ if info.freeze().is_some() {
+ binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION already set");
return Err(EINVAL);
}
- }
+ let node_ref = info.node_ref();
+ freeze_entry = node_refs.freeze_listeners.entry(cookie);
+
+ if let rbtree::Entry::Occupied(ref dupe) = freeze_entry {
+ if !dupe.get().allow_duplicate(&node_ref.node) {
+ binder_debug!(UserError, "BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie");
+ return Err(EINVAL);
+ }
+ }
- // All failure paths must come before this call, and all modifications must come after this
- // call.
- node_ref.node.add_freeze_listener(self, GFP_KERNEL)?;
+ // Now we add to the node's freeze listener list, with retry and re-allocate if the
+ // vector is full.
+ //
+ // To ensure that the node is added atomically, this is the first time we modify any
+ // state. When this call succeeds, all other modifications must occur without the
+ // possibility for any failure paths.
+ match node_ref
+ .node
+ .add_freeze_listener(self, &mut afl_vec_alloc)?
+ {
+ Ok(()) => break,
+ Err(resize_target) => {
+ drop(node_refs_guard);
+ afl_vec_alloc = KVVec::with_capacity(resize_target, GFP_KERNEL)?;
+ node_refs_guard = self.node_refs.lock();
+ }
+ }
+ }
match freeze_entry {
rbtree::Entry::Vacant(entry) => {
entry.insert(
FreezeListener {
cookie,
- node: node_ref.node.clone(),
+ node: info.node_ref().node.clone(),
last_is_frozen: None,
is_pending: false,
is_clearing: false,
@@ -233,7 +266,7 @@ impl Process {
}
*info.freeze() = Some(cookie);
- let msg = FreezeMessage::init(msg, cookie);
+ let msg = FreezeMessage::init(msg, cookie, self.task.pid());
drop(node_refs_guard);
let _ = self.push_work(msg);
Ok(())
@@ -245,18 +278,23 @@ impl Process {
let mut node_refs_guard = self.node_refs.lock();
let node_refs = &mut *node_refs_guard;
let Some(freeze) = node_refs.freeze_listeners.get_mut(&cookie) else {
- pr_warn!("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n", cookie.0);
+ binder_debug!(
+ UserError,
+ "BC_FREEZE_NOTIFICATION_DONE {:016x} not found",
+ cookie.0
+ );
return Err(EINVAL);
};
let mut clear_msg = None;
if freeze.num_pending_duplicates > 0 {
- clear_msg = Some(FreezeMessage::init(alloc, cookie));
+ clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
freeze.num_pending_duplicates -= 1;
freeze.num_cleared_duplicates += 1;
} else {
if !freeze.is_pending {
- pr_warn!(
- "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n",
+ binder_debug!(
+ UserError,
+ "BC_FREEZE_NOTIFICATION_DONE {:016x} not pending",
cookie.0
);
return Err(EINVAL);
@@ -264,7 +302,7 @@ impl Process {
let is_frozen = freeze.node.owner.inner.lock().is_frozen.is_fully_frozen();
if freeze.is_clearing || freeze.last_is_frozen != Some(is_frozen) {
// Immediately send another FreezeMessage.
- clear_msg = Some(FreezeMessage::init(alloc, cookie));
+ clear_msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
}
freeze.is_pending = false;
}
@@ -280,31 +318,44 @@ impl Process {
let handle = hc.handle;
let cookie = FreezeCookie(hc.cookie);
+ let _to_free_fl;
let alloc = FreezeMessage::new(GFP_KERNEL)?;
let mut node_refs_guard = self.node_refs.lock();
let node_refs = &mut *node_refs_guard;
let Some(info) = node_refs.by_handle.get_mut(&handle) else {
- pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n", handle);
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_FREEZE_NOTIFICATION invalid ref {handle}"
+ );
return Err(EINVAL);
};
let Some(info_cookie) = info.freeze() else {
- pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active"
+ );
return Err(EINVAL);
};
if *info_cookie != cookie {
- pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch"
+ );
return Err(EINVAL);
}
let Some(listener) = node_refs.freeze_listeners.get_mut(&cookie) else {
- pr_warn!("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n", handle);
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {handle}"
+ );
return Err(EINVAL);
};
listener.is_clearing = true;
- listener.node.remove_freeze_listener(self);
+ _to_free_fl = listener.node.remove_freeze_listener(self);
*info.freeze() = None;
let mut msg = None;
if !listener.is_pending {
- msg = Some(FreezeMessage::init(alloc, cookie));
+ msg = Some(FreezeMessage::init(alloc, cookie, self.task.pid()));
}
drop(node_refs_guard);
@@ -384,7 +435,7 @@ impl Process {
continue;
};
let msg_alloc = FreezeMessage::new(GFP_KERNEL)?;
- let msg = FreezeMessage::init(msg_alloc, cookie);
+ let msg = FreezeMessage::init(msg_alloc, cookie, proc.task.pid());
batch.push((proc, msg), GFP_KERNEL)?;
}
diff --git a/drivers/android/binder/netlink.rs b/drivers/android/binder/netlink.rs
new file mode 100644
index 000000000000..f34e1009432c
--- /dev/null
+++ b/drivers/android/binder/netlink.rs
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause)
+/* Based on: Documentation/netlink/specs/binder.yaml */
+
+#![allow(unreachable_pub, clippy::wrong_self_convention)]
+use kernel::{
+ net::netlink::{
+ Family,
+ GenlMsg,
+ MulticastGroup,
+ NetlinkSkBuff, //
+ },
+ prelude::*, //
+};
+
+pub static BINDER_NL_FAMILY: Family = Family::const_new(
+ kernel::module::this_module::<crate::LocalModule>(),
+ kernel::uapi::BINDER_FAMILY_NAME,
+ kernel::uapi::BINDER_FAMILY_VERSION,
+ &BINDER_NL_FAMILY_MCGRPS,
+);
+
+static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] = [MulticastGroup::const_new(c"report")];
+
+/// A multicast event sent to userspace subscribers to notify them about
+/// binder transaction failures. The generated report provides the full
+/// details of the specific transaction that failed. The intention is for
+/// programs to monitor these events and react to the failures as needed.
+pub struct Report {
+ skb: GenlMsg,
+}
+
+impl Report {
+ /// Create a new multicast message.
+ pub fn new(
+ size: usize,
+ portid: u32,
+ seq: u32,
+ flags: kernel::alloc::Flags,
+ ) -> Result<Self, kernel::alloc::AllocError> {
+ const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
+ let skb = NetlinkSkBuff::new(size, flags)?;
+ let skb = skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER_CMD_REPORT)?;
+ Ok(Self { skb })
+ }
+
+ /// Broadcast this message.
+ pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Result {
+ self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags)
+ }
+
+ /// Check if this message type has listeners.
+ pub fn has_listeners() -> bool {
+ BINDER_NL_FAMILY.has_listeners(0)
+ }
+
+ /// The enum binder_driver_return_protocol returned to the sender.
+ pub fn error(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_ERROR, val)
+ }
+
+ /// The binder context where the transaction occurred.
+ pub fn context(&mut self, val: &CStr) -> Result {
+ const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
+ self.skb.put_string(BINDER_A_REPORT_CONTEXT, val)
+ }
+
+ /// The PID of the sender process.
+ pub fn from_pid(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val)
+ }
+
+ /// The TID of the sender thread.
+ pub fn from_tid(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val)
+ }
+
+ /// The PID of the recipient process. This attribute may not be present
+ /// if the target could not be determined.
+ pub fn to_pid(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_TO_PID, val)
+ }
+
+ /// The TID of the recipient thread. This attribute may not be present
+ /// if the target could not be determined.
+ pub fn to_tid(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_TO_TID, val)
+ }
+
+ /// When present, indicates the failed transaction is a reply.
+ pub fn is_reply(&mut self) -> Result {
+ const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
+ self.skb.put_flag(BINDER_A_REPORT_IS_REPLY)
+ }
+
+ /// The bitmask of enum transaction_flags from the transaction.
+ pub fn flags(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_FLAGS, val)
+ }
+
+ /// The application-defined code from the transaction.
+ pub fn code(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_CODE, val)
+ }
+
+ /// The transaction payload size in bytes.
+ pub fn data_size(&mut self, val: u32) -> Result {
+ const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
+ self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val)
+ }
+}
diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs
index c10148e9069f..0a82af14cda3 100644
--- a/drivers/android/binder/node.rs
+++ b/drivers/android/binder/node.rs
@@ -9,6 +9,7 @@ use kernel::{
seq_print,
sync::lock::{spinlock::SpinLockBackend, Guard},
sync::{Arc, LockedBy, SpinLock},
+ uapi,
};
use crate::{
@@ -21,6 +22,7 @@ use crate::{
};
use core::mem;
+use core::ptr;
mod wrapper;
pub(crate) use self::wrapper::CritIncrWrapper;
@@ -321,7 +323,7 @@ impl Node {
/// An id that is unique across all binder nodes on the system. Used as the key in the
/// `by_node` map.
pub(crate) fn global_id(&self) -> usize {
- self as *const Node as usize
+ ptr::from_ref(self).addr()
}
pub(crate) fn get_id(&self) -> (u64, u64) {
@@ -333,6 +335,10 @@ impl Node {
death: ListArc<DTRWrap<NodeDeath>, 1>,
guard: &mut Guard<'_, ProcessInner, SpinLockBackend>,
) {
+ assert!(
+ core::ptr::eq(self, &**death.node),
+ "attempt to add NodeDeath to the wrong death list"
+ );
self.inner.access_mut(guard).death_list.push_back(death);
}
@@ -343,7 +349,7 @@ impl Node {
) -> Option<DLArc<Node>> {
let inner = self.inner.access_mut(owner_inner);
if inner.active_inc_refs == 0 {
- pr_err!("inc_ref_done called when no active inc_refs");
+ binder_debug!(UserError, "inc_ref_done called when no active inc_refs");
return None;
}
@@ -464,7 +470,7 @@ impl Node {
owner_inner: &mut ProcessInner,
) -> Option<DLArc<dyn DeliverToRead>> {
match self.incr_refcount_allow_zero2one(strong, owner_inner) {
- Ok(Some(node)) => Some(node as _),
+ Ok(Some(node)) => Some(node as DLArc<dyn DeliverToRead>),
Ok(None) => None,
Err(CouldNotDeliverCriticalIncrement) => {
assert!(strong);
@@ -489,8 +495,8 @@ impl Node {
guard: &Guard<'_, ProcessInner, SpinLockBackend>,
) {
let inner = self.inner.access(guard);
- out.strong_count = inner.strong.count as _;
- out.weak_count = inner.weak.count as _;
+ out.strong_count = inner.strong.count as u32;
+ out.weak_count = inner.weak.count as u32;
}
pub(crate) fn populate_debug_info(
@@ -498,8 +504,8 @@ impl Node {
out: &mut BinderNodeDebugInfo,
guard: &Guard<'_, ProcessInner, SpinLockBackend>,
) {
- out.ptr = self.ptr as _;
- out.cookie = self.cookie as _;
+ out.ptr = self.ptr as uapi::binder_uintptr_t;
+ out.cookie = self.cookie as uapi::binder_uintptr_t;
let inner = self.inner.access(guard);
if inner.strong.has_count {
out.has_strong_ref = 1;
@@ -536,7 +542,7 @@ impl Node {
inner.oneway_todo.push_back(transaction);
} else {
inner.has_oneway_transaction = true;
- guard.push_work(transaction)?;
+ guard.push_work(&self.owner, transaction)?;
}
Ok(())
}
@@ -568,7 +574,7 @@ impl Node {
let transaction = inner.oneway_todo.pop_front();
inner.has_oneway_transaction = transaction.is_some();
if let Some(transaction) = transaction {
- match guard.push_work(transaction) {
+ match guard.push_work(&self.owner, transaction) {
Ok(()) => {}
Err((_err, work)) => {
// Process is dead.
@@ -657,29 +663,26 @@ impl Node {
pub(crate) fn add_freeze_listener(
&self,
process: &Arc<Process>,
- flags: kernel::alloc::Flags,
- ) -> Result {
- let mut vec_alloc = KVVec::<Arc<Process>>::new();
- loop {
- let mut guard = self.owner.inner.lock();
- // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the
- // listener, no the target.
- let inner = self.inner.access_mut(&mut guard);
- let len = inner.freeze_list.len();
- if len >= inner.freeze_list.capacity() {
- if len >= vec_alloc.capacity() {
- drop(guard);
- vec_alloc = KVVec::with_capacity((1 + len).next_power_of_two(), flags)?;
- continue;
- }
- mem::swap(&mut inner.freeze_list, &mut vec_alloc);
- for elem in vec_alloc.drain_all() {
- inner.freeze_list.push_within_capacity(elem)?;
- }
+ // If the vector needs to be resized, it's done via this argument.
+ vec_alloc: &mut KVVec<Arc<Process>>,
+ ) -> Result<Result<(), usize>> {
+ let mut guard = self.owner.inner.lock();
+ // Do not check for `guard.dead`. The `dead` flag that matters here is the owner of the
+ // listener, not the target.
+ let inner = self.inner.access_mut(&mut guard);
+ let len = inner.freeze_list.len();
+ if len == inner.freeze_list.capacity() {
+ if len >= vec_alloc.capacity() {
+ // Request the caller to reallocate.
+ return Ok(Err((1 + len).next_power_of_two()));
+ }
+ mem::swap(&mut inner.freeze_list, vec_alloc);
+ for elem in vec_alloc.drain_all() {
+ inner.freeze_list.push_within_capacity(elem)?;
}
- inner.freeze_list.push_within_capacity(process.clone())?;
- return Ok(());
}
+ inner.freeze_list.push_within_capacity(process.clone())?;
+ Ok(Ok(()))
}
pub(crate) fn remove_freeze_listener(&self, p: &Process) -> KVVec<Arc<Process>> {
@@ -695,6 +698,8 @@ impl Node {
p.pid_in_current_ns()
);
}
+ // If the vector is empty it needs to be freed. However, we can't free it here because that
+ // might sleep, so return it to the caller.
if inner.freeze_list.is_empty() {
return mem::take(&mut inner.freeze_list);
}
@@ -820,6 +825,7 @@ impl NodeRef {
pub(crate) fn clone(&self, strong: bool) -> Result<NodeRef> {
if strong && self.strong_count == 0 {
+ binder_debug!(UserError, "tried to use weak ref as strong ref");
return Err(EINVAL);
}
Ok(self
@@ -860,9 +866,10 @@ impl NodeRef {
*count += 1;
} else {
if *count == 0 {
- pr_warn!(
- "pid {} performed invalid decrement on ref\n",
- kernel::current!().pid()
+ binder_debug!(
+ UserError,
+ "performed invalid {} decrement on ref",
+ if strong { "strong" } else { "weak" }
);
return false;
}
@@ -1104,6 +1111,11 @@ impl DeliverToRead for NodeDeath {
// We're still holding the inner lock, so it cannot be aborted while we insert it into
// the delivered list.
process_inner.death_delivered(self.clone());
+ binder_debug!(
+ DeathNotification,
+ "sending death notification, cookie {:016x}",
+ cookie
+ );
BR_DEAD_BINDER
};
@@ -1114,7 +1126,14 @@ impl DeliverToRead for NodeDeath {
Ok(cmd != BR_DEAD_BINDER)
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ binder_debug!(
+ pid = self.process.task.pid(),
+ DeadTransaction,
+ "undelivered death notification, {:016x}",
+ self.cookie
+ );
+ }
fn should_sync_wakeup(&self) -> bool {
false
diff --git a/drivers/android/binder/node/wrapper.rs b/drivers/android/binder/node/wrapper.rs
index 43294c050502..6e4ca01c941a 100644
--- a/drivers/android/binder/node/wrapper.rs
+++ b/drivers/android/binder/node/wrapper.rs
@@ -21,7 +21,7 @@ impl CritIncrWrapper {
pub(super) fn init(self, node: DArc<Node>) -> DLArc<dyn DeliverToRead> {
match self.inner.pin_init_with(DTRWrap::new(NodeWrapper { node })) {
- Ok(initialized) => ListArc::from(initialized) as _,
+ Ok(initialized) => ListArc::from(initialized) as DLArc<dyn DeliverToRead>,
Err(err) => match err {},
}
}
diff --git a/drivers/android/binder/page_range.rs b/drivers/android/binder/page_range.rs
index e82a5523804f..52ffbf3504e7 100644
--- a/drivers/android/binder/page_range.rs
+++ b/drivers/android/binder/page_range.rs
@@ -312,7 +312,7 @@ impl ShrinkablePageRange {
// SAFETY: This just initializes the pages array.
unsafe {
- let self_ptr = self as *const ShrinkablePageRange;
+ let self_ptr = ptr::from_ref(self);
for i in 0..num_pages {
let info = pages.as_mut_ptr().add(i);
(&raw mut (*info).range).write(self_ptr);
@@ -571,7 +571,7 @@ impl ShrinkablePageRange {
unsafe {
self.iterate(offset, size_of::<T>(), |page, offset, to_copy| {
// SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T.
- let obj_ptr = (out.as_mut_ptr() as *mut u8).add(out_offset);
+ let obj_ptr = out.as_mut_ptr().cast::<u8>().add(out_offset);
// SAFETY: The pointer points is in-bounds of the `out` variable, so it is valid.
page.read_raw(obj_ptr, offset, to_copy)?;
out_offset += to_copy;
@@ -593,7 +593,7 @@ impl ShrinkablePageRange {
unsafe {
self.iterate(offset, size_of_val(obj), |page, offset, to_copy| {
// SAFETY: The sum of `offset` and `to_copy` is bounded by the size of T.
- let obj_ptr = (obj as *const T as *const u8).add(obj_offset);
+ let obj_ptr = ptr::from_ref(obj).cast::<u8>().add(obj_offset);
// SAFETY: We have a reference to the object, so the pointer is valid.
page.write_raw(obj_ptr, offset, to_copy)?;
obj_offset += to_copy;
@@ -712,7 +712,7 @@ unsafe extern "C" fn rust_shrink_free_page(
{
// CAST: The `list_head` field is first in `PageInfo`.
- let info = item as *mut PageInfo;
+ let info = item.cast::<PageInfo>();
// SAFETY: The `range` field of `PageInfo` is immutable.
range_ptr = unsafe { (*info).range };
// SAFETY: The `range` outlives its `PageInfo` values.
diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/process.rs
index 5b8f73ec1931..5372bfbd93b3 100644
--- a/drivers/android/binder/process.rs
+++ b/drivers/android/binder/process.rs
@@ -16,6 +16,7 @@ use core::mem::take;
use kernel::{
bindings,
+ bits::bit_u8,
cred::Credential,
error::Error,
fs::file::{self, File},
@@ -30,9 +31,10 @@ use kernel::{
sync::{
aref::ARef,
lock::{spinlock::SpinLockBackend, Guard},
- Arc, ArcBorrow, CondVar, CondVarTimeoutResult, Mutex, SpinLock, UniqueArc,
+ poll::PollCondVarBox,
+ Arc, ArcBorrow, CondVar, CondVarTimeoutResult, SetOnce, SpinLock, UniqueArc,
},
- task::Task,
+ task::{Pid, Task},
uaccess::{UserSlice, UserSliceReader},
uapi,
workqueue::{self, Work},
@@ -70,9 +72,18 @@ impl Mapping {
}
}
-// bitflags for defer_work.
-const PROC_DEFER_FLUSH: u8 = 1;
-const PROC_DEFER_RELEASE: u8 = 2;
+kernel::impl_flags!(
+ /// Represents multiple deferred work flags.
+ #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
+ pub struct DeferWorks(u8);
+
+ /// Represents a single deferred work category.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum DeferWork {
+ Flush = bit_u8(0),
+ Release = bit_u8(1),
+ }
+);
#[derive(Copy, Clone)]
pub(crate) enum IsFrozen {
@@ -121,7 +132,7 @@ pub(crate) struct ProcessInner {
started_thread_count: u32,
/// Bitmap of deferred work to do.
- defer_work: u8,
+ defer_work: DeferWorks,
/// Number of transactions to be transmitted before processes in freeze_wait
/// are woken up.
@@ -151,7 +162,7 @@ impl ProcessInner {
requested_thread_count: 0,
max_threads: 0,
started_thread_count: 0,
- defer_work: 0,
+ defer_work: DeferWorks::default(),
outstanding_txns: 0,
is_frozen: IsFrozen::No,
sync_recv: false,
@@ -172,21 +183,26 @@ impl ProcessInner {
/// taken while holding the inner process lock.
pub(crate) fn push_work(
&mut self,
+ proc: &Process,
work: DLArc<dyn DeliverToRead>,
) -> Result<(), (BinderError, DLArc<dyn DeliverToRead>)> {
+ let sync = work.should_sync_wakeup();
+
// Try to find a ready thread to which to push the work.
if let Some(thread) = self.ready_threads.pop_front() {
// Push to thread while holding state lock. This prevents the thread from giving up
// (for example, because of a signal) when we're about to deliver work.
- match thread.push_work(work) {
+ match thread.push_work_inner(work, sync) {
PushWorkRes::Ok => Ok(()),
+ PushWorkRes::OkNotifyPoll => {
+ proc.notify_poll(sync);
+ Ok(())
+ }
PushWorkRes::FailedDead(work) => Err((BinderError::new_dead(), work)),
}
} else if self.is_dead {
Err((BinderError::new_dead(), work))
} else {
- let sync = work.should_sync_wakeup();
-
// Didn't find a thread waiting for proc work; this can happen
// in two scenarios:
// 1. All threads are busy handling transactions
@@ -194,17 +210,12 @@ impl ProcessInner {
// the kernel driver soon and pick up this work.
// 2. Threads are using the (e)poll interface, in which case
// they may be blocked on the waitqueue without having been
- // added to waiting_threads. For this case, we just iterate
- // over all threads not handling transaction work, and
- // wake them all up. We wake all because we don't know whether
- // a thread that called into (e)poll is handling non-binder
- // work currently.
+ // added to waiting_threads. For this case, we wake it up
+ // directly.
self.work.push_back(work);
// Wake up polling threads, if any.
- for thread in self.threads.values() {
- thread.notify_if_poll_ready(sync);
- }
+ proc.notify_poll(sync);
Ok(())
}
@@ -227,11 +238,11 @@ impl ProcessInner {
// If we decided that we need to push work, push either to the process or to a thread if
// one is specified.
- if let Some(node) = push {
+ if let Some(pnode) = push {
if let Some(thread) = othread {
- thread.push_work_deferred(node);
+ thread.push_work_deferred(pnode);
} else {
- let _ = self.push_work(node);
+ let _ = self.push_work(&node.owner, pnode);
// Nothing to do: `push_work` may fail if the process is dead, but that's ok as in
// that case, it doesn't care about the notification.
}
@@ -259,7 +270,7 @@ impl ProcessInner {
let push = match wrapper {
None => node
.incr_refcount_allow_zero2one(strong, self)?
- .map(|node| node as _),
+ .map(|node| node as DLArc<dyn DeliverToRead>),
Some(wrapper) => node.incr_refcount_allow_zero2one_with_wrapper(strong, wrapper, self),
};
if let Some(node) = push {
@@ -455,7 +466,13 @@ pub(crate) struct Process {
// Node references are in a different lock to avoid recursive acquisition when
// incrementing/decrementing a node in another process.
#[pin]
- node_refs: Mutex<ProcessNodeRefs>,
+ node_refs: SpinLock<ProcessNodeRefs>,
+
+ // Synchronizes `register_wait` calls to the `PollCondVarBox`.
+ //
+ // The `PollCondVarBox` is not stored here because synchronization is
+ // done for `register_wait` only. Wakeups do not take this lock.
+ poll: SetOnce<PollCondVarBox>,
// Work node for deferred work item.
#[pin]
@@ -489,13 +506,13 @@ impl workqueue::WorkItem for Process {
{
let mut inner = me.inner.lock();
defer = inner.defer_work;
- inner.defer_work = 0;
+ inner.defer_work = DeferWorks::default();
}
- if defer & PROC_DEFER_FLUSH != 0 {
+ if defer.contains(DeferWork::Flush) {
me.deferred_flush();
}
- if defer & PROC_DEFER_RELEASE != 0 {
+ if defer.contains(DeferWork::Release) {
me.deferred_release();
}
}
@@ -510,12 +527,13 @@ impl Process {
cred,
inner <- kernel::new_spinlock!(ProcessInner::new(), "Process::inner"),
pages <- ShrinkablePageRange::new(&super::BINDER_SHRINKER),
- node_refs <- kernel::new_mutex!(ProcessNodeRefs::new(), "Process::node_refs"),
+ node_refs <- kernel::new_spinlock!(ProcessNodeRefs::new(), "Process::node_refs"),
freeze_wait <- kernel::new_condvar!("Process::freeze_wait"),
task: current.group_leader().into(),
defer_work <- kernel::new_work!("Process::defer_work"),
links <- ListLinks::new(),
stats: BinderStats::new(),
+ poll: SetOnce::new(),
}),
GFP_KERNEL,
)?;
@@ -715,7 +733,7 @@ impl Process {
pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
// If push_work fails, drop the work item outside the lock.
- let res = self.inner.lock().push_work(work);
+ let res = self.inner.lock().push_work(self, work);
match res {
Ok(()) => Ok(()),
Err((err, work)) => {
@@ -741,7 +759,7 @@ impl Process {
} else {
(0, 0, 0)
};
- let node_ref = self.get_node(ptr, cookie, flags as _, true, thread)?;
+ let node_ref = self.get_node(ptr, cookie, flags, true, thread)?;
let node = node_ref.node.clone();
self.ctx.set_manager_node(node_ref)?;
self.inner.lock().is_manager = true;
@@ -861,14 +879,17 @@ impl Process {
let handle = unused_id.as_u32();
// Do a lookup again as node may have been inserted before the lock was reacquired.
- if let Some(handle_ref) = refs.by_node.get(&node_ref.node.global_id()) {
- let handle = *handle_ref;
- let info = refs.by_handle.get_mut(&handle).unwrap();
- info.node_ref().absorb(node_ref);
- return Ok(handle);
- }
+ let by_node_slot = match refs.by_node.entry(node_ref.node.global_id()) {
+ rbtree::Entry::Vacant(by_node_slot) => by_node_slot,
+ rbtree::Entry::Occupied(handle_ref) => {
+ // The node was inserted by another thread while we didn't hold the lock.
+ let handle = handle_ref.get();
+ let info = refs.by_handle.get_mut(handle).unwrap();
+ info.node_ref().absorb(node_ref);
+ return Ok(*handle);
+ }
+ };
- let gid = node_ref.node.global_id();
let (info_proc, info_node) = {
let info_init = NodeRefInfo::new(node_ref, handle, self.into());
match info.pin_init_with(info_init) {
@@ -884,6 +905,9 @@ impl Process {
// first thing in `deferred_release`, process cleanup will not miss the items inserted into
// `refs` below.
if self.inner.lock().is_dead {
+ // Explicitly drop the lock so that `info_proc` and `info_node` are dropped outside of
+ // the lock.
+ drop(refs_lock);
return Err(ESRCH);
}
@@ -891,7 +915,7 @@ impl Process {
// `info_node` into the right node's `refs` list.
unsafe { info_proc.node_ref2().node.insert_node_info(info_node) };
- refs.by_node.insert(reserve1.into_node(gid, handle));
+ by_node_slot.insert(handle, reserve1);
by_handle_slot.insert(info_proc, reserve2);
unused_id.acquire();
Ok(handle)
@@ -906,7 +930,13 @@ impl Process {
}
Ok(node_ref)
} else {
- Ok(self.get_node_from_handle(handle, true)?)
+ match self.get_node_from_handle(handle, true) {
+ Ok(node_ref) => Ok(node_ref),
+ Err(err) => {
+ binder_debug!(UserError, "got transaction to invalid handle {handle}");
+ Err(err.into())
+ }
+ }
}
}
@@ -946,15 +976,19 @@ impl Process {
// To preserve original binder behaviour, we only fail requests where the manager tries to
// 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) {
// Clean up death if there is one attached to this node reference.
- if let Some(death) = info.death().take() {
+ //
+ // We remove the entire `info` below, so no need to remove `death` from `info`.
+ if let Some(death) = info.death().as_ref() {
death.set_cleared(true);
- self.remove_from_delivered_deaths(&death);
+ self.remove_from_delivered_deaths(death);
}
// Remove reference from process tables, and from the node's `refs` list.
@@ -971,8 +1005,8 @@ impl Process {
}
}
- refs.by_handle.remove(&handle);
- refs.by_node.remove(&id);
+ _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);
if let Some(shrink) = refs.handle_is_present.shrink_request() {
@@ -987,7 +1021,7 @@ impl Process {
} else {
// All refs are cleared in process exit, so this warning is expected in that case.
if !self.inner.lock().is_dead {
- pr_warn!("{}: no such ref {handle}\n", self.pid_in_current_ns());
+ binder_debug!(UserError, "no such ref {handle}");
}
}
Ok(())
@@ -1008,7 +1042,7 @@ impl Process {
if let Ok(Some(node)) = inner.get_existing_node(ptr, cookie) {
if let Some(node) = node.inc_ref_done_locked(strong, &mut inner) {
// This only fails if the process is dead.
- let _ = inner.push_work(node);
+ let _ = inner.push_work(self, node);
}
}
Ok(())
@@ -1237,16 +1271,26 @@ impl Process {
// Queue BR_ERROR if we can't allocate memory for the death notification.
let death = UniqueArc::new_uninit(GFP_KERNEL).inspect_err(|_| {
thread.push_return_work(BR_ERROR);
+ binder_debug!(
+ DeathNotification,
+ "BC_REQUEST_DEATH_NOTIFICATION failed due to memory allocation failure"
+ );
})?;
let mut refs = self.node_refs.lock();
let Some(info) = refs.by_handle.get_mut(&handle) else {
- pr_warn!("BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}\n");
+ binder_debug!(
+ UserError,
+ "BC_REQUEST_DEATH_NOTIFICATION invalid ref {handle}"
+ );
return Ok(());
};
// Nothing to do if there is already a death notification request for this handle.
if info.death().is_some() {
- pr_warn!("BC_REQUEST_DEATH_NOTIFICATION death notification already set\n");
+ binder_debug!(
+ UserError,
+ "BC_REQUEST_DEATH_NOTIFICATION death notification already set"
+ );
return Ok(());
}
@@ -1274,6 +1318,11 @@ impl Process {
info.node_ref().node.add_death(death, &mut owner_inner);
}
}
+ binder_debug!(
+ DeathNotification,
+ "BC_REQUEST_DEATH_NOTIFICATION handle {handle} cookie {:016x}",
+ cookie
+ );
Ok(())
}
@@ -1283,28 +1332,45 @@ impl Process {
let mut refs = self.node_refs.lock();
let Some(info) = refs.by_handle.get_mut(&handle) else {
- pr_warn!("BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_DEATH_NOTIFICATION invalid ref {handle}"
+ );
return Ok(());
};
let Some(death) = info.death().take() else {
- pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification not active\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_DEATH_NOTIFICATION death notification not active"
+ );
return Ok(());
};
if death.cookie != cookie {
*info.death() = Some(death);
- pr_warn!("BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch\n");
+ binder_debug!(
+ UserError,
+ "BC_CLEAR_DEATH_NOTIFICATION death notification cookie mismatch"
+ );
return Ok(());
}
// Update state and determine if we need to queue a work item. We only need to do it when
// the node is not dead or if the user already completed the death notification.
- if death.set_cleared(false) {
+ let should_schedule = death.set_cleared(false);
+ drop(refs);
+
+ if should_schedule {
if let Some(death) = ListArc::try_from_arc_or_drop(death) {
let _ = thread.push_work_if_looper(death);
}
}
+ binder_debug!(
+ DeathNotification,
+ "BC_CLEAR_DEATH_NOTIFICATION handle {handle} cookie {:016x}",
+ cookie
+ );
Ok(())
}
@@ -1328,6 +1394,7 @@ impl Process {
}
fn deferred_flush(&self) {
+ binder_debug!(pid = self.task.pid(), OpenClose, "flushing process");
let inner = self.inner.lock();
for thread in inner.threads.values() {
thread.exit_looper();
@@ -1335,6 +1402,8 @@ impl Process {
}
fn deferred_release(self: Arc<Self>) {
+ binder_debug!(pid = self.task.pid(), OpenClose, "releasing process");
+
let is_manager = {
let mut inner = self.inner.lock();
inner.is_dead = true;
@@ -1382,13 +1451,11 @@ impl Process {
// SAFETY: We are removing the `NodeRefInfo` from the right node.
unsafe { info.node_ref2().node.remove_node_info(info) };
- // Remove all death notifications from the nodes (that belong to a different process).
- let death = if let Some(existing) = info.death().take() {
- existing
- } else {
- continue;
- };
- death.set_cleared(false);
+ // Clear death notifications from the nodes (that belong to a different process).
+ // No need to remove them from `info` as we clear info below.
+ if let Some(death) = info.death().as_ref() {
+ death.set_cleared(false);
+ }
}
// Clean up freeze listeners.
@@ -1524,6 +1591,15 @@ impl Process {
}
}
}
+
+ pub(crate) fn notify_poll(&self, sync: bool) {
+ if let Some(poll) = self.poll.as_ref() {
+ if sync {
+ poll.notify_sync();
+ }
+ poll.notify_all();
+ }
+ }
}
fn get_frozen_status(data: UserSlice) -> Result {
@@ -1536,13 +1612,13 @@ fn get_frozen_status(data: UserSlice) -> Result {
for ctx in crate::context::get_all_contexts()? {
ctx.for_each_proc(|proc| {
- if proc.task.pid() == info.pid as _ {
+ if proc.task.pid() == info.pid as Pid {
found = true;
let inner = proc.inner.lock();
let txns_pending = inner.txns_pending_locked();
- info.async_recv |= inner.async_recv as u32;
- info.sync_recv |= inner.sync_recv as u32;
- info.sync_recv |= (txns_pending as u32) << 1;
+ info.async_recv |= u32::from(inner.async_recv);
+ info.sync_recv |= u32::from(inner.sync_recv);
+ info.sync_recv |= u32::from(txns_pending) << 1;
}
});
}
@@ -1634,7 +1710,9 @@ impl Process {
/// The file operations supported by `Process`.
impl Process {
pub(crate) fn open(ctx: ArcBorrow<'_, Context>, file: &File) -> Result<Arc<Process>> {
- Self::new(ctx.into(), ARef::from(file.cred()))
+ let proc = Self::new(ctx.into(), ARef::from(file.cred()))?;
+ binder_debug!(OpenClose, "opened process");
+ Ok(proc)
}
pub(crate) fn release(this: Arc<Process>, _file: &File) {
@@ -1642,8 +1720,8 @@ impl Process {
let should_schedule;
{
let mut inner = this.inner.lock();
- should_schedule = inner.defer_work == 0;
- inner.defer_work |= PROC_DEFER_RELEASE;
+ should_schedule = inner.defer_work == DeferWorks::empty();
+ inner.defer_work |= DeferWork::Release;
binderfs_file = inner.binderfs_file.take();
}
@@ -1660,8 +1738,8 @@ impl Process {
let should_schedule;
{
let mut inner = this.inner.lock();
- should_schedule = inner.defer_work == 0;
- inner.defer_work |= PROC_DEFER_FLUSH;
+ should_schedule = inner.defer_work == DeferWorks::empty();
+ inner.defer_work |= DeferWork::Flush;
}
if should_schedule {
@@ -1719,7 +1797,21 @@ impl Process {
table: PollTable<'_>,
) -> Result<u32> {
let thread = this.get_current_thread()?;
- let (from_proc, mut mask) = thread.poll(file, table);
+ {
+ let poll = loop {
+ if let Some(poll) = this.poll.as_ref() {
+ break poll;
+ }
+
+ let poll = PollCondVarBox::new(c"Process::poll", kernel::static_lock_class!())?;
+ // Reuse our existing lock to synchronize callers initializing.
+ let _guard = this.node_refs.lock();
+ this.poll.populate(poll);
+ };
+
+ table.register_wait(file, poll);
+ }
+ let (from_proc, mut mask) = thread.poll()?;
if mask == 0 && from_proc && !this.inner.lock().work.is_empty() {
mask |= bindings::POLLIN;
}
diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
index d6ceebbd5f94..955c4c348f73 100644
--- a/drivers/android/binder/rust_binder_main.rs
+++ b/drivers/android/binder/rust_binder_main.rs
@@ -6,12 +6,6 @@
#![crate_name = "rust_binder"]
#![recursion_limit = "256"]
-#![allow(
- clippy::as_underscore,
- clippy::ref_as_ptr,
- clippy::ptr_as_ptr,
- clippy::cast_lossless
-)]
use kernel::{
bindings::{self, seq_file},
@@ -38,7 +32,10 @@ mod allocation;
mod context;
mod deferred_close;
mod defs;
+#[macro_use]
+mod debug;
mod error;
+mod netlink;
mod node;
mod page_range;
mod process;
@@ -226,6 +223,7 @@ impl<T: ListArcSafe> DTRWrap<T> {
struct DeliverCode {
code: u32,
skip: Atomic<bool>,
+ pid: i32,
}
kernel::list::impl_list_arc_safe! {
@@ -233,10 +231,11 @@ kernel::list::impl_list_arc_safe! {
}
impl DeliverCode {
- fn new(code: u32) -> Self {
+ fn new(code: u32, pid: i32) -> Self {
Self {
code,
skip: Atomic::new(false),
+ pid,
}
}
@@ -261,7 +260,15 @@ impl DeliverToRead for DeliverCode {
Ok(true)
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ if !self.skip.load(Relaxed) {
+ binder_debug!(
+ pid = self.pid,
+ DeadTransaction,
+ "undelivered TRANSACTION_COMPLETE"
+ );
+ }
+ }
fn should_sync_wakeup(&self) -> bool {
false
@@ -289,19 +296,22 @@ fn ptr_align(value: usize) -> Option<usize> {
// SAFETY: We call register in `init`.
static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() };
-struct BinderModule {}
+struct BinderModule {
+ _netlink: kernel::net::netlink::Registration,
+}
impl kernel::Module for BinderModule {
fn init(_module: &'static kernel::ThisModule) -> Result<Self> {
// SAFETY: The module initializer never runs twice, so we only call this once.
unsafe { crate::context::CONTEXTS.init() };
+ let netlink = crate::netlink::BINDER_NL_FAMILY.register()?;
BINDER_SHRINKER.register(c"android-binder")?;
// SAFETY: The module is being loaded, so we can initialize binderfs.
unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? };
- Ok(Self {})
+ Ok(Self { _netlink: netlink })
}
}
@@ -315,9 +325,6 @@ unsafe impl<T> Sync for AssertSync<T> {}
#[no_mangle]
#[used]
pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = {
- // SAFETY: All zeroes is safe for the `file_operations` type.
- let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
-
let ops = kernel::bindings::file_operations {
owner: this_module::<LocalModule>().as_ptr(),
poll: Some(rust_binder_poll),
@@ -327,7 +334,7 @@ pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = {
open: Some(rust_binder_open),
release: Some(rust_binder_release),
flush: Some(rust_binder_flush),
- ..zeroed_ops
+ ..pin_init::zeroed()
};
AssertSync(ops)
};
@@ -418,7 +425,7 @@ unsafe extern "C" fn rust_binder_ioctl(
// SAFETY: We previously set `private_data` in `rust_binder_open`.
let f = unsafe { Arc::<Process>::borrow((*file).private_data) };
// SAFETY: The caller ensures that the file is valid.
- match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd as _, arg as _) {
+ match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd, arg) {
Ok(()) => 0,
Err(err) => err.to_errno() as isize,
}
@@ -512,7 +519,7 @@ unsafe extern "C" fn rust_binder_proc_show(
_: *mut kernel::ffi::c_void,
) -> kernel::ffi::c_int {
// SAFETY: Accessing the private field of `seq_file` is okay.
- let pid = (unsafe { (*ptr).private }) as usize as Pid;
+ let pid = unsafe { (*ptr).private }.addr() as Pid;
// SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which
// this method is called.
let m = unsafe { SeqFile::from_raw(ptr) };
diff --git a/drivers/android/binder/rust_binderfs.c b/drivers/android/binder/rust_binderfs.c
index ade1c4d92499..300cc65562d1 100644
--- a/drivers/android/binder/rust_binderfs.c
+++ b/drivers/android/binder/rust_binderfs.c
@@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc);
char *rust_binder_devices_param = CONFIG_ANDROID_BINDER_DEVICES;
module_param_named(rust_devices, rust_binder_devices_param, charp, 0444);
+extern u32 rust_binder_debug_mask;
+module_param_named(debug_mask, rust_binder_debug_mask, uint, 0644);
+
static dev_t binderfs_dev;
static DEFINE_MUTEX(binderfs_minors_mutex);
static DEFINE_IDA(binderfs_minors);
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index bc0ef8927905..18a14aa8a835 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -9,15 +9,15 @@
use kernel::{
bindings,
- fs::{File, LocalFile},
+ bits::bit_u32,
+ fs::LocalFile,
list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc},
prelude::*,
security,
seq_file::SeqFile,
seq_print,
sync::atomic::{ordering::Relaxed, Atomic},
- sync::poll::{PollCondVar, PollTable},
- sync::{aref::ARef, Arc, SpinLock},
+ sync::{aref::ARef, Arc, CondVar, SpinLock},
task::Task,
uaccess::{UserPtr, UserSlice, UserSliceReader},
uapi,
@@ -30,7 +30,7 @@ use crate::{
process::{GetWorkOrRegister, Process},
ptr_align,
stats::GLOBAL_STATS,
- transaction::{Transaction, TransactionInfo},
+ transaction::{Transaction, TransactionFlag, TransactionFlags, TransactionInfo},
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead,
};
@@ -225,8 +225,10 @@ impl UnusedBufferSpace {
}
}
+#[must_use]
pub(crate) enum PushWorkRes {
Ok,
+ OkNotifyPoll,
FailedDead(DLArc<dyn DeliverToRead>),
}
@@ -234,6 +236,7 @@ impl PushWorkRes {
fn is_ok(&self) -> bool {
match self {
PushWorkRes::Ok => true,
+ PushWorkRes::OkNotifyPoll => true,
PushWorkRes::FailedDead(_) => false,
}
}
@@ -243,7 +246,7 @@ impl PushWorkRes {
struct InnerThread {
/// Determines the looper state of the thread. It is a bit-wise combination of the constants
/// prefixed with `LOOPER_`.
- looper_flags: u32,
+ looper_flags: LooperFlags,
/// Determines whether the looper should return.
looper_need_return: bool,
@@ -270,28 +273,38 @@ struct InnerThread {
extended_error: ExtendedError,
}
-const LOOPER_REGISTERED: u32 = 0x01;
-const LOOPER_ENTERED: u32 = 0x02;
-const LOOPER_EXITED: u32 = 0x04;
-const LOOPER_INVALID: u32 = 0x08;
-const LOOPER_WAITING: u32 = 0x10;
-const LOOPER_WAITING_PROC: u32 = 0x20;
-const LOOPER_POLL: u32 = 0x40;
+kernel::impl_flags!(
+ /// Represents multiple looper flags.
+ #[derive(Debug, Clone, Default, Copy, PartialEq, Eq)]
+ pub struct LooperFlags(u32);
+
+ /// Represents a single looper flag.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum LooperFlag {
+ Registered = bit_u32(0),
+ Entered = bit_u32(1),
+ Exited = bit_u32(2),
+ Invalid = bit_u32(3),
+ Waiting = bit_u32(4),
+ WaitingProc = bit_u32(5),
+ Poll = bit_u32(6),
+ }
+);
impl InnerThread {
- fn new() -> Result<Self> {
+ fn new(pid: i32) -> Result<Self> {
fn next_err_id() -> u32 {
static EE_ID: Atomic<u32> = Atomic::new(0);
EE_ID.fetch_add(1, Relaxed)
}
Ok(Self {
- looper_flags: 0,
+ looper_flags: LooperFlags::default(),
looper_need_return: false,
is_dead: false,
process_work_list: false,
- reply_work: ThreadError::try_new()?,
- return_work: ThreadError::try_new()?,
+ reply_work: ThreadError::try_new(pid)?,
+ return_work: ThreadError::try_new(pid)?,
work_list: List::new(),
current_transaction: None,
extended_error: ExtendedError::new(next_err_id(), BR_OK, 0),
@@ -310,27 +323,32 @@ impl InnerThread {
fn push_work(&mut self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
if self.is_dead {
- PushWorkRes::FailedDead(work)
+ return PushWorkRes::FailedDead(work);
+ }
+ self.work_list.push_back(work);
+ self.process_work_list = true;
+ if self.looper_flags.contains(LooperFlag::Poll) {
+ PushWorkRes::OkNotifyPoll
} else {
- self.work_list.push_back(work);
- self.process_work_list = true;
PushWorkRes::Ok
}
}
- fn push_reply_work(&mut self, code: u32) {
+ fn push_reply_work(&mut self, code: u32) -> PushWorkRes {
if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) {
work.set_error_code(code);
- self.push_work(work);
+ self.push_work(work)
} else {
pr_warn!("Thread reply work is already in use.");
+ PushWorkRes::Ok
}
}
fn push_return_work(&mut self, reply: u32) {
if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) {
work.set_error_code(reply);
- self.push_work(work);
+ // Not notifying: Reply to current thread.
+ let _ = self.push_work(work);
} else {
pr_warn!("Thread return work is already in use.");
}
@@ -373,26 +391,27 @@ impl InnerThread {
}
fn looper_enter(&mut self) {
- self.looper_flags |= LOOPER_ENTERED;
- if self.looper_flags & LOOPER_REGISTERED != 0 {
- self.looper_flags |= LOOPER_INVALID;
+ self.looper_flags |= LooperFlag::Entered;
+ if self.looper_flags.contains(LooperFlag::Registered) {
+ self.looper_flags |= LooperFlag::Invalid;
}
}
fn looper_register(&mut self, valid: bool) {
- self.looper_flags |= LOOPER_REGISTERED;
- if !valid || self.looper_flags & LOOPER_ENTERED != 0 {
- self.looper_flags |= LOOPER_INVALID;
+ self.looper_flags |= LooperFlag::Registered;
+ if !valid || self.looper_flags.contains(LooperFlag::Entered) {
+ self.looper_flags |= LooperFlag::Invalid;
}
}
fn looper_exit(&mut self) {
- self.looper_flags |= LOOPER_EXITED;
+ self.looper_flags |= LooperFlag::Exited;
}
/// Determines whether the thread is part of a pool, i.e., if it is a looper.
fn is_looper(&self) -> bool {
- self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0
+ self.looper_flags
+ .contains_any(LooperFlag::Entered | LooperFlag::Registered)
}
/// Determines whether the thread should attempt to fetch work items from the process queue.
@@ -404,7 +423,7 @@ impl InnerThread {
}
fn poll(&mut self) -> u32 {
- self.looper_flags |= LOOPER_POLL;
+ self.looper_flags |= LooperFlag::Poll;
if self.process_work_list || self.looper_need_return {
bindings::POLLIN
} else {
@@ -422,7 +441,7 @@ pub(crate) struct Thread {
#[pin]
inner: SpinLock<InnerThread>,
#[pin]
- work_condvar: PollCondVar,
+ work_condvar: CondVar,
/// Used to insert this thread into the process' `ready_threads` list.
///
/// INVARIANT: May never be used for any other list than the `self.process.ready_threads`.
@@ -445,7 +464,7 @@ kernel::list::impl_list_item! {
impl Thread {
pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> {
- let inner = InnerThread::new()?;
+ let inner = InnerThread::new(process.task.pid())?;
Arc::pin_init(
try_pin_init!(Thread {
@@ -453,7 +472,7 @@ impl Thread {
process,
task: ARef::from(&**kernel::current!()),
inner <- kernel::new_spinlock!(inner, "Thread::inner"),
- work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"),
+ work_condvar <- kernel::new_condvar!("Thread::work_condvar"),
links <- ListLinks::new(),
links_track <- AtomicTracker::new(),
}),
@@ -470,7 +489,7 @@ impl Thread {
m,
" thread {}: l {:02x} need_return {}\n",
self.id,
- inner.looper_flags,
+ u32::from(inner.looper_flags),
inner.looper_need_return,
);
}
@@ -543,9 +562,9 @@ impl Thread {
return Ok(Some(work));
}
- inner.looper_flags |= LOOPER_WAITING;
+ inner.looper_flags |= LooperFlag::Waiting;
let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
- inner.looper_flags &= !LOOPER_WAITING;
+ inner.looper_flags &= !LooperFlag::Waiting;
if signal_pending {
return Err(EINTR);
@@ -597,9 +616,9 @@ impl Thread {
return Ok(Some(work));
}
- inner.looper_flags |= LOOPER_WAITING | LOOPER_WAITING_PROC;
+ inner.looper_flags |= LooperFlag::Waiting | LooperFlag::WaitingProc;
let signal_pending = self.work_condvar.wait_interruptible_freezable(&mut inner);
- inner.looper_flags &= !(LOOPER_WAITING | LOOPER_WAITING_PROC);
+ inner.looper_flags &= !(LooperFlag::Waiting | LooperFlag::WaitingProc);
if signal_pending || inner.looper_need_return {
// We need to return now. We need to pull the thread off the list of ready threads
@@ -624,7 +643,14 @@ impl Thread {
/// Returns whether the item was successfully pushed. This can only fail if the thread is dead.
pub(crate) fn push_work(&self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {
let sync = work.should_sync_wakeup();
+ self.push_work_inner(work, sync)
+ }
+ pub(crate) fn push_work_inner(
+ &self,
+ work: DLArc<dyn DeliverToRead>,
+ sync: bool,
+ ) -> PushWorkRes {
let res = self.inner.lock().push_work(work);
if res.is_ok() {
@@ -643,7 +669,8 @@ impl Thread {
pub(crate) fn push_work_if_looper(&self, work: DLArc<dyn DeliverToRead>) -> BinderResult {
let mut inner = self.inner.lock();
if inner.is_looper() && !inner.is_dead {
- inner.push_work(work);
+ // Not notifying: Reply to current thread.
+ let _ = inner.push_work(work);
Ok(())
} else {
drop(inner);
@@ -673,9 +700,9 @@ impl Thread {
let strong = obj.hdr.type_ == BINDER_TYPE_BINDER;
// SAFETY: `binder` is a `binder_uintptr_t`; any bit pattern is a valid
// representation.
- let ptr = unsafe { obj.__bindgen_anon_1.binder } as _;
- let cookie = obj.cookie as _;
- let flags = obj.flags as _;
+ let ptr = unsafe { obj.__bindgen_anon_1.binder };
+ let cookie = obj.cookie;
+ let flags = obj.flags;
let node = self
.process
.as_arc_borrow()
@@ -686,7 +713,7 @@ impl Thread {
BinderObjectRef::Handle(obj) => {
let strong = obj.hdr.type_ == BINDER_TYPE_HANDLE;
// SAFETY: `handle` is a `u32`; any bit pattern is a valid representation.
- let handle = unsafe { obj.__bindgen_anon_1.handle } as _;
+ let handle = unsafe { obj.__bindgen_anon_1.handle };
let node = self.process.get_node_from_handle(handle, strong)?;
security::binder_transfer_binder(&self.process.cred, &view.alloc.process.cred)?;
view.transfer_binder_object(offset, obj, strong, node)?;
@@ -728,11 +755,12 @@ impl Thread {
let alloc_offset = match sg_state.unused_buffer_space.claim_next(obj_length) {
Ok(alloc_offset) => alloc_offset,
Err(err) => {
- pr_warn!(
- "Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})",
+ binder_debug!(
+ UserError,
+ "failed to claim space for a BINDER_TYPE_PTR (offset: {}, limit: {}, size: {})",
sg_state.unused_buffer_space.offset,
sg_state.unused_buffer_space.limit,
- obj_length,
+ obj_length
);
return Err(err.into());
}
@@ -743,7 +771,7 @@ impl Thread {
ScatterGatherEntry {
obj_index,
offset: alloc_offset,
- sender_uaddr: obj.buffer as _,
+ sender_uaddr: obj.buffer as usize,
length: obj_length,
pointer_fixups: KVec::new(),
fixup_min_offset: 0,
@@ -811,6 +839,7 @@ impl Thread {
let fds_len = num_fds.checked_mul(size_of::<u32>()).ok_or(EINVAL)?;
if !is_aligned(parent_offset, size_of::<u32>()) {
+ binder_debug!(UserError, "FDA parent offset not aligned correctly");
return Err(EINVAL.into());
}
@@ -829,6 +858,7 @@ impl Thread {
};
if !is_aligned(parent_entry.sender_uaddr, size_of::<u32>()) {
+ binder_debug!(UserError, "FDA parent buffer not aligned correctly");
return Err(EINVAL.into());
}
@@ -850,7 +880,7 @@ impl Thread {
.ok_or(EINVAL)?;
let mut fda_bytes = KVec::new();
- UserSlice::new(UserPtr::from_addr(fda_uaddr as _), fds_len)
+ UserSlice::new(UserPtr::from_addr(fda_uaddr as usize), fds_len)
.read_all(&mut fda_bytes, GFP_KERNEL)?;
if fds_len != fda_bytes.len() {
@@ -912,12 +942,9 @@ impl Thread {
let target_offset_end = fixup_offset.checked_add(fixup_len).ok_or(EINVAL)?;
if fixup_offset < end_of_previous_fixup || offset_end < target_offset_end {
- pr_warn!(
- "Fixups oob {} {} {} {}",
- fixup_offset,
- end_of_previous_fixup,
- offset_end,
- target_offset_end
+ binder_debug!(
+ UserError,
+ "fixups oob {fixup_offset} {end_of_previous_fixup} {offset_end} {target_offset_end}"
);
return Err(EINVAL.into());
}
@@ -925,18 +952,21 @@ impl Thread {
let copy_off = end_of_previous_fixup;
let copy_len = fixup_offset - end_of_previous_fixup;
if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
- pr_warn!("Failed copying into alloc: {:?}", err);
+ binder_debug!(UserError, "failed copying into alloc: {err:?}");
return Err(err.into());
}
if let PointerFixupEntry::Fixup { pointer_value, .. } = fixup {
let res = alloc.write::<u64>(fixup_offset, pointer_value);
if let Err(err) = res {
- pr_warn!("Failed copying ptr into alloc: {:?}", err);
+ binder_debug!(UserError, "failed copying ptr into alloc: {err:?}");
return Err(err.into());
}
}
if let Err(err) = reader.skip(fixup_len) {
- pr_warn!("Failed skipping {} from reader: {:?}", fixup_len, err);
+ binder_debug!(
+ UserError,
+ "failed skipping {fixup_len} from reader: {err:?}"
+ );
return Err(err.into());
}
end_of_previous_fixup = target_offset_end;
@@ -944,7 +974,7 @@ impl Thread {
let copy_off = end_of_previous_fixup;
let copy_len = offset_end - end_of_previous_fixup;
if let Err(err) = alloc.copy_into(&mut reader, copy_off, copy_len) {
- pr_warn!("Failed copying remainder into alloc: {:?}", err);
+ binder_debug!(UserError, "failed copying remainder into alloc: {err:?}");
return Err(err.into());
}
}
@@ -1048,7 +1078,7 @@ impl Thread {
let offset: usize = offset.try_into().map_err(|_| EINVAL)?;
if offset < end_of_previous_object || !is_aligned(offset, size_of::<u32>()) {
- pr_warn!("Got transaction with invalid offset.");
+ binder_debug!(UserError, "got transaction with invalid offset");
return Err(EINVAL.into());
}
@@ -1073,7 +1103,7 @@ impl Thread {
) {
Ok(()) => end_of_previous_object = offset + object.size(),
Err(err) => {
- pr_warn!("Error while translating object.");
+ binder_debug!(UserError, "error while translating object: {err:?}");
return Err(err);
}
}
@@ -1093,15 +1123,12 @@ impl Thread {
)?;
if let Some(sg_state) = sg_state.as_mut() {
- if let Err(err) = self.apply_sg(&mut alloc, sg_state) {
- pr_warn!("Failure in apply_sg: {:?}", err);
- return Err(err);
- }
+ self.apply_sg(&mut alloc, sg_state)?;
}
if let Some((off_out, secctx)) = secctx.as_mut() {
if let Err(err) = alloc.write(secctx_off, secctx.as_bytes()) {
- pr_warn!("Failed to write security context: {:?}", err);
+ binder_debug!(UserError, "failed to write security context: {err:?}");
return Err(err.into());
}
**off_out = secctx_off;
@@ -1115,6 +1142,12 @@ impl Thread {
let mut inner = thread.inner.lock();
inner.pop_transaction_to_reply(thread.as_ref())
} {
+ binder_debug!(
+ DeadTransaction,
+ "release transaction {} in, still active",
+ transaction.debug_id
+ );
+
let reply = Err(BR_DEAD_REPLY);
if !transaction
.from
@@ -1154,7 +1187,7 @@ impl Thread {
transaction.set_outstanding(&mut self.process.inner.lock());
}
- {
+ let ret = {
let mut inner = self.inner.lock();
if !inner.pop_transaction_replied(transaction) {
return false;
@@ -1171,15 +1204,16 @@ impl Thread {
}
match reply {
- Ok(work) => {
- inner.push_work(work);
- }
+ Ok(work) => inner.push_work(work),
Err(code) => inner.push_reply_work(code),
}
- }
+ };
// Notify the thread now that we've released the inner lock.
self.work_condvar.notify_sync();
+ if matches!(ret, PushWorkRes::OkNotifyPoll) {
+ self.process.notify_poll(true);
+ }
false
}
@@ -1232,7 +1266,7 @@ impl Thread {
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.flags = TransactionFlags::from_bits(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);
@@ -1274,16 +1308,36 @@ impl Thread {
ExtendedError::new(info.debug_id as u32, err.reply, source.to_errno());
}
- pr_warn!(
- "{}:{} transaction to {} failed: {err:?}",
- info.from_pid,
- info.from_tid,
- info.to_pid
+ binder_debug!(
+ FailedTransaction,
+ "transaction {} to {}:{} failed {:?}, code {} size {}-{}",
+ if info.is_reply {
+ "reply"
+ } else if info.is_oneway() {
+ "async"
+ } else {
+ "call"
+ },
+ info.to_pid,
+ info.to_tid,
+ err,
+ info.code,
+ info.data_size,
+ info.offsets_size
);
}
}
}
+ 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(())
}
@@ -1294,7 +1348,10 @@ impl Thread {
// 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 list_completion = DTRWrap::arc_try_new(DeliverCode::new(
+ BR_TRANSACTION_COMPLETE,
+ self.process.task.pid(),
+ ))?;
let completion = list_completion.clone_arc();
let transaction = Transaction::new(node_ref, top, self, info)?;
@@ -1303,7 +1360,7 @@ impl Thread {
{
let mut inner = self.inner.lock();
if !transaction.is_stacked_on(&inner.current_transaction) {
- pr_warn!("Transaction stack changed during transaction!");
+ binder_debug!(UserError, "got new transaction with bad transaction stack");
return Err(EINVAL.into());
}
inner.current_transaction = Some(transaction.clone_arc());
@@ -1326,8 +1383,18 @@ impl Thread {
}
fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
- let orig = self.inner.lock().pop_transaction_to_reply(self)?;
+ let orig = match self.inner.lock().pop_transaction_to_reply(self) {
+ Ok(orig) => orig,
+ Err(err) => {
+ binder_debug!(UserError, "got reply transaction with no transaction stack");
+ return Err(err.into());
+ }
+ };
if !orig.from.is_current_transaction(&orig) {
+ binder_debug!(
+ UserError,
+ "got reply transaction with bad transaction stack"
+ );
return Err(EINVAL.into());
}
@@ -1336,11 +1403,15 @@ impl Thread {
// 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 completion = DTRWrap::arc_try_new(DeliverCode::new(
+ BR_TRANSACTION_COMPLETE,
+ self.process.task.pid(),
+ ))?;
let process = orig.from.process.clone();
- let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
+ let allow_fds = orig.flags.contains(TransactionFlag::AcceptFds);
let reply = Transaction::new_reply(self, process, info, allow_fds)?;
- self.inner.lock().push_work(completion);
+ // Not notifying: Reply to current thread.
+ let _ = self.inner.lock().push_work(completion);
orig.from.deliver_reply(Ok(reply), &orig, None);
Ok(())
})()
@@ -1354,11 +1425,11 @@ impl Thread {
info.from_tid,
info.to_pid
);
-
let param = err.source.as_ref().map_or(0, |e| e.to_errno());
let ee = ExtendedError::new(info.debug_id as u32, err.reply, param);
orig.from
.deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee));
+ info.reply = BR_FAILED_REPLY;
err.reply = BR_TRANSACTION_COMPLETE;
err
});
@@ -1376,9 +1447,11 @@ impl Thread {
} else {
BR_TRANSACTION_COMPLETE
};
- let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?;
+ let list_completion =
+ DTRWrap::arc_try_new(DeliverCode::new(code, self.process.task.pid()))?;
let completion = list_completion.clone_arc();
- self.inner.lock().push_work(list_completion);
+ // Not notifying: Reply to current thread.
+ let _ = self.inner.lock().push_work(list_completion);
match transaction.submit(info) {
Ok(()) => Ok(()),
Err(err) => {
@@ -1392,7 +1465,7 @@ impl Thread {
let write_start = req.write_buffer.wrapping_add(req.write_consumed);
let write_len = req.write_size.saturating_sub(req.write_consumed);
let mut reader =
- UserSlice::new(UserPtr::from_addr(write_start as _), write_len as _).reader();
+ UserSlice::new(UserPtr::from_addr(write_start as usize), write_len as usize).reader();
while reader.len() >= size_of::<u32>() && self.inner.lock().return_work.is_unused() {
let before = reader.len();
@@ -1463,7 +1536,7 @@ impl Thread {
let read_start = req.read_buffer.wrapping_add(req.read_consumed);
let read_len = req.read_size.saturating_sub(req.read_consumed);
let mut writer = BinderReturnWriter::new(
- UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(),
+ UserSlice::new(UserPtr::from_addr(read_start as usize), read_len as usize).writer(),
self,
);
let (in_pool, has_transaction, thread_todo, use_proc_queue) = {
@@ -1527,9 +1600,11 @@ impl Thread {
// Write BR_SPAWN_LOOPER if the process needs more threads for its pool.
if has_noop_placeholder && in_pool && self.process.needs_thread() {
- let mut writer =
- UserSlice::new(UserPtr::from_addr(req.read_buffer as _), req.read_size as _)
- .writer();
+ let mut writer = UserSlice::new(
+ UserPtr::from_addr(req.read_buffer as usize),
+ req.read_size as usize,
+ )
+ .writer();
writer.write(&BR_SPAWN_LOOPER)?;
}
Ok(())
@@ -1578,16 +1653,15 @@ impl Thread {
ret
}
- pub(crate) fn poll(&self, file: &File, table: PollTable<'_>) -> (bool, u32) {
- table.register_wait(file, &self.work_condvar);
+ pub(crate) fn poll(&self) -> Result<(bool, u32)> {
let mut inner = self.inner.lock();
- (inner.should_use_process_work_queue(), inner.poll())
+ Ok((inner.should_use_process_work_queue(), inner.poll()))
}
/// Make the call to `get_work` or `get_work_local` return immediately, if any.
pub(crate) fn exit_looper(&self) {
let mut inner = self.inner.lock();
- let should_notify = inner.looper_flags & LOOPER_WAITING != 0;
+ let should_notify = inner.looper_flags.contains(LooperFlag::Waiting);
if should_notify {
inner.looper_need_return = true;
}
@@ -1598,26 +1672,9 @@ impl Thread {
}
}
- pub(crate) fn notify_if_poll_ready(&self, sync: bool) {
- // Determine if we need to notify. This requires the lock.
- let inner = self.inner.lock();
- let notify = inner.looper_flags & LOOPER_POLL != 0 && inner.should_use_process_work_queue();
- drop(inner);
-
- // Now that the lock is no longer held, notify the waiters if we have to.
- if notify {
- if sync {
- self.work_condvar.notify_sync();
- } else {
- self.work_condvar.notify_one();
- }
- }
- }
-
pub(crate) fn release(self: &Arc<Self>) {
self.inner.lock().is_dead = true;
- //self.work_condvar.clear();
self.unwind_transaction_stack();
// Cancel all pending work items.
@@ -1630,14 +1687,16 @@ impl Thread {
#[pin_data]
struct ThreadError {
error_code: Atomic<u32>,
+ pid: i32,
#[pin]
links_track: AtomicTracker,
}
impl ThreadError {
- fn try_new() -> Result<DArc<Self>> {
+ fn try_new(pid: i32) -> Result<DArc<Self>> {
DTRWrap::arc_pin_init(pin_init!(Self {
error_code: Atomic::new(BR_OK),
+ pid,
links_track <- AtomicTracker::new(),
}))
.map(ListArc::into_arc)
@@ -1664,7 +1723,16 @@ impl DeliverToRead for ThreadError {
Ok(true)
}
- fn cancel(self: DArc<Self>) {}
+ fn cancel(self: DArc<Self>) {
+ let code = self.error_code.load(Relaxed);
+ if code != BR_OK {
+ binder_debug!(
+ pid = self.pid,
+ DeadTransaction,
+ "undelivered TRANSACTION_ERROR: {code}"
+ );
+ }
+ }
fn should_sync_wakeup(&self) -> bool {
false
diff --git a/drivers/android/binder/trace.rs b/drivers/android/binder/trace.rs
index 5539672d7285..06aabb3cc2f1 100644
--- a/drivers/android/binder/trace.rs
+++ b/drivers/android/binder/trace.rs
@@ -4,6 +4,8 @@
use crate::transaction::Transaction;
+use core::ptr;
+
use kernel::bindings::{rust_binder_transaction, task_struct};
use kernel::error::Result;
use kernel::ffi::{c_int, c_uint, c_ulong};
@@ -26,7 +28,7 @@ declare_trace! {
#[inline]
fn raw_transaction(t: &Transaction) -> rust_binder_transaction {
- t as *const Transaction as rust_binder_transaction
+ ptr::from_ref(t).cast_mut().cast()
}
#[inline]
diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
index 0e5d07b7e6f0..245f1556b5db 100644
--- a/drivers/android/binder/transaction.rs
+++ b/drivers/android/binder/transaction.rs
@@ -3,6 +3,7 @@
// Copyright (C) 2025 Google LLC.
use kernel::{
+ net::netlink::GENLMSG_DEFAULT_SIZE,
prelude::*,
seq_file::SeqFile,
seq_print,
@@ -11,12 +12,14 @@ use kernel::{
task::{Kuid, Pid},
time::{Instant, Monotonic},
types::ScopeGuard,
+ uapi,
};
use crate::{
allocation::{Allocation, TranslatedFds},
defs::*,
error::{BinderError, BinderResult},
+ netlink::Report,
node::{Node, NodeRef},
process::{Process, ProcessInner},
ptr_align,
@@ -24,6 +27,33 @@ use crate::{
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
};
+kernel::impl_flags!(
+ /// Represents multiple transaction flags.
+ #[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Zeroable)]
+ pub struct TransactionFlags(u32);
+
+ /// Represents a single transaction flag.
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ pub enum TransactionFlag {
+ OneWay = TF_ONE_WAY,
+ AcceptFds = TF_ACCEPT_FDS,
+ ClearBuf = TF_CLEAR_BUF,
+ UpdateTxn = TF_UPDATE_TXN,
+ }
+);
+
+impl TransactionFlags {
+ /// Creates a `TransactionFlags` from a raw `u32` value.
+ pub(crate) fn from_bits(bits: u32) -> Self {
+ Self(bits)
+ }
+
+ /// Checks if the Oneway flag is set.
+ pub(crate) fn is_oneway(self) -> bool {
+ self.contains(TransactionFlag::OneWay)
+ }
+}
+
#[derive(Zeroable)]
pub(crate) struct TransactionInfo {
pub(crate) from_pid: Pid,
@@ -31,7 +61,7 @@ pub(crate) struct TransactionInfo {
pub(crate) to_pid: Pid,
pub(crate) to_tid: Pid,
pub(crate) code: u32,
- pub(crate) flags: u32,
+ pub(crate) flags: TransactionFlags,
pub(crate) data_ptr: UserPtr,
pub(crate) data_size: usize,
pub(crate) offsets_ptr: UserPtr,
@@ -48,7 +78,45 @@ pub(crate) struct TransactionInfo {
impl TransactionInfo {
#[inline]
pub(crate) fn is_oneway(&self) -> bool {
- self.flags & TF_ONE_WAY != 0
+ self.flags.is_oneway()
+ }
+
+ 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(u32::from(self.flags))?;
+ report.code(self.code)?;
+ report.data_size(self.data_size as u32)?;
+
+ report.multicast(0, GFP_KERNEL)?;
+ Ok(())
}
}
@@ -74,7 +142,7 @@ pub(crate) struct Transaction {
allocation: SpinLock<Option<Allocation>>,
is_outstanding: Atomic<bool>,
code: u32,
- pub(crate) flags: u32,
+ pub(crate) flags: TransactionFlags,
data_size: usize,
offsets_size: usize,
data_address: usize,
@@ -120,7 +188,7 @@ impl Transaction {
}
alloc.set_info_oneway_node(node_ref.node.clone());
}
- if info.flags & TF_CLEAR_BUF != 0 {
+ if info.flags.contains(TransactionFlag::ClearBuf) {
alloc.set_info_clear_on_drop();
}
let target_node = node_ref.node.clone();
@@ -160,7 +228,7 @@ impl Transaction {
return Err(err);
}
};
- if info.flags & TF_CLEAR_BUF != 0 {
+ if info.flags.contains(TransactionFlag::ClearBuf) {
alloc.set_info_clear_on_drop();
}
Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
@@ -193,7 +261,7 @@ impl Transaction {
self.from.id,
self.to.task.pid(),
self.code,
- self.flags,
+ u32::from(self.flags),
self.start_time.elapsed().as_millis(),
);
if let Some(target_node) = &self.target_node {
@@ -272,7 +340,7 @@ impl Transaction {
let _t_outdated;
let _oneway_node;
- let oneway = self.flags & TF_ONE_WAY != 0;
+ let oneway = self.flags.is_oneway();
let process = self.to.clone();
let mut process_inner = process.inner.lock();
@@ -283,7 +351,7 @@ impl Transaction {
crate::trace::trace_transaction(false, &self, None);
if process_inner.is_frozen.is_frozen() {
process_inner.async_recv = true;
- if self.flags & TF_UPDATE_TXN != 0 {
+ if self.flags.contains(TransactionFlag::UpdateTxn) {
if let Some(t_outdated) =
target_node.take_outdated_transaction(&self, &mut process_inner)
{
@@ -330,11 +398,15 @@ impl Transaction {
crate::trace::trace_transaction(false, &self, Some(&thread.task));
match thread.push_work(self) {
PushWorkRes::Ok => Ok(()),
+ PushWorkRes::OkNotifyPoll => {
+ process.notify_poll(true);
+ Ok(())
+ }
PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)),
}
} else {
crate::trace::trace_transaction(false, &self, None);
- process_inner.push_work(self)
+ process_inner.push_work(&process, self)
};
drop(process_inner);
@@ -354,7 +426,8 @@ impl Transaction {
return false;
}
- if self.flags & old.flags & (TF_ONE_WAY | TF_UPDATE_TXN) != (TF_ONE_WAY | TF_UPDATE_TXN) {
+ let required = TransactionFlag::OneWay | TransactionFlag::UpdateTxn;
+ if !(self.flags.contains_all(required) && old.flags.contains_all(required)) {
return false;
}
@@ -391,7 +464,7 @@ impl DeliverToRead for Transaction {
writer: &mut BinderReturnWriter<'_>,
) -> Result<bool> {
let send_failed_reply = ScopeGuard::new(|| {
- if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
+ if self.target_node.is_some() && !self.flags.is_oneway() {
let reply = Err(BR_FAILED_REPLY);
self.from.deliver_reply(reply, &self, None);
}
@@ -403,6 +476,14 @@ impl DeliverToRead for Transaction {
} else {
// On failure to process the list, we send a reply back to the sender and ignore the
// transaction on the recipient.
+ binder_debug!(
+ FailedTransaction,
+ "transaction {} to {} failed, fd fixups failed, size {}-{}",
+ self.debug_id,
+ self.to.task.pid(),
+ self.data_size,
+ self.offsets_size
+ );
return Ok(true);
};
@@ -410,20 +491,21 @@ impl DeliverToRead for Transaction {
let tr = tr_sec.tr_data();
if let Some(target_node) = &self.target_node {
let (ptr, cookie) = target_node.get_id();
- tr.target.ptr = ptr as _;
- tr.cookie = cookie as _;
+ tr.target.ptr = ptr as uapi::binder_uintptr_t;
+ tr.cookie = cookie as uapi::binder_uintptr_t;
};
tr.code = self.code;
- tr.flags = self.flags;
- tr.data_size = self.data_size as _;
- tr.data.ptr.buffer = self.data_address as _;
- tr.offsets_size = self.offsets_size as _;
+ tr.flags = u32::from(self.flags);
+ tr.data_size = self.data_size as uapi::binder_size_t;
+ tr.data.ptr.buffer = self.data_address as uapi::binder_uintptr_t;
+ tr.offsets_size = self.offsets_size as uapi::binder_size_t;
if tr.offsets_size > 0 {
- tr.data.ptr.offsets = (self.data_address + ptr_align(self.data_size).unwrap()) as _;
+ tr.data.ptr.offsets =
+ (self.data_address + ptr_align(self.data_size).unwrap()) as uapi::binder_uintptr_t;
}
tr.sender_euid = self.sender_euid.into_uid_in_current_ns();
tr.sender_pid = 0;
- if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
+ if self.target_node.is_some() && !self.flags.is_oneway() {
// Not a reply and not one-way.
tr.sender_pid = self.from.process.pid_in_current_ns();
}
@@ -475,16 +557,23 @@ impl DeliverToRead for Transaction {
drop(allocation);
// If this is not a reply or oneway transaction, then send a dead reply.
- if self.target_node.is_some() && self.flags & TF_ONE_WAY == 0 {
+ if self.target_node.is_some() && !self.flags.is_oneway() {
let reply = Err(BR_DEAD_REPLY);
self.from.deliver_reply(reply, &self, None);
+ } else {
+ binder_debug!(
+ pid = self.to.task.pid(),
+ DeadTransaction,
+ "undelivered transaction {}, process died",
+ self.debug_id
+ );
}
self.drop_outstanding_txn();
}
fn should_sync_wakeup(&self) -> bool {
- self.flags & TF_ONE_WAY == 0
+ !self.flags.is_oneway()
}
fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result<()> {