mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Carlos Llamas <cmllamas@google.com>
To: Chris Wiseman <chriswiseman@google.com>
Cc: "Alice Ryhl" <aliceryhl@google.com>,
	"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
	"Arve Hjønnevåg" <arve@android.com>,
	"Todd Kjos" <tkjos@android.com>,
	"Christian Brauner" <brauner@kernel.org>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
	stable@vger.kernel.org
Subject: Re: [PATCH 2/2] rust_binder: account for deferred work size in Thread::read()
Date: Mon, 9 Nov 2026 19:42:02 +0000	[thread overview]
Message-ID: <avIiCpJ3ctbQaExK@google.com> (raw)
In-Reply-To: <18e61a8cb2ff887c.3137322e32332e3235332e313736.9d011.0000sendgmr@google.com>

On Sun, Nov 08, 2026 at 03:11:32PM +0000, Chris Wiseman wrote:
> When a looper thread replies with TF_DEFER_COMPLETE, its 4-byte
> BR_TRANSACTION_COMPLETE work item is queued via push_work_deferred() in
> inner.work_list without setting inner.process_work_list. On the next
> blocking BINDER_WRITE_READ ioctl, inner.should_use_process_work_queue()
> returns true and Thread::read() checks only whether the remaining output
> buffer can fit a single transaction (4-byte command code plus
> size_of::<uapi::binder_transaction_data_secctx>()).
> 
> If userspace provides a read buffer with 76 bytes remaining after the
> initial 4-byte BR_NOOP placeholder (read_size = 80), Thread::read()
> calls Self::get_work(), sleeps in Process.ready_threads, and receives an
> incoming synchronous transaction queued behind the deferred
> BR_TRANSACTION_COMPLETE in inner.work_list. Iteration 1 of the loop
> writes the 4-byte BR_TRANSACTION_COMPLETE, reducing writer.len() to 72
> bytes, and iteration 2 exits the loop without delivering the incoming
> transaction, leaving it stranded in the thread-local work_list.

ok, so if userspace is setting TF_DEFER_COMPLETE, the read buffer size
should reflect the possiblity of a BR_TRANSACTION_COMPLETE no? I'm not
sure this is a driver issue. Maybe I'm missing something?

I guess we are trying to match the behavior we have without the
TF_DEFER_COMPLETE and using a smaller read buffer e.g. 65-bytes. I
suppose this makes sense.

