1688 lines · 62.7 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. //! This module defines the `Thread` type, which represents a userspace thread that is using//! binder.//!//! The `Process` object stores all of the threads in an rb tree. use kernel::{    bindings,    fs::{File, LocalFile},    list::{AtomicTracker, List, ListArc, ListLinks, TryNewListArc},    prelude::*,    security,    seq_file::SeqFile,    seq_print,    sync::atomic::{ordering::Relaxed, Atomic},    sync::poll::{PollCondVar, PollTable},    sync::{aref::ARef, Arc, SpinLock},    task::Task,    uaccess::{UserPtr, UserSlice, UserSliceReader},    uapi,}; use crate::{    allocation::{Allocation, AllocationView, BinderObject, BinderObjectRef, NewAllocation},    defs::*,    error::BinderResult,    process::{GetWorkOrRegister, Process},    ptr_align,    stats::GLOBAL_STATS,    transaction::{Transaction, TransactionInfo},    BinderReturnWriter, DArc, DLArc, DTRWrap, DeliverCode, DeliverToRead,}; use core::mem::size_of; fn is_aligned(value: usize, to: usize) -> bool {    value % to == 0} /// Stores the layout of the scatter-gather entries. This is used during the `translate_objects`/// call and is discarded when it returns.struct ScatterGatherState {    /// A struct that tracks the amount of unused buffer space.    unused_buffer_space: UnusedBufferSpace,    /// Scatter-gather entries to copy.    sg_entries: KVec<ScatterGatherEntry>,    /// Indexes into `sg_entries` corresponding to the last binder_buffer_object that    /// was processed and all of its ancestors. The array is in sorted order.    ancestors: KVec<usize>,} /// This entry specifies an additional buffer that should be copied using the scatter-gather/// mechanism.struct ScatterGatherEntry {    /// The index in the offset array of the BINDER_TYPE_PTR that this entry originates from.    obj_index: usize,    /// Offset in target buffer.    offset: usize,    /// User address in source buffer.    sender_uaddr: usize,    /// Number of bytes to copy.    length: usize,    /// The minimum offset of the next fixup in this buffer.    fixup_min_offset: usize,    /// The offsets within this buffer that contain pointers which should be translated.    pointer_fixups: KVec<PointerFixupEntry>,} /// This entry specifies that a fixup should happen at `target_offset` of the/// buffer.enum PointerFixupEntry {    /// A fixup for a `binder_buffer_object`.    Fixup {        /// The translated pointer to write.        pointer_value: u64,        /// The offset at which the value should be written. The offset is relative        /// to the original buffer.        target_offset: usize,    },    /// A skip for a `binder_fd_array_object`.    Skip {        /// The number of bytes to skip.        skip: usize,        /// The offset at which the skip should happen. The offset is relative        /// to the original buffer.        target_offset: usize,    },} /// Return type of `apply_and_validate_fixup_in_parent`.struct ParentFixupInfo {    /// The index of the parent buffer in `sg_entries`.    parent_sg_index: usize,    /// The number of ancestors of the buffer.    ///    /// The buffer is considered an ancestor of itself, so this is always at    /// least one.    num_ancestors: usize,    /// New value of `fixup_min_offset` if this fixup is applied.    new_min_offset: usize,    /// The offset of the fixup in the target buffer.    target_offset: usize,} impl ScatterGatherState {    /// Called when a `binder_buffer_object` or `binder_fd_array_object` tries    /// to access a region in its parent buffer. These accesses have various    /// restrictions, which this method verifies.    ///    /// The `parent_offset` and `length` arguments describe the offset and    /// length of the access in the parent buffer.    ///    /// # Detailed restrictions    ///    /// Obviously the fixup must be in-bounds for the parent buffer.    ///    /// For safety reasons, we only allow fixups inside a buffer to happen    /// at increasing offsets; additionally, we only allow fixup on the last    /// buffer object that was verified, or one of its parents.    ///    /// Example of what is allowed:    ///    /// A    ///   B (parent = A, offset = 0)    ///   C (parent = A, offset = 16)    ///     D (parent = C, offset = 0)    ///   E (parent = A, offset = 32) // min_offset is 16 (C.parent_offset)    ///    /// Examples of what is not allowed:    ///    /// Decreasing offsets within the same parent:    /// A    ///   C (parent = A, offset = 16)    ///   B (parent = A, offset = 0) // decreasing offset within A    ///    /// Arcerring to a parent that wasn't the last object or any of its parents:    /// A    ///   B (parent = A, offset = 0)    ///   C (parent = A, offset = 0)    ///   C (parent = A, offset = 16)    ///     D (parent = B, offset = 0) // B is not A or any of A's parents    fn validate_parent_fixup(        &self,        parent: usize,        parent_offset: usize,        length: usize,    ) -> Result<ParentFixupInfo> {        // Using `position` would also be correct, but `rposition` avoids        // quadratic running times.        let ancestors_i = self            .ancestors            .iter()            .copied()            .rposition(|sg_idx| self.sg_entries[sg_idx].obj_index == parent)            .ok_or(EINVAL)?;        let sg_idx = self.ancestors[ancestors_i];        let sg_entry = match self.sg_entries.get(sg_idx) {            Some(sg_entry) => sg_entry,            None => {                pr_err!(                    "self.ancestors[{}] is {}, but self.sg_entries.len() is {}",                    ancestors_i,                    sg_idx,                    self.sg_entries.len()                );                return Err(EINVAL);            }        };        if sg_entry.fixup_min_offset > parent_offset {            pr_warn!(                "validate_parent_fixup: fixup_min_offset={}, parent_offset={}",                sg_entry.fixup_min_offset,                parent_offset            );            return Err(EINVAL);        }        let new_min_offset = parent_offset.checked_add(length).ok_or(EINVAL)?;        if new_min_offset > sg_entry.length {            pr_warn!(                "validate_parent_fixup: new_min_offset={}, sg_entry.length={}",                new_min_offset,                sg_entry.length            );            return Err(EINVAL);        }        let target_offset = sg_entry.offset.checked_add(parent_offset).ok_or(EINVAL)?;        // The `ancestors_i + 1` operation can't overflow since the output of the addition is at        // most `self.ancestors.len()`, which also fits in a usize.        Ok(ParentFixupInfo {            parent_sg_index: sg_idx,            num_ancestors: ancestors_i + 1,            new_min_offset,            target_offset,        })    }} /// Keeps track of how much unused buffer space is left. The initial amount is the number of bytes/// requested by the user using the `buffers_size` field of `binder_transaction_data_sg`. Each time/// we translate an object of type `BINDER_TYPE_PTR`, some of the unused buffer space is consumed.struct UnusedBufferSpace {    /// The start of the remaining space.    offset: usize,    /// The end of the remaining space.    limit: usize,}impl UnusedBufferSpace {    /// Claim the next `size` bytes from the unused buffer space. The offset for the claimed chunk    /// into the buffer is returned.    fn claim_next(&mut self, size: usize) -> Result<usize> {        // We require every chunk to be aligned.        let size = ptr_align(size).ok_or(EINVAL)?;        let new_offset = self.offset.checked_add(size).ok_or(EINVAL)?;         if new_offset <= self.limit {            let offset = self.offset;            self.offset = new_offset;            Ok(offset)        } else {            Err(EINVAL)        }    }} pub(crate) enum PushWorkRes {    Ok,    FailedDead(DLArc<dyn DeliverToRead>),} impl PushWorkRes {    fn is_ok(&self) -> bool {        match self {            PushWorkRes::Ok => true,            PushWorkRes::FailedDead(_) => false,        }    }} /// The fields of `Thread` protected by the spinlock.struct InnerThread {    /// Determines the looper state of the thread. It is a bit-wise combination of the constants    /// prefixed with `LOOPER_`.    looper_flags: u32,     /// Determines whether the looper should return.    looper_need_return: bool,     /// Determines if thread is dead.    is_dead: bool,     /// Work item used to deliver error codes to the thread that started a transaction. Stored here    /// so that it can be reused.    reply_work: DArc<ThreadError>,     /// Work item used to deliver error codes to the current thread. Stored here so that it can be    /// reused.    return_work: DArc<ThreadError>,     /// Determines whether the work list below should be processed. When set to false, `work_list`    /// is treated as if it were empty.    process_work_list: bool,    /// List of work items to deliver to userspace.    work_list: List<DTRWrap<dyn DeliverToRead>>,    current_transaction: Option<DArc<Transaction>>,     /// Extended error information for this thread.    extended_error: ExtendedError,} const LOOPER_REGISTERED: u32 = 0x01;const LOOPER_ENTERED: u32 = 0x02;const LOOPER_EXITED: u32 = 0x04;const LOOPER_INVALID: u32 = 0x08;const LOOPER_WAITING: u32 = 0x10;const LOOPER_WAITING_PROC: u32 = 0x20;const LOOPER_POLL: u32 = 0x40; impl InnerThread {    fn new() -> Result<Self> {        fn next_err_id() -> u32 {            static EE_ID: Atomic<u32> = Atomic::new(0);            EE_ID.fetch_add(1, Relaxed)        }         Ok(Self {            looper_flags: 0,            looper_need_return: false,            is_dead: false,            process_work_list: false,            reply_work: ThreadError::try_new()?,            return_work: ThreadError::try_new()?,            work_list: List::new(),            current_transaction: None,            extended_error: ExtendedError::new(next_err_id(), BR_OK, 0),        })    }     fn pop_work(&mut self) -> Option<DLArc<dyn DeliverToRead>> {        if !self.process_work_list {            return None;        }         let ret = self.work_list.pop_front();        self.process_work_list = !self.work_list.is_empty();        ret    }     fn push_work(&mut self, work: DLArc<dyn DeliverToRead>) -> PushWorkRes {        if self.is_dead {            PushWorkRes::FailedDead(work)        } else {            self.work_list.push_back(work);            self.process_work_list = true;            PushWorkRes::Ok        }    }     fn push_reply_work(&mut self, code: u32) {        if let Ok(work) = ListArc::try_from_arc(self.reply_work.clone()) {            work.set_error_code(code);            self.push_work(work);        } else {            pr_warn!("Thread reply work is already in use.");        }    }     fn push_return_work(&mut self, reply: u32) {        if let Ok(work) = ListArc::try_from_arc(self.return_work.clone()) {            work.set_error_code(reply);            self.push_work(work);        } else {            pr_warn!("Thread return work is already in use.");        }    }     /// Used to push work items that do not need to be processed immediately and can wait until the    /// thread gets another work item.    fn push_work_deferred(&mut self, work: DLArc<dyn DeliverToRead>) {        self.work_list.push_back(work);    }     /// Fetches the transaction this thread can reply to. If the thread has a pending transaction    /// (that it could respond to) but it has also issued a transaction, it must first wait for the    /// previously-issued transaction to complete.    ///    /// The `thread` parameter should be the thread containing this `ThreadInner`.    fn pop_transaction_to_reply(&mut self, thread: &Thread) -> Result<DArc<Transaction>> {        let transaction = self.current_transaction.take().ok_or(EINVAL)?;        if core::ptr::eq(thread, transaction.from.as_ref()) {            self.current_transaction = Some(transaction);            return Err(EINVAL);        }        // Find a new current transaction for this thread.        self.current_transaction = transaction.find_from(thread).cloned();        Ok(transaction)    }     fn pop_transaction_replied(&mut self, transaction: &DArc<Transaction>) -> bool {        match self.current_transaction.take() {            None => false,            Some(old) => {                if !Arc::ptr_eq(transaction, &old) {                    self.current_transaction = Some(old);                    return false;                }                self.current_transaction = old.clone_next();                true            }        }    }     fn looper_enter(&mut self) {        self.looper_flags |= LOOPER_ENTERED;        if self.looper_flags & LOOPER_REGISTERED != 0 {            self.looper_flags |= LOOPER_INVALID;        }    }     fn looper_register(&mut self, valid: bool) {        self.looper_flags |= LOOPER_REGISTERED;        if !valid || self.looper_flags & LOOPER_ENTERED != 0 {            self.looper_flags |= LOOPER_INVALID;        }    }     fn looper_exit(&mut self) {        self.looper_flags |= LOOPER_EXITED;    }     /// Determines whether the thread is part of a pool, i.e., if it is a looper.    fn is_looper(&self) -> bool {        self.looper_flags & (LOOPER_ENTERED | LOOPER_REGISTERED) != 0    }     /// Determines whether the thread should attempt to fetch work items from the process queue.    /// This is generally case when the thread is registered as a looper and not part of a    /// transaction stack. But if there is local work, we want to return to userspace before we    /// deliver any remote work.    fn should_use_process_work_queue(&self) -> bool {        self.current_transaction.is_none() && !self.process_work_list && self.is_looper()    }     fn poll(&mut self) -> u32 {        self.looper_flags |= LOOPER_POLL;        if self.process_work_list || self.looper_need_return {            bindings::POLLIN        } else {            0        }    }} /// This represents a thread that's used with binder.#[pin_data]pub(crate) struct Thread {    pub(crate) id: i32,    pub(crate) process: Arc<Process>,    pub(crate) task: ARef<Task>,    #[pin]    inner: SpinLock<InnerThread>,    #[pin]    work_condvar: PollCondVar,    /// Used to insert this thread into the process' `ready_threads` list.    ///    /// INVARIANT: May never be used for any other list than the `self.process.ready_threads`.    #[pin]    links: ListLinks,    #[pin]    links_track: AtomicTracker,} kernel::list::impl_list_arc_safe! {    impl ListArcSafe<0> for Thread {        tracked_by links_track: AtomicTracker;    }}kernel::list::impl_list_item! {    impl ListItem<0> for Thread {        using ListLinks { self.links };    }} impl Thread {    pub(crate) fn new(id: i32, process: Arc<Process>) -> Result<Arc<Self>> {        let inner = InnerThread::new()?;         Arc::pin_init(            try_pin_init!(Thread {                id,                process,                task: ARef::from(&**kernel::current!()),                inner <- kernel::new_spinlock!(inner, "Thread::inner"),                work_condvar <- kernel::new_poll_condvar!("Thread::work_condvar"),                links <- ListLinks::new(),                links_track <- AtomicTracker::new(),            }),            GFP_KERNEL,        )    }     #[inline(never)]    pub(crate) fn debug_print(self: &Arc<Self>, m: &SeqFile, print_all: bool) -> Result<()> {        let inner = self.inner.lock();         if print_all || inner.current_transaction.is_some() || !inner.work_list.is_empty() {            seq_print!(                m,                "  thread {}: l {:02x} need_return {}\n",                self.id,                inner.looper_flags,                inner.looper_need_return,            );        }         let mut t_opt = inner.current_transaction.as_ref();        while let Some(t) = t_opt {            if Arc::ptr_eq(&t.from, self) {                t.debug_print_inner(m, "    outgoing transaction ");                t_opt = t.from_parent.as_ref();            } else if Arc::ptr_eq(&t.to, &self.process) {                t.debug_print_inner(m, "    incoming transaction ");                t_opt = t.find_from(self);            } else {                t.debug_print_inner(m, "    bad transaction ");                t_opt = None;            }        }         for work in &inner.work_list {            work.debug_print(m, "    ", "    pending transaction ")?;        }        Ok(())    }     pub(crate) fn clear_extended_error(&self, debug_id: usize) {        self.inner.lock().extended_error = ExtendedError::new(debug_id as u32, BR_OK, 0);    }

Lines 1–500 of 1688.