xref: /linux/drivers/android/binder/transaction.rs (revision 3a2c4d55e32ad65efebdb6de44eef3bfa08bb49d)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 // Copyright (C) 2025 Google LLC.
4 
5 use kernel::{
6     net::netlink::GENLMSG_DEFAULT_SIZE,
7     prelude::*,
8     seq_file::SeqFile,
9     seq_print,
10     sync::atomic::{ordering::Relaxed, Atomic},
11     sync::{Arc, SpinLock},
12     task::{Kuid, Pid},
13     time::{Instant, Monotonic},
14     types::ScopeGuard,
15     uapi,
16 };
17 
18 use crate::{
19     allocation::{Allocation, TranslatedFds},
20     defs::*,
21     error::{BinderError, BinderResult},
22     netlink::Report,
23     node::{Node, NodeRef},
24     process::{Process, ProcessInner},
25     ptr_align,
26     thread::{PushWorkRes, Thread},
27     BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead,
28 };
29 
30 kernel::impl_flags!(
31     /// Represents multiple transaction flags.
32     #[derive(Debug, Clone, Default, Copy, PartialEq, Eq, Zeroable)]
33     pub struct TransactionFlags(u32);
34 
35     /// Represents a single transaction flag.
36     #[derive(Debug, Clone, Copy, PartialEq, Eq)]
37     pub enum TransactionFlag {
38         OneWay = TF_ONE_WAY,
39         AcceptFds = TF_ACCEPT_FDS,
40         ClearBuf = TF_CLEAR_BUF,
41         UpdateTxn = TF_UPDATE_TXN,
42     }
43 );
44 
45 impl TransactionFlags {
46     /// Creates a `TransactionFlags` from a raw `u32` value.
47     pub(crate) fn from_bits(bits: u32) -> Self {
48         Self(bits)
49     }
50 
51     /// Checks if the Oneway flag is set.
52     pub(crate) fn is_oneway(self) -> bool {
53         self.contains(TransactionFlag::OneWay)
54     }
55 }
56 
57 #[derive(Zeroable)]
58 pub(crate) struct TransactionInfo {
59     pub(crate) from_pid: Pid,
60     pub(crate) from_tid: Pid,
61     pub(crate) to_pid: Pid,
62     pub(crate) to_tid: Pid,
63     pub(crate) code: u32,
64     pub(crate) flags: TransactionFlags,
65     pub(crate) data_ptr: UserPtr,
66     pub(crate) data_size: usize,
67     pub(crate) offsets_ptr: UserPtr,
68     pub(crate) offsets_size: usize,
69     pub(crate) buffers_size: usize,
70     pub(crate) target_handle: u32,
71     pub(crate) errno: i32,
72     pub(crate) reply: u32,
73     pub(crate) oneway_spam_suspect: bool,
74     pub(crate) is_reply: bool,
75     pub(crate) debug_id: usize,
76 }
77 
78 impl TransactionInfo {
79     #[inline]
80     pub(crate) fn is_oneway(&self) -> bool {
81         self.flags.is_oneway()
82     }
83 
84     pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) {
85         if let Err(err) = self.report_netlink_inner(reply, ctx) {
86             pr_warn!(
87                 "{}:{} netlink report failed: {err:?}\n",
88                 self.from_pid,
89                 self.from_tid
90             );
91         }
92     }
93 
94     fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> kernel::error::Result {
95         if !Report::has_listeners() {
96             return Ok(());
97         }
98         let mut report = Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KERNEL)?;
99 
100         report.error(reply)?;
101         report.context(&ctx.name)?;
102         report.from_pid(self.from_pid as u32)?;
103         report.from_tid(self.from_tid as u32)?;
104         if self.to_pid != 0 {
105             report.to_pid(self.to_pid as u32)?;
106         }
107         if self.to_tid != 0 {
108             report.to_tid(self.to_tid as u32)?;
109         }
110 
111         if self.is_reply {
112             report.is_reply()?;
113         }
114         report.flags(u32::from(self.flags))?;
115         report.code(self.code)?;
116         report.data_size(self.data_size as u32)?;
117 
118         report.multicast(0, GFP_KERNEL)?;
119         Ok(())
120     }
121 }
122 
123 use core::mem::offset_of;
124 use kernel::bindings::rb_transaction_layout;
125 pub(crate) const TRANSACTION_LAYOUT: rb_transaction_layout = rb_transaction_layout {
126     debug_id: offset_of!(Transaction, debug_id),
127     code: offset_of!(Transaction, code),
128     flags: offset_of!(Transaction, flags),
129     from_thread: offset_of!(Transaction, from),
130     to_proc: offset_of!(Transaction, to),
131     target_node: offset_of!(Transaction, target_node),
132 };
133 
134 #[pin_data(PinnedDrop)]
135 pub(crate) struct Transaction {
136     pub(crate) debug_id: usize,
137     target_node: Option<DArc<Node>>,
138     pub(crate) from_parent: Option<DArc<Transaction>>,
139     pub(crate) from: Arc<Thread>,
140     pub(crate) to: Arc<Process>,
141     #[pin]
142     allocation: SpinLock<Option<Allocation>>,
143     is_outstanding: Atomic<bool>,
144     code: u32,
145     pub(crate) flags: TransactionFlags,
146     data_size: usize,
147     offsets_size: usize,
148     data_address: usize,
149     sender_euid: Kuid,
150     txn_security_ctx_off: Option<usize>,
151     start_time: Instant<Monotonic>,
152 }
153 
154 kernel::list::impl_list_arc_safe! {
155     impl ListArcSafe<0> for Transaction { untracked; }
156 }
157 
158 impl Transaction {
159     pub(crate) fn new(
160         node_ref: NodeRef,
161         from_parent: Option<DArc<Transaction>>,
162         from: &Arc<Thread>,
163         info: &mut TransactionInfo,
164     ) -> BinderResult<DLArc<Self>> {
165         let allow_fds = node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_FDS != 0;
166         let txn_security_ctx = node_ref.node.flags & FLAT_BINDER_FLAG_TXN_SECURITY_CTX != 0;
167         let mut txn_security_ctx_off = if txn_security_ctx { Some(0) } else { None };
168         let to = node_ref.node.owner.clone();
169         let mut alloc = match from.copy_transaction_data(
170             to.clone(),
171             info,
172             info.debug_id,
173             allow_fds,
174             txn_security_ctx_off.as_mut(),
175         ) {
176             Ok(alloc) => alloc,
177             Err(err) => {
178                 if !err.is_dead() {
179                     pr_warn!("Failure in copy_transaction_data: {:?}", err);
180                 }
181                 return Err(err);
182             }
183         };
184         if info.is_oneway() {
185             if from_parent.is_some() {
186                 pr_warn!("Oneway transaction should not be in a transaction stack.");
187                 return Err(EINVAL.into());
188             }
189             alloc.set_info_oneway_node(node_ref.node.clone());
190         }
191         if info.flags.contains(TransactionFlag::ClearBuf) {
192             alloc.set_info_clear_on_drop();
193         }
194         let target_node = node_ref.node.clone();
195         alloc.set_info_target_node(node_ref);
196         let data_address = alloc.ptr;
197 
198         Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
199             debug_id: info.debug_id,
200             target_node: Some(target_node),
201             from_parent,
202             sender_euid: Kuid::current_euid(),
203             from: from.clone(),
204             to,
205             code: info.code,
206             flags: info.flags,
207             data_size: info.data_size,
208             offsets_size: info.offsets_size,
209             data_address,
210             allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"),
211             is_outstanding: Atomic::new(false),
212             txn_security_ctx_off,
213             start_time: Instant::now(),
214         }))?)
215     }
216 
217     pub(crate) fn new_reply(
218         from: &Arc<Thread>,
219         to: Arc<Process>,
220         info: &mut TransactionInfo,
221         allow_fds: bool,
222     ) -> BinderResult<DLArc<Self>> {
223         let mut alloc =
224             match from.copy_transaction_data(to.clone(), info, info.debug_id, allow_fds, None) {
225                 Ok(alloc) => alloc,
226                 Err(err) => {
227                     pr_warn!("Failure in copy_transaction_data: {:?}", err);
228                     return Err(err);
229                 }
230             };
231         if info.flags.contains(TransactionFlag::ClearBuf) {
232             alloc.set_info_clear_on_drop();
233         }
234         Ok(DTRWrap::arc_pin_init(pin_init!(Transaction {
235             debug_id: info.debug_id,
236             target_node: None,
237             from_parent: None,
238             sender_euid: Kuid::current_euid(),
239             from: from.clone(),
240             to,
241             code: info.code,
242             flags: info.flags,
243             data_size: info.data_size,
244             offsets_size: info.offsets_size,
245             data_address: alloc.ptr,
246             allocation <- kernel::new_spinlock!(Some(alloc.success()), "Transaction::new"),
247             is_outstanding: Atomic::new(false),
248             txn_security_ctx_off: None,
249             start_time: Instant::now(),
250         }))?)
251     }
252 
253     #[inline(never)]
254     pub(crate) fn debug_print_inner(&self, m: &SeqFile, prefix: &str) {
255         seq_print!(
256             m,
257             "{}{}: from {}:{} to {} code {:x} flags {:x} elapsed {}ms",
258             prefix,
259             self.debug_id,
260             self.from.process.task.pid(),
261             self.from.id,
262             self.to.task.pid(),
263             self.code,
264             u32::from(self.flags),
265             self.start_time.elapsed().as_millis(),
266         );
267         if let Some(target_node) = &self.target_node {
268             seq_print!(m, " node {}", target_node.debug_id);
269         }
270         seq_print!(m, " size {}:{}\n", self.data_size, self.offsets_size);
271     }
272 
273     /// Determines if the transaction is stacked on top of the given transaction.
274     pub(crate) fn is_stacked_on(&self, onext: &Option<DArc<Self>>) -> bool {
275         match (&self.from_parent, onext) {
276             (None, None) => true,
277             (Some(from_parent), Some(next)) => Arc::ptr_eq(from_parent, next),
278             _ => false,
279         }
280     }
281 
282     /// Returns a pointer to the next transaction on the transaction stack, if there is one.
283     pub(crate) fn clone_next(&self) -> Option<DArc<Self>> {
284         Some(self.from_parent.as_ref()?.clone())
285     }
286 
287     /// Searches in the transaction stack for a thread that belongs to the target process. This is
288     /// useful when finding a target for a new transaction: if the node belongs to a process that
289     /// is already part of the transaction stack, we reuse the thread.
290     fn find_target_thread(&self) -> Option<Arc<Thread>> {
291         let mut it = &self.from_parent;
292         while let Some(transaction) = it {
293             if Arc::ptr_eq(&transaction.from.process, &self.to) {
294                 return Some(transaction.from.clone());
295             }
296             it = &transaction.from_parent;
297         }
298         None
299     }
300 
301     /// Searches in the transaction stack for a transaction originating at the given thread.
302     pub(crate) fn find_from(&self, thread: &Thread) -> Option<&DArc<Transaction>> {
303         let mut it = &self.from_parent;
304         while let Some(transaction) = it {
305             if core::ptr::eq(thread, transaction.from.as_ref()) {
306                 return Some(transaction);
307             }
308 
309             it = &transaction.from_parent;
310         }
311         None
312     }
313 
314     pub(crate) fn set_outstanding(&self, to_process: &mut ProcessInner) {
315         // No race because this method is only called once.
316         if !self.is_outstanding.load(Relaxed) {
317             self.is_outstanding.store(true, Relaxed);
318             to_process.add_outstanding_txn();
319         }
320     }
321 
322     /// Decrement `outstanding_txns` in `to` if it hasn't already been decremented.
323     fn drop_outstanding_txn(&self) {
324         // No race because this is called at most twice, and one of the calls are in the
325         // destructor, which is guaranteed to not race with any other operations on the
326         // transaction. It also cannot race with `set_outstanding`, since submission happens
327         // before delivery.
328         if self.is_outstanding.load(Relaxed) {
329             self.is_outstanding.store(false, Relaxed);
330             self.to.drop_outstanding_txn();
331         }
332     }
333 
334     /// Submits the transaction to a work queue. Uses a thread if there is one in the transaction
335     /// stack, otherwise uses the destination process.
336     ///
337     /// Not used for replies.
338     pub(crate) fn submit(self: DLArc<Self>, info: &mut TransactionInfo) -> BinderResult {
339         // Defined before `process_inner` so that the destructor runs after releasing the lock.
340         let _t_outdated;
341         let _oneway_node;
342 
343         let oneway = self.flags.is_oneway();
344         let process = self.to.clone();
345         let mut process_inner = process.inner.lock();
346 
347         self.set_outstanding(&mut process_inner);
348 
349         if oneway {
350             if let Some(target_node) = self.target_node.clone() {
351                 crate::trace::trace_transaction(false, &self, None);
352                 if process_inner.is_frozen.is_frozen() {
353                     process_inner.async_recv = true;
354                     if self.flags.contains(TransactionFlag::UpdateTxn) {
355                         if let Some(t_outdated) =
356                             target_node.take_outdated_transaction(&self, &mut process_inner)
357                         {
358                             let mut alloc_guard = t_outdated.allocation.lock();
359                             if let Some(alloc) = (*alloc_guard).as_mut() {
360                                 // Take the oneway node to prevent `Allocation::drop` from calling
361                                 // `pending_oneway_finished()`, which would be incorrect as this
362                                 // transaction is not being submitted.
363                                 _oneway_node = alloc.take_oneway_node();
364                             }
365                             drop(alloc_guard);
366                             // Save the transaction to be dropped after locks are released.
367                             _t_outdated = t_outdated;
368                         }
369                     }
370                 }
371                 match target_node.submit_oneway(self, &mut process_inner) {
372                     Ok(()) => {}
373                     Err((err, work)) => {
374                         drop(process_inner);
375                         // Drop work after releasing process lock.
376                         drop(work);
377                         return Err(err);
378                     }
379                 }
380 
381                 if process_inner.is_frozen.is_frozen() {
382                     return Err(BinderError::new_frozen_oneway());
383                 } else {
384                     return Ok(());
385                 }
386             } else {
387                 pr_err!("Failed to submit oneway transaction to node.");
388             }
389         }
390 
391         if process_inner.is_frozen.is_frozen() {
392             process_inner.sync_recv = true;
393             return Err(BinderError::new_frozen());
394         }
395 
396         let res = if let Some(thread) = self.find_target_thread() {
397             info.to_tid = thread.id;
398             crate::trace::trace_transaction(false, &self, Some(&thread.task));
399             match thread.push_work(self) {
400                 PushWorkRes::Ok => Ok(()),
401                 PushWorkRes::OkNotifyPoll => {
402                     process.notify_poll(true);
403                     Ok(())
404                 }
405                 PushWorkRes::FailedDead(me) => Err((BinderError::new_dead(), me)),
406             }
407         } else {
408             crate::trace::trace_transaction(false, &self, None);
409             process_inner.push_work(&process, self)
410         };
411         drop(process_inner);
412 
413         match res {
414             Ok(()) => Ok(()),
415             Err((err, work)) => {
416                 // Drop work after releasing process lock.
417                 drop(work);
418                 Err(err)
419             }
420         }
421     }
422 
423     /// Check whether one oneway transaction can supersede another.
424     pub(crate) fn can_replace(&self, old: &Transaction) -> bool {
425         if self.from.process.task.pid() != old.from.process.task.pid() {
426             return false;
427         }
428 
429         let required = TransactionFlag::OneWay | TransactionFlag::UpdateTxn;
430         if !(self.flags.contains_all(required) && old.flags.contains_all(required)) {
431             return false;
432         }
433 
434         let target_node_match = match (self.target_node.as_ref(), old.target_node.as_ref()) {
435             (None, None) => true,
436             (Some(tn1), Some(tn2)) => Arc::ptr_eq(tn1, tn2),
437             _ => false,
438         };
439 
440         self.code == old.code && self.flags == old.flags && target_node_match
441     }
442 
443     fn prepare_file_list(&self) -> Result<TranslatedFds> {
444         let mut alloc = self.allocation.lock().take().ok_or(ESRCH)?;
445 
446         match alloc.translate_fds() {
447             Ok(translated) => {
448                 *self.allocation.lock() = Some(alloc);
449                 Ok(translated)
450             }
451             Err(err) => {
452                 // Free the allocation eagerly.
453                 drop(alloc);
454                 Err(err)
455             }
456         }
457     }
458 }
459 
460 impl DeliverToRead for Transaction {
461     fn do_work(
462         self: DArc<Self>,
463         thread: &Thread,
464         writer: &mut BinderReturnWriter<'_>,
465     ) -> Result<bool> {
466         let send_failed_reply = ScopeGuard::new(|| {
467             if self.target_node.is_some() && !self.flags.is_oneway() {
468                 let reply = Err(BR_FAILED_REPLY);
469                 self.from.deliver_reply(reply, &self, None);
470             }
471             self.drop_outstanding_txn();
472         });
473 
474         let files = if let Ok(list) = self.prepare_file_list() {
475             list
476         } else {
477             // On failure to process the list, we send a reply back to the sender and ignore the
478             // transaction on the recipient.
479             binder_debug!(
480                 FailedTransaction,
481                 "transaction {} to {} failed, fd fixups failed, size {}-{}",
482                 self.debug_id,
483                 self.to.task.pid(),
484                 self.data_size,
485                 self.offsets_size
486             );
487             return Ok(true);
488         };
489 
490         let mut tr_sec = BinderTransactionDataSecctx::default();
491         let tr = tr_sec.tr_data();
492         if let Some(target_node) = &self.target_node {
493             let (ptr, cookie) = target_node.get_id();
494             tr.target.ptr = ptr as uapi::binder_uintptr_t;
495             tr.cookie = cookie as uapi::binder_uintptr_t;
496         };
497         tr.code = self.code;
498         tr.flags = u32::from(self.flags);
499         tr.data_size = self.data_size as uapi::binder_size_t;
500         tr.data.ptr.buffer = self.data_address as uapi::binder_uintptr_t;
501         tr.offsets_size = self.offsets_size as uapi::binder_size_t;
502         if tr.offsets_size > 0 {
503             tr.data.ptr.offsets =
504                 (self.data_address + ptr_align(self.data_size).unwrap()) as uapi::binder_uintptr_t;
505         }
506         tr.sender_euid = self.sender_euid.into_uid_in_current_ns();
507         tr.sender_pid = 0;
508         if self.target_node.is_some() && !self.flags.is_oneway() {
509             // Not a reply and not one-way.
510             tr.sender_pid = self.from.process.pid_in_current_ns();
511         }
512         let code = if self.target_node.is_none() {
513             BR_REPLY
514         } else if self.txn_security_ctx_off.is_some() {
515             BR_TRANSACTION_SEC_CTX
516         } else {
517             BR_TRANSACTION
518         };
519 
520         // Write the transaction code and data to the user buffer.
521         writer.write_code(code)?;
522         if let Some(off) = self.txn_security_ctx_off {
523             tr_sec.secctx = (self.data_address + off) as u64;
524             writer.write_payload(&tr_sec)?;
525         } else {
526             writer.write_payload(&*tr)?;
527         }
528 
529         let mut alloc = self.allocation.lock().take().ok_or(ESRCH)?;
530 
531         // Dismiss the completion of transaction with a failure. No failure paths are allowed from
532         // here on out.
533         send_failed_reply.dismiss();
534 
535         // Commit files, and set FDs in FDA to be closed on buffer free.
536         let close_on_free = files.commit();
537         alloc.set_info_close_on_free(close_on_free);
538 
539         // It is now the user's responsibility to clear the allocation.
540         alloc.keep_alive();
541 
542         self.drop_outstanding_txn();
543 
544         crate::trace::trace_transaction_received(&self);
545 
546         // When this is not a reply and not a oneway transaction, update `current_transaction`. If
547         // it's a reply, `current_transaction` has already been updated appropriately.
548         if self.target_node.is_some() && tr_sec.transaction_data.flags & TF_ONE_WAY == 0 {
549             thread.set_current_transaction(self);
550         }
551 
552         Ok(false)
553     }
554 
555     fn cancel(self: DArc<Self>) {
556         let allocation = self.allocation.lock().take();
557         drop(allocation);
558 
559         // If this is not a reply or oneway transaction, then send a dead reply.
560         if self.target_node.is_some() && !self.flags.is_oneway() {
561             let reply = Err(BR_DEAD_REPLY);
562             self.from.deliver_reply(reply, &self, None);
563         } else {
564             binder_debug!(
565                 pid = self.to.task.pid(),
566                 DeadTransaction,
567                 "undelivered transaction {}, process died",
568                 self.debug_id
569             );
570         }
571 
572         self.drop_outstanding_txn();
573     }
574 
575     fn should_sync_wakeup(&self) -> bool {
576         !self.flags.is_oneway()
577     }
578 
579     fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result<()> {
580         self.debug_print_inner(m, tprefix);
581         Ok(())
582     }
583 }
584 
585 #[pinned_drop]
586 impl PinnedDrop for Transaction {
587     fn drop(self: Pin<&mut Self>) {
588         self.drop_outstanding_txn();
589     }
590 }
591