From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-wr1-f74.google.com (mail-wr1-f74.google.com [209.85.221.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 26BE43DB323 for ; Tue, 7 Jul 2026 10:07:10 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.74 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783418832; cv=none; b=OamSyGxAnsbRDL+bSecFTpIZe+YYTAYcQYSbvfp+yL0TtlF2pq9x1GhWoq7UBK7+MxAhZpWqLZROv6Wmz5pP0qQgoR+IO9L8uz1OPAqRYQ73NZEolK57Sjzo4fkMlbo2jfMGAHogHqRcPmJwC88CvkovqzvM5oieSyCA0Ld9gxw= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1783418832; c=relaxed/simple; bh=1Zxx4LiOeAyOWdSZ6ZMPcFZy38g99mF/0v3yfaYaNNs=; h=Date:In-Reply-To:Mime-Version:References:Message-ID:Subject:From: To:Cc:Content-Type; b=qcJuItY9R9xZUjt7RRl5crymidIabrKZu61AK9+mDBo4vV9wQAKAp/AUvpXusO0A7Ip68FWmTgGInitUVEBLJpSZO4uRPwic6wdPzyY6bnPyjTewvK61wNubiuDmN9LSL704ihfcIQ/lgLTI+Yu063rw4SU0qtqBbp84obi7L54= 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=TbEjdoMI; arc=none smtp.client-ip=209.85.221.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="TbEjdoMI" Received: by mail-wr1-f74.google.com with SMTP id ffacd0b85a97d-4773d2405c5so3186831f8f.3 for ; Tue, 07 Jul 2026 03:07:09 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20251104; t=1783418828; x=1784023628; darn=vger.kernel.org; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:from:to:cc:subject:date:message-id:reply-to; bh=2xijOFGFCtwwJDChJMsXNmytvX78Dq8djrhwcNAGZHI=; b=TbEjdoMIIy3DG5K88f8sjDFtV+DHrkBXl/KZ9suapuwA8+QpUAkoN/V2ftsKymaSF3 NE+0yA5sVXtf5hu7JNWMkocV4zAL01YGYU3dyqqn5OPg8gRNSYBM6k2pBBITsAB0Kweu zFm4mTttFpabOC+JETsPeq6IPAVd5RQGjcZg3Sxn7t2ZebfFCGrh4EzmX5sYqwZIUdW2 Z2RmslTeRurIXVlETnPB8UzMuCYqFAmSnh27qpwWvOm6kjBq4E3Gzj1c+j/y3j8vSaeU H/igsgX6ALqpMoD3JK2FiVUTrgY+H8m9THLwcPyfDSDlziRza/CdOUWK5E01+MldpKrn uvSQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1783418828; x=1784023628; h=cc:to:from:subject:message-id:references:mime-version:in-reply-to :date:x-gm-message-state:from:to:cc:subject:date:message-id:reply-to; bh=2xijOFGFCtwwJDChJMsXNmytvX78Dq8djrhwcNAGZHI=; b=HggkqFi1yrohNkTLMOUthIBetCK/7Zcn8SGhkgfHu8ywMGF3Z7vZy73877/0l8FZF7 JfmU68RkdpgLtjbZkF+Ox0gMncTSirZYg2aVF3gxvIJyJhqEt4tfRqyu8WiK06+JUJru 8Rz9aYmUlDGC5vY5G+9+Sz0ZPB5n1vnBf/nTYAaI2VMxam5lcd0TVnNV0RmBNNjDFK8e R3PnwgYeoPLS4TYimygwrKaV+o0YA4NTMtue2FPiSxZINAxVsqdCBvBbWoNhjSvUFoiJ F0tsD+e8cHCsCM6VrNdm1PeWEZ6ngHU2r9I+/qzI47cBRwTZOyGxbM6ikmflh2ZRWZPC Qaxg== X-Forwarded-Encrypted: i=1; AHgh+RoqE3oPUhZJb8KxZdUba2Q1SqGDl4HXJ3bw0ZqWBt/ZjxJzbNSWKyId9OjnrTSMTymTOzZG/v4=@vger.kernel.org X-Gm-Message-State: AOJu0YwY1dCWoQlSL1hcit5TQfl+fq1aK6vWGOegmTKJk6CkIPgj9Rnr OCleUloCejFlwBGV/NYIZI9KdV4U5hVtaj9+F+cXfIFAVsV2hq6Q9VnhgL2wnejLKme8ug1sTmK aGu9+VVxTZ/fK0egsKQ== X-Received: from wrsn1.prod.google.com ([2002:a5d:4841:0:b0:46c:a68f:93f8]) (user=aliceryhl job=prod-delivery.src-stubby-dispatcher) by 2002:adf:fe4b:0:b0:464:79f0:9162 with SMTP id ffacd0b85a97d-47de6611e85mr4150016f8f.19.1783418828201; Tue, 07 Jul 2026 03:07:08 -0700 (PDT) Date: Tue, 07 Jul 2026 10:06:51 +0000 In-Reply-To: <20260707-binder-netlink-v7-0-42b40e4b1ac8@google.com> Precedence: bulk X-Mailing-List: netdev@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: Mime-Version: 1.0 References: <20260707-binder-netlink-v7-0-42b40e4b1ac8@google.com> X-Developer-Key: i=aliceryhl@google.com; a=openpgp; fpr=49F6C1FAA74960F43A5B86A1EE7A392FDE96209F X-Developer-Signature: v=1; a=openpgp-sha256; l=11638; i=aliceryhl@google.com; h=from:subject:message-id; bh=Lu2CIOSpVAE4sfJk5Xp8L3aJMVpKCemx/+Um1VQAvJg=; b=owEBbQKS/ZANAwAKAQRYvu5YxjlGAcsmYgBqTM/D5X/D3fhlG2u6cugGjQkTlICg3KISM3Icn OOqAxi82TSJAjMEAAEKAB0WIQSDkqKUTWQHCvFIvbIEWL7uWMY5RgUCakzPwwAKCRAEWL7uWMY5 RhKzD/9m4do6i0lSFlIedkYhLlt1n03qLIqzfoQzoBanKPuo/fZkkLSDRAzeuigIQmNhqc1Ed95 1b3XsLM5EWZ2Gh/ecvkc3pG4N6RbL4HiGFr7wCZP2l9fKXQjoh9spAQJR93ELt8i6Wcm8pV5tfg J+CuYsnzBAPJAEkR+5FpBwE4ltuGnpsQXC38MdHku5zU7nWdFJLk4rnVNhyvpSU50GSPLIQv7Rc QMTwAyo0hfAUF93eemaC3XojJtdmoTS7k4PDWWFp2wKNkk6EtfhiAbK15qj34JCCBVcn73lPNEu gXOZAUxCx9AtA6R4WETtA0sFAEf4vIDR7Yhbu3DvnjdhkKD2pZhFoQVuZ4ghw3LXNSRr0wDgK60 5SubOWMbMzdrhQ7Gva2ObmdAHANJ+vvTN+FhKpeg7IHcgdgIVHkndjD0cUKsA15mms0I4yFiBWR /MkWdRrkgSaBMktoIxI5W+5u53bfioliAc4HfFpPs36CIU1tmxabEW8aWobLB4k4m1cWmG5q//M FfShqbD2Ek6CLuZ4WGGA5eQipJ49Mu72eRwx6rfYzi7ZR8gkYUdWAvIx4/+M2ZXSROPT5SyvEqB CsHuufxWnJasBvLCBFjxaL42TdAwdvDr5g5SOevR0FnrVqBRWCSh6mD4GZujp4vQ/Ui/o099VGU g1gL2lDxVCrWmRA== X-Mailer: b4 0.14.3 Message-ID: <20260707-binder-netlink-v7-3-42b40e4b1ac8@google.com> Subject: [PATCH v7 3/3] rust_binder: report netlink transactions From: Alice Ryhl To: Carlos Llamas , Greg Kroah-Hartman , Andrew Lunn , Donald Hunter , Jakub Kicinski , "David S. Miller" , Eric Dumazet , Paolo Abeni , Simon Horman , Matthew Maurer Cc: Miguel Ojeda , Boqun Feng , Gary Guo , "=?utf-8?q?Bj=C3=B6rn_Roy_Baron?=" , Benno Lossin , Andreas Hindborg , Trevor Gross , Danilo Krummrich , Christian Brauner , linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org, netdev@vger.kernel.org, Alice Ryhl Content-Type: text/plain; charset="utf-8" From: Carlos Llamas The Android Binder driver supports a netlink API that reports transaction *failures* to a userspace daemon. This allows devices to monitor processes with many failed transactions so that it can e.g. kill misbehaving apps. One very important thing that this monitors is when many oneway messages are sent to a frozen process, so there is special handling to ensure this scenario is surfaced over netlink. Signed-off-by: Carlos Llamas Acked-by: Carlos Llamas Co-developed-by: Alice Ryhl Signed-off-by: Alice Ryhl --- drivers/android/Kconfig | 2 +- drivers/android/binder/netlink.rs | 117 +++++++++++++++++++++++++++++ drivers/android/binder/rust_binder_main.rs | 8 +- drivers/android/binder/thread.rs | 11 ++- drivers/android/binder/transaction.rs | 40 ++++++++++ rust/uapi/uapi_helper.h | 1 + 6 files changed, 175 insertions(+), 4 deletions(-) diff --git a/drivers/android/Kconfig b/drivers/android/Kconfig index e2e402c9d175..606a9d07f774 100644 --- a/drivers/android/Kconfig +++ b/drivers/android/Kconfig @@ -16,7 +16,7 @@ config ANDROID_BINDER_IPC config ANDROID_BINDER_IPC_RUST bool "Rust version of Android Binder IPC Driver" - depends on RUST && MMU && !ANDROID_BINDER_IPC + depends on RUST && MMU && NET && !ANDROID_BINDER_IPC help This enables the Rust implementation of the Binder driver. diff --git a/drivers/android/binder/netlink.rs b/drivers/android/binder/netlink.rs new file mode 100644 index 000000000000..beb7ea2edaff --- /dev/null +++ b/drivers/android/binder/netlink.rs @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) +/* Based on: Documentation/netlink/specs/binder.yaml */ + +#![allow(unreachable_pub, clippy::wrong_self_convention)] +use kernel::{ + net::netlink::{ + Family, + GenlMsg, + MulticastGroup, + NetlinkSkBuff, // + }, + prelude::*, // +}; + +pub static BINDER_NL_FAMILY: Family = Family::const_new( + &crate::THIS_MODULE, + kernel::uapi::BINDER_FAMILY_NAME, + kernel::uapi::BINDER_FAMILY_VERSION, + &BINDER_NL_FAMILY_MCGRPS, +); + +static BINDER_NL_FAMILY_MCGRPS: [MulticastGroup; 1] = [MulticastGroup::const_new(c"report")]; + +/// A multicast event sent to userspace subscribers to notify them about +/// binder transaction failures. The generated report provides the full +/// details of the specific transaction that failed. The intention is for +/// programs to monitor these events and react to the failures as needed. +pub struct Report { + skb: GenlMsg, +} + +impl Report { + /// Create a new multicast message. + pub fn new( + size: usize, + portid: u32, + seq: u32, + flags: kernel::alloc::Flags, + ) -> Result { + const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8; + let skb = NetlinkSkBuff::new(size, flags)?; + let skb = skb.genlmsg_put(portid, seq, &BINDER_NL_FAMILY, BINDER_CMD_REPORT)?; + Ok(Self { skb }) + } + + /// Broadcast this message. + pub fn multicast(self, portid: u32, flags: kernel::alloc::Flags) -> Result { + self.skb.multicast(&BINDER_NL_FAMILY, portid, 0, flags) + } + + /// Check if this message type has listeners. + pub fn has_listeners() -> bool { + BINDER_NL_FAMILY.has_listeners(0) + } + + /// The enum binder_driver_return_protocol returned to the sender. + pub fn error(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int; + self.skb.put_u32(BINDER_A_REPORT_ERROR, val) + } + + /// The binder context where the transaction occurred. + pub fn context(&mut self, val: &CStr) -> Result { + const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int; + self.skb.put_string(BINDER_A_REPORT_CONTEXT, val) + } + + /// The PID of the sender process. + pub fn from_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_PID, val) + } + + /// The TID of the sender thread. + pub fn from_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_FROM_TID, val) + } + + /// The PID of the recipient process. This attribute may not be present + /// if the target could not be determined. + pub fn to_pid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_PID, val) + } + + /// The TID of the recipient thread. This attribute may not be present + /// if the target could not be determined. + pub fn to_tid(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int; + self.skb.put_u32(BINDER_A_REPORT_TO_TID, val) + } + + /// When present, indicates the failed transaction is a reply. + pub fn is_reply(&mut self) -> Result { + const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int; + self.skb.put_flag(BINDER_A_REPORT_IS_REPLY) + } + + /// The bitmask of enum transaction_flags from the transaction. + pub fn flags(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int; + self.skb.put_u32(BINDER_A_REPORT_FLAGS, val) + } + + /// The application-defined code from the transaction. + pub fn code(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int; + self.skb.put_u32(BINDER_A_REPORT_CODE, val) + } + + /// The transaction payload size in bytes. + pub fn data_size(&mut self, val: u32) -> Result { + const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int; + self.skb.put_u32(BINDER_A_REPORT_DATA_SIZE, val) + } +} diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs index 432390aab25b..df47aba05133 100644 --- a/drivers/android/binder/rust_binder_main.rs +++ b/drivers/android/binder/rust_binder_main.rs @@ -32,6 +32,7 @@ mod deferred_close; mod defs; mod error; +mod netlink; mod node; mod page_range; mod process; @@ -282,19 +283,22 @@ fn ptr_align(value: usize) -> Option { // SAFETY: We call register in `init`. static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() }; -struct BinderModule {} +struct BinderModule { + _netlink: kernel::net::netlink::Registration, +} impl kernel::Module for BinderModule { fn init(_module: &'static kernel::ThisModule) -> Result { // SAFETY: The module initializer never runs twice, so we only call this once. unsafe { crate::context::CONTEXTS.init() }; + let netlink = crate::netlink::BINDER_NL_FAMILY.register()?; BINDER_SHRINKER.register(c"android-binder")?; // SAFETY: The module is being loaded, so we can initialize binderfs. unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? }; - Ok(Self {}) + Ok(Self { _netlink: netlink }) } } diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs index 19f881948a84..33368f7fd551 100644 --- a/drivers/android/binder/thread.rs +++ b/drivers/android/binder/thread.rs @@ -1284,6 +1284,15 @@ fn transaction(self: &Arc, cmd: u32, reader: &mut UserSliceReader) -> Resu } } + if info.oneway_spam_suspect { + // If this is both a oneway spam suspect and a failure, we report it twice. This is + // useful in case the transaction failed with BR_TRANSACTION_PENDING_FROZEN. + info.report_netlink(BR_ONEWAY_SPAM_SUSPECT, &self.process.ctx); + } + if info.reply != 0 { + info.report_netlink(info.reply, &self.process.ctx); + } + Ok(()) } @@ -1354,11 +1363,11 @@ fn reply_inner(self: &Arc, info: &mut TransactionInfo) -> BinderResult { info.from_tid, info.to_pid ); - let param = err.source.as_ref().map_or(0, |e| e.to_errno()); let ee = ExtendedError::new(info.debug_id as u32, err.reply, param); orig.from .deliver_reply(Err(BR_FAILED_REPLY), &orig, Some(ee)); + info.reply = BR_FAILED_REPLY; err.reply = BR_TRANSACTION_COMPLETE; err }); diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs index afef5b46eac2..6cb7b745c52e 100644 --- a/drivers/android/binder/transaction.rs +++ b/drivers/android/binder/transaction.rs @@ -3,6 +3,7 @@ // Copyright (C) 2025 Google LLC. use kernel::{ + net::netlink::GENLMSG_DEFAULT_SIZE, prelude::*, seq_file::SeqFile, seq_print, @@ -18,6 +19,7 @@ allocation::{Allocation, TranslatedFds}, defs::*, error::{BinderError, BinderResult}, + netlink::Report, node::{Node, NodeRef}, process::{Process, ProcessInner}, ptr_align, @@ -51,6 +53,44 @@ impl TransactionInfo { pub(crate) fn is_oneway(&self) -> bool { self.flags & TF_ONE_WAY != 0 } + + pub(crate) fn report_netlink(&self, reply: u32, ctx: &crate::Context) { + if let Err(err) = self.report_netlink_inner(reply, ctx) { + pr_warn!( + "{}:{} netlink report failed: {err:?}\n", + self.from_pid, + self.from_tid + ); + } + } + + fn report_netlink_inner(&self, reply: u32, ctx: &crate::Context) -> kernel::error::Result { + if !Report::has_listeners() { + return Ok(()); + } + let mut report = Report::new(GENLMSG_DEFAULT_SIZE, 0, 0, GFP_KERNEL)?; + + report.error(reply)?; + report.context(&ctx.name)?; + report.from_pid(self.from_pid as u32)?; + report.from_tid(self.from_tid as u32)?; + if self.to_pid != 0 { + report.to_pid(self.to_pid as u32)?; + } + if self.to_tid != 0 { + report.to_tid(self.to_tid as u32)?; + } + + if self.is_reply { + report.is_reply()?; + } + report.flags(self.flags)?; + report.code(self.code)?; + report.data_size(self.data_size as u32)?; + + report.multicast(0, GFP_KERNEL)?; + Ok(()) + } } use core::mem::offset_of; diff --git a/rust/uapi/uapi_helper.h b/rust/uapi/uapi_helper.h index 06d7d1a2e8da..86c7b6b284b0 100644 --- a/rust/uapi/uapi_helper.h +++ b/rust/uapi/uapi_helper.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include -- 2.55.0.rc2.803.g1fd1e6609c-goog