> 
> Add a size(&self) -> usize method to DeliverToRead returning the maximum
> number of bytes that do_work() may write to the user read buffer, and
> sum the sizes of all deferred items in inner.work_list when
> use_proc_queue is true. Include deferred_size on the right-hand side of
> the while condition in Thread::read() so the thread does not wait for
> global events when the output buffer lacks room for both its deferred
> work items and an incoming transaction, and subtract work.size() as each
> item is popped so subsequent loop iterations do not double-count items
> already written to writer.
> 
> Suggested-by: Alice Ryhl <aliceryhl@google.com>
> Fixes: 8eb05e8e47f1 ("rust_binder: add TF_DEFER_COMPLETE flag for avoiding userspace roundtrip")
> Cc: stable@vger.kernel.org
> Signed-off-by: Chris Wiseman <chriswiseman@google.com>
> ---
>  drivers/android/binder/freeze.rs           |  4 +++
>  drivers/android/binder/node.rs             |  8 +++++
>  drivers/android/binder/node/wrapper.rs     |  4 +++
>  drivers/android/binder/rust_binder_main.rs |  8 +++++
>  drivers/android/binder/thread.rs           | 34 ++++++++++++++++------
>  drivers/android/binder/transaction.rs      |  4 +++
>  6 files changed, 53 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/android/binder/freeze.rs b/drivers/android/binder/freeze.rs
> index 69e058e6d818..5103bfac781f 100644
> --- a/drivers/android/binder/freeze.rs
> +++ b/drivers/android/binder/freeze.rs
> @@ -152,6 +152,10 @@ fn should_sync_wakeup(&self) -> bool {
>          false
>      }
>  
> +    fn size(&self) -> usize {
> +        size_of::<u32>() + size_of::<BinderFrozenStateInfo>()
> +    }
> +
>      #[inline(never)]
>      fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result {
>          seq_print!(m, "{}has frozen binder\n", prefix);
> diff --git a/drivers/android/binder/node.rs b/drivers/android/binder/node.rs
> index efdafc202e27..4f4c892cba58 100644
> --- a/drivers/android/binder/node.rs
> +++ b/drivers/android/binder/node.rs
> @@ -754,6 +754,10 @@ fn should_sync_wakeup(&self) -> bool {
>          false
>      }
>  
> +    fn size(&self) -> usize {
> +        2 * (size_of::<u32>() + 2 * size_of::<u64>())
> +    }
> +
>      #[inline(never)]
>      fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result {
>          seq_print!(
> @@ -1149,6 +1153,10 @@ fn should_sync_wakeup(&self) -> bool {
>          false
>      }
>  
> +    fn size(&self) -> usize {
> +        size_of::<u32>() + size_of::<u64>()
> +    }
> +
>      #[inline(never)]
>      fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result {
>          let inner = self.inner.lock();
> diff --git a/drivers/android/binder/node/wrapper.rs b/drivers/android/binder/node/wrapper.rs
> index edfbccf6e6da..37826349eafb 100644
> --- a/drivers/android/binder/node/wrapper.rs
> +++ b/drivers/android/binder/node/wrapper.rs
> @@ -93,6 +93,10 @@ fn should_sync_wakeup(&self) -> bool {
>          false
>      }
>  
> +    fn size(&self) -> usize {
> +        2 * (size_of::<u32>() + 2 * size_of::<u64>())
> +    }
> +
>      #[inline(never)]
>      fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result {
>          seq_print!(
> diff --git a/drivers/android/binder/rust_binder_main.rs b/drivers/android/binder/rust_binder_main.rs
> index 56c786475237..361f3a987235 100644
> --- a/drivers/android/binder/rust_binder_main.rs
> +++ b/drivers/android/binder/rust_binder_main.rs
> @@ -163,6 +163,10 @@ fn do_work(
>      /// Generally only set to true for non-oneway transactions.
>      fn should_sync_wakeup(&self) -> bool;
>  
> +    /// Returns the maximum size in bytes that [`DeliverToRead::do_work`] may write to the read
> +    /// buffer.
> +    fn size(&self) -> usize;
> +
>      fn debug_print(&self, m: &SeqFile, prefix: &str, transaction_prefix: &str) -> Result;
>  }
>  
> @@ -279,6 +283,10 @@ fn should_sync_wakeup(&self) -> bool {
>          false
>      }
>  
> +    fn size(&self) -> usize {
> +        size_of::<u32>()
> +    }
> +
>      fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result {
>          seq_print!(m, "{}", prefix);
>          if self.skip.load(Relaxed) {
> diff --git a/drivers/android/binder/thread.rs b/drivers/android/binder/thread.rs
> index 3ca5c95695e4..af7a340adcb3 100644
> --- a/drivers/android/binder/thread.rs
> +++ b/drivers/android/binder/thread.rs
> @@ -1599,13 +1599,20 @@ fn read(self: &Arc<Self>, req: &mut BinderWriteRead, wait: bool) -> Result {
>              UserSlice::new(UserPtr::from_addr(read_start as usize), read_len as usize).writer(),
>              self,
>          );
> -        let (in_pool, has_transaction, thread_todo, use_proc_queue) = {
> +        let (in_pool, has_transaction, thread_todo, use_proc_queue, mut deferred_size) = {
>              let inner = self.inner.lock();
> +            let use_proc_queue = inner.should_use_process_work_queue();
> +            let deferred_size = if use_proc_queue {
> +                inner.work_list.iter().map(|w| w.size()).sum()
> +            } else {
> +                0
> +            };
>              (
>                  inner.is_looper(),
>                  inner.current_transaction.is_some(),
>                  !inner.work_list.is_empty(),
> -                inner.should_use_process_work_queue(),
> +                use_proc_queue,
> +                deferred_size,
>              )
>          };
>  
> @@ -1630,15 +1637,20 @@ fn read(self: &Arc<Self>, req: &mut BinderWriteRead, wait: bool) -> Result {
>  
>          // Loop doing work while there is room in the buffer.
>          let initial_len = writer.len();
> -        while writer.len() >= size_of::<uapi::binder_transaction_data_secctx>() + 4 {
> +        while writer.len() >= size_of::<uapi::binder_transaction_data_secctx>() + 4 + deferred_size
> +        {
>              match getter(self, wait && initial_len == writer.len()) {
> -                Ok(Some(work)) => match work.into_arc().do_work(self, &mut writer) {
> -                    Ok(true) => {}
> -                    Ok(false) => break,
> -                    Err(err) => {
> -                        return Err(err);
> +                Ok(Some(work)) => {
> +                    let work = work.into_arc();
> +                    deferred_size = deferred_size.saturating_sub(work.size());
> +                    match work.do_work(self, &mut writer) {
> +                        Ok(true) => {}
> +                        Ok(false) => break,
> +                        Err(err) => {
> +                            return Err(err);
> +                        }
>                      }
> -                },
> +                }
>                  Ok(None) => {
>                      break;
>                  }
> @@ -1798,6 +1810,10 @@ fn should_sync_wakeup(&self) -> bool {
>          false
>      }
>  
> +    fn size(&self) -> usize {
> +        size_of::<u32>()
> +    }
> +
>      fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result {
>          seq_print!(
>              m,
> diff --git a/drivers/android/binder/transaction.rs b/drivers/android/binder/transaction.rs
> index 48ac942bb75a..6909d7a8a219 100644
> --- a/drivers/android/binder/transaction.rs
> +++ b/drivers/android/binder/transaction.rs
> @@ -762,6 +762,10 @@ fn should_sync_wakeup(&self) -> bool {
>          !self.flags.is_oneway()
>      }
>  
> +    fn size(&self) -> usize {
> +        size_of::<u32>() + size_of::<BinderTransactionDataSecctx>()
> +    }
> +
>      fn debug_print(&self, m: &SeqFile, _prefix: &str, tprefix: &str) -> Result {
>          self.debug_print_inner(m, tprefix);
>          Ok(())
> -- 
> 2.56.0.317.g454f8302e1-goog
> 

It seems sashiko has found a legit issue, see:
https://sashiko.dev/#/patchset/18e61a85b0d13e9c.3137322e32332e3235332e313736.9c152.0000sendgmr%40google.com

--
Carlos Llamas

  reply	other threads:[~2026-11-09 19:42 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-08 15:11 [PATCH 1/2] rust_binder: reject sync transaction with pending sync work Chris Wiseman
2026-11-08 15:11 ` [PATCH 2/2] rust_binder: account for deferred work size in Thread::read() Chris Wiseman
2026-11-09 19:42   ` Carlos Llamas [this message]
2026-11-09 19:15 ` [PATCH 1/2] rust_binder: reject sync transaction with pending sync work Carlos Llamas

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=avIiCpJ3ctbQaExK@google.com \
    --to=cmllamas@google.com \
    --cc=aliceryhl@google.com \
    --cc=arve@android.com \
    --cc=brauner@kernel.org \
    --cc=chriswiseman@google.com \
    --cc=gregkh@linuxfoundation.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=tkjos@android.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®