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