summaryrefslogtreecommitdiff
path: root/drivers/android/binder/thread.rs
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/android/binder/thread.rs')
-rw-r--r--drivers/android/binder/thread.rs222
1 files changed, 129 insertions, 93 deletions
diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
index 0b62d24b2118..97d5f31e8fe3 100644
--- a/drivers/android/binder/thread.rs
+++ b/drivers/android/binder/thread.rs
@@ -19,7 +19,7 @@ use kernel::{
sync::poll::{PollCondVar, PollTable},
sync::{aref::ARef, Arc, SpinLock},
task::Task,
- uaccess::UserSlice,
+ uaccess::{UserPtr, UserSlice, UserSliceReader},
uapi,
};
@@ -30,7 +30,7 @@ use crate::{
process::{GetWorkOrRegister, Process},
ptr_align,
stats::GLOBAL_STATS,
- transaction::Transaction,
+ transaction::{Transaction, TransactionInfo},
BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead,
};
@@ -513,6 +513,9 @@ impl Thread {
/// Attempts to fetch a work item from the thread-local queue. The behaviour if the queue is
/// empty depends on `wait`: if it is true, the function waits for some work to be queued (or a
/// signal); otherwise it returns indicating that none is available.
+ // #[export_name] is a temporary workaround so that ps output does not become unreadable from
+ // mangled symbol names.
+ #[export_name = "rust_binder_waitlcl"]
fn get_work_local(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn DeliverToRead>>> {
{
let mut inner = self.inner.lock();
@@ -551,6 +554,9 @@ impl Thread {
///
/// This must only be called when the thread is not participating in a transaction chain. If it
/// is, the local version (`get_work_local`) should be used instead.
+ // #[export_name] is a temporary workaround so that ps output does not become unreadable from
+ // mangled symbol names.
+ #[export_name = "rust_binder_wait"]
fn get_work(self: &Arc<Self>, wait: bool) -> Result<Option<DLArc<dyn DeliverToRead>>> {
// Try to get work from the thread's work queue, using only a local lock.
{
@@ -706,6 +712,7 @@ impl Thread {
core::mem::offset_of!(uapi::binder_fd_object, __bindgen_anon_1.fd);
let field_offset = offset + FD_FIELD_OFFSET;
+ crate::trace::trace_transaction_fd_send(view.alloc.debug_id, fd, field_offset);
view.alloc.info_add_fd(file, field_offset, false)?;
}
@@ -945,13 +952,11 @@ impl Thread {
pub(crate) fn copy_transaction_data(
&self,
to_process: Arc<Process>,
- tr: &BinderTransactionDataSg,
+ info: &mut TransactionInfo,
debug_id: usize,
allow_fds: bool,
txn_security_ctx_offset: Option<&mut usize>,
) -> BinderResult<NewAllocation> {
- let trd = &tr.transaction_data;
- let is_oneway = trd.flags & TF_ONE_WAY != 0;
let mut secctx = if let Some(offset) = txn_security_ctx_offset {
let secid = self.process.cred.get_secid();
let ctx = match security::SecurityCtx::from_secid(secid) {
@@ -966,10 +971,10 @@ impl Thread {
None
};
- let data_size = trd.data_size.try_into().map_err(|_| EINVAL)?;
+ let data_size = info.data_size;
let aligned_data_size = ptr_align(data_size).ok_or(EINVAL)?;
- let offsets_size: usize = trd.offsets_size.try_into().map_err(|_| EINVAL)?;
- let buffers_size: usize = tr.buffers_size.try_into().map_err(|_| EINVAL)?;
+ let offsets_size = info.offsets_size;
+ let buffers_size = info.buffers_size;
let aligned_secctx_size = match secctx.as_ref() {
Some((_offset, ctx)) => ptr_align(ctx.len()).ok_or(EINVAL)?,
None => 0,
@@ -992,35 +997,25 @@ impl Thread {
size_of::<u64>(),
);
let secctx_off = aligned_data_size + offsets_size + buffers_size;
- let mut alloc =
- match to_process.buffer_alloc(debug_id, len, is_oneway, self.process.task.pid()) {
- Ok(alloc) => alloc,
- Err(err) => {
- pr_warn!(
- "Failed to allocate buffer. len:{}, is_oneway:{}",
- len,
- is_oneway
- );
- return Err(err);
- }
- };
+ let mut alloc = match to_process.buffer_alloc(debug_id, len, info) {
+ Ok(alloc) => alloc,
+ Err(err) => {
+ pr_warn!(
+ "Failed to allocate buffer. len:{}, is_oneway:{}",
+ len,
+ info.is_oneway(),
+ );
+ return Err(err);
+ }
+ };
- // SAFETY: This accesses a union field, but it's okay because the field's type is valid for
- // all bit-patterns.
- let trd_data_ptr = unsafe { &trd.data.ptr };
- let mut buffer_reader =
- UserSlice::new(UserPtr::from_addr(trd_data_ptr.buffer as _), data_size).reader();
+ let mut buffer_reader = UserSlice::new(info.data_ptr, data_size).reader();
let mut end_of_previous_object = 0;
let mut sg_state = None;
// Copy offsets if there are any.
if offsets_size > 0 {
- {
- let mut reader =
- UserSlice::new(UserPtr::from_addr(trd_data_ptr.offsets as _), offsets_size)
- .reader();
- alloc.copy_into(&mut reader, aligned_data_size, offsets_size)?;
- }
+ let mut offsets_reader = UserSlice::new(info.offsets_ptr, offsets_size).reader();
let offsets_start = aligned_data_size;
let offsets_end = aligned_data_size + offsets_size;
@@ -1041,11 +1036,9 @@ impl Thread {
.step_by(size_of::<u64>())
.enumerate()
{
- let offset: usize = view
- .alloc
- .read::<u64>(index_offset)?
- .try_into()
- .map_err(|_| EINVAL)?;
+ let offset = offsets_reader.read::<u64>()?;
+ view.alloc.write(index_offset, &offset)?;
+ 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.");
@@ -1197,37 +1190,92 @@ impl Thread {
}
}
- fn transaction<T>(self: &Arc<Self>, tr: &BinderTransactionDataSg, inner: T)
- where
- T: FnOnce(&Arc<Self>, &BinderTransactionDataSg) -> BinderResult,
- {
- if let Err(err) = inner(self, tr) {
- if err.should_pr_warn() {
- let mut ee = self.inner.lock().extended_error;
- ee.command = err.reply;
- ee.param = err.as_errno();
- pr_warn!(
- "Transaction failed: {:?} my_pid:{}",
- err,
- self.process.pid_in_current_ns()
- );
+ // No inlining avoids allocating stack space for `BinderTransactionData` for the entire
+ // duration of `transaction()`.
+ #[inline(never)]
+ fn read_transaction_info(
+ &self,
+ cmd: u32,
+ reader: &mut UserSliceReader,
+ info: &mut TransactionInfo,
+ ) -> Result<()> {
+ let td = match cmd {
+ BC_TRANSACTION | BC_REPLY => {
+ reader.read::<BinderTransactionData>()?.with_buffers_size(0)
+ }
+ BC_TRANSACTION_SG | BC_REPLY_SG => reader.read::<BinderTransactionDataSg>()?,
+ _ => return Err(EINVAL),
+ };
+
+ // SAFETY: Above `read` call initializes all bytes, so this union read is ok.
+ let trd_data_ptr = unsafe { &td.transaction_data.data.ptr };
+
+ info.is_reply = matches!(cmd, BC_REPLY | BC_REPLY_SG);
+ info.from_pid = self.process.task.pid();
+ info.from_tid = self.id;
+ info.code = td.transaction_data.code;
+ info.flags = td.transaction_data.flags;
+ info.data_ptr = UserPtr::from_addr(trd_data_ptr.buffer as usize);
+ info.data_size = td.transaction_data.data_size as usize;
+ info.offsets_ptr = UserPtr::from_addr(trd_data_ptr.offsets as usize);
+ info.offsets_size = td.transaction_data.offsets_size as usize;
+ info.buffers_size = td.buffers_size as usize;
+ // SAFETY: Above `read` call initializes all bytes, so this union read is ok.
+ info.target_handle = unsafe { td.transaction_data.target.handle };
+ Ok(())
+ }
+
+ #[inline(never)]
+ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Result<()> {
+ let mut info = TransactionInfo::zeroed();
+ self.read_transaction_info(cmd, reader, &mut info)?;
+
+ let ret = if info.is_reply {
+ self.reply_inner(&mut info)
+ } else if info.is_oneway() {
+ self.oneway_transaction_inner(&mut info)
+ } else {
+ self.transaction_inner(&mut info)
+ };
+
+ if let Err(err) = ret {
+ if err.reply != BR_TRANSACTION_COMPLETE {
+ info.reply = err.reply;
}
self.push_return_work(err.reply);
+ if let Some(source) = &err.source {
+ info.errno = source.to_errno();
+ info.reply = err.reply;
+
+ {
+ let mut ee = self.inner.lock().extended_error;
+ ee.command = err.reply;
+ ee.param = source.to_errno();
+ }
+
+ pr_warn!(
+ "{}:{} transaction to {} failed: {source:?}",
+ info.from_pid,
+ info.from_tid,
+ info.to_pid
+ );
+ }
}
+
+ Ok(())
}
- fn transaction_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
- // SAFETY: Handle's type has no invalid bit patterns.
- let handle = unsafe { tr.transaction_data.target.handle };
- let node_ref = self.process.get_transaction_node(handle)?;
+ fn transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
+ let node_ref = self.process.get_transaction_node(info.target_handle)?;
+ info.to_pid = node_ref.node.owner.task.pid();
security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?;
// TODO: We need to ensure that there isn't a pending transaction in the work queue. How
// could this happen?
let top = self.top_of_transaction_stack()?;
let list_completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
let completion = list_completion.clone_arc();
- let transaction = Transaction::new(node_ref, top, self, tr)?;
+ let transaction = Transaction::new(node_ref, top, self, info)?;
// Check that the transaction stack hasn't changed while the lock was released, then update
// it with the new transaction.
@@ -1243,7 +1291,7 @@ impl Thread {
inner.push_work_deferred(list_completion);
}
- if let Err(e) = transaction.submit() {
+ if let Err(e) = transaction.submit(info) {
completion.skip();
// Define `transaction` first to drop it after `inner`.
let transaction;
@@ -1256,18 +1304,21 @@ impl Thread {
}
}
- fn reply_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
+ fn reply_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
let orig = self.inner.lock().pop_transaction_to_reply(self)?;
if !orig.from.is_current_transaction(&orig) {
return Err(EINVAL.into());
}
+ info.to_tid = orig.from.id;
+ info.to_pid = orig.from.process.task.pid();
+
// We need to complete the transaction even if we cannot complete building the reply.
let out = (|| -> BinderResult<_> {
let completion = DTRWrap::arc_try_new(DeliverCode::new(BR_TRANSACTION_COMPLETE))?;
let process = orig.from.process.clone();
let allow_fds = orig.flags & TF_ACCEPT_FDS != 0;
- let reply = Transaction::new_reply(self, process, tr, allow_fds)?;
+ let reply = Transaction::new_reply(self, process, info, allow_fds)?;
self.inner.lock().push_work(completion);
orig.from.deliver_reply(Ok(reply), &orig);
Ok(())
@@ -1288,16 +1339,12 @@ impl Thread {
out
}
- fn oneway_transaction_inner(self: &Arc<Self>, tr: &BinderTransactionDataSg) -> BinderResult {
- // SAFETY: The `handle` field is valid for all possible byte values, so reading from the
- // union is okay.
- let handle = unsafe { tr.transaction_data.target.handle };
- let node_ref = self.process.get_transaction_node(handle)?;
+ fn oneway_transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResult {
+ let node_ref = self.process.get_transaction_node(info.target_handle)?;
+ info.to_pid = node_ref.node.owner.task.pid();
security::binder_transaction(&self.process.cred, &node_ref.node.owner.cred)?;
- let transaction = Transaction::new(node_ref, None, self, tr)?;
- let code = if self.process.is_oneway_spam_detection_enabled()
- && transaction.oneway_spam_detected
- {
+ let transaction = Transaction::new(node_ref, None, self, info)?;
+ let code = if self.process.is_oneway_spam_detection_enabled() && info.oneway_spam_suspect {
BR_ONEWAY_SPAM_SUSPECT
} else {
BR_TRANSACTION_COMPLETE
@@ -1305,7 +1352,7 @@ impl Thread {
let list_completion = DTRWrap::arc_try_new(DeliverCode::new(code))?;
let completion = list_completion.clone_arc();
self.inner.lock().push_work(list_completion);
- match transaction.submit() {
+ match transaction.submit(info) {
Ok(()) => Ok(()),
Err(err) => {
completion.skip();
@@ -1323,32 +1370,12 @@ impl Thread {
while reader.len() >= size_of::<u32>() && self.inner.lock().return_work.is_unused() {
let before = reader.len();
let cmd = reader.read::<u32>()?;
+ crate::trace::trace_command(cmd);
GLOBAL_STATS.inc_bc(cmd);
self.process.stats.inc_bc(cmd);
match cmd {
- BC_TRANSACTION => {
- let tr = reader.read::<BinderTransactionData>()?.with_buffers_size(0);
- if tr.transaction_data.flags & TF_ONE_WAY != 0 {
- self.transaction(&tr, Self::oneway_transaction_inner);
- } else {
- self.transaction(&tr, Self::transaction_inner);
- }
- }
- BC_TRANSACTION_SG => {
- let tr = reader.read::<BinderTransactionDataSg>()?;
- if tr.transaction_data.flags & TF_ONE_WAY != 0 {
- self.transaction(&tr, Self::oneway_transaction_inner);
- } else {
- self.transaction(&tr, Self::transaction_inner);
- }
- }
- BC_REPLY => {
- let tr = reader.read::<BinderTransactionData>()?.with_buffers_size(0);
- self.transaction(&tr, Self::reply_inner)
- }
- BC_REPLY_SG => {
- let tr = reader.read::<BinderTransactionDataSg>()?;
- self.transaction(&tr, Self::reply_inner)
+ BC_TRANSACTION | BC_TRANSACTION_SG | BC_REPLY | BC_REPLY_SG => {
+ self.transaction(cmd, &mut reader)?;
}
BC_FREE_BUFFER => {
let buffer = self.process.buffer_get(reader.read()?);
@@ -1412,11 +1439,18 @@ impl Thread {
UserSlice::new(UserPtr::from_addr(read_start as _), read_len as _).writer(),
self,
);
- let (in_pool, use_proc_queue) = {
+ let (in_pool, has_transaction, thread_todo, use_proc_queue) = {
let inner = self.inner.lock();
- (inner.is_looper(), inner.should_use_process_work_queue())
+ (
+ inner.is_looper(),
+ inner.current_transaction.is_some(),
+ !inner.work_list.is_empty(),
+ inner.should_use_process_work_queue(),
+ )
};
+ crate::trace::trace_wait_for_work(use_proc_queue, has_transaction, thread_todo);
+
let getter = if use_proc_queue {
Self::get_work
} else {
@@ -1482,6 +1516,7 @@ impl Thread {
let mut ret = Ok(());
if req.write_size > 0 {
ret = self.write(&mut req);
+ crate::trace::trace_write_done(ret);
if let Err(err) = ret {
pr_warn!(
"Write failure {:?} in pid:{}",
@@ -1498,6 +1533,7 @@ impl Thread {
// Go through the work queue.
if req.read_size > 0 {
ret = self.read(&mut req, wait);
+ crate::trace::trace_read_done(ret);
if ret.is_err() && ret != Err(EINTR) {
pr_warn!(
"Read failure {:?} in pid:{}",