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