blob: d0f017b7d30bca4ae80970ffe1cdd55b5cb558be [file] [log] [blame]
// Copyright 2025 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use crate::objects::{LocalBinderObject, TransactionData};
use crate::process::{BinderProcess, BinderProcessGuard};
use starnix_core::mm::{MemoryAccessor, MemoryAccessorExt};
use starnix_core::task::{
CurrentTask, EventHandler, Kernel, SchedulerState, SimpleWaiter, Task, WaitCanceler, WaitQueue,
Waiter,
};
use starnix_logging::{log_trace, log_warn, trace_instaflow_begin, trace_instaflow_end};
use starnix_sync::{Mutex, MutexGuard, ordered_lock};
use starnix_uapi::vfs::FdEvents;
use starnix_types::ownership::{
OwnedRef, Releasable, ReleaseGuard, TempRef, WeakRef, release_on_error,
};
use starnix_types::user_buffer::UserBuffer;
use starnix_uapi::errors::{EACCES, EPERM, Errno};
use starnix_uapi::user_address::UserRef;
use starnix_uapi::{
binder_driver_return_protocol, binder_driver_return_protocol_BR_ACQUIRE,
binder_driver_return_protocol_BR_CLEAR_DEATH_NOTIFICATION_DONE,
binder_driver_return_protocol_BR_CLEAR_FREEZE_NOTIFICATION_DONE,
binder_driver_return_protocol_BR_DEAD_BINDER, binder_driver_return_protocol_BR_DEAD_REPLY,
binder_driver_return_protocol_BR_DECREFS, binder_driver_return_protocol_BR_ERROR,
binder_driver_return_protocol_BR_FAILED_REPLY, binder_driver_return_protocol_BR_FROZEN_BINDER,
binder_driver_return_protocol_BR_FROZEN_REPLY, binder_driver_return_protocol_BR_INCREFS,
binder_driver_return_protocol_BR_RELEASE, binder_driver_return_protocol_BR_REPLY,
binder_driver_return_protocol_BR_SPAWN_LOOPER, binder_driver_return_protocol_BR_TRANSACTION,
binder_driver_return_protocol_BR_TRANSACTION_COMPLETE,
binder_driver_return_protocol_BR_TRANSACTION_PENDING_FROZEN,
binder_driver_return_protocol_BR_TRANSACTION_SEC_CTX, binder_frozen_state_info,
binder_transaction_data, binder_uintptr_t, errno, error, pid_t,
};
use std::collections::VecDeque;
use std::ops::{Deref, DerefMut};
use std::sync::atomic::{AtomicBool, AtomicU8, Ordering};
use zerocopy::{Immutable, IntoBytes};
/// The trace category used for binder command tracing.
const TRACE_CATEGORY: &'static str = "starnix:binder";
#[derive(Default, Debug)]
pub struct CommandQueueWithWaitQueue {
pub commands: VecDeque<(Command, CommandTraceGuard)>,
pub waiters: WaitQueue,
}
impl CommandQueueWithWaitQueue {
pub fn is_empty(&self) -> bool {
self.commands.is_empty()
}
pub fn pop_front(&mut self) -> Option<Command> {
// Dropping the guard will terminate the trace flow.
self.commands.pop_front().map(|(command, _guard)| command)
}
pub fn push_back(&mut self, command: Command) {
let guard = command.begin_trace_flow();
self.commands.push_back((command, guard));
self.waiters.notify_fd_events(FdEvents::POLLIN);
}
pub fn has_waiters(&self) -> bool {
!self.waiters.is_empty()
}
pub fn query_events(&self) -> FdEvents {
if self.is_empty() { FdEvents::POLLOUT } else { FdEvents::POLLIN | FdEvents::POLLOUT }
}
pub fn wait_async_simple(&self, waiter: &mut SimpleWaiter) {
self.waiters.wait_async_simple(waiter);
}
pub fn wait_async_fd_events(
&self,
waiter: &Waiter,
events: FdEvents,
handler: EventHandler,
) -> WaitCanceler {
self.waiters.wait_async_fd_events(waiter, events, handler)
}
pub fn notify_all(&self) {
self.waiters.notify_all();
}
}
/// transaction's `sender_thread`.
pub(crate) fn generate_dead_replies(
commands: VecDeque<(Command, CommandTraceGuard)>,
target_proc: u64,
target_thread: Option<i32>,
) {
// Notify all callers that had transactions scheduled for this process that the recipient is
// dead.
for (command, _trace_guard) in commands {
if let Command::Transaction { sender, .. } = command {
if let Some(sender_thread) = sender.thread.upgrade() {
let sender_thread = &mut sender_thread.lock();
generate_dead_replies_for_transactions(sender_thread, target_proc, target_thread);
}
}
}
}
/// Generates dead replies for all the `sender_thread`'s transactions targeting `target_thread`
/// and `target_proc`.
///
/// If a transaction has a target thread specified, it must match `target_thread` in order to be
/// marked dead. If a transaction does not specify a target thread, it is marked dead if the
/// target process matches `target_proc`.
///
/// If the top transaction of the transaction's `sender_thread` is targeting `target_thread` or
/// `target_proc`, the transaction is popped and a `DeadReply` command is enqueued on the
/// transaction's `sender_thread`.
pub(crate) fn generate_dead_replies_for_transactions(
sender_thread: &mut BinderThreadState,
target_proc: u64,
target_thread: Option<i32>,
) {
if let Some((top_transaction, remaining_transactions)) =
sender_thread.transactions.split_last_mut()
{
// Check if the currently active transaction of the sender_thread is
// targeting this process. If it is, that means that we might need
// to pop the transaction and schedule a dead reply.
let top_transaction_was_marked_dead = top_transaction.mark_dead(target_proc, target_thread);
// Mark all other sender_thread transactions as dead if they are targeting
// a dead process.
for transaction in remaining_transactions {
transaction.mark_dead(target_proc, target_thread);
}
// If the top transaction is targeting this process, then pop the
// transaction and enqueue the `DeadReply`.
if top_transaction_was_marked_dead {
let _ = sender_thread.transactions.pop();
sender_thread.enqueue_command(Command::DeadReply);
}
}
}
#[derive(Debug)]
pub struct BinderThread {
/// Weak reference to self.
pub weak_self: WeakRef<BinderThread>,
pub tid: pid_t,
/// The mutable state of the binder thread, protected by a single lock.
pub state: Mutex<BinderThreadState>,
/// A hint as to whether or not the thread is available. This is eventually consistent with the
/// state protected by the lock.
pub available: AtomicBool,
/// A hint as to the registration state of the thread. This is eventually consistent with the
/// state protected by the lock.
pub registration: AtomicU8,
/// A reference to the wait queue for the command queue. This allows other threads to notify
/// this thread without acquiring the lock.
pub command_queue_waiters: WaitQueue,
}
impl BinderThread {
pub fn new(binder_proc: &BinderProcessGuard<'_>, tid: pid_t) -> OwnedRef<Self> {
let inner_state = BinderThreadState::new(tid, binder_proc.base.identifier);
let command_queue_waiters = inner_state.command_queue.waiters.clone();
let state = Mutex::new(inner_state);
#[cfg(any(test, debug_assertions))]
{
// The state must be acquired after the mutable state from the `BinderProcess` and before
// `command_queue`. `binder_proc` being a guard, the mutable state of `BinderProcess` is
// already locked.
let _l1 = state.lock();
let _l2 = binder_proc.base.command_queue.lock();
}
OwnedRef::new_cyclic(|weak_self| Self {
weak_self,
tid,
state,
available: AtomicBool::new(false),
registration: AtomicU8::new(RegistrationState::Unregistered.to_u8()),
command_queue_waiters,
})
}
/// Acquire the lock to the binder thread's mutable state.
pub fn lock(&self) -> BinderThreadGuard<'_> {
BinderThreadGuard { guard: self.state.lock(), thread: self }
}
pub fn lock_both<'a>(
t1: &'a Self,
t2: &'a Self,
) -> (BinderThreadGuard<'a>, BinderThreadGuard<'a>) {
let (g1, g2) = ordered_lock(&t1.state, &t2.state);
(BinderThreadGuard { guard: g1, thread: t1 }, BinderThreadGuard { guard: g2, thread: t2 })
}
}
impl Releasable for BinderThread {
type Context<'a> = &'a Kernel;
fn release<'a>(self, context: Self::Context<'a>) {
self.state.into_inner().release(context);
}
}
/// The mutable state of a binder thread.
#[derive(Debug)]
pub struct BinderThreadState {
pub tid: pid_t,
/// The process identifier of the `BinderProcess` to which this thread belongs. Note that this
/// is not the same as the actual `pid` of the process.
pub process_identifier: u64,
/// The registered state of the thread.
pub registration: RegistrationState,
/// The stack of transactions that are active for this thread.
pub transactions: Vec<TransactionRole>,
/// The binder driver uses this queue to communicate with a binder thread. When a binder thread
/// issues a [`uapi::BINDER_WRITE_READ`] ioctl, it will read from this command queue.
pub command_queue: CommandQueueWithWaitQueue,
/// The thread should finish waiting without returning anything, then reset the flag. Used by
/// kick_all_threads by flush.
pub request_kick: bool,
}
pub struct BinderThreadGuard<'a> {
guard: MutexGuard<'a, BinderThreadState>,
thread: &'a BinderThread,
}
impl Deref for BinderThreadGuard<'_> {
type Target = BinderThreadState;
fn deref(&self) -> &Self::Target {
self.guard.deref()
}
}
impl DerefMut for BinderThreadGuard<'_> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.guard.deref_mut()
}
}
impl Drop for BinderThreadGuard<'_> {
fn drop(&mut self) {
self.thread.available.store(self.guard.is_available(), Ordering::Release);
self.thread.registration.store(self.guard.registration.to_u8(), Ordering::Release);
}
}
impl BinderThreadState {
pub fn new(tid: pid_t, process_identifier: u64) -> Self {
Self {
tid,
process_identifier,
registration: RegistrationState::default(),
transactions: Default::default(),
command_queue: Default::default(),
request_kick: false,
}
}
pub fn is_main_or_registered(&self) -> bool {
self.registration != RegistrationState::Unregistered
}
pub fn is_available(&self) -> bool {
if !self.is_main_or_registered() {
log_trace!("thread {:?} not registered {:?}", self.tid, self.registration);
return false;
}
if !self.command_queue.is_empty() {
log_trace!("thread {:?} has non empty queue", self.tid);
return false;
}
if !self.command_queue.has_waiters() {
log_trace!("thread {:?} is not waiting", self.tid);
return false;
}
if !self.transactions.is_empty() {
log_trace!("thread {:?} is in a transaction {:?}", self.tid, self.transactions);
return false;
}
log_trace!("thread {:?} is available", self.tid);
true
}
/// Handle a binder thread's request to register itself with the binder driver.
/// This makes the binder thread eligible for receiving commands from the driver.
pub fn handle_looper_registration(
&mut self,
binder_process: &mut BinderProcessGuard<'_>,
registration: RegistrationState,
) -> Result<(), Errno> {
log_trace!("BinderThreadState id={} looper registration", self.tid);
if self.is_main_or_registered() {
// This thread is already registered.
error!(EINVAL)
} else {
if registration == RegistrationState::Auxilliary {
binder_process.thread_requested = false;
}
self.registration = registration;
Ok(())
}
}
/// Enqueues `command` for the thread and wakes it up if necessary.
pub fn enqueue_command(&mut self, command: Command) {
log_trace!("BinderThreadState id={} enqueuing command {:?}", self.tid, command);
self.command_queue.push_back(command);
}
/// Get the binder process and thread to reply to, or fail if there is no ongoing transaction or
/// the calling process/thread are dead.
pub fn pop_transaction_caller(
&mut self,
current_task: &CurrentTask,
) -> Result<
(TempRef<'static, BinderProcess>, TempRef<'static, BinderThread>, SchedulerGuard),
TransactionError,
> {
let transaction = self.transactions.pop().ok_or_else(|| errno!(EINVAL))?;
match transaction {
TransactionRole::Receiver(peer, scheduler_state) => {
log_trace!(
"binder transaction popped from thread {} for peer {:?}",
self.tid,
peer
);
let (process, thread) = release_on_error!(scheduler_state, current_task, {
peer.upgrade().ok_or(TransactionError::Dead)
});
Ok((process, thread, scheduler_state))
}
TransactionRole::Sender(_) => {
log_warn!("caller got confused, nothing to reply to!");
error!(EINVAL)?
}
}
}
pub fn has_pending_transactions(&self) -> bool {
!self.transactions.is_empty()
}
}
impl Releasable for BinderThreadState {
type Context<'a> = &'a Kernel;
fn release<'a>(self, context: Self::Context<'a>) {
log_trace!("Dropping BinderThreadState id={}", self.tid);
// If there are any transactions queued, we need to tell the caller that this thread is now
// dead.
generate_dead_replies(self.command_queue.commands, self.process_identifier, Some(self.tid));
// If there are any transactions that this thread was processing, we need to tell the caller
// that this thread is now dead and to not expect a reply.
// The scheduler state need to be restored to the initial one.
let mut updated_scheduler_state = false;
for transaction in self.transactions {
if let TransactionRole::Receiver(peer, scheduler_state) = transaction {
if !updated_scheduler_state {
updated_scheduler_state = scheduler_state.release_for_task(context, self.tid);
} else {
scheduler_state.disarm();
}
if let Some(peer_thread) = peer.thread.upgrade() {
let sender_thread = &mut peer_thread.lock();
generate_dead_replies_for_transactions(
sender_thread,
self.process_identifier,
Some(self.tid),
);
}
}
}
}
}
/// The registration state of a thread.
#[derive(Debug, PartialEq, Eq)]
pub enum RegistrationState {
/// The thread is not registered.
Unregistered,
/// The thread is the main binder thread.
Main,
/// The thread is an auxiliary binder thread.
Auxilliary,
}
impl RegistrationState {
pub fn to_u8(&self) -> u8 {
match self {
Self::Unregistered => 0,
Self::Main => 1,
Self::Auxilliary => 2,
}
}
}
impl Default for RegistrationState {
fn default() -> Self {
Self::Unregistered
}
}
/// A pair of weak references to the process and thread of a binder transaction peer.
#[derive(Debug)]
pub struct WeakBinderPeer {
pub proc: WeakRef<BinderProcess>,
pub thread: WeakRef<BinderThread>,
}
impl WeakBinderPeer {
pub fn new(proc: &BinderProcess, thread: &BinderThread) -> Self {
Self { proc: proc.weak_self.clone(), thread: thread.weak_self.clone() }
}
/// Upgrades the process and thread weak references as a tuple.
pub fn upgrade(
&self,
) -> Option<(TempRef<'static, BinderProcess>, TempRef<'static, BinderThread>)> {
self.proc
.upgrade()
.map(TempRef::into_static)
.zip(self.thread.upgrade().map(TempRef::into_static))
}
}
/// Commands for a binder thread to execute.
#[derive(Debug)]
pub enum Command {
/// Notifies a binder thread that a remote process acquired a strong reference to the specified
/// binder object. The object should not be destroyed until a [`Command::ReleaseRef`] is
/// delivered.
AcquireRef(LocalBinderObject),
/// Notifies a binder thread that there are no longer any remote processes holding strong
/// references to the specified binder object. The object may still have references within the
/// owning process.
ReleaseRef(LocalBinderObject),
/// Notifies a binder thread that a remote process acquired a weak reference to the specified
/// binder object. The object should not be destroyed until a [`Command::DecRef`] is
/// delivered.
IncRef(LocalBinderObject),
/// Notifies a binder thread that there are no longer any remote processes holding weak
/// references to the specified binder object. The object may still have references within the
/// owning process.
DecRef(LocalBinderObject),
/// Notifies a binder thread that the last processed command contained an error.
Error(i32),
/// Commands a binder thread to start processing an incoming oneway transaction, which requires
/// no reply.
OnewayTransaction(TransactionData),
/// Commands a binder thread to start processing an incoming synchronous transaction from
/// another binder process.
/// Sent from the client to the server.
Transaction {
/// The binder peer that sent this transaction.
sender: WeakBinderPeer,
/// The transaction payload.
data: TransactionData,
/// The eventual scheduler state to use when the transaction is running.
scheduler_state: Option<SchedulerState>,
},
/// Commands a binder thread to process an incoming reply to its transaction.
/// Sent from the server to the client.
Reply(TransactionData),
/// Notifies a binder thread that a transaction has completed.
/// Sent from binder to the server.
TransactionComplete,
/// Notifies a binder thread that a oneway transaction has been sent.
/// Sent from binder to the client.
OnewayTransactionComplete,
/// The transaction was well formed but failed. Possible causes are a nonexistent handle, no
/// more memory available to allocate a buffer.
FailedReply,
/// Notifies the initiator of a transaction that the recipient is dead.
DeadReply,
/// Notifies a binder process that a binder object has died.
DeadBinder(binder_uintptr_t),
/// Notifies the initiator of a sync transaction that the recipient is frozen.
FrozenReply,
/// Notifies the initiator of an async transaction that the recipient is frozen and transaction
/// is queued.
PendingFrozen,
/// Notifies a binder process that the death notification has been cleared.
ClearDeathNotificationDone(binder_uintptr_t),
/// Notified the binder process that it should spawn a new looper.
SpawnLooper,
/// Notifies a binder process whether it is transitioned into a frozen state.
FrozenBinder(binder_frozen_state_info),
/// Notifies a binder process that the freeze notification has been cleared.
ClearFreezeNotificationDone(binder_uintptr_t),
}
impl Command {
/// Initiates a trace flow for the command and returns a guard that will terminate the flow
/// when dropped
pub fn begin_trace_flow(&self) -> CommandTraceGuard {
CommandTraceGuard::begin(&self)
}
/// Returns the command's BR_* code for serialization.
pub fn driver_return_code(&self) -> binder_driver_return_protocol {
match self {
Self::AcquireRef(..) => binder_driver_return_protocol_BR_ACQUIRE,
Self::ReleaseRef(..) => binder_driver_return_protocol_BR_RELEASE,
Self::IncRef(..) => binder_driver_return_protocol_BR_INCREFS,
Self::DecRef(..) => binder_driver_return_protocol_BR_DECREFS,
Self::Error(..) => binder_driver_return_protocol_BR_ERROR,
Self::OnewayTransaction(data) | Self::Transaction { data, .. } => {
if data.buffers.security_context.is_none() {
binder_driver_return_protocol_BR_TRANSACTION
} else {
binder_driver_return_protocol_BR_TRANSACTION_SEC_CTX
}
}
Self::Reply(..) => binder_driver_return_protocol_BR_REPLY,
Self::TransactionComplete | Self::OnewayTransactionComplete => {
binder_driver_return_protocol_BR_TRANSACTION_COMPLETE
}
Self::FailedReply => binder_driver_return_protocol_BR_FAILED_REPLY,
Self::DeadReply { .. } => binder_driver_return_protocol_BR_DEAD_REPLY,
Self::DeadBinder(..) => binder_driver_return_protocol_BR_DEAD_BINDER,
Self::FrozenReply => binder_driver_return_protocol_BR_FROZEN_REPLY,
Self::PendingFrozen => binder_driver_return_protocol_BR_TRANSACTION_PENDING_FROZEN,
Self::ClearDeathNotificationDone(..) => {
binder_driver_return_protocol_BR_CLEAR_DEATH_NOTIFICATION_DONE
}
Self::SpawnLooper => binder_driver_return_protocol_BR_SPAWN_LOOPER,
Self::FrozenBinder(..) => binder_driver_return_protocol_BR_FROZEN_BINDER,
Self::ClearFreezeNotificationDone(..) => {
binder_driver_return_protocol_BR_CLEAR_FREEZE_NOTIFICATION_DONE
}
}
}
/// Serializes and writes the command into userspace memory at `buffer`.
pub fn write_to_memory(
&self,
memory_accessor: &dyn MemoryAccessor,
buffer: &UserBuffer,
) -> Result<usize, Errno> {
match self {
Self::AcquireRef(obj)
| Self::ReleaseRef(obj)
| Self::IncRef(obj)
| Self::DecRef(obj) => {
#[repr(C, packed)]
#[derive(IntoBytes, Immutable)]
struct AcquireRefData {
command: binder_driver_return_protocol,
weak_ref_addr: u64,
strong_ref_addr: u64,
}
if buffer.length < std::mem::size_of::<AcquireRefData>() {
return error!(ENOMEM);
}
memory_accessor.write_object(
UserRef::new(buffer.address),
&AcquireRefData {
command: self.driver_return_code(),
weak_ref_addr: obj.weak_ref_addr.ptr() as u64,
strong_ref_addr: obj.strong_ref_addr.ptr() as u64,
},
)
}
Self::Error(error_val) => {
#[repr(C, packed)]
#[derive(IntoBytes, Immutable)]
struct ErrorData {
command: binder_driver_return_protocol,
error_val: i32,
}
if buffer.length < std::mem::size_of::<ErrorData>() {
return error!(ENOMEM);
}
memory_accessor.write_object(
UserRef::new(buffer.address),
&ErrorData { command: self.driver_return_code(), error_val: *error_val },
)
}
Self::OnewayTransaction(data) | Self::Transaction { data, .. } | Self::Reply(data) => {
if let Some(security_context_buffer) = data.buffers.security_context.as_ref() {
#[repr(C, packed)]
#[derive(IntoBytes, Immutable)]
struct TransactionData {
command: binder_driver_return_protocol,
data: [u8; std::mem::size_of::<binder_transaction_data>()],
secctx: binder_uintptr_t,
}
if buffer.length < std::mem::size_of::<TransactionData>() {
return error!(ENOMEM);
}
memory_accessor.write_object(
UserRef::new(buffer.address),
&TransactionData {
command: self.driver_return_code(),
data: data.as_bytes(),
secctx: security_context_buffer.address.ptr() as binder_uintptr_t,
},
)
} else {
#[repr(C, packed)]
#[derive(IntoBytes, Immutable)]
struct TransactionData {
command: binder_driver_return_protocol,
data: [u8; std::mem::size_of::<binder_transaction_data>()],
}
if buffer.length < std::mem::size_of::<TransactionData>() {
return error!(ENOMEM);
}
memory_accessor.write_object(
UserRef::new(buffer.address),
&TransactionData {
command: self.driver_return_code(),
data: data.as_bytes(),
},
)
}
}
Self::TransactionComplete
| Self::OnewayTransactionComplete
| Self::FailedReply
| Self::FrozenReply
| Self::PendingFrozen
| Self::DeadReply { .. }
| Self::SpawnLooper => {
if buffer.length < std::mem::size_of::<binder_driver_return_protocol>() {
return error!(ENOMEM);
}
memory_accessor
.write_object(UserRef::new(buffer.address), &self.driver_return_code())
}
Self::DeadBinder(cookie)
| Self::ClearDeathNotificationDone(cookie)
| Self::ClearFreezeNotificationDone(cookie) => {
#[repr(C, packed)]
#[derive(IntoBytes, Immutable)]
struct CookieData {
command: binder_driver_return_protocol,
cookie: binder_uintptr_t,
}
if buffer.length < std::mem::size_of::<CookieData>() {
return error!(ENOMEM);
}
memory_accessor.write_object(
UserRef::new(buffer.address),
&CookieData { command: self.driver_return_code(), cookie: *cookie },
)
}
Self::FrozenBinder(state) => {
#[repr(C, packed)]
#[derive(IntoBytes, Immutable)]
struct FreezeBinderData {
command: binder_driver_return_protocol,
state: binder_frozen_state_info,
}
if buffer.length < std::mem::size_of::<FreezeBinderData>() {
return error!(ENOMEM);
}
memory_accessor.write_object(
UserRef::new(buffer.address),
&FreezeBinderData { command: self.driver_return_code(), state: *state },
)
}
}
}
}
#[derive(Debug)]
pub struct CommandTraceGuard(Option<CommandTraceGuardInner>);
#[derive(Debug)]
struct CommandTraceGuardInner {
id: fuchsia_trace::Id,
kind: &'static str,
}
impl CommandTraceGuard {
fn begin(command: &Command) -> Self {
if !starnix_logging::regular_trace_category_enabled(TRACE_CATEGORY) {
return Self(None);
}
let kind = match command {
Command::AcquireRef(_) => "AcquireRef",
Command::ReleaseRef(_) => "ReleaseRef",
Command::IncRef(_) => "IncRef",
Command::DecRef(_) => "DecRef",
Command::Error(_) => "Error",
Command::OnewayTransaction(_) => "OnewayTransaction",
Command::Transaction { .. } => "Transaction",
Command::Reply(_) => "Reply",
Command::TransactionComplete => "TransactionComplete",
Command::OnewayTransactionComplete => "OnewayTransactionComplete",
Command::FailedReply => "FailedReply",
Command::DeadReply { .. } => "DeadReply",
Command::DeadBinder(_) => "DeadBinder",
Command::ClearDeathNotificationDone(_) => "ClearDeathNotificationDone",
Command::SpawnLooper => "SpawnLooper",
Command::FrozenReply => "FrozenReply",
Command::PendingFrozen => "PendingFrozen",
Command::FrozenBinder(_) => "FrozenBinder",
Command::ClearFreezeNotificationDone(_) => "ClearFreezeNotificationDone",
};
let id = fuchsia_trace::Id::random();
let f = format!("{:?}", command);
trace_instaflow_begin!(TRACE_CATEGORY, "BinderFlow", kind, id, "cmd" => &*f);
Self(Some(CommandTraceGuardInner { id, kind }))
}
}
impl Drop for CommandTraceGuard {
fn drop(&mut self) {
if let Some(CommandTraceGuardInner { id, kind }) = self.0.take() {
trace_instaflow_end!(TRACE_CATEGORY, "BinderFlow", kind, id);
}
}
}
/// A binder thread's role (sender or receiver) in a synchronous transaction. Oneway transactions
/// do not record roles, since they end as soon as they begin.
#[derive(Debug)]
pub enum TransactionRole {
/// The binder thread initiated the transaction and is awaiting a reply from a peer.
Sender(TransactionSender),
/// The binder thread is receiving a transaction and is expected to reply to the peer binder
/// process and thread.
Receiver(WeakBinderPeer, SchedulerGuard),
}
#[derive(Debug)]
pub struct TransactionSender {
/// The target process of the transaction. Used to determine whether or not this transaction is
/// still alive.
pub target_proc: u64,
/// The target thread of the transaction. Used to determine whether or not this transaction is
/// still alive. If `None`, the transaction will be marked dead when the handling thread in
/// `target_proc` is released.
pub target_thread: Option<i32>,
/// Whether or not the target of this transaction is still alive. Used to determine whether or
/// not a `DeadReply` should be inserted into the command queue when a thread is waiting for
/// this transaction to complete.
pub is_alive: bool,
}
impl TransactionRole {
/// Marks the transaction as dead if it is a `Sender` targeting `thread` or `process`.
fn mark_dead(&mut self, process: u64, thread: Option<i32>) -> bool {
match (thread, self) {
(
// If a thread is provided to `mark_dead`, it means that the transaction should
// only be marked dead if the `target_thread` actually matches `thread`.
Some(thread),
TransactionRole::Sender(TransactionSender {
target_thread: Some(target),
is_alive,
..
}),
) if *target == thread => {
*is_alive = false;
true
}
(
// If there is no target thread for the transaction, the transaction is process
// bound. This means that the transaction should be marked dead if the
// `target_proc` matches `process`, regardless of whether or not a `thread` was
// provided to `mark_dead`.
_,
TransactionRole::Sender(TransactionSender {
target_thread: None,
target_proc,
is_alive,
..
}),
) if *target_proc == process => {
*is_alive = false;
true
}
// The transaction specifies a `target_thread` that does not match `thread`, or the
// transaction's `target_proc` does not match `process`.
_ => false,
}
}
}
#[derive(Debug, Default)]
pub struct SchedulerGuard(Option<ReleaseGuard<SchedulerState>>);
impl SchedulerGuard {
pub fn release_for_task(self, kernel: &Kernel, tid: pid_t) -> bool {
let task = kernel.pids.read().get_task(tid);
if let Some(task) = task.upgrade() {
self.release(&task);
return true;
} else {
// The task has been killed. There is no scheduler state to update.
self.disarm();
return false;
};
}
pub fn disarm(self) {
if let Some(scheduler_state) = self.0 {
ReleaseGuard::take(scheduler_state);
}
}
}
impl From<SchedulerState> for SchedulerGuard {
fn from(scheduler_state: SchedulerState) -> Self {
Self(Some(scheduler_state.into()))
}
}
impl Releasable for SchedulerGuard {
type Context<'a> = &'a Task;
fn release<'a>(self, task: &'a Task) {
if let Some(scheduler_state) = self.0 {
let scheduler_state = ReleaseGuard::take(scheduler_state);
if let Err(e) = task.set_scheduler_state(scheduler_state) {
log_warn!(
"Unable to update scheduler state of task {} to {scheduler_state:?}: {e:?}",
task.tid
);
}
}
}
}
/// An error processing a binder transaction/reply.
///
/// Some errors, like a malformed transaction request, should be propagated as the return value of
/// an ioctl. Other errors, like a dead recipient or invalid binder handle, should be propagated
/// through a command read by the binder thread.
///
/// This type differentiates between these strategies.
#[derive(Debug, Eq, PartialEq)]
pub enum TransactionError {
/// The transaction payload was malformed. Send a [`Command::Error`] command to the issuing
/// thread.
Malformed(Errno),
/// The transaction payload was correctly formed, but either the recipient, or a handle embedded
/// in the transaction, is invalid. Send a [`Command::FailedReply`] command to the issuing
/// thread.
Failure,
/// The transaction payload was correctly formed, but either the recipient, or a handle embedded
/// in the transaction, is dead. Send a [`Command::DeadReply`] command to the issuing thread.
Dead,
/// The binder thread is frozen. Send a [`Command::FrozenReply`] command to the issuing thread.
Frozen,
}
impl TransactionError {
/// Dispatches the error, by potentially queueing a command to `binder_thread` and/or returning
/// an error.
pub fn dispatch(&self, binder_thread: &BinderThread) -> Result<(), Errno> {
log_trace!("Dispatching transaction error {:?} for thread {}", self, binder_thread.tid);
binder_thread.lock().enqueue_command(match self {
TransactionError::Malformed(err) => {
log_warn!(
"binder thread {} sent a malformed transaction: {:?}",
binder_thread.tid,
&err
);
// Negate the value, as the binder runtime assumes error values are already
// negative.
Command::Error(err.return_value() as i32)
}
TransactionError::Failure => Command::FailedReply,
TransactionError::Dead => Command::DeadReply,
TransactionError::Frozen => Command::FrozenReply,
});
Ok(())
}
}
impl From<Errno> for TransactionError {
fn from(errno: Errno) -> TransactionError {
match errno.code {
EACCES | EPERM => TransactionError::Failure,
_ => TransactionError::Malformed(errno),
}
}
}