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