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