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;
pub(crate) unsafe trait ParcelPod: Copy {}
macro_rules! impl_parcel_pod {
($($t:ty),* $(,)?) => { $(
unsafe impl ParcelPod for $t {}
)* };
}
impl_parcel_pod!(i8, u8, i16, u16, i32, u32, i64, u64, u128, f32, f64);
pub(crate) trait WireScalar: Copy {
type Bytes: AsRef<[u8]>;
fn to_wire(self) -> Self::Bytes;
fn from_wire(bytes: &[u8]) -> Result<Self>;
}
pub(crate) trait NativeScalar: Copy {
type Bytes: AsRef<[u8]>;
fn to_native(self) -> Self::Bytes;
fn from_native(bytes: &[u8]) -> Result<Self>;
}
macro_rules! impl_scalar_codecs {
($($t:ty),* $(,)?) => { $(
impl WireScalar for $t {
type Bytes = [u8; std::mem::size_of::<$t>()];
fn to_wire(self) -> Self::Bytes {
self.to_le_bytes()
}
fn from_wire(bytes: &[u8]) -> Result<Self> {
Ok(<$t>::from_le_bytes(bytes.try_into()?))
}
}
impl NativeScalar for $t {
type Bytes = [u8; std::mem::size_of::<$t>()];
fn to_native(self) -> Self::Bytes {
self.to_ne_bytes()
}
fn from_native(bytes: &[u8]) -> Result<Self> {
Ok(<$t>::from_ne_bytes(bytes.try_into()?))
}
}
)* };
}
impl_scalar_codecs!(i8, u8, i16, u16, i32, u32, i64, u64, u128, f32, f64);
unsafe impl ParcelPod for flat_binder_object {}
unsafe impl ParcelPod for crate::sys::binder_transaction_data {}
unsafe impl ParcelPod for crate::sys::binder_transaction_data_secctx {}
#[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))
}
#[cfg(feature = "rpc")]
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()
}
unsafe 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!("push() is only available for ParcelData::Vec."),
}
}
}
pub(crate) type FnFreeBuffer =
fn(Option<&Parcel>, binder_uintptr_t, usize, binder_uintptr_t, usize) -> Result<()>;
#[cfg(feature = "rpc")]
pub(crate) 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>,
pinned: Vec<crate::binder::SIBinder>,
}
#[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)
}
}
const MAX_NESTED_READ_DEPTH: usize = 1000;
pub struct Parcel {
data: ParcelData<u8>,
pub(crate) objects: ParcelData<binder_size_t>,
pos: usize,
next_object_hint: usize,
read_boundary: Option<usize>,
nested_read_depth: 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,
nested_read_depth: 0,
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,
nested_read_depth: 0,
request_header_present: false,
work_source_request_header_pos: 0,
free_buffer: Some(free_buffer),
#[cfg(feature = "rpc")]
rpc: None,
}
}
#[cfg(feature = "rpc")]
pub(crate) 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,
nested_read_depth: 0,
request_header_present: false,
work_source_request_header_pos: 0,
free_buffer: None,
#[cfg(feature = "rpc")]
rpc: None,
}
}
#[cfg(feature = "rpc")]
pub(crate) fn new_data_only() -> Self {
let mut p = Parcel::new();
p.set_for_rpc(true);
p
}
#[cfg(feature = "rpc")]
pub(crate) fn from_slice(bytes: &[u8]) -> Self {
let mut p = Parcel::from_vec(bytes.to_vec());
p.set_for_rpc(true);
p
}
#[cfg(feature = "rpc")]
pub(crate) fn is_self_contained(&self) -> bool {
self.objects.len() == 0
&& self.rpc_object_positions().is_empty()
&& self.rpc_out_fds().is_empty()
}
#[cfg(feature = "rpc")]
pub(crate) fn as_bytes(&self) -> Result<&[u8]> {
if !self.is_self_contained() {
return Err(StatusCode::BadType);
}
Ok(self.data.as_slice())
}
#[cfg(feature = "rpc")]
pub(crate) fn into_bytes(self) -> Result<Vec<u8>> {
self.as_bytes().map(<[u8]>::to_vec)
}
pub(crate) fn as_mut_ptr(&mut self) -> *mut u8 {
self.data.as_mut_ptr()
}
pub(crate) fn as_ptr(&self) -> *const u8 {
self.data.as_ptr()
}
pub(crate) fn ipc_data_size(&self) -> usize {
self.data.len()
}
pub(crate) fn capacity(&self) -> usize {
self.data.capacity()
}
pub(crate) fn is_empty(&self) -> bool {
self.pos >= self.data.len()
}
#[cfg(feature = "rpc")]
pub(crate) fn set_for_rpc(&mut self, yes: bool) {
if yes {
self.rpc.get_or_insert_with(RpcFields::default);
} else {
self.rpc = None;
}
}
#[cfg(all(feature = "rpc", feature = "test-util"))]
#[doc(hidden)]
pub fn __set_for_rpc(&mut self, yes: bool) {
self.set_for_rpc(yes)
}
#[cfg(feature = "rpc")]
pub fn is_kernel_backed(&self) -> bool {
self.rpc.is_none()
}
#[cfg(not(feature = "rpc"))]
pub fn is_kernel_backed(&self) -> bool {
true
}
#[deprecated(
since = "0.11.0",
note = "renamed and inverted: use `!parcel.is_kernel_backed()`. The old name \
described one consumer of the mode rather than what the flag controls, \
and read fail-open at the one security branch that uses it."
)]
pub fn is_for_rpc(&self) -> bool {
!self.is_kernel_backed()
}
#[cfg(feature = "rpc")]
pub(crate) 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_pin_binder(&mut self, binder: crate::binder::SIBinder) {
if let Some(rpc) = self.rpc.as_mut() {
rpc.pinned.push(binder);
}
}
#[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(all(feature = "rpc", any(test, feature = "fuzzing")))]
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(all(feature = "rpc", any(test, feature = "fuzzing")))]
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(crate) fn set_data_size(&mut self, new_len: usize) -> Result<()> {
if new_len > self.data.len() {
log::error!(
"set_data_size({new_len}) would grow past the initialized length {}",
self.data.len()
);
return Err(StatusCode::BadValue);
}
unsafe { self.data.set_len(new_len) };
if new_len < self.pos {
self.pos = new_len;
}
Ok(())
}
pub(crate) unsafe fn set_data_size_driver_filled(&mut self, new_len: usize) -> Result<()> {
if new_len > self.data.capacity() {
log::error!(
"set_data_size_driver_filled({new_len}) exceeds capacity {}",
self.data.capacity()
);
return Err(StatusCode::BadValue);
}
unsafe { self.data.set_len(new_len) };
if new_len < self.pos {
self.pos = new_len;
}
Ok(())
}
pub(crate) 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) {
if pos > i32::MAX as usize {
log::error!("Parcel::set_data_position({pos}) exceeds i32::MAX; ignored");
return;
}
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 write_i32(&mut self, val: i32) -> Result<()> {
self.write(&val)
}
pub fn write_u32(&mut self, val: u32) -> Result<()> {
self.write(&val)
}
pub fn write_i64(&mut self, val: i64) -> Result<()> {
self.write(&val)
}
pub fn write_u64(&mut self, val: u64) -> Result<()> {
self.write(&val)
}
pub fn write_f32(&mut self, val: f32) -> Result<()> {
self.write(&val)
}
pub fn write_f64(&mut self, val: f64) -> Result<()> {
self.write(&val)
}
pub fn write_bool(&mut self, val: bool) -> Result<()> {
self.write(&val)
}
pub fn write_i8(&mut self, val: i8) -> Result<()> {
self.write(&val)
}
pub fn write_u8(&mut self, val: u8) -> Result<()> {
self.write(&val)
}
pub fn read_i32(&mut self) -> Result<i32> {
self.read()
}
pub fn read_u32(&mut self) -> Result<u32> {
self.read()
}
pub fn read_i64(&mut self) -> Result<i64> {
self.read()
}
pub fn read_u64(&mut self) -> Result<u64> {
self.read()
}
pub fn read_f32(&mut self) -> Result<f32> {
self.read()
}
pub fn read_f64(&mut self) -> Result<f64> {
self.read()
}
pub fn read_bool(&mut self) -> Result<bool> {
self.read()
}
pub fn read_i8(&mut self) -> Result<i8> {
self.read()
}
pub fn read_u8(&mut self) -> Result<u8> {
self.read()
}
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);
}
if self.nested_read_depth >= MAX_NESTED_READ_DEPTH {
log::error!("Parcel: nested parcelable read depth exceeded {MAX_NESTED_READ_DEPTH}");
return Err(StatusCode::BadValue);
}
self.nested_read_depth += 1;
let prev_boundary = self.read_boundary;
self.read_boundary = Some(end);
let result = f(self);
self.read_boundary = prev_boundary;
self.nested_read_depth -= 1;
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 + ParcelPod + WireScalar>(
&mut self,
) -> Result<Option<Vec<D>>> {
let len: i32 = self.read()?;
if len < -1 {
log::error!("Parcel: bad array length: {len}");
return Err(StatusCode::UnexpectedNull);
}
if len == -1 {
return Ok(None);
}
if len == 0 {
return Ok(Some(Vec::new()));
}
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);
if cfg!(target_endian = "big") && std::mem::size_of::<D>() > 1 {
let bytes = std::slice::from_raw_parts_mut(result.as_mut_ptr() as *mut u8, size);
for chunk in bytes.chunks_exact_mut(std::mem::size_of::<D>()) {
chunk.reverse();
}
}
}
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::UnexpectedNull);
}
if len == -1 {
return Ok(None);
}
if len == 0 {
return Ok(Some(Vec::new()));
}
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_le_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 + ParcelPod + WireScalar>(
&mut self,
parcelable: &[S],
) -> Result<()> {
let len = parcelable.len();
let len_i32: i32 = len.try_into().or(Err(StatusCode::BadValue))?;
self.write::<i32>(&len_i32)?;
if len == 0 {
return Ok(());
}
let size = std::mem::size_of_val(parcelable);
let padded = pad_size(size);
let pos = self.pos;
let end = pos
.checked_add(padded)
.filter(|&e| e <= i32::MAX as usize)
.ok_or(StatusCode::BadValue)?;
self.data.reserve(end.saturating_sub(self.data.len()));
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() < end {
self.data.set_len(end);
}
}
if cfg!(target_endian = "big") && std::mem::size_of::<S>() > 1 {
for chunk in
self.data.as_mut_slice()[pos..pos + size].chunks_exact_mut(std::mem::size_of::<S>())
{
chunk.reverse();
}
}
self.set_data_position(end);
Ok(())
}
pub(crate) fn write_array_char<S: CharType>(&mut self, parcelable: &[S]) -> Result<()> {
let len = parcelable.len();
let len_i32: i32 = len.try_into().or(Err(StatusCode::BadValue))?;
self.write::<i32>(&len_i32)?;
let size = len.checked_mul(4).ok_or(StatusCode::BadValue)?;
let padded = pad_size(size);
let end = self
.pos
.checked_add(padded)
.filter(|&e| e <= i32::MAX as usize)
.ok_or(StatusCode::BadValue)?;
self.data.reserve(end.saturating_sub(self.data.len()));
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_le<T: WireScalar>(&mut self, val: &T) -> Result<()> {
self.write_aligned_data(val.to_wire().as_ref())
}
pub(crate) fn read_le<T: WireScalar>(&mut self) -> Result<T> {
let data = self.read_aligned_data(std::mem::size_of::<T>())?;
T::from_wire(data)
}
pub(crate) fn write_native<T: NativeScalar>(&mut self, val: &T) -> Result<()> {
self.write_aligned_data(val.to_native().as_ref())
}
pub(crate) fn read_native<T: NativeScalar>(&mut self) -> Result<T> {
let data = self.read_aligned_data(std::mem::size_of::<T>())?;
T::from_native(data)
}
pub(crate) fn write_aligned<T: ParcelPod>(&mut self, val: &T) -> Result<()> {
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]) -> Result<()> {
let unaligned = data.len();
let aligned = pad_size(unaligned);
let pos = self.pos;
let end = pos
.checked_add(aligned)
.filter(|&e| e <= i32::MAX as usize)
.ok_or(StatusCode::BadValue)?;
self.data.reserve(end.saturating_sub(self.data.len()));
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 end > self.data.len() {
self.data.set_len(end);
}
}
self.set_data_position(end);
Ok(())
}
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.len();
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);
}
#[cfg(feature = "rpc")]
if self.rpc.is_none() && other.rpc.is_some() {
log::error!("Parcel::append_from: refusing RPC/data-only bytes into a kernel parcel");
return Err(StatusCode::BadType);
}
#[cfg(feature = "rpc")]
if other.rpc.as_ref().is_some_and(|r| r.ops.is_some())
&& self.rpc.as_ref().is_none_or(|r| r.ops.is_none())
{
log::error!("Parcel::append_from: refusing session bytes into a session-less parcel");
return Err(StatusCode::BadType);
}
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;
#[cfg(feature = "rpc")]
if self.rpc.is_some() && num_objects > 0 {
let src_data = other.data.as_slice();
let src_objects = other.objects.as_slice();
let only_fds = (first_idx..=last_idx).all(|i| {
matches!(
read_flat_binder(src_data, src_objects[i as usize] as usize),
Ok(flat) if flat.header_type() == BINDER_TYPE_FD
)
});
return Err(if only_fds {
StatusCode::FdsNotAllowed
} else {
StatusCode::BadType
});
}
let end = self
.pos
.checked_add(size)
.filter(|&e| e <= i32::MAX as usize)
.ok_or(StatusCode::BadValue)?;
self.data.reserve(end.saturating_sub(self.data.len()));
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 end > self.data.len() {
self.data.set_len(end);
}
}
self.set_data_position(end);
#[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(feature = "rpc")]
pub fn to_bytes<T: Serialize + ?Sized>(value: &T) -> Result<Vec<u8>> {
let mut parcel = Parcel::new_data_only();
parcel.write(value)?;
parcel.into_bytes()
}
#[cfg(feature = "rpc")]
pub fn from_bytes<T: Deserialize>(bytes: &[u8]) -> Result<T> {
if bytes.len() >= i32::MAX as usize {
return Err(StatusCode::BadValue);
}
let mut parcel = Parcel::from_slice(bytes);
let value = parcel.read::<T>()?;
if parcel.data_avail() != 0 {
return Err(StatusCode::BadValue);
}
Ok(value)
}
#[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_passes_i32_max_on_64_bit_and_rejects_it_on_32() {
#[cfg(target_pointer_width = "64")]
{
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));
}
#[cfg(not(target_pointer_width = "64"))]
assert_eq!(
super::checked_array_layout(i32::MAX, 4),
Err(StatusCode::BadValue)
);
}
#[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 parcel_array_empty_is_not_null() {
let mut parcel = Parcel::new();
parcel.write_array::<u8>(&[]).unwrap();
parcel.write_array_char::<u16>(&[]).unwrap();
parcel.set_data_position(0);
assert_eq!(parcel.read_array::<u8>(), Ok(Some(Vec::new())));
assert_eq!(parcel.read_array_char::<u16>(), Ok(Some(Vec::new())));
}
#[test]
fn vec_deserialize_rejects_null_but_accepts_empty() {
let mut parcel = Parcel::new();
parcel.write(&-1i32).unwrap();
parcel.write(&0i32).unwrap();
parcel.write(&0i32).unwrap();
parcel.write(&-2i32).unwrap();
parcel.set_data_position(0);
assert_eq!(parcel.read::<Vec<u8>>(), Err(StatusCode::UnexpectedNull));
assert_eq!(parcel.read::<Vec<u8>>(), Ok(Vec::new()));
assert_eq!(parcel.read::<Option<Vec<u8>>>(), Ok(Some(Vec::<u8>::new())));
assert_eq!(parcel.read::<Vec<u8>>(), Err(StatusCode::UnexpectedNull));
}
#[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_typed_scalar_helpers() -> Result<()> {
let mut p = Parcel::new();
p.write_i32(-7)?;
p.write_u32(7)?;
p.write_i64(-8)?;
p.write_u64(8)?;
p.write_f32(1.5)?;
p.write_f64(2.5)?;
p.write_bool(true)?;
p.write_i8(-9)?;
p.write_u8(9)?;
p.set_data_position(0);
assert_eq!(p.read_i32()?, -7);
assert_eq!(p.read_u32()?, 7);
assert_eq!(p.read_i64()?, -8);
assert_eq!(p.read_u64()?, 8);
assert_eq!(p.read_f32()?, 1.5);
assert_eq!(p.read_f64()?, 2.5);
assert!(p.read_bool()?);
assert_eq!(p.read_i8()?, -9);
assert_eq!(p.read_u8()?, 9);
let mut q = Parcel::new();
q.write_i32(0x1234_5678)?;
q.set_data_position(0);
assert_eq!(q.read::<i32>()?, 0x1234_5678);
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_only_shrinks() {
let mut parcel = Parcel::new();
parcel.write(&0u64).expect("write u64");
assert_eq!(parcel.data_size(), 8);
assert!(parcel.capacity() > 8);
assert_eq!(parcel.set_data_size(9), Err(StatusCode::BadValue));
assert_eq!(parcel.data_size(), 8);
assert!(parcel.set_data_size(4).is_ok());
assert_eq!(parcel.data_size(), 4);
assert_eq!(parcel.data_position(), 4);
assert!(parcel.set_data_size(0).is_ok());
}
#[test]
fn set_data_size_driver_filled_is_bounded_by_capacity() {
let mut parcel = Parcel::new();
let cap = parcel.capacity();
unsafe {
std::ptr::write_bytes(parcel.as_mut_ptr(), 0xAB, cap);
assert!(parcel.set_data_size_driver_filled(cap).is_ok());
assert_eq!(
parcel.set_data_size_driver_filled(cap + 1),
Err(StatusCode::BadValue)
);
}
assert_eq!(parcel.data_size(), cap);
assert!(parcel.data.as_slice().iter().all(|&b| b == 0xAB));
}
#[test]
fn ipc_data_size_never_exceeds_backing_buffer() {
let mut parcel = Parcel::new();
parcel.write(&1u32).expect("write");
parcel.set_data_position(1024);
assert_eq!(parcel.data_size(), 1024, "AOSP dataSize() = max(len, pos)");
assert_eq!(
parcel.ipc_data_size(),
4,
"only the written bytes go to the kernel"
);
parcel.write(&2u32).expect("write past gap");
assert_eq!(parcel.ipc_data_size(), 1028);
assert_eq!(parcel.ipc_data_size(), parcel.data_size());
}
#[test]
fn set_data_position_rejects_past_i32_max() {
let mut parcel = Parcel::new();
parcel.write(&7u32).expect("write");
parcel.set_data_position(0);
parcel.set_data_position(i32::MAX as usize + 1);
assert_eq!(parcel.data_position(), 0, "out-of-range seek is ignored");
parcel.set_data_position(i32::MAX as usize);
assert_eq!(parcel.data_position(), i32::MAX as usize);
}
#[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"
);
}
#[test]
fn sized_read_depth_is_bounded() {
fn write_nested(p: &mut Parcel, depth: usize) -> Result<()> {
p.sized_write(|s| {
s.write(&(depth as i32))?;
if depth > 1 {
write_nested(s, depth - 1)?;
}
Ok(())
})
}
fn read_nested(p: &mut Parcel) -> Result<()> {
p.sized_read(|s| {
let _marker: i32 = s.read()?;
if s.has_more_data() {
read_nested(s)?;
}
Ok(())
})
}
let mut over = Parcel::new();
write_nested(&mut over, super::MAX_NESTED_READ_DEPTH + 50).expect("write over-deep");
over.set_data_position(0);
assert_eq!(read_nested(&mut over).unwrap_err(), StatusCode::BadValue);
let mut ok = Parcel::new();
write_nested(&mut ok, 8).expect("write shallow");
for _ in 0..2 {
ok.set_data_position(0);
read_nested(&mut ok).expect("shallow read");
}
}
#[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]).unwrap(); 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]).unwrap();
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:?}"),
}
}
}
#[cfg(test)]
mod wire_golden {
use super::*;
fn enc<S: Serialize + ?Sized>(value: &S) -> Vec<u8> {
let mut parcel = Parcel::new();
parcel.write(value).unwrap();
parcel.data.as_slice().to_vec()
}
#[test]
fn scalar_wire_is_absolute_little_endian() {
assert_eq!(enc(&true), [0x01, 0x00, 0x00, 0x00]);
assert_eq!(enc(&false), [0x00, 0x00, 0x00, 0x00]);
assert_eq!(enc(&0x0102_0304i32), [0x04, 0x03, 0x02, 0x01]);
assert_eq!(enc(&0xDEAD_BEEFu32), [0xEF, 0xBE, 0xAD, 0xDE]);
assert_eq!(
enc(&0x0102_0304_0506_0708i64),
[0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01]
);
assert_eq!(
enc(&0xDEAD_BEEF_CAFE_BABEu64),
[0xBE, 0xBA, 0xFE, 0xCA, 0xEF, 0xBE, 0xAD, 0xDE]
);
assert_eq!(
enc(&0x0102_0304_0506_0708_090A_0B0C_0D0E_0F10u128),
[
0x10, 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, 0x07, 0x06, 0x05, 0x04, 0x03,
0x02, 0x01
]
);
assert_eq!(enc(&1.0f32), [0x00, 0x00, 0x80, 0x3F]);
assert_eq!(enc(&-2.0f32), [0x00, 0x00, 0x00, 0xC0]);
assert_eq!(
enc(&1.0f64),
[0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F]
);
}
#[test]
fn scalar_widening_matches_the_aidl_wire() {
assert_eq!(enc(&-2i8), [0xFE, 0xFF, 0xFF, 0xFF]);
assert_eq!(enc(&0xABu8), [0xAB, 0x00, 0x00, 0x00]);
assert_eq!(enc(&-2i16), [0xFE, 0xFF, 0xFF, 0xFF]);
assert_eq!(enc(&0xBEEFu16), [0xEF, 0xBE, 0x00, 0x00]);
}
#[test]
fn array_wire_is_absolute_little_endian() {
assert_eq!(
enc(&[0xABu8, 0xCD, 0xEF][..]),
[0x03, 0x00, 0x00, 0x00, 0xAB, 0xCD, 0xEF, 0x00]
);
assert_eq!(
enc(&[-2i8, 1][..]),
[0x02, 0x00, 0x00, 0x00, 0xFE, 0x01, 0x00, 0x00]
);
assert_eq!(
enc(&[-2i16, 3][..]),
[0x02, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00]
);
assert_eq!(
enc(&[0xBEEFu16][..]),
[0x01, 0x00, 0x00, 0x00, 0xEF, 0xBE, 0x00, 0x00]
);
assert_eq!(
enc(&[0x0102_0304i32, -1][..]),
[0x02, 0x00, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01, 0xFF, 0xFF, 0xFF, 0xFF]
);
assert_eq!(
enc(&[1i64][..]),
[0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]
);
assert_eq!(
enc(&[1.0f64][..]),
[0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x3F]
);
assert_eq!(enc(&[0i32; 0][..]), [0x00, 0x00, 0x00, 0x00]);
assert_eq!(enc(&None::<Vec<i32>>), [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn string16_wire_is_absolute_little_endian() {
assert_eq!(
enc("AB"),
[0x02, 0x00, 0x00, 0x00, 0x41, 0x00, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00]
);
assert_eq!(enc("한"), [0x01, 0x00, 0x00, 0x00, 0x5C, 0xD5, 0x00, 0x00]);
assert_eq!(enc(""), [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
assert_eq!(enc(&None::<String>), [0xFF, 0xFF, 0xFF, 0xFF]);
}
#[test]
fn stability_word_is_little_endian() {
let level = i32::from(crate::Stability::Vintf);
assert_eq!(
enc(&level),
[
(level & 0xFF) as u8,
((level >> 8) & 0xFF) as u8,
((level >> 16) & 0xFF) as u8,
((level >> 24) & 0xFF) as u8,
]
);
#[cfg(not(target_os = "android"))]
assert_eq!(enc(&level), [0x3F, 0x00, 0x00, 0x00]);
}
#[test]
fn a_parcel_of_mixed_fields_keeps_every_slot_aligned() {
let mut parcel = Parcel::new();
parcel.write(&-2i8).unwrap();
parcel.write("한").unwrap();
parcel.write(&[0x0102_0304i32][..]).unwrap();
assert_eq!(
parcel.data.as_slice().to_vec(),
[
0xFE, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x5C, 0xD5, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x04, 0x03, 0x02, 0x01, ]
);
}
#[test]
fn the_command_stream_is_native_not_wire() {
let cmd: u32 = crate::sys::binder::BC_ACQUIRE;
let mut native = Parcel::new();
native.write_native::<u32>(&cmd).unwrap();
assert_eq!(native.data.as_slice().to_vec(), cmd.to_ne_bytes());
let mut wire = Parcel::new();
wire.write_le::<u32>(&cmd).unwrap();
assert_eq!(wire.data.as_slice().to_vec(), cmd.to_le_bytes());
if cfg!(target_endian = "big") {
assert_ne!(native.data.as_slice(), wire.data.as_slice());
}
native.set_data_position(0);
assert_eq!(native.read_native::<u32>().unwrap(), cmd);
}
#[test]
fn null_binder_stays_a_native_island() {
let mut parcel = Parcel::new();
SerializeOption::serialize_option(None::<&crate::SIBinder>, &mut parcel).unwrap();
let bytes = parcel.data.as_slice();
let obj_len = std::mem::size_of::<flat_binder_object>();
assert_eq!(
bytes[..4],
crate::sys::BINDER_TYPE_BINDER.to_ne_bytes()[..],
"object header is native, not little-endian"
);
assert!(
bytes[4..obj_len].iter().all(|&b| b == 0),
"a null binder carries no handle, cookie or flags"
);
}
}
#[cfg(all(test, feature = "rpc"))]
mod data_serde {
use super::*;
#[test]
fn a_value_survives_the_round_trip_and_encodes_the_same_way_twice() {
let value: Vec<i32> = vec![1, -2, 0x0102_0304];
let bytes = to_bytes(&value).unwrap();
assert_eq!(
bytes,
to_bytes(&value).unwrap(),
"encoding is deterministic"
);
assert_eq!(from_bytes::<Vec<i32>>(&bytes).unwrap(), value);
let text = String::from("한 quiet");
assert_eq!(
from_bytes::<String>(&to_bytes(&text).unwrap()).unwrap(),
text
);
}
#[test]
fn the_bytes_are_the_ipc_bytes() {
let value = -2i64;
let mut kernel = Parcel::new();
kernel.write(&value).unwrap();
assert_eq!(to_bytes(&value).unwrap(), kernel.data.as_slice());
}
#[test]
fn trailing_bytes_are_an_error_rather_than_a_partial_read() {
let mut bytes = to_bytes(&7i32).unwrap();
bytes.extend_from_slice(&[0, 0, 0, 0]);
assert_eq!(from_bytes::<i32>(&bytes), Err(StatusCode::BadValue));
}
#[test]
fn a_truncated_input_is_not_enough_data() {
let bytes = to_bytes(&7i64).unwrap();
assert_eq!(
from_bytes::<i64>(&bytes[..4]),
Err(StatusCode::NotEnoughData)
);
}
#[test]
fn a_binder_field_is_refused_at_write_time() {
let mut parcel = Parcel::new_data_only();
let binder: Option<&crate::SIBinder> = None;
assert_eq!(
crate::SerializeOption::serialize_option(binder, &mut parcel),
Err(StatusCode::BadType),
"even a null binder needs an object table it will not get"
);
}
#[test]
fn a_file_descriptor_field_is_refused_before_the_dup() {
use std::os::fd::AsRawFd;
let file = std::fs::File::open("/dev/null").expect("/dev/null");
let pfd = crate::ParcelFileDescriptor::new(file);
let mut parcel = Parcel::new_data_only();
assert_eq!(parcel.write(&pfd), Err(StatusCode::FdsNotAllowed));
assert!(
pfd.as_raw_fd() >= 0,
"the caller's fd is untouched by the refusal"
);
}
#[test]
fn a_forged_object_in_the_input_never_becomes_a_binder() {
let mut forged = Vec::new();
forged.extend_from_slice(&crate::sys::BINDER_TYPE_BINDER.to_ne_bytes());
forged.extend_from_slice(&[0u8; 20]);
assert_eq!(
Parcel::from_vec(forged.clone()).read_object(true).err(),
Some(StatusCode::BadType),
"the object decoder refuses an object with no offset-table entry"
);
assert_eq!(
Parcel::from_vec(vec![0u8; 8]).read_object(true).err(),
Some(StatusCode::NotEnoughData),
"too few bytes to be an object fails before the table lookup"
);
assert_eq!(
from_bytes::<crate::SIBinder>(&forged),
Err(StatusCode::BadType)
);
}
#[test]
fn a_parcel_holding_a_reference_refuses_to_hand_out_its_bytes() {
let mut parcel = Parcel::new_data_only();
parcel.write(&1i32).unwrap();
assert!(parcel.as_bytes().is_ok());
parcel.rpc_record_object_position(0);
assert_eq!(
parcel.as_bytes().err(),
Some(StatusCode::BadType),
"an object position makes the bytes incomplete"
);
}
#[test]
fn an_appended_object_table_is_refused_before_the_bytes_are_copied() {
for (object, expected) in [
(
flat_binder_object::new_binder_with_flags(0),
StatusCode::BadType,
),
(
flat_binder_object::new_with_fd(0, false),
StatusCode::FdsNotAllowed,
),
] {
let mut source = Parcel::new();
source.write_object(&object, true).unwrap();
let mut sink = Parcel::new_data_only();
assert_eq!(sink.append_all_from(&mut source), Err(expected));
assert!(
sink.as_bytes().unwrap().is_empty(),
"refused before the copy — no object bytes reached the sink"
);
}
}
#[test]
fn a_holder_cut_from_a_session_parcel_cannot_be_exported() {
use crate::Parcelable;
struct SessionOps;
impl RpcParcelOps for SessionOps {
fn write_binder(&self, _b: Option<&crate::SIBinder>, _p: &mut Parcel) -> Result<()> {
Err(StatusCode::DeadObject)
}
fn read_binder(&self, _p: &mut Parcel) -> Result<Option<crate::SIBinder>> {
Err(StatusCode::DeadObject)
}
}
const ADDR: [u8; 8] = *b"\xde\xad\xbe\xef\xfe\xed\xfa\xce";
let mut txn = Parcel::new();
txn.set_for_rpc(true);
txn.attach_rpc_ops(std::sync::Arc::new(SessionOps));
txn.write(&0i32).unwrap(); txn.write(&12i32).unwrap(); let obj_pos = txn.data_position();
txn.write(&1i32).unwrap(); txn.write_aligned_data(&ADDR).unwrap();
txn.rpc_record_object_position(obj_pos);
txn.set_data_position(0);
let mut holder = crate::ParcelableHolder::new(crate::Stability::Local);
holder.read_from_parcel(&mut txn).unwrap();
assert!(
txn.rpc_object_positions().len() == 1,
"the source records the position; the sub-parcel does not get it"
);
let exported = to_bytes(&holder);
assert!(
!exported
.as_deref()
.is_ok_and(|b| b.windows(ADDR.len()).any(|w| w == ADDR)),
"the session address reached the exported bytes"
);
assert_eq!(exported.err(), Some(StatusCode::BadType));
}
}