rust_binder : Implement dynamic debug logging mask
Subsystems (2)
v2 cover rust_binder : Implement dynamic debug logging mask
When a user-space application sends malformed data or makes a lifecycle mistake, the driver rejects it with a generic error code (like -EINVAL). Without internal logs, the driver acts as a "black box," forcing developers to guess which check failed.
In the legacy C Binder driver, this issue is solved using a dynamic debug_mask module parameter that toggles verbose logs for specific subsystems. This series brings the same critical capability to the Rust Binder driver to provide developers with clear, real-time feedback.
Instead of rebuilds, reboots, or guessing:
- Developers can enable logs instantly on a running device by writing to /sys/module/rust_binder/parameters/debug_mask.
- It prints the exact reason for failures (such as alignment errors, mismatched call stacks, or invalid handle references) directly into dmesg, reducing debugging time from hours to seconds.
- It protects system logs by keeping logging off by default and only enabling it when developers are actively troubleshooting.
Based on top of:
https://lore.kernel.org/rust-for-linux/[email protected]/
---
Changes in v2:
- Defined the debug mask categories using bitflags (impl_flags) to
resolve potential Undefined Behavior and match Rust idioms.
- Added a tgid helper to rust/kernel/task.rs to expose the task
group ID and optimize default "PID:TID" prefixing.
- Implemented the BINDER_DEBUG_DEATH_NOTIFICATION mask to log OOM
failures, lifecycle updates, and async delivery to user-space.
- Added pid fields to ThreadError, DeliverCode, and FreezeMessage
to ensure cancellation logs show the correct process PID instead
of background kworker PIDs.
- Removed duplicate PID printing across workqueue, transaction
failure, and stack unwinding logs.
- Refactored log formatting (e.g. formatted BinderError with {:?},
used "strong"/"weak" strings, and improved message wording).
- Fixed logic bugs (restored missing update_ref block, moved manager
lookup warnings).
- Cleaned up spurious newlines, corrected indentations, and adjusted
all commit titles to be shorter and more meaningful.
- Link to v1: https://lore.kernel.org/r/[email protected]
---
Jahnavi MN (7):
rust_binder: Add dynamic debug logging mask
rust_binder: Implement BINDER_DEBUG_USER_ERROR for freezer-related operation
rust_binder: Implement BINDER_DEBUG_USER_ERROR for refcounting and death notifications
rust_binder: Implement BINDER_DEBUG_USER_ERROR for transaction parsing failures
rust_binder: Implement BINDER_DEBUG_FAILED_TRANSACTION
rust_binder: Implement BINDER_DEBUG_DEATH_NOTIFICATION
rust_binder: Implement BINDER_DEBUG_DEAD_TRANSACTION
drivers/android/binder/debug.rs | 80 ++++++++++++++++ drivers/android/binder/freeze.rs | 70 ++++++++++---- drivers/android/binder/node.rs | 30 +++++- drivers/android/binder/process.rs | 62 ++++++++++-- drivers/android/binder/rust_binder_main.rs | 16 +++- drivers/android/binder/rust_binderfs.c | 3 + drivers/android/binder/thread.rs | 149 ++++++++++++++++++++++------- drivers/android/binder/transaction.rs | 15 +++ rust/kernel/task.rs | 7 ++ 9 files changed, 363 insertions(+), 69 deletions(-)
---
base-commit: bf694eb01b1313f5510645ca34be4ed034ae08de
change-id: 20260702-rust_binder_debug_mask-636737015624
Best regards,
Signature · 1 line
Jahnavi MN <[email protected]>
PATCH v2 1/7 rust_binder: Add dynamic debug logging mask
Implement a dynamic debug logging mask (debug_mask) for the rust_binder module to allow dynamic runtime configuration of log levels. This enables parity with the legacy C driver's debug mask.
Since the Rust module! macro in the current kernel build does not yet support declaring module parameters directly in Rust, we define the debug_mask variable in a C companion file to expose it to the kernel runtime and import it using FFI with volatile reads.
To verify the setup, instrument process lifecycle events (open, flush, and release) in process.rs under the new BINDER_DEBUG_OPEN_CLOSE logging mask. These entry-point events are chosen for initial validation because they represent the start of the Binder lifecycle and occur at low frequency, allowing simple runtime verification of the dynamic toggle without log noise.
--- drivers/android/binder/debug.rs | 80 ++++++++++++++++++++++++++++++ drivers/android/binder/process.rs | 7 ++- drivers/android/binder/rust_binder_main.rs | 2 + drivers/android/binder/rust_binderfs.c | 3 ++ rust/kernel/task.rs | 7 +++ 5 files changed, 98 insertions(+), 1 deletion(-)
drivers/android/binder/debug.rs new file +80 -0
@@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Google LLC. + +//! Binder debugging helpers. + +#![allow(dead_code)] + +kernel::impl_flags!( +/// Represents multiple debug mask flags. +#[derive( Debug , Clone , Default , Copy , PartialEq , Eq )] +pub struct DebugMasks (u32 ); + +/// Represents a single debug mask category. +#[derive( Debug , Clone , Copy , PartialEq , Eq )] +pub enum DebugMask { +UserError =1 <<0 , +FailedTransaction =1 <<1 , +DeadTransaction =1 <<2 , +OpenClose =1 <<3 , +DeadBinder =1 <<4 , +DeathNotification =1 <<5 , +ReadWrite =1 <<6 , +UserRefs =1 <<7 , +Threads =1 <<8 , +Transaction =1 <<9 , +TransactionComplete =1 <<10 , +FreeBuffer =1 <<11 , +InternalRefs =1 <<12 , +PriorityCap =1 <<13 , +Spinlocks =1 <<14 , + } +); + +extern "C" { +// Declared as static mut because the value is mutated dynamically by C sysfs. +static mut rust_binder_debug_mask:u32 ; +} + +/// Checks if the given debug logging category is enabled in the mask. +pub (crate )fn debug_mask_enabled (mask:DebugMask ) ->bool { +// SAFETY: `rust_binder_debug_mask` is defined in C and can be mutated at any time. +// We read it via volatile to ensure we fetch the current value from memory. +let current_mask =unsafe { core::ptr::read_volatile (&raw const rust_binder_debug_mask) }; + (current_mask & (maskas u32 )) !=0 +} + +/// Prints a debug log if the specified mask category is enabled. +#[macro_export] +macro_rules! binder_debug { +// 1. Internal helper rule to do the raw print. + (raw, $($arg:tt)*) => { + kernel::pr_info!($($arg)*); + }; + +// 2. Rule to explicitly specify a PID (used in kworkers). + (pid=$pid:expr, $mask:ident, $($arg:tt)*) => { +if $crate::debug::debug_mask_enabled ($crate::debug::DebugMask ::$mask) { + $crate::binder_debug!( + raw, +"{}: {}\n" , + $pid, + kernel::prelude::fmt!($($arg)*) + ); + } + }; + +// 3. Default rule (automatically prepends "PID:TID" of the current calling thread). + ($mask:ident, $($arg:tt)*) => { +if $crate::debug::debug_mask_enabled ($crate::debug::DebugMask ::$mask) { +let thread = kernel::current!(); + $crate::binder_debug!( + raw, +"{}:{} {}\n" , + thread.tgid (), + thread.pid (), + kernel::prelude::fmt!($($arg)*) + ); + } + }; +}
drivers/android/binder/process.rs +6 -1
@@ -1349,6 +1349,7 @@ pub(crate) fn lock_with_nodes(&self) -> WithNodes<'_> { }fn deferred_flush (&self) { +binder_debug! (pid=self.task.pid (),OpenClose ,"flushing process" );let inner = self.inner.lock ();for threadin inner.threads.values () { thread.exit_looper ();
@@ -1356,6 +1357,8 @@ fn deferred_flush(&self) { }fn deferred_release (self:Arc <Self >) { +binder_debug! (pid=self.task.pid (),OpenClose ,"releasing process" ); +let is_manager = {let mut inner = self.inner.lock (); inner.is_dead =true ;
@@ -1658,7 +1661,9 @@ fn ioctl_write_read(/// The file operations supported by `Process`. impl Process {pub (crate )fn open (ctx:ArcBorrow <'_,Context >, file: &File ) ->Result <Arc <Process >> { -Self ::new (ctx.into (),ARef ::from (file.cred ())) +let proc =Self ::new (ctx.into (),ARef ::from (file.cred ()))?; +binder_debug! (OpenClose ,"opened process" ); +Ok (proc) }pub (crate )fn release (this:Arc <Process >, _file: &File ) {
drivers/android/binder/rust_binder_main.rs +2 -0
@@ -31,6 +31,8 @@mod context;mod deferred_close;mod defs; +#[macro_use] +mod debug;mod error;mod netlink;mod node;
drivers/android/binder/rust_binderfs.c +3 -0
@@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc);char *rust_binder_devices_param =CONFIG_ANDROID_BINDER_DEVICES ;module_param_named (rust_devices, rust_binder_devices_param, charp,0444 ); +u32 rust_binder_debug_mask =7 ; +module_param_named (debug_mask, rust_binder_debug_mask, uint,0644 ); +static dev_t binderfs_dev;static DEFINE_MUTEX (binderfs_minors_mutex);static DEFINE_IDA (binderfs_minors);
rust/kernel/task.rs +7 -0
@@ -210,6 +210,13 @@ pub fn pid(&self) -> Pid {unsafe { *ptr::addr_of!((*self.as_ptr ()).pid) } } +/// Returns the TGID (Thread Group ID / Process ID) of the given task. +pub fn tgid (&self) ->Pid { +// SAFETY: The tgid of a task never changes after initialization, so reading this field is +// not a data race. +unsafe { *ptr::addr_of!((*self.as_ptr ()).tgid) } + } +/// Returns the UID of the given task. #[inline] pub fn uid (&self) ->Kuid {
Signature · 1 line
2.55.0.795.g602f6c329a-goog
Greg Kroah-Hartman
maintainerRe: [PATCH v2 1/7] rust_binder: Add dynamic debug logging mask
Quoted 53 lines · On Fri, Jul 10, 2026 at 02:32:52PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
Implement a dynamic debug logging mask (
debug_mask) for therust_bindermodule to allow dynamic runtime configuration of log levels. This enables parity with the legacy C driver's debug mask.Since the Rust
module!macro in the current kernel build does not yet support declaring module parameters directly in Rust, we define thedebug_maskvariable in a C companion file to expose it to the kernel runtime and import it using FFI with volatile reads.To verify the setup, instrument process lifecycle events (open, flush, and release) in
process.rsunder the newBINDER_DEBUG_OPEN_CLOSElogging mask. These entry-point events are chosen for initial validation because they represent the start of the Binder lifecycle and occur at low frequency, allowing simple runtime verification of the dynamic toggle without log noise.Signed-off-by Jahnavi MN <[email protected]>--- drivers/android/binder/debug.rs | 80 ++++++++++++++++++++++++++++++ drivers/android/binder/process.rs | 7 ++- drivers/android/binder/rust_binder_main.rs | 2 + drivers/android/binder/rust_binderfs.c | 3 ++ rust/kernel/task.rs | 7 +++ 5 files changed, 98 insertions(+), 1 deletion(-)drivers/android/binder/debug.rs new file +20
-0@@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Google LLC. + +//! Binder debugging helpers. + +#![allow(dead_code)] + +kernel::impl_flags!( +/// Represents multiple debug mask flags. +#[derive( Debug , Clone , Default , Copy , PartialEq , Eq )] +pub struct DebugMasks (u32 ); + +/// Represents a single debug mask category. +#[derive( Debug , Clone , Copy , PartialEq , Eq )] +pub enum DebugMask { +UserError =1 <<0 , +FailedTransaction =1 <<1 , +DeadTransaction =1 <<2 , +OpenClose =1 <<3 , +DeadBinder =1 <<4 ,
drivers/android/binder/debug.rs line 30
Quoted 10 lines
drivers/android/binder/debug.rs new file +10
-0+ DeathNotification = 1 << 5, + ReadWrite = 1 << 6, + UserRefs = 1 << 7, + Threads = 1 << 8, + Transaction = 1 << 9, + TransactionComplete = 1 << 10, + FreeBuffer = 1 << 11, + InternalRefs = 1 << 12, + PriorityCap = 1 << 13, + Spinlocks = 1 << 14,
Not an objection, but don't we have the equivalent of the BIT() macro in rust? I think that's what this should be using if we do...
thanks,
greg k-h
Re: [PATCH v2 1/7] rust_binder: Add dynamic debug logging mask
Quoted 3 lines · Greg Kroah-Hartman
Not an objection, but don't we have the equivalent of the
BIT()macro in rust? I think that's what this should be using if we do...
Yeah:
https://rust.docs.kernel.org/kernel/bits/
We should probably update the example in the impl_flags! macro so that people use it here too -- the macro already takes an expression so it works.
Cheers,
Miguel
Re: [PATCH v2 1/7] rust_binder: Add dynamic debug logging mask
Quoted 4 lines · Miguel Ojeda
We should probably update the example in the
impl_flags!macro so that people use it here too -- the macro already takes an expression so it works.
Filled "good first issue":
https://github.com/Rust-for-Linux/linux/issues/1244
Cheers,
Miguel
Gary Guo
reviewerRe: [PATCH v2 1/7] rust_binder: Add dynamic debug logging mask
In reply to Jahnavi MNQuoted 60 lines · On Fri Jul 10, 2026 at 3:32 PM BST, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
Implement a dynamic debug logging mask (
debug_mask) for therust_bindermodule to allow dynamic runtime configuration of log levels. This enables parity with the legacy C driver's debug mask.Since the Rust
module!macro in the current kernel build does not yet support declaring module parameters directly in Rust, we define thedebug_maskvariable in a C companion file to expose it to the kernel runtime and import it using FFI with volatile reads.To verify the setup, instrument process lifecycle events (open, flush, and release) in
process.rsunder the newBINDER_DEBUG_OPEN_CLOSElogging mask. These entry-point events are chosen for initial validation because they represent the start of the Binder lifecycle and occur at low frequency, allowing simple runtime verification of the dynamic toggle without log noise.Signed-off-by Jahnavi MN <[email protected]>--- drivers/android/binder/debug.rs | 80 ++++++++++++++++++++++++++++++ drivers/android/binder/process.rs | 7 ++- drivers/android/binder/rust_binder_main.rs | 2 + drivers/android/binder/rust_binderfs.c | 3 ++ rust/kernel/task.rs | 7 +++ 5 files changed, 98 insertions(+), 1 deletion(-)drivers/android/binder/debug.rs new file +27
-0@@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Google LLC. + +//! Binder debugging helpers. + +#![allow(dead_code)] + +kernel::impl_flags!( +/// Represents multiple debug mask flags. +#[derive( Debug , Clone , Default , Copy , PartialEq , Eq )] +pub struct DebugMasks (u32 ); + +/// Represents a single debug mask category. +#[derive( Debug , Clone , Copy , PartialEq , Eq )] +pub enum DebugMask { +UserError =1 <<0 , +FailedTransaction =1 <<1 , +DeadTransaction =1 <<2 , +OpenClose =1 <<3 , +DeadBinder =1 <<4 , +DeathNotification =1 <<5 , +ReadWrite =1 <<6 , +UserRefs =1 <<7 , +Threads =1 <<8 , +Transaction =1 <<9 , +TransactionComplete =1 <<10 , +FreeBuffer =1 <<11 ,
drivers/android/binder/debug.rs line 37
Quoted 10 lines
drivers/android/binder/debug.rs new file +10
-0+ InternalRefs = 1 << 12, + PriorityCap = 1 << 13, + Spinlocks = 1 << 14, + } +); + +extern "C" { +// Declared as static mut because the value is mutated dynamically by C sysfs. +static mut rust_binder_debug_mask:u32 ; +}
This should be Atomic<u32> so the read_volatile below is avoided.
drivers/android/binder/debug.rs line 50
Quoted 13 lines
drivers/android/binder/debug.rs new file +13
-0+ +/// Checks if the given debug logging category is enabled in the mask. +pub (crate )fn debug_mask_enabled (mask:DebugMask ) ->bool { +// SAFETY: `rust_binder_debug_mask` is defined in C and can be mutated at any time. +// We read it via volatile to ensure we fetch the current value from memory. +let current_mask =unsafe { core::ptr::read_volatile (&raw const rust_binder_debug_mask) }; + (current_mask & (maskas u32 )) !=0 +} + +/// Prints a debug log if the specified mask category is enabled. +#[macro_export] +macro_rules! binder_debug { +// 1. Internal helper rule to do the raw print.
This 1, 2, 3 numbering is very strange.
drivers/android/binder/debug.rs line 53
Quoted 3 lines
drivers/android/binder/debug.rs new file +3
-0+ (raw, $($arg:tt)*) => { + kernel::pr_info!($($arg)*); + };
Why is this needed instead of just calling pr_info!?
Best,
Gary
Quoted 27 lines
drivers/android/binder/debug.rs new file +27
-0+ +// 2. Rule to explicitly specify a PID (used in kworkers). + (pid=$pid:expr, $mask:ident, $($arg:tt)*) => { + if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { + $crate::binder_debug!( + raw, +"{}: {}\n" , + $pid, + kernel::prelude::fmt!($($arg)*) + ); + } + }; + +// 3. Default rule (automatically prepends "PID:TID" of the current calling thread). + ($mask:ident, $($arg:tt)*) => { + if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { +let thread = kernel::current!(); + $crate::binder_debug!( + raw, +"{}:{} {}\n" , + thread.tgid (), + thread.pid (), + kernel::prelude::fmt!($($arg)*) + ); + } + }; +}
Re: [PATCH v2 1/7] rust_binder: Add dynamic debug logging mask
In reply to Jahnavi MNQuoted 10 lines · On Fri, Jul 10, 2026 at 02:32:52PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
Implement a dynamic debug logging mask (
debug_mask) for therust_bindermodule to allow dynamic runtime configuration of log levels. This enables parity with the legacy C driver's debug mask.Since the Rust
module!macro in the current kernel build does not yet support declaring module parameters directly in Rust, we define thedebug_maskvariable in a C companion file to expose it to the kernel runtime and import it using FFI with volatile reads.
Yeah, declaring it under rust_binderfs.c is not great but I guess we don't have any other alternative for now. So this is fine.
Quoted 24 lines
To verify the setup, instrument process lifecycle events (open, flush, and release) in
process.rsunder the newBINDER_DEBUG_OPEN_CLOSElogging mask. These entry-point events are chosen for initial validation because they represent the start of the Binder lifecycle and occur at low frequency, allowing simple runtime verification of the dynamic toggle without log noise.Signed-off-by Jahnavi MN <[email protected]>--- drivers/android/binder/debug.rs | 80 ++++++++++++++++++++++++++++++ drivers/android/binder/process.rs | 7 ++- drivers/android/binder/rust_binder_main.rs | 2 + drivers/android/binder/rust_binderfs.c | 3 ++ rust/kernel/task.rs | 7 +++ 5 files changed, 98 insertions(+), 1 deletion(-)drivers/android/binder/debug.rs new file +1
-0@@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0
drivers/android/binder/debug.rs line 11
Quoted 10 lines
drivers/android/binder/debug.rs new file +10
-0+// Copyright (C) 2026 Google LLC. + +//! Binder debugging helpers. + +#![allow(dead_code)] + +kernel::impl_flags!( +/// Represents multiple debug mask flags. +#[derive( Debug , Clone , Default , Copy , PartialEq , Eq )] +pub struct DebugMasks (u32 );
Where is this being used? I can't find it on subsequent patches. Maybe leftover from previous implementation?
Quoted 46 lines
drivers/android/binder/debug.rs new file +46
-0+ +/// Represents a single debug mask category. +#[derive( Debug , Clone , Copy , PartialEq , Eq )] +pub enum DebugMask { +UserError =1 <<0 , +FailedTransaction =1 <<1 , +DeadTransaction =1 <<2 , +OpenClose =1 <<3 , +DeadBinder =1 <<4 , +DeathNotification =1 <<5 , +ReadWrite =1 <<6 , +UserRefs =1 <<7 , +Threads =1 <<8 , +Transaction =1 <<9 , +TransactionComplete =1 <<10 , +FreeBuffer =1 <<11 , +InternalRefs =1 <<12 , +PriorityCap =1 <<13 , +Spinlocks =1 <<14 , + } +); + +extern "C" { +// Declared as static mut because the value is mutated dynamically by C sysfs. +static mut rust_binder_debug_mask:u32 ; +} + +/// Checks if the given debug logging category is enabled in the mask. +pub (crate )fn debug_mask_enabled (mask:DebugMask ) ->bool { +// SAFETY: `rust_binder_debug_mask` is defined in C and can be mutated at any time. +// We read it via volatile to ensure we fetch the current value from memory. +let current_mask =unsafe { core::ptr::read_volatile (&raw const rust_binder_debug_mask) }; + (current_mask & (maskas u32 )) !=0 +} + +/// Prints a debug log if the specified mask category is enabled. +#[macro_export] +macro_rules! binder_debug { +// 1. Internal helper rule to do the raw print. + (raw, $($arg:tt)*) => { + kernel::pr_info!($($arg)*); + }; + +// 2. Rule to explicitly specify a PID (used in kworkers). + (pid=$pid:expr, $mask:ident, $($arg:tt)*) => { +if $crate::debug::debug_mask_enabled ($crate::debug::DebugMask ::$mask) {
drivers/android/binder/debug.rs line 67
Quoted 10 lines
drivers/android/binder/debug.rs new file +10
-0+ $crate::binder_debug!( + raw, +"{}: {}\n" , + $pid, + kernel::prelude::fmt!($($arg)*) + ); + } + }; + +// 3. Default rule (automatically prepends "PID:TID" of the current calling thread).
Adding the pid:tid implicitly is a good idea. Nice!
Quoted 63 lines
drivers/android/binder/debug.rs new file +13
-0+ ($mask:ident, $($arg:tt)*) => { + if $crate::debug::debug_mask_enabled($crate::debug::DebugMask::$mask) { +let thread = kernel::current!(); + $crate::binder_debug!( + raw, +"{}:{} {}\n" , + thread.tgid (), + thread.pid (), + kernel::prelude::fmt!($($arg)*) + ); + } + }; +}drivers/android/binder/process.rs +6
-1@@ -1349,6 +1349,7 @@ pub(crate) fn lock_with_nodes(&self) -> WithNodes<'_> { }fn deferred_flush (&self) { +binder_debug! (pid=self.task.pid (),OpenClose ,"flushing process" );let inner = self.inner.lock ();for threadin inner.threads.values () { thread.exit_looper ();@@ -1356,6 +1357,8 @@ fn deferred_flush(&self) { }fn deferred_release (self:Arc <Self >) { +binder_debug! (pid=self.task.pid (),OpenClose ,"releasing process" ); +let is_manager = {let mut inner = self.inner.lock (); inner.is_dead =true ;@@ -1658,7 +1661,9 @@ fn ioctl_write_read(/// The file operations supported by `Process`. impl Process {pub (crate )fn open (ctx:ArcBorrow <'_,Context >, file: &File ) ->Result <Arc <Process >> { -Self ::new (ctx.into (),ARef ::from (file.cred ())) +let proc =Self ::new (ctx.into (),ARef ::from (file.cred ()))?; +binder_debug! (OpenClose ,"opened process" ); +Ok (proc) }pub (crate )fn release (this:Arc <Process >, _file: &File ) {drivers/android/binder/rust_binder_main.rs +2
-0@@ -31,6 +31,8 @@mod context;mod deferred_close;mod defs; +#[macro_use] +mod debug;mod error;mod netlink;mod node;drivers/android/binder/rust_binderfs.c +0
-0
drivers/android/binder/rust_binderfs.c DEFINE_SHOW_ATTRIBUTE(rust_binder_proc); · line 54
Quoted 4 lines
drivers/android/binder/rust_binderfs.c +1
-0@@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc);char *rust_binder_devices_param =CONFIG_ANDROID_BINDER_DEVICES ;module_param_named (rust_devices, rust_binder_devices_param, charp,0444 ); +u32 rust_binder_debug_mask =7 ;
hmm, I'm not sure about this magic number. Do you think we can initialize on the rust end at init()? Or at least a comment about what flags we are setting as default here?
Quoted 25 lines
drivers/android/binder/rust_binderfs.c +2
-0+module_param_named (debug_mask, rust_binder_debug_mask, uint,0644 ); +static dev_t binderfs_dev;static DEFINE_MUTEX (binderfs_minors_mutex);static DEFINE_IDA (binderfs_minors);rust/kernel/task.rs +7
-0@@ -210,6 +210,13 @@ pub fn pid(&self) -> Pid {unsafe { *ptr::addr_of!((*self.as_ptr ()).pid) } } +/// Returns the TGID (Thread Group ID / Process ID) of the given task. +pub fn tgid (&self) ->Pid { +// SAFETY: The tgid of a task never changes after initialization, so reading this field is +// not a data race. +unsafe { *ptr::addr_of!((*self.as_ptr ()).tgid) } + } +/// Returns the UID of the given task. #[inline] pub fn uid (&self) ->Kuid {Signature · 1 line
--
Only a few optional suggestions so...
Gary Guo
reviewerRe: [PATCH v2 1/7] rust_binder: Add dynamic debug logging mask
Quoted 40 lines · Carlos Llamas
Quoted 39 lines · On Fri, Jul 10, 2026 at 02:32:52PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
Implement a dynamic debug logging mask (
debug_mask) for therust_bindermodule to allow dynamic runtime configuration of log levels. This enables parity with the legacy C driver's debug mask.Since the Rust
module!macro in the current kernel build does not yet support declaring module parameters directly in Rust, we define thedebug_maskvariable in a C companion file to expose it to the kernel runtime and import it using FFI with volatile reads.To verify the setup, instrument process lifecycle events (open, flush, and release) in
process.rsunder the newBINDER_DEBUG_OPEN_CLOSElogging mask. These entry-point events are chosen for initial validation because they represent the start of the Binder lifecycle and occur at low frequency, allowing simple runtime verification of the dynamic toggle without log noise.Signed-off-by Jahnavi MN <[email protected]>--- drivers/android/binder/debug.rs | 80 ++++++++++++++++++++++++++++++ drivers/android/binder/process.rs | 7 ++- drivers/android/binder/rust_binder_main.rs | 2 + drivers/android/binder/rust_binderfs.c | 3 ++ rust/kernel/task.rs | 7 +++ 5 files changed, 98 insertions(+), 1 deletion(-)drivers/android/binder/debug.rs new file +6
-0@@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Google LLC. + +//! Binder debugging helpers. + +#![allow(dead_code)]
Quoted 8 lines
drivers/android/binder/debug.rs line 11
Quoted 5 lines
drivers/android/binder/debug.rs new file +5
-0+ +kernel::impl_flags!( +/// Represents multiple debug mask flags. +#[derive( Debug , Clone , Default , Copy , PartialEq , Eq )] +pub struct DebugMasks (u32 );Where is this being used? I can't find it on subsequent patches. Maybe leftover from previous implementation?
impl_flags macro requires a type for a single flag and a type for flag masks.
Quoted 43 lines
Quoted 43 lines
drivers/android/binder/debug.rs new file +21
-0+ +/// Represents a single debug mask category. +#[derive( Debug , Clone , Copy , PartialEq , Eq )] +pub enum DebugMask { +UserError =1 <<0 , +FailedTransaction =1 <<1 , +DeadTransaction =1 <<2 , +OpenClose =1 <<3 , +DeadBinder =1 <<4 , +DeathNotification =1 <<5 , +ReadWrite =1 <<6 , +UserRefs =1 <<7 , +Threads =1 <<8 , +Transaction =1 <<9 , +TransactionComplete =1 <<10 , +FreeBuffer =1 <<11 , +InternalRefs =1 <<12 , +PriorityCap =1 <<13 , +Spinlocks =1 <<14 , + } +);drivers/android/binder/rust_binder_main.rs +2
-0@@ -31,6 +31,8 @@mod context;mod deferred_close;mod defs; +#[macro_use] +mod debug;mod error;mod netlink;mod node;drivers/android/binder/rust_binderfs.c +0
-0@@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc);char *rust_binder_devices_param =CONFIG_ANDROID_BINDER_DEVICES ;module_param_named (rust_devices, rust_binder_devices_param, charp,0444 );
Quoted 5 lines
drivers/android/binder/rust_binderfs.c DEFINE_SHOW_ATTRIBUTE(rust_binder_proc); · line 54
Quoted 1 line
drivers/android/binder/rust_binderfs.c +1
-0+u32 rust_binder_debug_mask =7 ;hmm, I'm not sure about this magic number. Do you think we can initialize on the rust end at
init()? Or at least a comment about what flags we are setting as default here?
It doesn't even need that, just a
static rust_binder_debug_mask: Atomic<u32> = Atomic::new((FLAG_A as u32) | (FLAG_B as u32) | ...);
and a extern u32 on C side should do.
would do. Unfortunately this has to be integer bit-or with casts (and cannot use DebugMasks due to lack of const trait impl).
Best,
Gary
Carlos Llamas
maintainerRe: [PATCH v2 1/7] rust_binder: Add dynamic debug logging mask
Quoted 50 lines · Gary Guo
Quoted 40 lines · Carlos Llamas
Quoted 39 lines · On Fri, Jul 10, 2026 at 02:32:52PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
Implement a dynamic debug logging mask (
debug_mask) for therust_bindermodule to allow dynamic runtime configuration of log levels. This enables parity with the legacy C driver's debug mask.Since the Rust
module!macro in the current kernel build does not yet support declaring module parameters directly in Rust, we define thedebug_maskvariable in a C companion file to expose it to the kernel runtime and import it using FFI with volatile reads.To verify the setup, instrument process lifecycle events (open, flush, and release) in
process.rsunder the newBINDER_DEBUG_OPEN_CLOSElogging mask. These entry-point events are chosen for initial validation because they represent the start of the Binder lifecycle and occur at low frequency, allowing simple runtime verification of the dynamic toggle without log noise.Signed-off-by Jahnavi MN <[email protected]>--- drivers/android/binder/debug.rs | 80 ++++++++++++++++++++++++++++++ drivers/android/binder/process.rs | 7 ++- drivers/android/binder/rust_binder_main.rs | 2 + drivers/android/binder/rust_binderfs.c | 3 ++ rust/kernel/task.rs | 7 +++ 5 files changed, 98 insertions(+), 1 deletion(-)drivers/android/binder/debug.rs new file +6
-0@@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2026 Google LLC. + +//! Binder debugging helpers. + +#![allow(dead_code)] Quoted 8 lines
drivers/android/binder/debug.rs line 11
Quoted 5 lines
drivers/android/binder/debug.rs new file +5
-0+ +kernel::impl_flags!( +/// Represents multiple debug mask flags. +#[derive( Debug , Clone , Default , Copy , PartialEq , Eq )] +pub struct DebugMasks (u32 );Where is this being used? I can't find it on subsequent patches. Maybe leftover from previous implementation?
Quoted 1 line
impl_flagsmacro requires a type for a single flag and a type for flag masks.
Oh I see. I was expecting the bitops to be peformed on this type somewhere. I guess the reason it wasn't is because of the issue with module parameters and their C macros.
Quoted 50 lines
Quoted 44 lines
Quoted 44 lines
drivers/android/binder/debug.rs new file +21
-0+ +/// Represents a single debug mask category. +#[derive( Debug , Clone , Copy , PartialEq , Eq )] +pub enum DebugMask { +UserError =1 <<0 , +FailedTransaction =1 <<1 , +DeadTransaction =1 <<2 , +OpenClose =1 <<3 , +DeadBinder =1 <<4 , +DeathNotification =1 <<5 , +ReadWrite =1 <<6 , +UserRefs =1 <<7 , +Threads =1 <<8 , +Transaction =1 <<9 , +TransactionComplete =1 <<10 , +FreeBuffer =1 <<11 , +InternalRefs =1 <<12 , +PriorityCap =1 <<13 , +Spinlocks =1 <<14 , + } +);drivers/android/binder/rust_binder_main.rs +2
-0@@ -31,6 +31,8 @@mod context;mod deferred_close;mod defs; +#[macro_use] +mod debug;mod error;mod netlink;mod node;drivers/android/binder/rust_binderfs.c +0
-0@@ -51,6 +51,9 @@ DEFINE_SHOW_ATTRIBUTE(rust_binder_proc);char *rust_binder_devices_param =CONFIG_ANDROID_BINDER_DEVICES ;module_param_named (rust_devices, rust_binder_devices_param, charp,0444 );Quoted 5 lines
drivers/android/binder/rust_binderfs.c DEFINE_SHOW_ATTRIBUTE(rust_binder_proc); · line 54
Quoted 1 line
drivers/android/binder/rust_binderfs.c +1
-0+u32 rust_binder_debug_mask =7 ;hmm, I'm not sure about this magic number. Do you think we can initialize on the rust end at
init()? Or at least a comment about what flags we are setting as default here?
Quoted 5 lines
It doesn't even need that, just a
static
rust_binder_debug_mask: Atomic<u32> =Atomic::new((FLAG_A as u32) | (FLAG_B as u32) | ...);and a
extern u32on C side should do.
ha sure, reversing the declaration and extern would also work.
Alice Ryhl
maintainerRe: [PATCH v2 1/7] rust_binder: Add dynamic debug logging mask
Quoted 8 lines · Carlos Llamas
Quoted 7 lines · Gary Guo
Quoted 6 lines · Carlos Llamas
Quoted 4 lines · On Fri, Jul 10, 2026 at 02:32:52PM +0000, Jahnavi MN via B4 Relay wrote:
drivers/android/binder/debug.rs new file +4
-0+kernel::impl_flags!( +/// Represents multiple debug mask flags. +#[derive( Debug , Clone , Default , Copy , PartialEq , Eq )] +pub struct DebugMasks (u32 );
Quoted 8 lines
Quoted 4 lines
Quoted 2 lines
Where is this being used? I can't find it on subsequent patches. Maybe leftover from previous implementation?
impl_flagsmacro requires a type for a single flag and a type for flag masks.Oh I see. I was expecting the bitops to be peformed on this type somewhere. I guess the reason it wasn't is because of the issue with module parameters and their C macros.
I suppose we could implement debug_mask_enabled() like this:
/// Checks if the given debug logging category is enabled in the mask.
pub(crate) fn debug_mask_enabled(mask: DebugMask) -> bool {
let current_mask = unsafe { core::ptr::read_volatile(&raw const rust_binder_debug_mask) };
DebugMasks(current_mask).contains(mask)
}By wrapping the value from the volatile read (or atomic read) in DebugMasks, we do actually make use of the DebugMasks type too.
Alice
PATCH v2 2/7 rust_binder: Implement BINDER_DEBUG_USER_ERROR for freezer-related operation
In reply to Jahnavi MNThis adds dynamic debug logs for:
- Requesting freeze notifications on invalid references, duplicate cookies, or already active registrations.
- Completing freeze notifications that are not pending or not found.
- Clearing freeze notifications on invalid references, inactive notifications, or cookie mismatches.
--- drivers/android/binder/freeze.rs | 46 +++++++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 10 deletions(-)
drivers/android/binder/freeze.rs +36 -10
@@ -189,12 +189,18 @@ pub(crate) fn request_freeze_notif( info =match node_refs.by_handle.get_mut (&handle) {Some (info) => info,None => { -pr_warn! ("BC_REQUEST_FREEZE_NOTIFICATION invalid ref {}\n" , handle); +binder_debug! ( +UserError , +"BC_REQUEST_FREEZE_NOTIFICATION invalid ref {handle}" + );return Err (EINVAL ); } };if info.freeze ().is_some () { -pr_warn! ("BC_REQUEST_FREEZE_NOTIFICATION already set\n" ); +binder_debug! ( +UserError , +"BC_REQUEST_FREEZE_NOTIFICATION already set" + );return Err (EINVAL ); }let node_ref = info.node_ref ();
@@ -202,7 +208,10 @@ pub(crate) fn request_freeze_notif(if let rbtree::Entry ::Occupied (ref dupe) = freeze_entry {if !dupe.get ().allow_duplicate (&node_ref.node) { -pr_warn! ("BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie\n" ); +binder_debug! ( +UserError , +"BC_REQUEST_FREEZE_NOTIFICATION duplicate cookie" + );return Err (EINVAL ); } }
@@ -267,7 +276,11 @@ pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader)let mut node_refs_guard = self.node_refs.lock ();let node_refs = &mut *node_refs_guard;let Some (freeze) = node_refs.freeze_listeners.get_mut (&cookie)else { -pr_warn! ("BC_FREEZE_NOTIFICATION_DONE {:016x} not found\n" , cookie.0 ); +binder_debug! ( +UserError , +"BC_FREEZE_NOTIFICATION_DONE {:016x} not found" , + cookie.0 + );return Err (EINVAL ); };let mut clear_msg =None ;
@@ -277,8 +290,9 @@ pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader) freeze.num_cleared_duplicates +=1 ; }else {if !freeze.is_pending { -pr_warn! ( -"BC_FREEZE_NOTIFICATION_DONE {:016x} not pending\n" , +binder_debug! ( +UserError , +"BC_FREEZE_NOTIFICATION_DONE {:016x} not pending" , cookie.0 );return Err (EINVAL );
@@ -307,19 +321,31 @@ pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader)let mut node_refs_guard = self.node_refs.lock ();let node_refs = &mut *node_refs_guard;let Some (info) = node_refs.by_handle.get_mut (&handle)else { -pr_warn! ("BC_CLEAR_FREEZE_NOTIFICATION invalid ref {}\n" , handle); +binder_debug! ( +UserError , +"BC_CLEAR_FREEZE_NOTIFICATION invalid ref {handle}" + );return Err (EINVAL ); };let Some (info_cookie) = info.freeze ()else { -pr_warn! ("BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active\n" ); +binder_debug! ( +UserError , +"BC_CLEAR_FREEZE_NOTIFICATION freeze notification not active" + );return Err (EINVAL ); };if *info_cookie != cookie { -pr_warn! ("BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch\n" ); +binder_debug! ( +UserError , +"BC_CLEAR_FREEZE_NOTIFICATION freeze notification cookie mismatch" + );return Err (EINVAL ); }let Some (listener) = node_refs.freeze_listeners.get_mut (&cookie)else { -pr_warn! ("BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {}\n" , handle); +binder_debug! ( +UserError , +"BC_CLEAR_FREEZE_NOTIFICATION invalid cookie {handle}" + );return Err (EINVAL ); }; listener.is_clearing =true ;
Signature · 1 line
2.55.0.795.g602f6c329a-goog
Re: [PATCH v2 2/7] rust_binder: Implement BINDER_DEBUG_USER_ERROR for freezer-related operation
Quoted 11 lines · On Fri, Jul 10, 2026 at 02:32:53PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
This adds dynamic debug logs for:
- Requesting freeze notifications on invalid references, duplicate cookies, or already active registrations.
- Completing freeze notifications that are not pending or not found.
- Clearing freeze notifications on invalid references, inactive notifications, or cookie mismatches.Signed-off-by Jahnavi MN <[email protected]>---
LGTM, thanks!
PATCH v2 3/7 rust_binder: Implement BINDER_DEBUG_USER_ERROR for refcounting and death notifications
In reply to Jahnavi MNThis adds dynamic debug logs for:
- Decrementing handle reference counts that are already zero.
- Mismatched reference states (calling inc_ref_done with no active inc_refs, or using a weak reference as a strong reference).
- Requesting or clearing death notifications on invalid references, already active notifications, or with mismatched cookies.
--- drivers/android/binder/node.rs | 16 +++++++++++---- drivers/android/binder/process.rs | 41 ++++++++++++++++++++++++++++++++------- 2 files changed, 46 insertions(+), 11 deletions(-)
drivers/android/binder/node.rs +12 -4
@@ -345,7 +345,10 @@ pub(crate) fn inc_ref_done_locked( ) -> Option<DLArc<Node>> {let inner = self.inner.access_mut (owner_inner);if inner.active_inc_refs ==0 { -pr_err! ("inc_ref_done called when no active inc_refs" ); +binder_debug! ( +UserError , +"inc_ref_done called when no active inc_refs" + );return None ; }
@@ -821,6 +824,10 @@ pub(crate) fn get_count(&self) -> (usize, usize) {pub (crate )fn clone (&self, strong:bool ) ->Result <NodeRef > {if strong && self.strong_count ==0 { +binder_debug! ( +UserError , +"tried to use weak ref as strong ref" + );return Err (EINVAL ); } Ok(self
@@ -861,9 +868,10 @@ pub(crate) fn update(&mut self, inc: bool, strong: bool) -> bool { *count +=1 ; }else {if *count ==0 { -pr_warn! ( -"pid {} performed invalid decrement on ref\n" , - kernel::current!().pid () +binder_debug! ( +UserError , +"performed invalid {} decrement on ref" , +if strong {"strong" }else {"weak" } );return false ; }
drivers/android/binder/process.rs +34 -7
78 lines not on this page · raw message
Signature · 1 line
2.55.0.795.g602f6c329a-goog
Re: [PATCH v2 3/7] rust_binder: Implement BINDER_DEBUG_USER_ERROR for refcounting and death notifications
Quoted 11 lines · On Fri, Jul 10, 2026 at 02:32:54PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
This adds dynamic debug logs for:
- Decrementing handle reference counts that are already zero.
- Mismatched reference states (callinginc_ref_donewith no activeinc_refs, or using a weak reference as a strong reference).
- Requesting or clearing death notifications on invalid references, already active notifications, or with mismatched cookies.Signed-off-by Jahnavi MN <[email protected]>---
LGTM, thanks!
Re: [PATCH v2 3/7] rust_binder: Implement BINDER_DEBUG_USER_ERROR for refcounting and death notifications
In reply to Jahnavi MNQuoted 11 lines · On Fri, Jul 10, 2026 at 02:32:54PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
This adds dynamic debug logs for:
- Decrementing handle reference counts that are already zero.
- Mismatched reference states (callinginc_ref_donewith no activeinc_refs, or using a weak reference as a strong reference).
- Requesting or clearing death notifications on invalid references, already active notifications, or with mismatched cookies.Signed-off-by Jahnavi MN <[email protected]>---
LGTM, thanks!
PATCH v2 4/7 rust_binder: Implement BINDER_DEBUG_USER_ERROR for transaction parsing failures
In reply to Jahnavi MNThis adds dynamic debug logs in thread.rs for:
- File descriptor array (FDA) parent offset and parent buffer address alignment misalignments.
- Memory copy, write, and translation failures during transaction serialization (including out-of-bounds pointer fixups).
- Incoming transactions or replies that do not match the expected thread calling stack (such as out-of-order replies).
--- drivers/android/binder/thread.rs | 84 +++++++++++++++++++++++++++++----------- 1 file changed, 62 insertions(+), 22 deletions(-)
drivers/android/binder/thread.rs +62 -22
@@ -743,11 +743,12 @@ fn translate_object( let alloc_offset = match sg_state.unused_buffer_space.claim_next(obj_length) { Ok(alloc_offset) => alloc_offset, Err(err) => { - pr_warn!( - "Failed to claim space for a BINDER_TYPE_PTR. (offset: {}, limit: {}, size: {})", + binder_debug!( + UserError, + "failed to claim space for a BINDER_TYPE_PTR (offset: {}, limit: {}, size: {})", sg_state.unused_buffer_space.offset, sg_state.unused_buffer_space.limit, - obj_length, + obj_length ); return Err(err.into()); }
@@ -826,6 +827,10 @@ fn translate_object(let fds_len = num_fds.checked_mul (size_of ::<u32 >()).ok_or (EINVAL )?;if !is_aligned (parent_offset,size_of ::<u32 >()) { +binder_debug! ( +UserError , +"FDA parent offset not aligned correctly" + );return Err (EINVAL .into ()); }
@@ -844,6 +849,10 @@ fn translate_object( };if !is_aligned (parent_entry.sender_uaddr,size_of ::<u32 >()) { +binder_debug! ( +UserError , +"FDA parent buffer not aligned correctly" + );return Err (EINVAL .into ()); }
@@ -927,12 +936,9 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) ->let target_offset_end = fixup_offset.checked_add (fixup_len).ok_or (EINVAL )?;if fixup_offset < end_of_previous_fixup || offset_end < target_offset_end { -pr_warn! ( -"Fixups oob {} {} {} {}" , - fixup_offset, - end_of_previous_fixup, - offset_end, - target_offset_end +binder_debug! ( +UserError , +"fixups oob {fixup_offset} {end_of_previous_fixup} {offset_end} {target_offset_end}" );return Err (EINVAL .into ()); }
@@ -940,18 +946,27 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) ->let copy_off = end_of_previous_fixup;let copy_len = fixup_offset - end_of_previous_fixup;if let Err (err) = alloc.copy_into (&mut reader, copy_off, copy_len) { -pr_warn! ("Failed copying into alloc: {:?}" , err); +binder_debug! ( +UserError , +"failed copying into alloc: {err:?}" + );return Err (err.into ()); }if let PointerFixupEntry ::Fixup { pointer_value, .. } = fixup {let res = alloc.write ::<u64 >(fixup_offset, pointer_value);if let Err (err) = res { -pr_warn! ("Failed copying ptr into alloc: {:?}" , err); +binder_debug! ( +UserError , +"failed copying ptr into alloc: {err:?}" + );return Err (err.into ()); } }if let Err (err) = reader.skip (fixup_len) { -pr_warn! ("Failed skipping {} from reader: {:?}" , fixup_len, err); +binder_debug! ( +UserError , +"failed skipping {fixup_len} from reader: {err:?}" + );return Err (err.into ()); } end_of_previous_fixup = target_offset_end;
@@ -959,7 +974,10 @@ fn apply_sg(&self, alloc: &mut Allocation, sg_state: &mut ScatterGatherState) ->let copy_off = end_of_previous_fixup;let copy_len = offset_end - end_of_previous_fixup;if let Err (err) = alloc.copy_into (&mut reader, copy_off, copy_len) { -pr_warn! ("Failed copying remainder into alloc: {:?}" , err); +binder_debug! ( +UserError , +"failed copying remainder into alloc: {err:?}" + );return Err (err.into ()); } }
@@ -1063,7 +1081,10 @@ pub(crate) fn copy_transaction_data(let offset:usize = offset.try_into ().map_err (|_|EINVAL )?;if offset < end_of_previous_object || !is_aligned (offset,size_of ::<u32 >()) { -pr_warn! ("Got transaction with invalid offset." ); +binder_debug! ( +UserError , +"got transaction with invalid offset" + );return Err (EINVAL .into ()); }
@@ -1088,7 +1109,10 @@ pub(crate) fn copy_transaction_data( ) { Ok(()) => end_of_previous_object = offset + object.size(), Err(err) => { -pr_warn! ("Error while translating object." ); +binder_debug! ( +UserError , +"error while translating object: {err:?}" + );return Err (err); } }
@@ -1108,15 +1132,15 @@ pub(crate) fn copy_transaction_data( )?;if let Some (sg_state) = sg_state.as_mut () { -if let Err (err) = self.apply_sg (&mut alloc, sg_state) { -pr_warn! ("Failure in apply_sg: {:?}" , err); -return Err (err); - } + self.apply_sg (&mut alloc, sg_state)?; }if let Some ((off_out, secctx)) = secctx.as_mut () {if let Err (err) = alloc.write (secctx_off, secctx.as_bytes ()) { -pr_warn! ("Failed to write security context: {:?}" , err); +binder_debug! ( +UserError , +"failed to write security context: {err:?}" + );return Err (err.into ()); } **off_out = secctx_off;
@@ -1328,7 +1352,10 @@ fn transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResu {let mut inner = self.inner.lock ();if !transaction.is_stacked_on (&inner.current_transaction) { -pr_warn! ("Transaction stack changed during transaction!" ); +binder_debug! ( +UserError , +"got new transaction with bad transaction stack" + );return Err (EINVAL .into ()); } inner.current_transaction =Some (transaction.clone_arc ());
@@ -1351,8 +1378,21 @@ fn transaction_inner(self: &Arc<Self>, info: &mut TransactionInfo) -> BinderResu }fn reply_inner (self: &Arc <Self >, info: &mut TransactionInfo ) ->BinderResult { -let orig = self.inner.lock ().pop_transaction_to_reply (self)?; +let orig =match self.inner.lock ().pop_transaction_to_reply (self) { +Ok (orig) => orig, +Err (err) => { +binder_debug! ( +UserError , +"got reply transaction with no transaction stack" + ); +return Err (err.into ()); + } + };if !orig.from.is_current_transaction (&orig) { +binder_debug! ( +UserError , +"got reply transaction with bad transaction stack" + );return Err (EINVAL .into ()); }
Signature · 1 line
2.55.0.795.g602f6c329a-goog
Re: [PATCH v2 4/7] rust_binder: Implement BINDER_DEBUG_USER_ERROR for transaction parsing failures
Quoted 10 lines · On Fri, Jul 10, 2026 at 02:32:55PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
This adds dynamic debug logs in
thread.rsfor:
- File descriptor array (FDA) parent offset and parent buffer address alignment misalignments.
- Memory copy, write, and translation failures during transaction serialization (including out-of-bounds pointer fixups).
- Incoming transactions or replies that do not match the expected thread calling stack (such as out-of-order replies).
Quoted 2 lines
Signed-off-by Jahnavi MN <[email protected]>---
LGTM, thanks!
PATCH v2 5/7 rust_binder: Implement BINDER_DEBUG_FAILED_TRANSACTION
In reply to Jahnavi MNThis adds dynamic debug logs for:
- Failed replies, target process deaths, and error code deliveries.
- Detailed transaction failure diagnostics (including sender/receiver PIDs, TIDs, transaction IDs, buffer sizes, and error codes).
--- drivers/android/binder/thread.rs | 21 ++++++++++++++++----- drivers/android/binder/transaction.rs | 8 ++++++++ 2 files changed, 24 insertions(+), 5 deletions(-)
drivers/android/binder/thread.rs +16 -5
@@ -1315,11 +1315,22 @@ fn transaction(self: &Arc<Self>, cmd: u32, reader: &mut UserSliceReader) -> Resu } } -pr_warn! ( -"{}:{} transaction to {} failed: {err:?}" , - info.from_pid, - info.from_tid, - info.to_pid +binder_debug! ( +FailedTransaction , +"transaction {} to {}:{} failed {:?}, code {} size {}-{}" , +if info.is_reply { +"reply" + }else if info.is_oneway () { +"async" + }else { +"call" + }, + info.to_pid, + info.to_tid, + err, + info.code, + info.data_size, + info.offsets_size ); } }
drivers/android/binder/transaction.rs +8 -0
@@ -448,6 +448,14 @@ fn do_work( }else {// On failure to process the list, we send a reply back to the sender and ignore the // transaction on the recipient. +binder_debug! ( +FailedTransaction , +"transaction {} to {} failed, fd fixups failed, size {}-{}" , + self.debug_id, + self.to.task.pid (), + self.data_size, + self.offsets_size + );return Ok (true ); };
Signature · 1 line
2.55.0.795.g602f6c329a-goog
Re: [PATCH v2 5/7] rust_binder: Implement BINDER_DEBUG_FAILED_TRANSACTION
Quoted 9 lines · On Fri, Jul 10, 2026 at 02:32:56PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
This adds dynamic debug logs for:
- Failed replies, target process deaths, and error code deliveries.
- Detailed transaction failure diagnostics (including sender/receiver PIDs, TIDs, transaction IDs, buffer sizes, and error codes).Signed-off-by Jahnavi MN <[email protected]>---
LGTM, thanks!
PATCH v2 6/7 rust_binder: Implement BINDER_DEBUG_DEATH_NOTIFICATION
In reply to Jahnavi MNThis adds dynamic debug logs for:
- Memory allocation (OOM) failures when requesting death notifications
- Registration and cancellation lifecycle events (BC_REQUEST / BC_CLEAR)
- Delivery of death notification events to userspace (BR_DEAD_BINDER)
--- drivers/android/binder/node.rs | 5 +++++ drivers/android/binder/process.rs | 14 ++++++++++++++ 2 files changed, 19 insertions(+)
drivers/android/binder/node.rs +5 -0
@@ -1113,6 +1113,11 @@ fn do_work(// We're still holding the inner lock, so it cannot be aborted while we insert it into // the delivered list. process_inner.death_delivered (self.clone ()); +binder_debug! ( +DeathNotification , +"sending death notification, cookie {:016x}" , + cookie + );BR_DEAD_BINDER };
drivers/android/binder/process.rs +14 -0
@@ -1267,6 +1267,10 @@ pub(crate) fn request_death(// Queue BR_ERROR if we can't allocate memory for the death notification. let death =UniqueArc ::new_uninit (GFP_KERNEL ).inspect_err (|_| { thread.push_return_work (BR_ERROR ); +binder_debug! ( +DeathNotification , +"BC_REQUEST_DEATH_NOTIFICATION failed due to memory allocation failure" + ); })?;let mut refs = self.node_refs.lock ();let Some (info) = refs.by_handle.get_mut (&handle)else {
@@ -1310,6 +1314,11 @@ pub(crate) fn request_death( info.node_ref ().node.add_death (death, &mut owner_inner); } } +binder_debug! ( +DeathNotification , +"BC_REQUEST_DEATH_NOTIFICATION handle {handle} cookie {:016x}" , + cookie + );Ok (()) }
@@ -1353,6 +1362,11 @@ pub(crate) fn clear_death(&self, reader: &mut UserSliceReader, thread: &Thread) } } +binder_debug! ( +DeathNotification , +"BC_CLEAR_DEATH_NOTIFICATION handle {handle} cookie {:016x}" , + cookie + );Ok (()) }
Signature · 1 line
2.55.0.795.g602f6c329a-goog
Re: [PATCH v2 6/7] rust_binder: Implement BINDER_DEBUG_DEATH_NOTIFICATION
Quoted 10 lines · On Fri, Jul 10, 2026 at 02:32:57PM +0000, Jahnavi MN via B4 Relay wrote:
From: Jahnavi MN <[email protected]>
This adds dynamic debug logs for:
- Memory allocation (OOM) failures when requesting death notifications
- Registration and cancellation lifecycle events (BC_REQUEST/BC_CLEAR)
- Delivery of death notification events to userspace (BR_DEAD_BINDER)
Quoted 2 lines
Signed-off-by Jahnavi MN <[email protected]>---
LGTM, thanks!
PATCH v2 7/7 rust_binder: Implement BINDER_DEBUG_DEAD_TRANSACTION
In reply to Jahnavi MNThis adds dynamic debug logs for:
- Releasing active transactions during thread stack unwinding.
- Discarded transaction error codes when a thread exits.
- Undelivered transaction acknowledgments (TRANSACTION_COMPLETE) upon thread exit.
- Undelivered process death and freeze notifications when processes exit or die.
- Undelivered transactions canceled due to target process death.
We now store the process PID in ThreadError, DeliverCode, and FreezeMessage to ensure the correct PID is logged on cancellation. This is necessary because cancel() runs from background kworkers, which would otherwise print the wrong PID.
--- drivers/android/binder/freeze.rs | 24 ++++++++++------ drivers/android/binder/node.rs | 9 +++++- drivers/android/binder/rust_binder_main.rs | 14 ++++++++-- drivers/android/binder/thread.rs | 44 ++++++++++++++++++++++++------ drivers/android/binder/transaction.rs | 7 +++++ 5 files changed, 78 insertions(+), 20 deletions(-)
drivers/android/binder/freeze.rs +16 -8
@@ -60,6 +60,7 @@ fn allow_duplicate(&self, node: &DArc<Node>) -> bool {/// Represents a notification that the freeze state has changed. pub (crate )struct FreezeMessage { cookie:FreezeCookie , + pid:i32 , } kernel::list::impl_list_arc_safe! {
@@ -73,8 +74,8 @@ fn new(flags: kernel::alloc::Flags) -> Result<UninitFM, AllocError> {UniqueArc ::new_uninit (flags) } -fn init (ua:UninitFM , cookie:FreezeCookie ) ->DLArc <FreezeMessage > { -match ua.pin_init_with (DTRWrap ::new (FreezeMessage { cookie })) { +fn init (ua:UninitFM , cookie:FreezeCookie , pid:i32 ) ->DLArc <FreezeMessage > { +match ua.pin_init_with (DTRWrap ::new (FreezeMessage { cookie, pid })) {Ok (msg) =>ListArc ::from (msg),Err (err) =>match err {}, }
@@ -140,7 +141,14 @@ fn do_work( } } -fn cancel (self:DArc <Self >) {} +fn cancel (self:DArc <Self >) { +binder_debug! ( + pid=self.pid, +DeadTransaction , +"undelivered freeze notification, {:016x}" , + self.cookie.0 + ); + }fn should_sync_wakeup (&self) ->bool {false
@@ -264,7 +272,7 @@ pub(crate) fn request_freeze_notif( } *info.freeze () =Some (cookie); -let msg =FreezeMessage ::init (msg, cookie); +let msg =FreezeMessage ::init (msg, cookie, self.task.pid ());drop (node_refs_guard);let _ = self.push_work (msg);Ok (())
@@ -285,7 +293,7 @@ pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader) };let mut clear_msg =None ;if freeze.num_pending_duplicates >0 { - clear_msg =Some (FreezeMessage ::init (alloc, cookie)); + clear_msg =Some (FreezeMessage ::init (alloc, cookie, self.task.pid ())); freeze.num_pending_duplicates -=1 ; freeze.num_cleared_duplicates +=1 ; }else {
@@ -300,7 +308,7 @@ pub(crate) fn freeze_notif_done(self: &Arc<Self>, reader: &mut UserSliceReader)let is_frozen = freeze.node.owner.inner.lock ().is_frozen.is_fully_frozen ();if freeze.is_clearing || freeze.last_is_frozen !=Some (is_frozen) {// Immediately send another FreezeMessage. - clear_msg =Some (FreezeMessage ::init (alloc, cookie)); + clear_msg =Some (FreezeMessage ::init (alloc, cookie, self.task.pid ())); } freeze.is_pending =false ; }
@@ -353,7 +361,7 @@ pub(crate) fn clear_freeze_notif(self: &Arc<Self>, reader: &mut UserSliceReader) *info.freeze () =None ;let mut msg =None ;if !listener.is_pending { - msg =Some (FreezeMessage ::init (alloc, cookie)); + msg =Some (FreezeMessage ::init (alloc, cookie, self.task.pid ())); }drop (node_refs_guard);
@@ -433,7 +441,7 @@ pub(crate) fn prepare_freeze_messages(&self) -> Result<FreezeMessages, AllocErrocontinue ; };let msg_alloc =FreezeMessage ::new (GFP_KERNEL )?; -let msg =FreezeMessage ::init (msg_alloc, cookie); +let msg =FreezeMessage ::init (msg_alloc, cookie, proc.task.pid ()); batch.push ((proc, msg),GFP_KERNEL )?; }
drivers/android/binder/node.rs +8 -1
@@ -1128,7 +1128,14 @@ fn do_work(Ok (cmd !=BR_DEAD_BINDER ) } -fn cancel (self:DArc <Self >) {} +fn cancel (self:DArc <Self >) { +binder_debug! ( + pid=self.process.task.pid (), +DeadTransaction , +"undelivered death notification, {:016x}" , + self.cookie + ); + }fn should_sync_wakeup (&self) ->bool {false
drivers/android/binder/rust_binder_main.rs +12 -2
@@ -222,6 +222,7 @@ fn arc_pin_init(init: impl PinInit<T>) -> Result<DLArc<T>, kernel::error::Error>struct DeliverCode { code:u32 , skip:Atomic <bool >, + pid:i32 , } kernel::list::impl_list_arc_safe! {
@@ -229,10 +230,11 @@ struct DeliverCode { }impl DeliverCode { -fn new (code:u32 ) ->Self { +fn new (code:u32 , pid:i32 ) ->Self {Self { code, skip:Atomic ::new (false ), + pid, } }
@@ -257,7 +259,15 @@ fn do_work(Ok (true ) } -fn cancel (self:DArc <Self >) {} +fn cancel (self:DArc <Self >) { +if !self.skip.load (Relaxed ) { +binder_debug! ( + pid=self.pid, +DeadTransaction , +"undelivered TRANSACTION_COMPLETE" + ); + } + }fn should_sync_wakeup (&self) ->bool {false
drivers/android/binder/thread.rs +35 -9
105 lines not on this page · raw message
drivers/android/binder/transaction.rs +7 -0
@@ -532,6 +532,13 @@ fn cancel(self: DArc<Self>) {if self.target_node.is_some () && self.flags &TF_ONE_WAY ==0 {let reply =Err (BR_DEAD_REPLY ); self.from.deliver_reply (reply, &self,None ); + }else { +binder_debug! ( + pid=self.to.task.pid (), +DeadTransaction , +"undelivered transaction {}, process died" , + self.debug_id + ); } self.drop_outstanding_txn ();
Signature · 1 line
2.55.0.795.g602f6c329a-goog
Quoted 21 lines · On Fri, Jul 10, 2026 at 4:33 PM Jahnavi MN via B4 Relay…
When a user-space application sends malformed data or makes a lifecycle mistake, the driver rejects it with a generic error code (like
-EINVAL). Without internal logs, the driver acts as a "black box," forcing developers to guess which check failed.In the legacy C Binder driver, this issue is solved using a dynamic
debug_maskmodule parameter that toggles verbose logs for specific subsystems. This series brings the same critical capability to the Rust Binder driver to provide developers with clear, real-time feedback.Instead of rebuilds, reboots, or guessing:
- Developers can enable logs instantly on a running device by writing to/sys/module/rust_binder/parameters/debug_mask.
- It prints the exact reason for failures (such as alignment errors, mismatched call stacks, or invalid handle references) directly intodmesg, reducing debugging time from hours to seconds.
- It protects system logs by keeping logging off by default and only enabling it when developers are actively troubleshooting.
Quoted 4 lines
Based on top of:
https://lore.kernel.org/rust-for-linux/[email protected]/Signed-off-by Jahnavi MN <[email protected]>
Greg Kroah-Hartman
maintainerQuoted 20 lines · On Fri, Jul 10, 2026 at 02:32:51PM +0000, Jahnavi MN via B4 Relay wrote:
When a user-space application sends malformed data or makes a lifecycle mistake, the driver rejects it with a generic error code (like
-EINVAL). Without internal logs, the driver acts as a "black box," forcing developers to guess which check failed.In the legacy C Binder driver, this issue is solved using a dynamic
debug_maskmodule parameter that toggles verbose logs for specific subsystems. This series brings the same critical capability to the Rust Binder driver to provide developers with clear, real-time feedback.Instead of rebuilds, reboots, or guessing:
- Developers can enable logs instantly on a running device by writing to/sys/module/rust_binder/parameters/debug_mask.
- It prints the exact reason for failures (such as alignment errors, mismatched call stacks, or invalid handle references) directly intodmesg, reducing debugging time from hours to seconds.
- It protects system logs by keeping logging off by default and only enabling it when developers are actively troubleshooting.
Quoted 4 lines
Based on top of:
https://lore.kernel.org/rust-for-linux/[email protected]/Signed-off-by Jahnavi MN <[email protected]>
Patches 5 and 7 did not apply to my tree, can you rebase against the char-misc-testing branch and send just those? Other ones are now all queued up, nice work!
thanks,
greg k-h
Quoted 27 lines · Greg Kroah-Hartman
Quoted 20 lines · On Fri, Jul 10, 2026 at 02:32:51PM +0000, Jahnavi MN via B4 Relay wrote:
When a user-space application sends malformed data or makes a lifecycle mistake, the driver rejects it with a generic error code (like
-EINVAL). Without internal logs, the driver acts as a "black box," forcing developers to guess which check failed.In the legacy C Binder driver, this issue is solved using a dynamic
debug_maskmodule parameter that toggles verbose logs for specific subsystems. This series brings the same critical capability to the Rust Binder driver to provide developers with clear, real-time feedback.Instead of rebuilds, reboots, or guessing:
- Developers can enable logs instantly on a running device by writing to/sys/module/rust_binder/parameters/debug_mask.
- It prints the exact reason for failures (such as alignment errors, mismatched call stacks, or invalid handle references) directly intodmesg, reducing debugging time from hours to seconds.
- It protects system logs by keeping logging off by default and only enabling it when developers are actively troubleshooting.Quoted 4 lines
Based on top of:
https://lore.kernel.org/rust-for-linux/[email protected]/Signed-off-by Jahnavi MN <[email protected]>
Quoted 7 lines
Patches 5 and 7 did not apply to my tree, can you rebase against the char-misc-testing branch and send just those? Other ones are now all queued up, nice work!
thanks,
greg k-h
Hi Greg,
Thanks! I will rebase and resubmit only patches 5 and 7 as a new version (v3) of this series shortly.
Regarding the feedback I received on Patch 1 (using the BIT macro and Atomic<u32>), since Patch 1 is already queued, I will send those cleanups as a separate, follow-up patch. Does that sound good?
Thanks,
Jahnavi
Alice Ryhl
maintainerQuoted 38 lines · Jahnavi Nitheesha Mandava
Quoted 27 lines · Greg Kroah-Hartman
Quoted 20 lines · On Fri, Jul 10, 2026 at 02:32:51PM +0000, Jahnavi MN via B4 Relay wrote:
When a user-space application sends malformed data or makes a lifecycle mistake, the driver rejects it with a generic error code (like
-EINVAL). Without internal logs, the driver acts as a "black box," forcing developers to guess which check failed.In the legacy C Binder driver, this issue is solved using a dynamic
debug_maskmodule parameter that toggles verbose logs for specific subsystems. This series brings the same critical capability to the Rust Binder driver to provide developers with clear, real-time feedback.Instead of rebuilds, reboots, or guessing:
- Developers can enable logs instantly on a running device by writing to/sys/module/rust_binder/parameters/debug_mask.
- It prints the exact reason for failures (such as alignment errors, mismatched call stacks, or invalid handle references) directly intodmesg, reducing debugging time from hours to seconds.
- It protects system logs by keeping logging off by default and only enabling it when developers are actively troubleshooting.Quoted 4 lines
Based on top of:
https://lore.kernel.org/rust-for-linux/[email protected]/Signed-off-by Jahnavi MN <[email protected]>Quoted 7 lines
Patches 5 and 7 did not apply to my tree, can you rebase against the char-misc-testing branch and send just those? Other ones are now all queued up, nice work!
thanks,
greg k-h
Quoted 8 lines
Hi Greg,
Thanks! I will rebase and resubmit only patches 5 and 7 as a new version (v3) of this series shortly.
Regarding the feedback I received on Patch 1 (using the BIT macro and Atomic<u32>), since Patch 1 is already queued, I will send those cleanups as a separate, follow-up patch. Does that sound good?
Since it's only queued in char-misc-testing and not char-misc-next, I don't think it's too late to change the patches themselves. So I guess you could also resend the entire series based on top of commit f8d269390cd2 ("rust_binder: update Process::node_refs to use SpinLock") from testing?
Alice
Greg Kroah-Hartman
maintainerQuoted 49 lines · Alice Ryhl
Quoted 38 lines · Jahnavi Nitheesha Mandava
Quoted 27 lines · Greg Kroah-Hartman
Quoted 20 lines · On Fri, Jul 10, 2026 at 02:32:51PM +0000, Jahnavi MN via B4 Relay wrote:
When a user-space application sends malformed data or makes a lifecycle mistake, the driver rejects it with a generic error code (like
-EINVAL). Without internal logs, the driver acts as a "black box," forcing developers to guess which check failed.In the legacy C Binder driver, this issue is solved using a dynamic
debug_maskmodule parameter that toggles verbose logs for specific subsystems. This series brings the same critical capability to the Rust Binder driver to provide developers with clear, real-time feedback.Instead of rebuilds, reboots, or guessing:
- Developers can enable logs instantly on a running device by writing to/sys/module/rust_binder/parameters/debug_mask.
- It prints the exact reason for failures (such as alignment errors, mismatched call stacks, or invalid handle references) directly intodmesg, reducing debugging time from hours to seconds.
- It protects system logs by keeping logging off by default and only enabling it when developers are actively troubleshooting.Quoted 4 lines
Based on top of:
https://lore.kernel.org/rust-for-linux/[email protected]/Signed-off-by Jahnavi MN <[email protected]>Quoted 7 lines
Patches 5 and 7 did not apply to my tree, can you rebase against the char-misc-testing branch and send just those? Other ones are now all queued up, nice work!
thanks,
greg k-h
Quoted 8 lines
Hi Greg,
Thanks! I will rebase and resubmit only patches 5 and 7 as a new version (v3) of this series shortly.
Regarding the feedback I received on Patch 1 (using the BIT macro and Atomic<u32>), since Patch 1 is already queued, I will send those cleanups as a separate, follow-up patch. Does that sound good?
Quoted 5 lines
Since it's only queued in char-misc-testing and not char-misc-next, I don't think it's too late to change the patches themselves. So I guess you could also resend the entire series based on top of commit
f8d269390cd2("rust_binder: updateProcess::node_refsto use SpinLock") from testing?
Yes, I messed up the application of this series to my tree as Alice pointed out in another email. If you could just rebase and resend a new version, that would be great.
thanks,
greg k-h