// SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2025 Google LLC. //! Binder -- the Android IPC mechanism. #![recursion_limit = "256"] #![allow( clippy::as_underscore, clippy::ref_as_ptr, clippy::ptr_as_ptr, clippy::cast_lossless )] use kernel::{ bindings::{self, seq_file}, fs::File, list::{ListArc, ListArcSafe, ListLinksSelfPtr, TryNewListArc}, prelude::*, seq_file::SeqFile, seq_print, 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, sync::atomic::{AtomicBool, AtomicUsize, Ordering}, }; mod allocation; mod context; mod deferred_close; mod defs; mod error; 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::::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", } fn next_debug_id() -> usize { static NEXT_DEBUG_ID: AtomicUsize = AtomicUsize::new(0); NEXT_DEBUG_ID.fetch_add(1, Ordering::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 { 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(&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`. 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, thread: &Thread, writer: &mut BinderReturnWriter<'_>, ) -> Result; /// Cancels the given work item. This is called instead of [`DeliverToRead::do_work`] when work /// won't be delivered. fn cancel(self: DArc); /// 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 { #[pin] links: ListLinksSelfPtr>, #[pin] wrapped: T, } kernel::list::impl_list_arc_safe! { impl{T: ListArcSafe + ?Sized} ListArcSafe<0> for DTRWrap { tracked_by wrapped: T; } } kernel::list::impl_list_item! { impl ListItem<0> for DTRWrap { using ListLinksSelfPtr { self.links }; } } impl core::ops::Deref for DTRWrap { type Target = T; fn deref(&self) -> &T { &self.wrapped } } type DArc = kernel::sync::Arc>; type DLArc = kernel::list::ListArc>; impl DTRWrap { fn new(val: impl PinInit) -> impl PinInit { pin_init!(Self { links <- ListLinksSelfPtr::new(), wrapped <- val, }) } fn arc_try_new(val: T) -> Result, 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) -> Result, kernel::error::Error> { ListArc::pin_init( try_pin_init!(Self { links <- ListLinksSelfPtr::new(), wrapped <- init, }), GFP_KERNEL, ) } } struct DeliverCode { code: u32, skip: AtomicBool, } kernel::list::impl_list_arc_safe! { impl ListArcSafe<0> for DeliverCode { untracked; } } impl DeliverCode { fn new(code: u32) -> Self { Self { code, skip: AtomicBool::new(false), } } /// 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, Ordering::Relaxed); } } impl DeliverToRead for DeliverCode { fn do_work( self: DArc, _thread: &Thread, writer: &mut BinderReturnWriter<'_>, ) -> Result { if !self.skip.load(Ordering::Relaxed) { writer.write_code(self.code)?; } Ok(true) } fn cancel(self: DArc) {} 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(Ordering::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 { let size = core::mem::size_of::() - 1; Some(value.checked_add(size)? & !size) } // SAFETY: We call register in `init`. static BINDER_SHRINKER: Shrinker = unsafe { Shrinker::new() }; struct BinderModule {} impl kernel::Module for BinderModule { fn init(_module: &'static kernel::ThisModule) -> Result { // SAFETY: The module initializer never runs twice, so we only call this once. unsafe { crate::context::CONTEXTS.init() }; pr_warn!("Loaded Rust Binder."); BINDER_SHRINKER.register(kernel::c_str!("android-binder"))?; // SAFETY: The module is being loaded, so we can initialize binderfs. unsafe { kernel::error::to_result(binderfs::init_rust_binderfs())? }; Ok(Self {}) } } /// Makes the inner type Sync. #[repr(transparent)] pub struct AssertSync(T); // SAFETY: Used only to insert `file_operations` into a global, which is safe. unsafe impl Sync for AssertSync {} /// File operations that rust_binderfs.c can use. #[no_mangle] #[used] pub static rust_binder_fops: AssertSync = { // SAFETY: All zeroes is safe for the `file_operations` type. let zeroed_ops = unsafe { core::mem::MaybeUninit::zeroed().assume_init() }; let ops = kernel::bindings::file_operations { owner: THIS_MODULE.as_ptr(), poll: Some(rust_binder_poll), unlocked_ioctl: Some(rust_binder_unlocked_ioctl), compat_ioctl: Some(rust_binder_compat_ioctl), mmap: Some(rust_binder_mmap), open: Some(rust_binder_open), release: Some(rust_binder_release), flush: Some(rust_binder_flush), ..zeroed_ops }; 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::::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::::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::::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_compat_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::::borrow((*file).private_data) }; // SAFETY: The caller ensures that the file is valid. match Process::compat_ioctl(f, unsafe { File::from_raw_file(file) }, cmd as _, arg as _) { Ok(()) => 0, Err(err) => err.to_errno() as isize, } } /// # Safety /// Only called by binderfs. unsafe extern "C" fn rust_binder_unlocked_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::::borrow((*file).private_data) }; // SAFETY: The caller ensures that the file is valid. match Process::ioctl(f, unsafe { File::from_raw_file(file) }, cmd as _, arg as _) { 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::::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::::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::::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 }) as usize 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); // 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> { // 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()) }; } }