| // SPDX-License-Identifier: GPL-2.0 | |
| // Copyright (C) 2025 Google LLC. | |
| //! Binder -- the Android IPC mechanism. | |
| #![crate_name = "rust_binder"] | |
| #![recursion_limit = "256"] | |
| use kernel::{ | |
| bindings::{self, seq_file}, | |
| fs::File, | |
| list::{ListArc, ListArcSafe, ListLinksSelfPtr, TryNewListArc}, | |
| module::this_module, | |
| prelude::*, | |
| seq_file::SeqFile, | |
| seq_print, | |
| sync::atomic::{ordering::Relaxed, Atomic}, | |
| sync::poll::PollTable, | |
| sync::Arc, | |
| task::Pid, | |
| transmute::AsBytes, | |
| types::ForeignOwnable, | |
| uaccess::UserSliceWriter, | |
| }; | |
| use crate::{context::Context, page_range::Shrinker, process::Process, thread::Thread}; | |
| use core::ptr::NonNull; | |
| mod allocation; | |
| mod context; | |
| mod deferred_close; | |
| mod defs; | |
| #[macro_use] | |
| mod debug; | |
| mod error; | |
| mod netlink; | |
| mod node; | |
| mod page_range; | |
| mod process; | |
| mod range_alloc; | |
| mod stats; | |
| mod thread; | |
| mod trace; | |
| mod transaction; | |
| #[allow(warnings)] // generated bindgen code | |
| mod binderfs { | |
| use kernel::bindings::{dentry, inode}; | |
| extern "C" { | |
| pub fn init_rust_binderfs() -> kernel::ffi::c_int; | |
| } | |
| extern "C" { | |
| pub fn rust_binderfs_create_proc_file( | |
| nodp: *mut inode, | |
| pid: kernel::ffi::c_int, | |
| ) -> *mut dentry; | |
| } | |
| extern "C" { | |
| pub fn rust_binderfs_remove_file(dentry: *mut dentry); | |
| } | |
| pub type rust_binder_context = *mut kernel::ffi::c_void; | |
| #[repr(C)] | |
| #[derive(Copy, Clone)] | |
| pub struct binder_device { | |
| pub minor: kernel::ffi::c_int, | |
| pub ctx: rust_binder_context, | |
| } | |
| impl Default for binder_device { | |
| fn default() -> Self { | |
| let mut s = ::core::mem::MaybeUninit::<Self>::uninit(); | |
| unsafe { | |
| ::core::ptr::write_bytes(s.as_mut_ptr(), 0, 1); | |
| s.assume_init() | |
| } | |
| } | |
| } | |
| } | |
| module! { | |
| type: BinderModule, | |
| name: "rust_binder", | |
| authors: ["Wedson Almeida Filho", "Alice Ryhl"], | |
| description: "Android Binder", | |
| license: "GPL", | |
| } | |
| use kernel::bindings::rust_binder_layout; | |
| #[no_mangle] | |
| static RUST_BINDER_LAYOUT: rust_binder_layout = rust_binder_layout { | |
| t: transaction::TRANSACTION_LAYOUT, | |
| p: process::PROCESS_LAYOUT, | |
| n: node::NODE_LAYOUT, | |
| }; | |
| fn next_debug_id() -> usize { | |
| static NEXT_DEBUG_ID: Atomic<usize> = Atomic::new(0); | |
| NEXT_DEBUG_ID.fetch_add(1, Relaxed) | |
| } | |
| /// Provides a single place to write Binder return values via the | |
| /// supplied `UserSliceWriter`. | |
| pub(crate) struct BinderReturnWriter<'a> { | |
| writer: UserSliceWriter, | |
| thread: &'a Thread, | |
| } | |
| impl<'a> BinderReturnWriter<'a> { | |
| fn new(writer: UserSliceWriter, thread: &'a Thread) -> Self { | |
| BinderReturnWriter { writer, thread } | |
| } | |
| /// Write a return code back to user space. | |
| /// Should be a `BR_` constant from [`defs`] e.g. [`defs::BR_TRANSACTION_COMPLETE`]. | |
| fn write_code(&mut self, code: u32) -> Result { | |
| crate::trace::trace_return(code); | |
| stats::GLOBAL_STATS.inc_br(code); | |
| self.thread.process.stats.inc_br(code); | |
| self.writer.write(&code) | |
| } | |
| /// Write something *other than* a return code to user space. | |
| fn write_payload<T: AsBytes>(&mut self, payload: &T) -> Result { | |
| self.writer.write(payload) | |
| } | |
| fn len(&self) -> usize { | |
| self.writer.len() | |
| } | |
| } | |
| /// Specifies how a type should be delivered to the read part of a BINDER_WRITE_READ ioctl. | |
| /// | |
| /// When a value is pushed to the todo list for a process or thread, it is stored as a trait object | |
| /// with the type `Arc<dyn DeliverToRead>`. Trait objects are a Rust feature that lets you | |
| /// implement dynamic dispatch over many different types. This lets us store many different types | |
| /// in the todo list. | |
| trait DeliverToRead: ListArcSafe + Send + Sync { | |
| /// Performs work. Returns true if remaining work items in the queue should be processed | |
| /// immediately, or false if it should return to caller before processing additional work | |
| /// items. | |
| fn do_work( | |
| self: DArc<Self>, | |
| thread: &Thread, | |
| writer: &mut BinderReturnWriter<'_>, | |
| ) -> Result<bool>; | |
| /// Cancels the given work item. This is called instead of [`DeliverToRead::do_work`] when work | |
| /// won't be delivered. | |
| fn cancel(self: DArc<Self>); | |
| /// Should we use `wake_up_interruptible_sync` or `wake_up_interruptible` when scheduling this | |
| /// work item? | |
| /// | |
| /// Generally only set to true for non-oneway transactions. | |
| fn should_sync_wakeup(&self) -> bool; | |
| fn debug_print(&self, m: &SeqFile, prefix: &str, transaction_prefix: &str) -> Result<()>; | |
| } | |
| // Wrapper around a `DeliverToRead` with linked list links. | |
| #[pin_data] | |
| struct DTRWrap<T: ?Sized> { | |
| #[pin] | |
| links: ListLinksSelfPtr<DTRWrap<dyn DeliverToRead>>, | |
| #[pin] | |
| wrapped: T, | |
| } | |
| kernel::list::impl_list_arc_safe! { | |
| impl{T: ListArcSafe + ?Sized} ListArcSafe<0> for DTRWrap<T> { | |
| tracked_by wrapped: T; | |
| } | |
| } | |
| kernel::list::impl_list_item! { | |
| impl ListItem<0> for DTRWrap<dyn DeliverToRead> { | |
| using ListLinksSelfPtr { self.links }; | |
| } | |
| } | |
| impl<T: ?Sized> core::ops::Deref for DTRWrap<T> { | |
| type Target = T; | |
| fn deref(&self) -> &T { | |
| &self.wrapped | |
| } | |
| } | |
| type DArc<T> = kernel::sync::Arc<DTRWrap<T>>; | |
| type DLArc<T> = kernel::list::ListArc<DTRWrap<T>>; | |
| impl<T: ListArcSafe> DTRWrap<T> { | |
| fn new(val: impl PinInit<T>) -> impl PinInit<Self> { | |
| pin_init!(Self { | |
| links <- ListLinksSelfPtr::new(), | |
| wrapped <- val, | |
| }) | |
| } | |
| fn arc_try_new(val: T) -> Result<DLArc<T>, kernel::alloc::AllocError> { | |
| ListArc::pin_init( | |
| try_pin_init!(Self { | |
| links <- ListLinksSelfPtr::new(), | |
| wrapped: val, | |
| }), | |
| GFP_KERNEL, | |
| ) | |
| .map_err(|_| kernel::alloc::AllocError) | |
| } | |
| fn arc_pin_init(init: impl PinInit<T>) -> Result<DLArc<T>, kernel::error::Error> { | |
| ListArc::pin_init( | |
| try_pin_init!(Self { | |
| links <- ListLinksSelfPtr::new(), | |
| wrapped <- init, | |
| }), | |
| GFP_KERNEL, | |
| ) | |
| } | |
| } | |
| struct DeliverCode { | |
| code: u32, | |
| skip: Atomic<bool>, | |
| pid: i32, | |
| } | |
| kernel::list::impl_list_arc_safe! { | |
| impl ListArcSafe<0> for DeliverCode { untracked; } | |
| } | |
| impl DeliverCode { | |
| fn new(code: u32, pid: i32) -> Self { | |
| Self { | |
| code, | |
| skip: Atomic::new(false), | |
| pid, | |
| } | |
| } | |
| /// Disable this DeliverCode and make it do nothing. | |
| /// | |
| /// This is used instead of removing it from the work list, since `LinkedList::remove` is | |
| /// unsafe, whereas this method is not. | |
| fn skip(&self) { | |
| self.skip.store(true, Relaxed); | |
| } | |
| } | |
| impl DeliverToRead for DeliverCode { | |
| fn do_work( | |
| self: DArc<Self>, | |
| _thread: &Thread, | |
| writer: &mut BinderReturnWriter<'_>, | |
| ) -> Result<bool> { | |
| if !self.skip.load(Relaxed) { | |
| writer.write_code(self.code)?; | |
| } | |
| Ok(true) | |
| } | |
| 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 | |
| } | |
| fn debug_print(&self, m: &SeqFile, prefix: &str, _tprefix: &str) -> Result<()> { | |
| seq_print!(m, "{}", prefix); | |
| if self.skip.load(Relaxed) { | |
| seq_print!(m, "(skipped) "); | |
| } | |
| if self.code == defs::BR_TRANSACTION_COMPLETE { | |
| seq_print!(m, "transaction complete\n"); | |
| } else { | |
| seq_print!(m, "transaction error: {}\n", self.code); | |
| } | |
| Ok(()) | |
| } | |
| } | |
| fn ptr_align(value: usize) -> Option<usize> { | |
| let size = core::mem::size_of::<usize>() - 1; | |
| Some(value.checked_add(size)? & !size) | |
| } | |
| // SAFETY: We call register in `init`. | |
| static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() }; | |
| struct BinderModule { | |
| _netlink: kernel::net::netlink::Registration, | |
| } | |
| impl kernel::Module for BinderModule { | |
| fn init(_module: &'static kernel::ThisModule) -> Result<Self> { | |
| // SAFETY: The module initializer never runs twice, so we only call this once. | |
| unsafe { crate::context::CONTEXTS.init() }; | |
| let netlink = crate::netlink::BINDER_NL_FAMILY.register()?; | |
| BINDER_SHRINKER.register(c"android-binder")?; | |
| // SAFETY: The module is being loaded, so we can initialize binderfs. | |
| unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? }; | |
| Ok(Self { _netlink: netlink }) | |
| } | |
| } | |
| /// Makes the inner type Sync. | |
| #[repr(transparent)] | |
| pub struct AssertSync<T>(T); | |
| // SAFETY: Used only to insert C bindings types into globals, which is safe. | |
| unsafe impl<T> Sync for AssertSync<T> {} | |
| /// File operations that rust_binderfs.c can use. | |
| #[no_mangle] | |
| #[used] | |
| pub static rust_binder_fops: AssertSync<kernel::bindings::file_operations> = { | |
| let ops = kernel::bindings::file_operations { | |
| owner: this_module::<LocalModule>().as_ptr(), | |
| poll: Some(rust_binder_poll), | |
| unlocked_ioctl: Some(rust_binder_ioctl), | |
| compat_ioctl: bindings::compat_ptr_ioctl, | |
| mmap: Some(rust_binder_mmap), | |
| open: Some(rust_binder_open), | |
| release: Some(rust_binder_release), | |
| flush: Some(rust_binder_flush), | |
| ..pin_init::zeroed() | |
| }; | |
| AssertSync(ops) | |
| }; | |
| /// # Safety | |
| /// Only called by binderfs. | |
| #[no_mangle] | |
| unsafe extern "C" fn rust_binder_new_context( | |
| name: *const kernel::ffi::c_char, | |
| ) -> *mut kernel::ffi::c_void { | |
| // SAFETY: The caller will always provide a valid c string here. | |
| let name = unsafe { kernel::str::CStr::from_char_ptr(name) }; | |
| match Context::new(name) { | |
| Ok(ctx) => Arc::into_foreign(ctx), | |
| Err(_err) => core::ptr::null_mut(), | |
| } | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| #[no_mangle] | |
| unsafe extern "C" fn rust_binder_remove_context(device: *mut kernel::ffi::c_void) { | |
| if !device.is_null() { | |
| // SAFETY: The caller ensures that the `device` pointer came from a previous call to | |
| // `rust_binder_new_device`. | |
| let ctx = unsafe { Arc::<Context>::from_foreign(device) }; | |
| ctx.deregister(); | |
| drop(ctx); | |
| } | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| unsafe extern "C" fn rust_binder_open( | |
| inode: *mut bindings::inode, | |
| file_ptr: *mut bindings::file, | |
| ) -> kernel::ffi::c_int { | |
| // SAFETY: The `rust_binderfs.c` file ensures that `i_private` is set to a | |
| // `struct binder_device`. | |
| let device = unsafe { (*inode).i_private } as *const binderfs::binder_device; | |
| assert!(!device.is_null()); | |
| // SAFETY: The `rust_binderfs.c` file ensures that `device->ctx` holds a binder context when | |
| // using the rust binder fops. | |
| let ctx = unsafe { Arc::<Context>::borrow((*device).ctx) }; | |
| // SAFETY: The caller provides a valid file pointer to a new `struct file`. | |
| let file = unsafe { File::from_raw_file(file_ptr) }; | |
| let process = match Process::open(ctx, file) { | |
| Ok(process) => process, | |
| Err(err) => return err.to_errno(), | |
| }; | |
| // SAFETY: This is an `inode` for a newly created binder file. | |
| match unsafe { BinderfsProcFile::new(inode, process.task.pid()) } { | |
| Ok(Some(file)) => process.inner.lock().binderfs_file = Some(file), | |
| Ok(None) => { /* pid already exists */ } | |
| Err(err) => return err.to_errno(), | |
| } | |
| // SAFETY: This file is associated with Rust binder, so we own the `private_data` field. | |
| unsafe { (*file_ptr).private_data = process.into_foreign() }; | |
| 0 | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| unsafe extern "C" fn rust_binder_release( | |
| _inode: *mut bindings::inode, | |
| file: *mut bindings::file, | |
| ) -> kernel::ffi::c_int { | |
| // SAFETY: We previously set `private_data` in `rust_binder_open`. | |
| let process = unsafe { Arc::<Process>::from_foreign((*file).private_data) }; | |
| // SAFETY: The caller ensures that the file is valid. | |
| let file = unsafe { File::from_raw_file(file) }; | |
| Process::release(process, file); | |
| 0 | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| unsafe extern "C" fn rust_binder_ioctl( | |
| file: *mut bindings::file, | |
| cmd: kernel::ffi::c_uint, | |
| arg: kernel::ffi::c_ulong, | |
| ) -> kernel::ffi::c_long { | |
| // SAFETY: We previously set `private_data` in `rust_binder_open`. | |
| let f = unsafe { Arc::<Process>::borrow((*file).private_data) }; | |
| // SAFETY: The caller ensures that the file is valid. | |
| match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd, arg) { | |
| Ok(()) => 0, | |
| Err(err) => err.to_errno() as isize, | |
| } | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| unsafe extern "C" fn rust_binder_mmap( | |
| file: *mut bindings::file, | |
| vma: *mut bindings::vm_area_struct, | |
| ) -> kernel::ffi::c_int { | |
| // SAFETY: We previously set `private_data` in `rust_binder_open`. | |
| let f = unsafe { Arc::<Process>::borrow((*file).private_data) }; | |
| // SAFETY: The caller ensures that the vma is valid. | |
| let area = unsafe { kernel::mm::virt::VmaNew::from_raw(vma) }; | |
| // SAFETY: The caller ensures that the file is valid. | |
| match Process::mmap(f, unsafe { File::from_raw_file(file) }, area) { | |
| Ok(()) => 0, | |
| Err(err) => err.to_errno(), | |
| } | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| unsafe extern "C" fn rust_binder_poll( | |
| file: *mut bindings::file, | |
| wait: *mut bindings::poll_table_struct, | |
| ) -> bindings::__poll_t { | |
| // SAFETY: We previously set `private_data` in `rust_binder_open`. | |
| let f = unsafe { Arc::<Process>::borrow((*file).private_data) }; | |
| // SAFETY: The caller ensures that the file is valid. | |
| let fileref = unsafe { File::from_raw_file(file) }; | |
| // SAFETY: The caller ensures that the `PollTable` is valid. | |
| match Process::poll(f, fileref, unsafe { PollTable::from_raw(wait) }) { | |
| Ok(v) => v, | |
| Err(_) => bindings::POLLERR, | |
| } | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| unsafe extern "C" fn rust_binder_flush( | |
| file: *mut bindings::file, | |
| _id: bindings::fl_owner_t, | |
| ) -> kernel::ffi::c_int { | |
| // SAFETY: We previously set `private_data` in `rust_binder_open`. | |
| let f = unsafe { Arc::<Process>::borrow((*file).private_data) }; | |
| match Process::flush(f) { | |
| Ok(()) => 0, | |
| Err(err) => err.to_errno(), | |
| } | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| #[no_mangle] | |
| unsafe extern "C" fn rust_binder_stats_show( | |
| ptr: *mut seq_file, | |
| _: *mut kernel::ffi::c_void, | |
| ) -> kernel::ffi::c_int { | |
| // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which | |
| // this method is called. | |
| let m = unsafe { SeqFile::from_raw(ptr) }; | |
| if let Err(err) = rust_binder_stats_show_impl(m) { | |
| seq_print!(m, "failed to generate state: {:?}\n", err); | |
| } | |
| 0 | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| #[no_mangle] | |
| unsafe extern "C" fn rust_binder_state_show( | |
| ptr: *mut seq_file, | |
| _: *mut kernel::ffi::c_void, | |
| ) -> kernel::ffi::c_int { | |
| // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which | |
| // this method is called. | |
| let m = unsafe { SeqFile::from_raw(ptr) }; | |
| if let Err(err) = rust_binder_state_show_impl(m) { | |
| seq_print!(m, "failed to generate state: {:?}\n", err); | |
| } | |
| 0 | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| #[no_mangle] | |
| unsafe extern "C" fn rust_binder_proc_show( | |
| ptr: *mut seq_file, | |
| _: *mut kernel::ffi::c_void, | |
| ) -> kernel::ffi::c_int { | |
| // SAFETY: Accessing the private field of `seq_file` is okay. | |
| let pid = unsafe { (*ptr).private }.addr() as Pid; | |
| // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which | |
| // this method is called. | |
| let m = unsafe { SeqFile::from_raw(ptr) }; | |
| if let Err(err) = rust_binder_proc_show_impl(m, pid) { | |
| seq_print!(m, "failed to generate state: {:?}\n", err); | |
| } | |
| 0 | |
| } | |
| /// # Safety | |
| /// Only called by binderfs. | |
| #[no_mangle] | |
| unsafe extern "C" fn rust_binder_transactions_show( | |
| ptr: *mut seq_file, | |
| _: *mut kernel::ffi::c_void, | |
| ) -> kernel::ffi::c_int { | |
| // SAFETY: The caller ensures that the pointer is valid and exclusive for the duration in which | |
| // this method is called. | |
| let m = unsafe { SeqFile::from_raw(ptr) }; | |
| if let Err(err) = rust_binder_transactions_show_impl(m) { | |
| seq_print!(m, "failed to generate state: {:?}\n", err); | |
| } | |
| 0 | |
| } | |
| fn rust_binder_transactions_show_impl(m: &SeqFile) -> Result<()> { | |
| seq_print!(m, "binder transactions:\n"); | |
| let contexts = context::get_all_contexts()?; | |
| for ctx in contexts { | |
| let procs = ctx.get_all_procs()?; | |
| for proc in procs { | |
| proc.debug_print(m, &ctx, false)?; | |
| seq_print!(m, "\n"); | |
| } | |
| } | |
| Ok(()) | |
| } | |
| fn rust_binder_stats_show_impl(m: &SeqFile) -> Result<()> { | |
| seq_print!(m, "binder stats:\n"); | |
| stats::GLOBAL_STATS.debug_print("", m); | |
| let contexts = context::get_all_contexts()?; | |
| for ctx in contexts { | |
| let procs = ctx.get_all_procs()?; | |
| for proc in procs { | |
| proc.debug_print_stats(m, &ctx)?; | |
| seq_print!(m, "\n"); | |
| } | |
| } | |
| Ok(()) | |
| } | |
| fn rust_binder_state_show_impl(m: &SeqFile) -> Result<()> { | |
| seq_print!(m, "binder state:\n"); | |
| let contexts = context::get_all_contexts()?; | |
| for ctx in contexts { | |
| let procs = ctx.get_all_procs()?; | |
| for proc in procs { | |
| proc.debug_print(m, &ctx, true)?; | |
| seq_print!(m, "\n"); | |
| } | |
| } | |
| Ok(()) | |
| } | |
| fn rust_binder_proc_show_impl(m: &SeqFile, pid: Pid) -> Result<()> { | |
| seq_print!(m, "binder proc state:\n"); | |
| let contexts = context::get_all_contexts()?; | |
| for ctx in contexts { | |
| let procs = ctx.get_procs_with_pid(pid)?; | |
| for proc in procs { | |
| proc.debug_print(m, &ctx, true)?; | |
| seq_print!(m, "\n"); | |
| } | |
| } | |
| Ok(()) | |
| } | |
| struct BinderfsProcFile(NonNull<bindings::dentry>); | |
| // SAFETY: Safe to drop any thread. | |
| unsafe impl Send for BinderfsProcFile {} | |
| impl BinderfsProcFile { | |
| /// # Safety | |
| /// | |
| /// Takes an inode from a newly created binder file. | |
| unsafe fn new(nodp: *mut bindings::inode, pid: i32) -> Result<Option<Self>> { | |
| // SAFETY: The caller passes an `inode` for a newly created binder file. | |
| let dentry = unsafe { binderfs::rust_binderfs_create_proc_file(nodp, pid) }; | |
| match kernel::error::from_err_ptr(dentry) { | |
| Ok(dentry) => Ok(NonNull::new(dentry).map(Self)), | |
| Err(err) if err == EEXIST => Ok(None), | |
| Err(err) => Err(err), | |
| } | |
| } | |
| } | |
| impl Drop for BinderfsProcFile { | |
| fn drop(&mut self) { | |
| // SAFETY: This is a dentry from `rust_binderfs_remove_file` that has not been deleted yet. | |
| unsafe { binderfs::rust_binderfs_remove_file(self.0.as_ptr()) }; | |
| } | |
| } |