From nobody Thu Apr 9 17:10:07 2026 Received: from mail-wm1-f74.google.com (mail-wm1-f74.google.com [209.85.128.74]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 4CCE4359A8F for ; Fri, 6 Mar 2026 11:28:57 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.128.74 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772796539; cv=none; b=ZRBtI9gOlpBaihxrRwAPFJY9nUvSr8OkDmWcxM7eKXgidRKCH6Bza+StuVwZ2YWw6tNWNgFb5Bt8SbpfQFEZ4JZ2xqhmr3jmirO/SqUf0Tg5QqRD5xXkCUCPStxsN26E/DG9/eojfM2qrpwJk6TFWrcSEGw2re/kqxpmS3MnqiU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1772796539; c=relaxed/simple; bh=7RIQpIBTi8ras99qSujDqTwhHj9NSVSSg+jlmRDr5/8=; h=Date:Mime-Version:Message-ID:Subject:From:To:Cc:Content-Type; b=Nyxg0AAspNL5gHm88kOFsD6T+s+0sTz+3Djv38S2nW10xsK3mY5Af70PZfVOq/FbDb55k/WoQ0V+ASLM/LijIsHpJTnIr+s5ctQvJwd8hcn0jPoi5KshExAmu7ge+fv1BNp/4FtLs+KC9SPVvKQ2r/W3HYFsSXiRtyyYQn6cKxY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b=OsIlokJp; arc=none smtp.client-ip=209.85.128.74 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=reject dis=none) header.from=google.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=flex--aliceryhl.bounces.google.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="OsIlokJp" Received: by mail-wm1-f74.google.com with SMTP id 5b1f17b1804b1-4836ff58111so97083475e9.1 for ; Fri, 06 Mar 2026 03:28:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20230601; t=1772796536; x=1773401336; darn=vger.kernel.org; h=cc:to:from:subject:message-id:mime-version:date:from:to:cc:subject :date:message-id:reply-to; bh=Zbo4Hu1ScegKDIbfeAknCaTicVQy5upTDK1+hKqIK7w=; b=OsIlokJpLM65nhGu6zhBJN/+lLhUqfv6dPlia6SpHUzq5vXubxSHcvHEUg+qOAQBvu Sw5w2akJipFMgtIONpN13dT9eWIiPKpuq+rYYUhBtgSzUznQFZljxtskcp1YQQr1n9aF uQ8xNp8FptP857z9tz1JKQ9UzONMe9XI3A+15G+9bZ+IqeFhdDCeQJAu5FGGAAuZkT0W +BDuOoylW2/i3N2CyYaD8XTCQIXpg4ImgzaOAdaEOQeoNdC/3QgNYcfLV4Ftxnx2sFnJ STTaw5NQQiOTMLTSXKI6vfGWBZm19xRGvgvWG0LDi8qxdW1Kb/6xLXGPthJ8hmJku5Ir ZogA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772796536; x=1773401336; h=cc:to:from:subject:message-id:mime-version:date:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=Zbo4Hu1ScegKDIbfeAknCaTicVQy5upTDK1+hKqIK7w=; b=fFTjxPmSHr4n52nA4+/aXXZsRNlmYR3jRVn9Z3LIc808gndDZ/a2PfHWv/VLMmu66W V2OEvIGRDZhWbm66SoAMzpVroprQof/BAcx15z0wzBGHi0+MPbLWgs/syHFQYr7MP66D yYcq+PmCqShD0R3tWSoihBbZTC1eL5ZR2W1WjCdNImK26/6SdWqR5dqyA6Lv1+YCDlPK HVsXbH76784YvzK8FjW5X+22Ofn4SXu+r24nsY9DXLRJ5y/oEbaeWxAyACz9YjSZci34 U0gkWZFx50wWdUru7rYHin0NB9c6Vkd0RdD24FcTlebSQ8wtq7LjWAm+nSqp2r16rKhg RQ3A== X-Forwarded-Encrypted: i=1; AJvYcCX8cu/9q2SaUl9uaaDyu6HHWXGtFcToWDaYYvtDdydeABKfb+XtwWSebxJR9D8XiPdZfOD6/yAZiHH2joA=@vger.kernel.org X-Gm-Message-State: AOJu0YxIl5EIUUtDklXf6Qwl5BOlCwluP8f/W/MMAiXZjqavcN7R4Ecq 1ckHTynj+s0IfIJAFwpY7ArBUdwAWGYT9MtDoV/HEsoBZdAsBi1VqTIMAG9Y1ZhBMA5FyC96hgD LOivIKt3s6L8ZF12HxA== X-Received: from wmoh17.prod.google.com ([2002:a05:600c:3151:b0:480:3227:a124]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:a05:600c:3acf:b0:483:6bb1:117 with SMTP id 5b1f17b1804b1-48526978984mr25665405e9.32.1772796535580; Fri, 06 Mar 2026 03:28:55 -0800 (PST) Date: Fri, 06 Mar 2026 11:28:46 +0000 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 X-B4-Tracking: v=1; b=H4sIAG66qmkC/x2MSwqAIBQAryJvnWCK9rlKtBB71ttoqEQg3j1pO TAzFTImwgwrq5DwoUwxdBgHBu6y4UROR2eQQhqhhOYl2ZCtK93jFHzkRhqtnNF+mhfo2Z3Q0/s vt721DwR4c4diAAAA X-Change-Id: 20260305-transaction-info-62653c65f789 X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=27898; i=aliceryhl@google.com; h=from:subject:message-id; bh=7RIQpIBTi8ras99qSujDqTwhHj9NSVSSg+jlmRDr5/8=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBpqrpxHosUzhFUaIRgDp444ROwReQD388EdDasV BdNc8knWN+JAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCaaq6cQAKCRAEWL7uWMY5 RuVQEACasg+PVH/huoO0m5f5pzwqbLwHG4nR8PlGAn3H8bdT7PuVpRGlvPWj9D1SgRSYGDT4FaI bNegx2k7jMtv4oht4YWYRdM8N/i6x7cRbccpAyNiPWoq1sbA5/QofdGTbSwIykn2PahKvAoy7Kk U4B2ZuzKDWaZ8v1NoXm8gL6dg6uC3CWGzM3X5YdWkKmTCAou98eUeDVeuuYFT9rmS3cg7cqXmBJ /mTmSY7W4qvOmmYzd4SLbytLE0aIbid8VLXtA6ZmpWVpPcHZNgRB1+LpJZ1+CbpiAyewezh8hfF 9OqdRYc3C6D+iq66NtCO6uLx+GPbKhn0JrsnJyionziUEtBz4X/UasPvlbCEVXRJUEbqUeVQPwz 7rhQMnVOsZ5gRrHQm4s7MItUNcF18xgARmwl8e8knJkl0Fhi8RWsxQivECQy2iNnMjtNRSxVQ6t q+KaRqk98iigkqZdPB10gw/LNRUEyehc7JqLgzdkjPL3q127n3k7z2Opukde9922ZCqyof1vc1t yA8A3wBpJ+kUF/pYP+L99mAAB8AJjF+ZMrodAG39yzp5A5YLUHOUnnBkWJU2S/eKKBr6bCoWAkk 2fdrIkv1/Ue960avsX0JSEeIBfwAJ/eg9Y7hs+ZYco7S+6wkmGQmeMhcLTRha8bMjph09ykiBkK rVe9Fh4sMWZGBtg== X-Mailer: b4 0.14.3 Message-ID: <20260306-transaction-info-v1-1-fda58fca558b@google.com> Subject: [PATCH] rust_binder: introduce TransactionInfo From: Alice Ryhl To: Greg Kroah-Hartman , Carlos Llamas Cc: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Rust Binder exposes information about transactions that are sent in various ways: printing to the kernel log, tracepoints, files in binderfs, and the upcoming netlink support. Currently all these mechanisms use disparate ways of obtaining the same information, so let's introduce a single Info struct that collects all the required information in a single place, so that all of these different mechanisms can operate in a more uniform way. For now, the new info struct is only used to replace a few things: * The BinderTransactionDataSg struct that is passed as an argument to several methods is removed as the information is moved into the new info struct and passed down that way. * The oneway spam detection fields on Transaction and Allocation can be removed, as the information can be returned to the caller via the mutable info struct instead. But several other uses of the info struct are planned in follow-up patches. Signed-off-by: Alice Ryhl --- Note that this patch depends on commit 4cb9e13fec0d ("rust_binder: avoid reading the written value in offsets array") from char-misc-linus. Patches in char-misc-next also affect the diff. This diff was generated based on linux-next 20260305, but also applies on the result of merging together char-misc-linus and char-misc-next. --- drivers/android/binder/allocation.rs | 3 - drivers/android/binder/error.rs | 10 +- drivers/android/binder/process.rs | 15 ++- drivers/android/binder/thread.rs | 190 +++++++++++++++++++-----------= ---- drivers/android/binder/transaction.rs | 83 +++++++++------ rust/kernel/uaccess.rs | 2 +- 6 files changed, 168 insertions(+), 135 deletions(-) diff --git a/drivers/android/binder/allocation.rs b/drivers/android/binder/= allocation.rs index 7f65a9c3a0e5..97edfb1ff382 100644 --- a/drivers/android/binder/allocation.rs +++ b/drivers/android/binder/allocation.rs @@ -56,7 +56,6 @@ pub(crate) struct Allocation { pub(crate) process: Arc, allocation_info: Option, free_on_drop: bool, - pub(crate) oneway_spam_detected: bool, #[allow(dead_code)] pub(crate) debug_id: usize, } @@ -68,7 +67,6 @@ pub(crate) fn new( offset: usize, size: usize, ptr: usize, - oneway_spam_detected: bool, ) -> Self { Self { process, @@ -76,7 +74,6 @@ pub(crate) fn new( size, ptr, debug_id, - oneway_spam_detected, allocation_info: None, free_on_drop: true, } diff --git a/drivers/android/binder/error.rs b/drivers/android/binder/error= .rs index b24497cfa292..45d85d4c2815 100644 --- a/drivers/android/binder/error.rs +++ b/drivers/android/binder/error.rs @@ -13,7 +13,7 @@ /// errno. pub(crate) struct BinderError { pub(crate) reply: u32, - source: Option, + pub(crate) source: Option, } =20 impl BinderError { @@ -41,14 +41,6 @@ pub(crate) fn new_frozen_oneway() -> Self { pub(crate) fn is_dead(&self) -> bool { self.reply =3D=3D BR_DEAD_REPLY } - - pub(crate) fn as_errno(&self) -> kernel::ffi::c_int { - self.source.unwrap_or(EINVAL).to_errno() - } - - pub(crate) fn should_pr_warn(&self) -> bool { - self.source.is_some() - } } =20 /// Convert an errno into a `BinderError` and store the errno used to cons= truct it. The errno diff --git a/drivers/android/binder/process.rs b/drivers/android/binder/pro= cess.rs index da81a9569365..ae26fe817794 100644 --- a/drivers/android/binder/process.rs +++ b/drivers/android/binder/process.rs @@ -48,6 +48,7 @@ range_alloc::{RangeAllocator, ReserveNew, ReserveNewArgs}, stats::BinderStats, thread::{PushWorkRes, Thread}, + transaction::TransactionInfo, BinderfsProcFile, DArc, DLArc, DTRWrap, DeliverToRead, }; =20 @@ -1003,16 +1004,15 @@ pub(crate) fn buffer_alloc( self: &Arc, debug_id: usize, size: usize, - is_oneway: bool, - from_pid: i32, + info: &mut TransactionInfo, ) -> BinderResult { use kernel::page::PAGE_SIZE; =20 let mut reserve_new_args =3D ReserveNewArgs { debug_id, size, - is_oneway, - pid: from_pid, + is_oneway: info.is_oneway(), + pid: info.from_pid, ..ReserveNewArgs::default() }; =20 @@ -1028,13 +1028,13 @@ pub(crate) fn buffer_alloc( reserve_new_args =3D alloc_request.make_alloc()?; }; =20 + info.oneway_spam_suspect =3D new_alloc.oneway_spam_detected; let res =3D Allocation::new( self.clone(), debug_id, new_alloc.offset, size, addr + new_alloc.offset, - new_alloc.oneway_spam_detected, ); =20 // This allocation will be marked as in use until the `Allocation`= is used to free it. @@ -1066,7 +1066,7 @@ pub(crate) fn buffer_get(self: &Arc, ptr: usize= ) -> Option { let mapping =3D inner.mapping.as_mut()?; let offset =3D ptr.checked_sub(mapping.address)?; let (size, debug_id, odata) =3D mapping.alloc.reserve_existing(off= set).ok()?; - let mut alloc =3D Allocation::new(self.clone(), debug_id, offset, = size, ptr, false); + let mut alloc =3D Allocation::new(self.clone(), debug_id, offset, = size, ptr); if let Some(data) =3D odata { alloc.set_info(data); } @@ -1414,8 +1414,7 @@ fn deferred_release(self: Arc) { .alloc .take_for_each(|offset, size, debug_id, odata| { let ptr =3D offset + address; - let mut alloc =3D - Allocation::new(self.clone(), debug_id, offset, si= ze, ptr, false); + let mut alloc =3D Allocation::new(self.clone(), debug_= id, offset, size, ptr); if let Some(data) =3D odata { alloc.set_info(data); } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thre= ad.rs index 6f283de53213..97a5e4acf64c 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -19,7 +19,7 @@ sync::poll::{PollCondVar, PollTable}, sync::{aref::ARef, Arc, SpinLock}, task::Task, - uaccess::UserSlice, + uaccess::{UserPtr, UserSlice, UserSliceReader}, uapi, }; =20 @@ -30,7 +30,7 @@ process::{GetWorkOrRegister, Process}, ptr_align, stats::GLOBAL_STATS, - transaction::Transaction, + transaction::{Transaction, TransactionInfo}, BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead, }; =20 @@ -951,13 +951,11 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: = &mut ScatterGatherState) -> pub(crate) fn copy_transaction_data( &self, to_process: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, debug_id: usize, allow_fds: bool, txn_security_ctx_offset: Option<&mut usize>, ) -> BinderResult { - let trd =3D &tr.transaction_data; - let is_oneway =3D trd.flags & TF_ONE_WAY !=3D 0; let mut secctx =3D if let Some(offset) =3D txn_security_ctx_offset= { let secid =3D self.process.cred.get_secid(); let ctx =3D match security::SecurityCtx::from_secid(secid) { @@ -972,10 +970,10 @@ pub(crate) fn copy_transaction_data( None }; =20 - let data_size =3D trd.data_size.try_into().map_err(|_| EINVAL)?; + let data_size =3D info.data_size; let aligned_data_size =3D ptr_align(data_size).ok_or(EINVAL)?; - let offsets_size: usize =3D trd.offsets_size.try_into().map_err(|_= | EINVAL)?; - let buffers_size: usize =3D tr.buffers_size.try_into().map_err(|_|= EINVAL)?; + let offsets_size =3D info.offsets_size; + let buffers_size =3D info.buffers_size; let aligned_secctx_size =3D match secctx.as_ref() { Some((_offset, ctx)) =3D> ptr_align(ctx.len()).ok_or(EINVAL)?, None =3D> 0, @@ -998,32 +996,25 @@ pub(crate) fn copy_transaction_data( size_of::(), ); let secctx_off =3D aligned_data_size + offsets_size + buffers_size; - let mut alloc =3D - match to_process.buffer_alloc(debug_id, len, is_oneway, self.p= rocess.task.pid()) { - Ok(alloc) =3D> alloc, - Err(err) =3D> { - pr_warn!( - "Failed to allocate buffer. len:{}, is_oneway:{}", - len, - is_oneway - ); - return Err(err); - } - }; + let mut alloc =3D match to_process.buffer_alloc(debug_id, len, inf= o) { + Ok(alloc) =3D> alloc, + Err(err) =3D> { + pr_warn!( + "Failed to allocate buffer. len:{}, is_oneway:{}", + len, + info.is_oneway(), + ); + return Err(err); + } + }; =20 - // SAFETY: This accesses a union field, but it's okay because the = field's type is valid for - // all bit-patterns. - let trd_data_ptr =3D unsafe { &trd.data.ptr }; - let mut buffer_reader =3D - UserSlice::new(UserPtr::from_addr(trd_data_ptr.buffer as _), d= ata_size).reader(); + let mut buffer_reader =3D UserSlice::new(info.data_ptr, data_size)= .reader(); let mut end_of_previous_object =3D 0; let mut sg_state =3D None; =20 // Copy offsets if there are any. if offsets_size > 0 { - let mut offsets_reader =3D - UserSlice::new(UserPtr::from_addr(trd_data_ptr.offsets as = _), offsets_size) - .reader(); + let mut offsets_reader =3D UserSlice::new(info.offsets_ptr, of= fsets_size).reader(); =20 let offsets_start =3D aligned_data_size; let offsets_end =3D aligned_data_size + offsets_size; @@ -1198,37 +1189,92 @@ fn top_of_transaction_stack(&self) -> Result>> { } } =20 - fn transaction(self: &Arc, tr: &BinderTransactionDataSg, inne= r: T) - where - T: FnOnce(&Arc, &BinderTransactionDataSg) -> BinderResult, - { - if let Err(err) =3D inner(self, tr) { - if err.should_pr_warn() { - let mut ee =3D self.inner.lock().extended_error; - ee.command =3D err.reply; - ee.param =3D err.as_errno(); - pr_warn!( - "Transaction failed: {:?} my_pid:{}", - err, - self.process.pid_in_current_ns() - ); + // No inlining avoids allocating stack space for `BinderTransactionDat= a` for the entire + // duration of `transaction()`. + #[inline(never)] + fn read_transaction_info( + &self, + cmd: u32, + reader: &mut UserSliceReader, + info: &mut TransactionInfo, + ) -> Result<()> { + let td =3D match cmd { + BC_TRANSACTION | BC_REPLY =3D> { + reader.read::()?.with_buffers_size(= 0) + } + BC_TRANSACTION_SG | BC_REPLY_SG =3D> reader.read::()?, + _ =3D> return Err(EINVAL), + }; + + // SAFETY: Above `read` call initializes all bytes, so this union = read is ok. + let trd_data_ptr =3D unsafe { &td.transaction_data.data.ptr }; + + info.is_reply =3D matches!(cmd, BC_REPLY | BC_REPLY_SG); + info.from_pid =3D self.process.task.pid(); + info.from_tid =3D self.id; + info.code =3D td.transaction_data.code; + info.flags =3D td.transaction_data.flags; + info.data_ptr =3D UserPtr::from_addr(trd_data_ptr.buffer as usize); + info.data_size =3D td.transaction_data.data_size as usize; + info.offsets_ptr =3D UserPtr::from_addr(trd_data_ptr.offsets as us= ize); + info.offsets_size =3D td.transaction_data.offsets_size as usize; + info.buffers_size =3D td.buffers_size as usize; + // SAFETY: Above `read` call initializes all bytes, so this union = read is ok. + info.target_handle =3D unsafe { td.transaction_data.target.handle = }; + Ok(()) + } + + #[inline(never)] + fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReade= r) -> Result<()> { + let mut info =3D TransactionInfo::zeroed(); + self.read_transaction_info(cmd, reader, &mut info)?; + + let ret =3D if info.is_reply { + self.reply_inner(&mut info) + } else if info.is_oneway() { + self.oneway_transaction_inner(&mut info) + } else { + self.transaction_inner(&mut info) + }; + + if let Err(err) =3D ret { + if err.reply !=3D BR_TRANSACTION_COMPLETE { + info.reply =3D err.reply; } =20 self.push_return_work(err.reply); + if let Some(source) =3D &err.source { + info.errno =3D source.to_errno(); + info.reply =3D err.reply; + + { + let mut ee =3D self.inner.lock().extended_error; + ee.command =3D err.reply; + ee.param =3D source.to_errno(); + } + + pr_warn!( + "{}:{} transaction to {} failed: {source:?}", + info.from_pid, + info.from_tid, + info.to_pid + ); + } } + + Ok(()) } =20 - fn transaction_inner(self: &Arc, tr: &BinderTransactionDataSg) -= > BinderResult { - // SAFETY: Handle's type has no invalid bit patterns. - let handle =3D unsafe { tr.transaction_data.target.handle }; - let node_ref =3D self.process.get_transaction_node(handle)?; + fn transaction_inner(self: &Arc, info: &mut TransactionInfo) -> = BinderResult { + let node_ref =3D self.process.get_transaction_node(info.target_han= dle)?; + info.to_pid =3D node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.ow= ner.cred)?; // TODO: We need to ensure that there isn't a pending transaction = in the work queue. How // could this happen? let top =3D self.top_of_transaction_stack()?; let list_completion =3D DTRWrap::arc_try_new(DeliverCode::new(BR_T= RANSACTION_COMPLETE))?; let completion =3D list_completion.clone_arc(); - let transaction =3D Transaction::new(node_ref, top, self, tr)?; + let transaction =3D Transaction::new(node_ref, top, self, info)?; =20 // Check that the transaction stack hasn't changed while the lock = was released, then update // it with the new transaction. @@ -1244,7 +1290,7 @@ fn transaction_inner(self: &Arc, tr: &BinderTra= nsactionDataSg) -> BinderRe inner.push_work_deferred(list_completion); } =20 - if let Err(e) =3D transaction.submit() { + if let Err(e) =3D transaction.submit(info) { completion.skip(); // Define `transaction` first to drop it after `inner`. let transaction; @@ -1257,18 +1303,21 @@ fn transaction_inner(self: &Arc, tr: &BinderT= ransactionDataSg) -> BinderRe } } =20 - fn reply_inner(self: &Arc, tr: &BinderTransactionDataSg) -> Bind= erResult { + fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> Binder= Result { let orig =3D self.inner.lock().pop_transaction_to_reply(self)?; if !orig.from.is_current_transaction(&orig) { return Err(EINVAL.into()); } =20 + info.to_tid =3D orig.from.id; + info.to_pid =3D orig.from.process.task.pid(); + // We need to complete the transaction even if we cannot complete = building the reply. let out =3D (|| -> BinderResult<_> { let completion =3D DTRWrap::arc_try_new(DeliverCode::new(BR_TR= ANSACTION_COMPLETE))?; let process =3D orig.from.process.clone(); let allow_fds =3D orig.flags & TF_ACCEPT_FDS !=3D 0; - let reply =3D Transaction::new_reply(self, process, tr, allow_= fds)?; + let reply =3D Transaction::new_reply(self, process, info, allo= w_fds)?; self.inner.lock().push_work(completion); orig.from.deliver_reply(Ok(reply), &orig); Ok(()) @@ -1289,16 +1338,12 @@ fn reply_inner(self: &Arc, tr: &BinderTransac= tionDataSg) -> BinderResult { out } =20 - fn oneway_transaction_inner(self: &Arc, tr: &BinderTransactionDa= taSg) -> BinderResult { - // SAFETY: The `handle` field is valid for all possible byte value= s, so reading from the - // union is okay. - let handle =3D unsafe { tr.transaction_data.target.handle }; - let node_ref =3D self.process.get_transaction_node(handle)?; + fn oneway_transaction_inner(self: &Arc, info: &mut TransactionIn= fo) -> BinderResult { + let node_ref =3D self.process.get_transaction_node(info.target_han= dle)?; + info.to_pid =3D node_ref.node.owner.task.pid(); security::binder_transaction(&self.process.cred, &node_ref.node.ow= ner.cred)?; - let transaction =3D Transaction::new(node_ref, None, self, tr)?; - let code =3D if self.process.is_oneway_spam_detection_enabled() - && transaction.oneway_spam_detected - { + let transaction =3D Transaction::new(node_ref, None, self, info)?; + let code =3D if self.process.is_oneway_spam_detection_enabled() &&= info.oneway_spam_suspect { BR_ONEWAY_SPAM_SUSPECT } else { BR_TRANSACTION_COMPLETE @@ -1306,7 +1351,7 @@ fn oneway_transaction_inner(self: &Arc, tr: &Bi= nderTransactionDataSg) -> B let list_completion =3D DTRWrap::arc_try_new(DeliverCode::new(code= ))?; let completion =3D list_completion.clone_arc(); self.inner.lock().push_work(list_completion); - match transaction.submit() { + match transaction.submit(info) { Ok(()) =3D> Ok(()), Err(err) =3D> { completion.skip(); @@ -1327,29 +1372,8 @@ fn write(self: &Arc, req: &mut BinderWriteRead= ) -> Result { GLOBAL_STATS.inc_bc(cmd); self.process.stats.inc_bc(cmd); match cmd { - BC_TRANSACTION =3D> { - let tr =3D reader.read::()?.wit= h_buffers_size(0); - if tr.transaction_data.flags & TF_ONE_WAY !=3D 0 { - self.transaction(&tr, Self::oneway_transaction_inn= er); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_TRANSACTION_SG =3D> { - let tr =3D reader.read::()?; - if tr.transaction_data.flags & TF_ONE_WAY !=3D 0 { - self.transaction(&tr, Self::oneway_transaction_inn= er); - } else { - self.transaction(&tr, Self::transaction_inner); - } - } - BC_REPLY =3D> { - let tr =3D reader.read::()?.wit= h_buffers_size(0); - self.transaction(&tr, Self::reply_inner) - } - BC_REPLY_SG =3D> { - let tr =3D reader.read::()?; - self.transaction(&tr, Self::reply_inner) + BC_TRANSACTION | BC_TRANSACTION_SG | BC_REPLY | BC_REPLY_S= G =3D> { + self.transaction(cmd, &mut reader)?; } BC_FREE_BUFFER =3D> { let buffer =3D self.process.buffer_get(reader.read()?); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder= /transaction.rs index 10af40527ca7..5dff3d655c4d 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -8,7 +8,7 @@ seq_print, sync::atomic::{ordering::Relaxed, Atomic}, sync::{Arc, SpinLock}, - task::Kuid, + task::{Kuid, Pid}, time::{Instant, Monotonic}, types::ScopeGuard, }; @@ -24,6 +24,33 @@ BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverToRead, }; =20 +#[derive(Zeroable)] +pub(crate) struct TransactionInfo { + pub(crate) from_pid: Pid, + pub(crate) from_tid: Pid, + pub(crate) to_pid: Pid, + pub(crate) to_tid: Pid, + pub(crate) code: u32, + pub(crate) flags: u32, + pub(crate) data_ptr: UserPtr, + pub(crate) data_size: usize, + pub(crate) offsets_ptr: UserPtr, + pub(crate) offsets_size: usize, + pub(crate) buffers_size: usize, + pub(crate) target_handle: u32, + pub(crate) errno: i32, + pub(crate) reply: u32, + pub(crate) oneway_spam_suspect: bool, + pub(crate) is_reply: bool, +} + +impl TransactionInfo { + #[inline] + pub(crate) fn is_oneway(&self) -> bool { + self.flags & TF_ONE_WAY !=3D 0 + } +} + use core::mem::offset_of; use kernel::bindings::rb_transaction_layout; pub(crate) const TRANSACTION_LAYOUT: rb_transaction_layout =3D rb_transact= ion_layout { @@ -52,7 +79,6 @@ pub(crate) struct Transaction { data_address: usize, sender_euid: Kuid, txn_security_ctx_off: Option, - pub(crate) oneway_spam_detected: bool, start_time: Instant, } =20 @@ -65,17 +91,16 @@ pub(crate) fn new( node_ref: NodeRef, from_parent: Option>, from: &Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, ) -> BinderResult> { let debug_id =3D super::next_debug_id(); - let trd =3D &tr.transaction_data; let allow_fds =3D node_ref.node.flags & FLAT_BINDER_FLAG_ACCEPTS_F= DS !=3D 0; let txn_security_ctx =3D node_ref.node.flags & FLAT_BINDER_FLAG_TX= N_SECURITY_CTX !=3D 0; let mut txn_security_ctx_off =3D if txn_security_ctx { Some(0) } e= lse { None }; let to =3D node_ref.node.owner.clone(); let mut alloc =3D match from.copy_transaction_data( to.clone(), - tr, + info, debug_id, allow_fds, txn_security_ctx_off.as_mut(), @@ -88,15 +113,14 @@ pub(crate) fn new( return Err(err); } }; - let oneway_spam_detected =3D alloc.oneway_spam_detected; - if trd.flags & TF_ONE_WAY !=3D 0 { + if info.is_oneway() { if from_parent.is_some() { pr_warn!("Oneway transaction should not be in a transactio= n stack."); return Err(EINVAL.into()); } alloc.set_info_oneway_node(node_ref.node.clone()); } - if trd.flags & TF_CLEAR_BUF !=3D 0 { + if info.flags & TF_CLEAR_BUF !=3D 0 { alloc.set_info_clear_on_drop(); } let target_node =3D node_ref.node.clone(); @@ -110,15 +134,14 @@ pub(crate) fn new( sender_euid: Kuid::current_euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Tr= ansaction::new"), is_outstanding: Atomic::new(false), txn_security_ctx_off, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -126,21 +149,19 @@ pub(crate) fn new( pub(crate) fn new_reply( from: &Arc, to: Arc, - tr: &BinderTransactionDataSg, + info: &mut TransactionInfo, allow_fds: bool, ) -> BinderResult> { let debug_id =3D super::next_debug_id(); - let trd =3D &tr.transaction_data; - let mut alloc =3D match from.copy_transaction_data(to.clone(), tr,= debug_id, allow_fds, None) - { - Ok(alloc) =3D> alloc, - Err(err) =3D> { - pr_warn!("Failure in copy_transaction_data: {:?}", err); - return Err(err); - } - }; - let oneway_spam_detected =3D alloc.oneway_spam_detected; - if trd.flags & TF_CLEAR_BUF !=3D 0 { + let mut alloc =3D + match from.copy_transaction_data(to.clone(), info, debug_id, a= llow_fds, None) { + Ok(alloc) =3D> alloc, + Err(err) =3D> { + pr_warn!("Failure in copy_transaction_data: {:?}", err= ); + return Err(err); + } + }; + if info.flags & TF_CLEAR_BUF !=3D 0 { alloc.set_info_clear_on_drop(); } Ok(DTRWrap::arc_pin_init(pin_init!(Transaction { @@ -150,15 +171,14 @@ pub(crate) fn new_reply( sender_euid: Kuid::current_euid(), from: from.clone(), to, - code: trd.code, - flags: trd.flags, - data_size: trd.data_size as _, - offsets_size: trd.offsets_size as _, + code: info.code, + flags: info.flags, + data_size: info.data_size, + offsets_size: info.offsets_size, data_address: alloc.ptr, allocation <- kernel::new_spinlock!(Some(alloc.success()), "Tr= ansaction::new"), is_outstanding: Atomic::new(false), txn_security_ctx_off: None, - oneway_spam_detected, start_time: Instant::now(), }))?) } @@ -248,7 +268,7 @@ fn drop_outstanding_txn(&self) { /// stack, otherwise uses the destination process. /// /// Not used for replies. - pub(crate) fn submit(self: DLArc) -> BinderResult { + pub(crate) fn submit(self: DLArc, info: &mut TransactionInfo) ->= BinderResult { // Defined before `process_inner` so that the destructor runs afte= r releasing the lock. let mut _t_outdated; =20 @@ -298,6 +318,7 @@ pub(crate) fn submit(self: DLArc) -> BinderResult= { } =20 let res =3D if let Some(thread) =3D self.find_target_thread() { + info.to_tid =3D thread.id; crate::trace::trace_transaction(false, &self, Some(&thread.tas= k)); match thread.push_work(self) { PushWorkRes::Ok =3D> Ok(()), diff --git a/rust/kernel/uaccess.rs b/rust/kernel/uaccess.rs index f989539a31b4..984c3ec03a7b 100644 --- a/rust/kernel/uaccess.rs +++ b/rust/kernel/uaccess.rs @@ -19,7 +19,7 @@ /// /// This is the Rust equivalent to C pointers tagged with `__user`. #[repr(transparent)] -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Zeroable)] pub struct UserPtr(*mut c_void); =20 impl UserPtr { --- base-commit: 3f9cd19e764b782706dbaacc69e502099cb014ba change-id: 20260305-transaction-info-62653c65f789 Best regards, --=20 Alice Ryhl