use std::default::Default;
use std::vec::Vec;
use pretty_hex::*;
use rustix::fd::IntoRawFd;
use crate::{
binder,
binder_object::{read_flat_binder, write_flat_binder},
error::{Result, StatusCode},
parcelable::*,
sys::binder::{binder_size_t, flat_binder_object},
sys::{binder_uintptr_t, BINDER_TYPE_FD},
thread_state,
};
const STRICT_MODE_PENALTY_GATHER: i32 = 1 << 31;
#[inline]
pub(crate) fn pad_size(len: usize) -> usize {
(len + 3) & (!3)
}
#[inline]
pub(crate) fn checked_array_layout(len: i32, elem_size: usize) -> Result<(usize, usize)> {
debug_assert!(
len >= 1,
"checked_array_layout: caller must validate len >= 1"
);
let size = (len as usize)
.checked_mul(elem_size)
.ok_or(StatusCode::BadValue)?;
let padded = size.checked_add(3).ok_or(StatusCode::BadValue)? & !3;
Ok((size, padded))
}
pub(crate) trait CharType: Clone {
type Output;
fn as_i32(&self) -> i32;
fn from(v: &i32) -> Self::Output;
}
impl CharType for i16 {
type Output = i16;
fn as_i32(&self) -> i32 {
*self as _
}
fn from(v: &i32) -> Self::Output {
*v as _
}
}
impl CharType for u16 {
type Output = u16;
fn as_i32(&self) -> i32 {
*self as _
}
fn from(v: &i32) -> Self::Output {
*v as _
}
}
pub(crate) enum ParcelData<T: Clone + Default + 'static> {
Vec(Vec<T>),
Slice(&'static mut [T]),
}
impl<T: Clone + Default> ParcelData<T> {
fn new() -> Self {
ParcelData::Vec(Vec::new())
}
fn with_capacity(capacity: usize) -> Self {
ParcelData::Vec(Vec::with_capacity(capacity))
}
fn from_vec(data: Vec<T>) -> Self {
ParcelData::Vec(data)
}
unsafe fn from_raw_parts_mut(data: *mut T, len: usize) -> Self {
ParcelData::Slice(if data.is_null() {
debug_assert_eq!(len, 0, "non-zero length with null data is invalid");
&mut []
} else {
unsafe { std::slice::from_raw_parts_mut(data, len) }
})
}
fn as_slice(&self) -> &[T] {
match self {
ParcelData::Vec(v) => v.as_slice(),
ParcelData::Slice(s) => s,
}
}
fn as_mut_slice(&mut self) -> &mut [T] {
match self {
ParcelData::Vec(v) => v.as_mut_slice(),
ParcelData::Slice(s) => s,
}
}
pub(crate) fn as_ptr(&self) -> *const T {
match self {
ParcelData::Vec(ref v) => v.as_ptr(),
ParcelData::Slice(s) => s.as_ptr(),
}
}
fn as_mut_ptr(&mut self) -> *mut T {
match self {
ParcelData::Vec(ref mut v) => v.as_mut_ptr(),
ParcelData::Slice(s) => s.as_mut_ptr(),
}
}
pub(crate) fn len(&self) -> usize {
self.as_slice().len()
}
fn set_len(&mut self, len: usize) {
match self {
ParcelData::Vec(v) => unsafe { v.set_len(len) },
_ => panic!("&[u8] can't support set_len()."),
}
}
fn capacity(&self) -> usize {
match self {
ParcelData::Vec(v) => v.capacity(),
ParcelData::Slice(s) => s.len(),
}
}
fn reserve(&mut self, additional: usize) {
match self {
ParcelData::Vec(v) => v.reserve(additional),
_ => panic!("&[u8] can't support reserve()."),
}
}
fn push(&mut self, other: T) {
match self {
ParcelData::Vec(v) => v.push(other),
_ => panic!("extend_from_slice() is only available for ParcelData::Vec."),
}
}
}
pub type FnFreeBuffer =
fn(Option<&Parcel>, binder_uintptr_t, usize, binder_uintptr_t, usize) -> Result<()>;
#[cfg(feature = "rpc")]
pub trait RpcParcelOps: Send + Sync {
fn write_binder(
&self,
binder: Option<&crate::binder::SIBinder>,
parcel: &mut Parcel,
) -> Result<()>;
fn read_binder(&self, parcel: &mut Parcel) -> Result<Option<crate::binder::SIBinder>>;
}
#[cfg(feature = "rpc")]
#[derive(Default)]
struct RpcFields {
ops: Option<std::sync::Arc<dyn RpcParcelOps>>,
fd_mode: crate::rpc::FileDescriptorTransportMode,
fds_out: Vec<std::os::fd::OwnedFd>,
fds_in: Vec<Option<std::os::fd::OwnedFd>>,
object_positions: Vec<u32>,
record_fd_positions: bool,
leaving_addrs: Vec<crate::rpc::RpcAddress>,
}
#[cfg(feature = "rpc")]
impl RpcFields {
fn record_object_position(&mut self, pos: usize) {
let pos = pos as u32;
let at = self.object_positions.partition_point(|&p| p <= pos);
self.object_positions.insert(at, pos);
}
fn object_position_present(&self, pos: usize) -> bool {
let Ok(pos) = u32::try_from(pos) else {
return false;
};
self.object_positions.binary_search(&pos).is_ok()
}
fn push_out_fd(&mut self, fd: std::os::fd::OwnedFd) -> i32 {
let idx = self.fds_out.len() as i32;
self.fds_out.push(fd);
idx
}
fn set_in_fds(&mut self, fds: Vec<std::os::fd::OwnedFd>) {
self.fds_in = fds.into_iter().map(Some).collect();
}
fn take_in_fd(&mut self, index: usize) -> Option<std::os::fd::OwnedFd> {
self.fds_in.get_mut(index).and_then(Option::take)
}
}
pub struct Parcel {
data: ParcelData<u8>,
pub(crate) objects: ParcelData<binder_size_t>,
pos: usize,
next_object_hint: usize,
read_boundary: Option<usize>,
request_header_present: bool,
work_source_request_header_pos: usize,
free_buffer: Option<FnFreeBuffer>,
#[cfg(feature = "rpc")]
rpc: Option<RpcFields>,
}
impl Default for Parcel {
fn default() -> Self {
Parcel::with_capacity(256)
}
}
impl Parcel {
pub fn new() -> Self {
Parcel::with_capacity(256)
}
pub fn with_capacity(capacity: usize) -> Self {
Parcel {
data: ParcelData::with_capacity(capacity),
objects: ParcelData::new(),
pos: 0,
next_object_hint: 0,
read_boundary: None,
request_header_present: false,
work_source_request_header_pos: 0,
free_buffer: None,
#[cfg(feature = "rpc")]
rpc: None,
}
}
pub unsafe fn from_ipc_parts(
data: *mut u8,
length: usize,
objects: *mut binder_size_t,
object_count: usize,
free_buffer: fn(
Option<&Parcel>,
binder_uintptr_t,
usize,
binder_uintptr_t,
usize,
) -> Result<()>,
) -> Self {
Parcel {
data: ParcelData::from_raw_parts_mut(data, length),
objects: ParcelData::from_raw_parts_mut(objects, object_count),
pos: 0,
next_object_hint: 0,
read_boundary: None,
request_header_present: false,
work_source_request_header_pos: 0,
free_buffer: Some(free_buffer),
#[cfg(feature = "rpc")]
rpc: None,
}
}
pub fn from_vec(data: Vec<u8>) -> Self {
Parcel {
data: ParcelData::from_vec(data),
objects: ParcelData::new(),
pos: 0,
next_object_hint: 0,
read_boundary: None,
request_header_present: false,
work_source_request_header_pos: 0,
free_buffer: None,
#[cfg(feature = "rpc")]
rpc: None,
}
}
pub fn as_mut_ptr(&mut self) -> *mut u8 {
self.data.as_mut_ptr()
}
pub fn as_ptr(&self) -> *const u8 {
self.data.as_ptr()
}
pub fn capacity(&self) -> usize {
self.data.capacity()
}
pub fn is_empty(&self) -> bool {
self.pos >= self.data.len()
}
#[cfg(feature = "rpc")]
pub fn set_for_rpc(&mut self, yes: bool) {
if yes {
self.rpc.get_or_insert_with(RpcFields::default);
} else {
self.rpc = None;
}
}
#[cfg(feature = "rpc")]
pub fn is_for_rpc(&self) -> bool {
self.rpc.is_some()
}
#[cfg(not(feature = "rpc"))]
pub fn is_for_rpc(&self) -> bool {
false
}
#[cfg(feature = "rpc")]
pub fn attach_rpc_ops(&mut self, ops: std::sync::Arc<dyn RpcParcelOps>) {
self.rpc.get_or_insert_with(RpcFields::default).ops = Some(ops);
}
#[cfg(feature = "rpc")]
pub(crate) fn configure_rpc(
&mut self,
ops: std::sync::Arc<dyn RpcParcelOps>,
fd_mode: crate::rpc::FileDescriptorTransportMode,
record_fd_positions: bool,
) {
let rpc = self.rpc.get_or_insert_with(RpcFields::default);
rpc.ops = Some(ops);
rpc.fd_mode = fd_mode;
rpc.record_fd_positions = record_fd_positions;
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_ops(&self) -> Option<std::sync::Arc<dyn RpcParcelOps>> {
self.rpc.as_ref().and_then(|r| r.ops.clone())
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_data_bytes(&self) -> &[u8] {
self.data.as_slice()
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_object_positions(&self) -> &[u32] {
self.rpc
.as_ref()
.map_or(&[], |r| r.object_positions.as_slice())
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_set_object_positions(&mut self, positions: Vec<u32>) {
if let Some(rpc) = self.rpc.as_mut() {
rpc.object_positions = positions;
}
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_record_leaving_addr(&mut self, addr: crate::rpc::RpcAddress) {
if let Some(rpc) = self.rpc.as_mut() {
rpc.leaving_addrs.push(addr);
}
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_leaving_addrs(&self) -> &[crate::rpc::RpcAddress] {
self.rpc
.as_ref()
.map_or(&[], |r| r.leaving_addrs.as_slice())
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_object_position_present(&self, pos: usize) -> bool {
self.rpc
.as_ref()
.is_some_and(|r| r.object_position_present(pos))
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_record_object_position(&mut self, pos: usize) {
if let Some(rpc) = self.rpc.as_mut() {
rpc.record_object_position(pos);
}
}
#[cfg(feature = "rpc")]
pub(crate) fn set_rpc_fd_mode(&mut self, mode: crate::rpc::FileDescriptorTransportMode) {
if let Some(rpc) = self.rpc.as_mut() {
rpc.fd_mode = mode;
}
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_fd_mode(&self) -> crate::rpc::FileDescriptorTransportMode {
self.rpc.as_ref().map_or(Default::default(), |r| r.fd_mode)
}
#[cfg(feature = "rpc")]
pub(crate) fn set_rpc_record_fd_positions(&mut self, yes: bool) {
if let Some(rpc) = self.rpc.as_mut() {
rpc.record_fd_positions = yes;
}
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_record_fd_positions(&self) -> bool {
self.rpc.as_ref().is_some_and(|r| r.record_fd_positions)
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_push_out_fd(&mut self, fd: std::os::fd::OwnedFd) -> i32 {
self.rpc
.as_mut()
.expect("rpc_push_out_fd on kernel parcel")
.push_out_fd(fd)
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_out_fds(&self) -> &[std::os::fd::OwnedFd] {
self.rpc.as_ref().map_or(&[], |r| r.fds_out.as_slice())
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_set_in_fds(&mut self, fds: Vec<std::os::fd::OwnedFd>) {
if let Some(rpc) = self.rpc.as_mut() {
rpc.set_in_fds(fds);
}
}
#[cfg(feature = "rpc")]
pub(crate) fn rpc_take_in_fd(&mut self, index: usize) -> Option<std::os::fd::OwnedFd> {
self.rpc.as_mut().and_then(|r| r.take_in_fd(index))
}
pub fn set_data_size(&mut self, new_len: usize) -> Result<()> {
if new_len > self.data.capacity() {
log::error!(
"set_data_size({new_len}) exceeds capacity {}",
self.data.capacity()
);
return Err(StatusCode::BadValue);
}
self.data.set_len(new_len);
if new_len < self.pos {
self.pos = new_len;
}
Ok(())
}
pub fn close_file_descriptors(&self) {
#[cfg(feature = "rpc")]
if self.rpc.is_some() {
return;
}
for offset in self.objects.as_slice() {
let Ok(obj) = read_flat_binder(self.data.as_slice(), *offset as usize) else {
log::error!("Parcel: unable to read object at offset {offset}");
continue;
};
if obj.header_type() == BINDER_TYPE_FD {
obj.owned_fd();
}
}
}
pub fn set_data_position(&mut self, pos: usize) {
self.pos = pos;
}
pub fn data_position(&self) -> usize {
self.pos
}
pub fn data_size(&self) -> usize {
if self.data.len() > self.pos {
self.data.len()
} else {
self.pos
}
}
pub fn read<D: Deserialize>(&mut self) -> Result<D> {
D::deserialize(self)
}
pub fn read_onto<D: Deserialize>(&mut self, x: &mut D) -> Result<()> {
x.deserialize_from(self)
}
pub fn data_avail(&self) -> usize {
let result = self.data.len().saturating_sub(self.pos);
assert!(result < i32::MAX as _, "data too big: {result}");
result
}
pub(crate) fn read_aligned_data(&mut self, len: usize) -> Result<&[u8]> {
let aligned = pad_size(len);
let pos = self.pos;
if aligned <= self.data_avail() {
self.pos = pos + aligned;
Ok(&self.data.as_slice()[pos..pos + len])
} else {
log::error!(
"Not enough data to read aligned data.: {aligned} <= {}",
self.data_avail()
);
Err(StatusCode::NotEnoughData)
}
}
pub(crate) fn read_object(&mut self, null_meta: bool) -> Result<flat_binder_object> {
#[cfg(feature = "rpc")]
if self.rpc.is_some() {
return Err(StatusCode::BadType);
}
let data_pos = self.pos as u64;
let size = std::mem::size_of::<flat_binder_object>();
let obj = read_flat_binder(self.read_aligned_data(size)?, 0)?;
if !null_meta && obj.cookie == 0 && obj.pointer() == 0 {
return Ok(obj);
}
let objects = self.objects.as_slice();
let count = objects.len();
let mut opos = self.next_object_hint;
if count > 0 {
log::trace!("Parcel looking for obj at {data_pos}, hint={opos}");
if opos < count {
while opos < (count - 1) && objects[opos] < data_pos {
opos += 1;
}
} else {
opos = count - 1;
}
if objects[opos] == data_pos {
self.next_object_hint = opos + 1;
return Ok(obj);
}
while opos > 0 && objects[opos] > data_pos {
opos -= 1;
}
if objects[opos] == data_pos {
self.next_object_hint = opos + 1;
return Ok(obj);
}
}
log::error!("Parcel: unable to find object at index {data_pos}");
Err(StatusCode::BadType)
}
pub fn sized_read<F>(&mut self, f: F) -> Result<()>
where
for<'b> F: FnOnce(&mut Parcel) -> Result<()>,
{
let start = self.data_position();
let parcelable_size: i32 = self.read()?;
if parcelable_size < 4 {
log::error!("Parcel: bad size for object: {parcelable_size}");
return Err(StatusCode::BadValue);
}
let end = start.checked_add(parcelable_size as _).ok_or_else(|| {
log::error!("Parcel: check_add error: {parcelable_size}");
StatusCode::BadValue
})?;
if end > self.data_size() {
log::error!("Parcel: not enough data: {} > {}", end, self.data_size());
return Err(StatusCode::NotEnoughData);
}
let prev_boundary = self.read_boundary;
self.read_boundary = Some(end);
let result = f(self);
self.read_boundary = prev_boundary;
result?;
self.set_data_position(end);
Ok(())
}
pub fn has_more_data(&self) -> bool {
let end = self.read_boundary.unwrap_or_else(|| self.data_size());
self.pos < end
}
pub(crate) fn read_array<D: Deserialize>(&mut self) -> Result<Option<Vec<D>>> {
let len: i32 = self.read()?;
if len < -1 {
log::error!("Parcel: bad array length: {len}");
return Err(StatusCode::BadValue);
}
if len <= 0 {
return Ok(None);
}
let (size, padded) = checked_array_layout(len, std::mem::size_of::<D>())?;
if padded > self.data_avail() {
log::error!(
"Parcel: not enough data to read array: {} > {}",
padded,
self.data_avail()
);
return Err(StatusCode::NotEnoughData);
}
let pos = self.pos;
let data_slice = self
.data
.as_slice()
.get(pos..pos + size)
.ok_or(StatusCode::NotEnoughData)?;
let mut result = Vec::with_capacity(len as usize);
unsafe {
std::ptr::copy_nonoverlapping(
data_slice.as_ptr(),
result.as_mut_ptr() as *mut u8,
size,
);
result.set_len(len as usize);
}
self.set_data_position(pos + padded);
Ok(Some(result))
}
pub(crate) fn read_array_char<D: CharType>(
&mut self,
) -> Result<Option<Vec<<D as CharType>::Output>>> {
let len: i32 = self.read()?;
if len < -1 {
log::error!("Parcel: bad array length: {len}");
return Err(StatusCode::BadValue);
}
if len <= 0 {
return Ok(None);
}
let (size, padded) = checked_array_layout(len, std::mem::size_of::<i32>())?;
if padded > self.data_avail() {
log::error!(
"Parcel: not enough data to read array char: {} > {}",
padded,
self.data_avail()
);
return Err(StatusCode::NotEnoughData);
}
let pos = self.pos;
let result = self.data.as_slice()[pos..pos + size]
.chunks_exact(std::mem::size_of::<i32>())
.map(|c| D::from(&i32::from_ne_bytes([c[0], c[1], c[2], c[3]])))
.collect();
self.set_data_position(pos + padded);
Ok(Some(result))
}
pub fn resize_out_vec<D: Default + Deserialize>(&mut self, out_vec: &mut Vec<D>) -> Result<()> {
let len: i32 = self.read()?;
if len < 0 {
return Err(StatusCode::UnexpectedNull);
}
let len = len.try_into().or(Err(StatusCode::BadValue))?;
out_vec.resize_with(len, Default::default);
Ok(())
}
pub fn resize_nullable_out_vec<D: Default + Deserialize>(
&mut self,
out_vec: &mut Option<Vec<D>>,
) -> Result<()> {
let len: i32 = self.read()?;
if len < 0 {
*out_vec = None;
} else {
let len = len.try_into().or(Err(StatusCode::BadValue))?;
let mut vec = Vec::with_capacity(len);
vec.resize_with(len, Default::default);
*out_vec = Some(vec);
}
Ok(())
}
pub(crate) fn update_work_source_request_header_pos(&mut self) {
if !self.request_header_present {
self.work_source_request_header_pos = self.data.len();
self.request_header_present = true;
}
}
pub fn write<S: Serialize + ?Sized>(&mut self, parcelable: &S) -> Result<()> {
parcelable.serialize(self)
}
pub(crate) fn write_array<S: Serialize + Sized>(&mut self, parcelable: &[S]) -> Result<()> {
let len = parcelable.len();
self.write::<i32>(&(len as _))?;
if len == 0 {
return Ok(());
}
let size = std::mem::size_of_val(parcelable);
let padded = pad_size(size);
let pos = self.pos;
self.data.reserve(pos + padded);
unsafe {
let old_len = self.data.len();
if pos > old_len {
std::ptr::write_bytes(self.data.as_mut_ptr().add(old_len), 0, pos - old_len);
}
std::ptr::copy_nonoverlapping::<u8>(
parcelable.as_ptr() as _,
self.data.as_mut_ptr().add(pos),
size,
);
if padded > size {
std::ptr::write_bytes(self.data.as_mut_ptr().add(pos + size), 0, padded - size);
}
if self.data.len() < pos + padded {
self.data.set_len(pos + padded);
}
}
self.set_data_position(pos + padded);
Ok(())
}
pub(crate) fn write_array_char<S: CharType>(&mut self, parcelable: &[S]) -> Result<()> {
let len = parcelable.len();
self.write::<i32>(&(len as _))?;
let size = 4 * len;
let padded = pad_size(size);
self.data.reserve(self.pos + padded);
for c in parcelable {
self.write(&c.as_i32())?;
}
Ok(())
}
pub fn write_slice_size<T>(&mut self, slice: Option<&[T]>) -> Result<()> {
if let Some(slice) = slice {
let len: i32 = slice.len().try_into().or(Err(StatusCode::BadValue))?;
self.write(&len)
} else {
self.write(&-1i32)
}
}
pub(crate) fn write_aligned<T>(&mut self, val: &T) {
let unaligned = std::mem::size_of::<T>();
let val_bytes: &[u8] =
unsafe { std::slice::from_raw_parts(val as *const T as *const u8, unaligned) };
self.write_aligned_data(val_bytes);
}
pub(crate) fn write_aligned_data(&mut self, data: &[u8]) {
let unaligned = data.len();
let aligned = pad_size(unaligned);
let pos = self.pos;
self.data.reserve(pos + aligned);
unsafe {
let old_len = self.data.len();
if pos > old_len {
std::ptr::write_bytes(self.data.as_mut_ptr().add(old_len), 0, pos - old_len);
}
std::ptr::copy_nonoverlapping::<u8>(
data.as_ptr(),
self.data.as_mut_ptr().add(pos),
unaligned,
);
if aligned > unaligned {
std::ptr::write_bytes(
self.data.as_mut_ptr().add(pos + unaligned),
0,
aligned - unaligned,
);
}
if pos + aligned > self.data.len() {
self.data.set_len(pos + aligned);
}
}
self.set_data_position(pos + aligned);
}
pub(crate) fn write_object(&mut self, obj: &flat_binder_object, null_meta: bool) -> Result<()> {
#[cfg(feature = "rpc")]
if self.rpc.is_some() {
self.write_aligned(obj);
return Ok(());
}
let data_pos = self.pos;
self.write_aligned(obj);
if null_meta || obj.pointer() != 0 {
obj.acquire()?;
self.objects.push(data_pos as _);
}
Ok(())
}
pub(crate) fn write_interface_token(&mut self, interface: &str) -> Result<()> {
self.write(&(thread_state::get_strict_mode_policy() | STRICT_MODE_PENALTY_GATHER))?;
self.update_work_source_request_header_pos();
let work_source: i32 = if thread_state::should_propagate_work_source() {
thread_state::calling_work_source_uid() as _
} else {
thread_state::UNSET_WORK_SOURCE
};
self.write(&work_source)?;
if crate::sdk_at_least(30) {
self.write(&binder::INTERFACE_HEADER)?;
}
self.write(&interface)?;
Ok(())
}
pub fn sized_write<F>(&mut self, f: F) -> Result<()>
where
for<'b> F: FnOnce(&mut Parcel) -> Result<()>,
{
let start = self.data_position();
self.write(&0i32)?;
{
f(self)?;
}
let end = self.data_position();
self.set_data_position(start);
assert!(end >= start);
self.write::<i32>(&((end - start) as _))?;
self.set_data_position(end);
Ok(())
}
pub(crate) fn append_all_from(&mut self, other: &mut Parcel) -> Result<()> {
self.append_from(other, 0, other.data_size())
}
pub(crate) fn append_from(
&mut self,
other: &mut Parcel,
offset: usize,
size: usize,
) -> Result<()> {
if size == 0 {
return Ok(());
}
if size > i32::MAX as usize {
log::error!("Parcel::append_from: the size is too large: {size}");
return Err(StatusCode::BadValue);
}
let other_len = other.data_size();
if offset > other_len || size > other_len || (offset + size) > other_len {
log::error!("Parcel::append_from: The given offset({offset}) and size({size}) exceed the data range of the parcel.");
return Err(StatusCode::BadValue);
}
let start_pos = self.pos;
let mut first_idx: i32 = -1;
let mut last_idx: i32 = -2;
{
let object_size = std::mem::size_of::<flat_binder_object>() as u64;
let objects = other.objects.as_slice();
for (i, &off) in objects.iter().enumerate() {
if off >= offset as _ && (off + object_size) <= (offset + size) as u64 {
if first_idx == -1 {
first_idx = i as i32;
}
last_idx = i as i32;
}
}
}
let num_objects = last_idx - first_idx + 1;
self.data.reserve(self.pos + size);
unsafe {
let old_len = self.data.len();
if self.pos > old_len {
std::ptr::write_bytes(self.data.as_mut_ptr().add(old_len), 0, self.pos - old_len);
}
std::ptr::copy_nonoverlapping::<u8>(
other.data.as_slice()[offset..offset + size].as_ptr(),
self.data.as_mut_ptr().add(self.pos),
size,
);
if self.pos + size > self.data.len() {
self.data.set_len(self.pos + size);
}
}
self.set_data_position(self.pos + size);
#[cfg(feature = "rpc")]
let skip_objects = self.rpc.is_some();
#[cfg(not(feature = "rpc"))]
let skip_objects = false;
if num_objects > 0 && !skip_objects {
self.objects.reserve(num_objects as usize);
let src_objects = other.objects.as_slice();
for i in first_idx..=last_idx {
let off = src_objects[i as usize] as usize - offset + start_pos;
let mut flat = read_flat_binder(self.data.as_slice(), off)?;
flat.acquire()?;
if flat.header_type() == BINDER_TYPE_FD {
let newfd = match rustix::io::fcntl_dupfd_cloexec(flat.borrowed_fd(), 0) {
Ok(newfd) => newfd,
Err(e) => {
return Err(std::io::Error::from(e).into());
}
};
flat.set_handle(newfd.into_raw_fd() as _);
flat.set_cookie(1);
write_flat_binder(self.data.as_mut_slice(), off, &flat)?;
}
self.objects.push(off as _);
}
}
Ok(())
}
fn release_objects(&self) {
#[cfg(feature = "rpc")]
if self.rpc.is_some() {
return;
}
if self.objects.len() == 0 {
return;
}
for pos in self.objects.as_slice() {
let Ok(obj) = read_flat_binder(self.data.as_slice(), *pos as usize) else {
log::error!("Parcel: unable to read object at position {pos}");
continue;
};
obj.release()
.map_err(|e| log::error!("Parcel: unable to release object: {e:?}"))
.ok();
}
}
}
impl Drop for Parcel {
fn drop(&mut self) {
match self.free_buffer {
Some(free_buffer) => {
if let Err(e) = free_buffer(
Some(self),
self.data.as_ptr() as _,
self.data.len(),
self.objects.as_ptr() as _,
self.objects.len(),
) {
log::error!("Failed to free parcel buffer ({e}); leaking the kernel buffer");
}
}
None => {
self.release_objects();
}
}
}
}
impl std::fmt::Debug for Parcel {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
writeln!(f, "Parcel: pos {}, len {}", self.pos, self.data.len())?;
if self.objects.len() > 0 {
let bytes: &[u8] = unsafe {
std::slice::from_raw_parts(
self.objects.as_ptr() as *const u8,
self.objects.len() * std::mem::size_of::<binder_size_t>(),
)
};
writeln!(
f,
"Object count {}\n{}",
self.objects.len(),
pretty_hex(&bytes)
)?;
}
write!(f, "{}", pretty_hex(&self.data.as_slice()))
}
}
impl<const N: usize> TryFrom<&mut Parcel> for [u8; N] {
type Error = StatusCode;
fn try_from(parcel: &mut Parcel) -> Result<Self> {
let data = parcel.read_aligned_data(N)?;
Ok(<[u8; N] as TryFrom<&[u8]>>::try_from(data)?)
}
}
#[cfg(test)]
mod tests {
use crate::*;
#[test]
fn parcel_data_slice_as_mut_slice_round_trips() {
let leaked: &'static mut [u8] = Box::leak(vec![0u8, 1, 2, 3].into_boxed_slice());
let ptr = leaked.as_mut_ptr();
let len = leaked.len();
let mut pd: super::ParcelData<u8> =
unsafe { super::ParcelData::from_raw_parts_mut(ptr, len) };
let slice = pd.as_mut_slice();
assert_eq!(slice, &mut [0u8, 1, 2, 3][..]);
slice[0] = 99;
slice[3] = 200;
assert_eq!(pd.as_slice(), &[99u8, 1, 2, 200][..]);
}
#[test]
fn write_array_zeroes_trailing_pad() {
let mut parcel = Parcel::new();
parcel.write(&(-1i32)).unwrap();
parcel.write(&(-1i32)).unwrap();
parcel.set_data_position(0);
let payload: &[u8] = &[0xAB];
SerializeArray::serialize_array(payload, &mut parcel).unwrap();
let bytes = parcel.data.as_slice();
assert_eq!(&bytes[4..8], &[0xAB, 0x00, 0x00, 0x00]);
}
#[test]
fn checked_array_layout_normal_case() {
let (size, padded) = super::checked_array_layout(10, 4).unwrap();
assert_eq!((size, padded), (40, 40));
let (size, padded) = super::checked_array_layout(3, 5).unwrap();
assert_eq!((size, padded), (15, 16));
}
#[test]
fn checked_array_layout_rejects_size_mul_overflow() {
assert_eq!(
super::checked_array_layout(i32::MAX, usize::MAX),
Err(StatusCode::BadValue)
);
}
#[test]
fn checked_array_layout_rejects_pad_overflow() {
assert_eq!(
super::checked_array_layout(1, usize::MAX),
Err(StatusCode::BadValue)
);
assert_eq!(
super::checked_array_layout(7, usize::MAX / 3),
Err(StatusCode::BadValue)
);
}
#[test]
fn checked_array_layout_64bit_no_op_for_i32_max() {
let (size, padded) = super::checked_array_layout(i32::MAX, 4).unwrap();
assert_eq!(size, (i32::MAX as usize) * 4);
assert_eq!(padded, super::pad_size(size));
}
#[test]
fn read_array_rejects_hostile_len_gracefully() {
let mut parcel = Parcel::new();
parcel.write::<i32>(&1_000_000_000).unwrap();
parcel.set_data_position(0);
let r = parcel.read_array::<i32>();
assert!(r.is_err(), "expected Err, got {r:?}");
}
#[test]
fn read_array_char_rejects_hostile_len_gracefully() {
let mut parcel = Parcel::new();
parcel.write::<i32>(&1_000_000_000).unwrap();
parcel.set_data_position(0);
let r = parcel.read_array_char::<u16>();
assert!(r.is_err(), "expected Err, got {r:?}");
}
#[test]
fn test_primitives() -> Result<()> {
let v_i32: i32 = 1234;
let v_f32: f32 = 5678.0;
let v_u32: u32 = 9012;
let v_i64: i64 = 3456;
let v_u64: u64 = 7890;
let v_f64: f64 = 9876.0;
let v_str = "Hello World".to_owned();
let mut parcel = Parcel::new();
{
parcel.write::<i32>(&v_i32)?;
parcel.write::<u32>(&v_u32)?;
parcel.write::<f32>(&v_f32)?;
parcel.write::<i64>(&v_i64)?;
parcel.write::<u64>(&v_u64)?;
parcel.write::<f64>(&v_f64)?;
parcel.write(&v_str)?;
}
parcel.set_data_position(0);
{
assert_eq!(parcel.read::<i32>()?, v_i32);
assert_eq!(parcel.read::<u32>()?, v_u32);
assert_eq!(parcel.read::<f32>()?, v_f32);
assert_eq!(parcel.read::<i64>()?, v_i64);
assert_eq!(parcel.read::<u64>()?, v_u64);
assert_eq!(parcel.read::<f64>()?, v_f64);
assert_eq!(parcel.read::<String>()?, v_str);
}
Ok(())
}
#[test]
fn test_array_byte() {
let array = vec![255u8, 0u8, 127u8];
let mut reverse = array.clone();
reverse.reverse();
let mut parcel = Parcel::new();
parcel.write_array(&array).unwrap();
parcel.write_array(&reverse).unwrap();
parcel.set_data_position(0);
let res = parcel.read_array::<u8>().unwrap();
assert_eq!(array, res.unwrap());
let res = parcel.read_array::<u8>().unwrap();
assert_eq!(reverse, res.unwrap());
}
#[test]
fn test_array_double() {
let array = vec![1.0f64 / 3.0f64, 1.0f64 / 7.0f64, 42.0f64];
let mut reverse = array.clone();
reverse.reverse();
let mut parcel = Parcel::new();
parcel.write_array(&array).unwrap();
parcel.write_array(&reverse).unwrap();
println!("{parcel:?}");
parcel.set_data_position(0);
let res = parcel.read_array::<f64>().unwrap();
assert_eq!(array, res.unwrap());
let res = parcel.read_array::<f64>().unwrap();
assert_eq!(reverse, res.unwrap());
}
#[test]
fn test_array_char() {
let array = vec![255u16, 0u16, 127u16];
let mut reverse = array.clone();
reverse.reverse();
let mut parcel = Parcel::new();
parcel.write_array_char(&array).unwrap();
parcel.write_array_char(&reverse).unwrap();
parcel.set_data_position(0);
let res = parcel.read_array_char::<u16>().unwrap();
assert_eq!(array, res.unwrap());
let res = parcel.read_array_char::<u16>().unwrap();
assert_eq!(reverse, res.unwrap());
}
#[test]
fn test_errors() -> Result<()> {
Ok(())
}
#[test]
fn from_ipc_parts_preserves_data_pointer_when_length_is_zero() {
use crate::sys::binder::binder_uintptr_t;
use std::sync::atomic::{AtomicUsize, Ordering};
static FREED_DATA_PTR: AtomicUsize = AtomicUsize::new(0);
fn capture(
_: Option<&Parcel>,
data: binder_uintptr_t,
_: usize,
_: binder_uintptr_t,
_: usize,
) -> Result<()> {
FREED_DATA_PTR.store(data as usize, Ordering::SeqCst);
Ok(())
}
let mut backing = vec![0u8; 4096];
let original = backing.as_mut_ptr();
{
let parcel =
unsafe { Parcel::from_ipc_parts(original, 0, std::ptr::null_mut(), 0, capture) };
assert_eq!(
parcel.as_ptr() as usize,
original as usize,
"as_ptr() must return the original buffer pointer for empty IPC parcels",
);
}
assert_eq!(
FREED_DATA_PTR.load(Ordering::SeqCst),
original as usize,
"BC_FREE_BUFFER must be issued with the original kernel-supplied pointer",
);
}
#[test]
fn from_ipc_parts_with_null_data_uses_empty_slice() {
fn noop(
_: Option<&Parcel>,
_: crate::sys::binder::binder_uintptr_t,
_: usize,
_: crate::sys::binder::binder_uintptr_t,
_: usize,
) -> Result<()> {
Ok(())
}
let parcel = unsafe {
Parcel::from_ipc_parts(std::ptr::null_mut(), 0, std::ptr::null_mut(), 0, noop)
};
assert_eq!(parcel.data_size(), 0);
drop(parcel);
}
#[test]
fn set_data_size_rejects_over_capacity() {
let mut parcel = Parcel::new();
let cap = parcel.capacity();
assert!(parcel.set_data_size(cap).is_ok());
assert_eq!(parcel.set_data_size(cap + 1), Err(StatusCode::BadValue));
assert!(parcel.set_data_size(0).is_ok());
}
#[test]
fn data_avail_saturates_when_pos_past_end() {
let mut parcel = Parcel::new();
parcel.write(&0u64).expect("write u64");
assert_eq!(parcel.data_avail(), 0, "cursor at end → nothing available");
parcel.set_data_position(0);
assert_eq!(parcel.data_avail(), 8, "8 bytes available from start");
parcel.set_data_position(9999);
assert_eq!(
parcel.data_avail(),
0,
"saturating_sub, not underflow panic"
);
}
#[test]
fn sized_read_field_truncation_via_has_more_data() {
let mut wv1 = Parcel::new();
wv1.sized_write(|p| {
p.write(&11i32)?;
p.write(&22i32)
})
.expect("v1 write");
wv1.write(&0x7777_7777i32).expect("sentinel");
wv1.set_data_position(0);
let (mut a, mut b, mut c) = (0i32, 0i32, -1i32);
wv1.sized_read(|p| {
if !p.has_more_data() {
return Ok(());
}
a = p.read()?;
if !p.has_more_data() {
return Ok(());
}
b = p.read()?;
if !p.has_more_data() {
return Ok(());
}
c = p.read()?;
Ok(())
})
.expect("v3 read of v1 data");
assert_eq!((a, b), (11, 22), "present fields read");
assert_eq!(c, -1, "absent trailing field left at default, no over-read");
assert_eq!(wv1.read::<i32>().expect("sentinel"), 0x7777_7777);
let mut wv3 = Parcel::new();
wv3.sized_write(|p| {
p.write(&1i32)?;
p.write(&2i32)?;
p.write(&3i32)
})
.expect("v3 write");
wv3.write(&0x5555_5555i32).expect("sentinel");
wv3.set_data_position(0);
let (mut x, mut y) = (0i32, 0i32);
wv3.sized_read(|p| {
if !p.has_more_data() {
return Ok(());
}
x = p.read()?;
if !p.has_more_data() {
return Ok(());
}
y = p.read()?;
Ok(())
})
.expect("v1 read of v3 data");
assert_eq!((x, y), (1, 2), "first two fields read");
assert_eq!(
wv3.read::<i32>().expect("sentinel"),
0x5555_5555,
"extra field skipped to block end, sentinel intact"
);
}
#[cfg(feature = "rpc")]
#[test]
fn rpc_object_position_table_is_aosp_faithful_and_sorted() {
use crate::rpc::wire::{WireCodec, WireMessage, WireTransaction};
use crate::rpc::wire_android13::Android13PlusCodec;
let mut kparcel = Parcel::new();
kparcel.write(&7i32).unwrap();
kparcel.rpc_record_object_position(0); assert!(
kparcel.rpc_object_positions().is_empty(),
"kernel parcel must never grow an object table"
);
let mut p = Parcel::new();
p.set_for_rpc(true);
p.set_rpc_record_fd_positions(true);
p.write(&0xDEAD_BEEFu32).unwrap(); p.write(&"iface".to_owned()).unwrap();
let mut expect: Vec<u32> = Vec::new();
let pos = p.data_position();
expect.push(pos as u32);
p.write(&1i32).unwrap(); p.write_aligned_data(&[0u8; 8]); p.write(&0x0Ci32).unwrap(); p.rpc_record_object_position(pos);
p.write(&123i64).unwrap();
let pos = p.data_position();
expect.push(pos as u32);
p.write(&1i32).unwrap(); p.write(&0i32).unwrap(); p.rpc_record_object_position(pos);
let pos = p.data_position();
expect.push(pos as u32);
p.write(&1i32).unwrap();
p.write_aligned_data(&[0u8; 8]);
p.write(&0x0Ci32).unwrap();
p.rpc_record_object_position(pos);
assert_eq!(
p.rpc_object_positions(),
&expect[..],
"AOSP dataPos offsets"
);
assert!(
p.rpc_object_positions().windows(2).all(|w| w[0] < w[1]),
"table strictly ascending"
);
let mut q = Parcel::new();
q.set_for_rpc(true);
for pos in [40u32, 8, 24, 8, 0] {
q.rpc_record_object_position(pos as usize);
}
assert_eq!(
q.rpc_object_positions(),
&[0, 8, 8, 24, 40],
"upper_bound insert keeps the table sorted (dups allowed)"
);
for &good in &[0u32, 8, 24, 40] {
assert!(q.rpc_object_position_present(good as usize), "pos {good}");
}
for bad in [4usize, 12, 41, 9999] {
assert!(
!q.rpc_object_position_present(bad),
"unrecorded pos {bad} must fail the v2 binary_search"
);
}
let c = Android13PlusCodec::android16();
let data = p.rpc_data_bytes().to_vec();
let positions = p.rpc_object_positions().to_vec();
let txn = WireTransaction {
address: crate::rpc::address::RpcAddress::zero(),
code: 1,
flags: 0,
async_number: 0,
data: data.clone(),
object_positions: positions.clone(),
};
let enc = c.encode_transact(&txn).unwrap();
let body = u32::from_le_bytes([enc[4], enc[5], enc[6], enc[7]]) as usize;
assert_eq!(
body,
40 + data.len() + 4 * positions.len(),
"bodySize = fixed(40) + parcelDataSize + 4·N"
);
match c.decode_message(&enc).unwrap() {
WireMessage::Transact(d) => {
assert_eq!(d.data, data, "parcel data intact");
assert_eq!(d.object_positions, positions, "object table intact");
}
o => panic!("expected Transact, got {o:?}"),
}
}
}