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