use std::os::raw::{c_char, c_void};
pub(super) const MAX_BINDER_STRING_LEN: usize = 1024 * 1024;
#[allow(dead_code)]
pub(super) type StringAllocator = unsafe extern "C" fn(*mut c_void, i32, *mut *mut c_char) -> bool;
#[allow(dead_code)]
pub(super) type ByteArrayAllocator = unsafe extern "C" fn(*mut c_void, i32, *mut *mut i8) -> bool;
pub(super) unsafe extern "C" fn string_alloc(
cookie: *mut c_void,
length: i32,
buffer: *mut *mut c_char,
) -> bool {
let s = unsafe { &mut *(cookie as *mut StringBuf) };
if length == -1 {
s.is_null = true;
return true;
}
if length < 0 {
return false;
}
let len = length as usize;
if len > MAX_BINDER_STRING_LEN {
return false;
}
s.alloc_len = len;
s.s.reserve_exact(len + 1);
unsafe { s.s.as_mut_vec().resize(len + 1, 0) };
unsafe { *buffer = s.s.as_mut_ptr() as *mut c_char };
true
}
pub(super) struct StringBuf {
s: String,
is_null: bool,
alloc_len: usize,
}
impl StringBuf {
pub(super) fn new() -> Self {
Self {
s: String::new(),
is_null: false,
alloc_len: 0,
}
}
pub(super) fn finish(mut self) -> Option<String> {
if self.is_null {
return None;
}
let data_len = self.alloc_len.saturating_sub(1);
unsafe { self.s.as_mut_vec().truncate(data_len) };
Some(self.s)
}
}
pub(super) unsafe extern "C" fn byte_alloc(
cookie: *mut c_void,
length: i32,
buffer: *mut *mut i8,
) -> bool {
let s = unsafe { &mut *(cookie as *mut ByteBuf) };
if length == -1 {
s.is_null = true;
return true;
}
if length < 0 {
return false;
}
let len = length as usize;
if len > MAX_BINDER_STRING_LEN {
return false;
}
s.v.resize(len, 0);
unsafe { *buffer = s.v.as_mut_ptr() as *mut i8 };
true
}
pub(super) struct ByteBuf {
v: Vec<u8>,
is_null: bool,
}
impl ByteBuf {
pub(super) fn new() -> Self {
Self {
v: Vec::new(),
is_null: false,
}
}
pub(super) fn finish(self) -> Option<Vec<u8>> {
if self.is_null {
return None;
}
Some(self.v)
}
}
#[cfg(test)]
mod tests {
use super::*;
fn alloc(length: i32) -> bool {
let mut buf = StringBuf::new();
let mut out: *mut c_char = std::ptr::null_mut();
unsafe { string_alloc(&mut buf as *mut StringBuf as *mut c_void, length, &mut out) }
}
#[test]
fn string_alloc_rejects_oversized() {
assert!(!alloc(MAX_BINDER_STRING_LEN as i32 + 1));
}
#[test]
fn string_alloc_accepts_null_marker_rejects_other_negative() {
assert!(alloc(-1));
assert!(!alloc(-2));
}
#[test]
fn string_alloc_null_marker_finishes_to_none() {
let mut buf = StringBuf::new();
let mut out: *mut c_char = std::ptr::null_mut();
assert!(unsafe { string_alloc(&mut buf as *mut StringBuf as *mut c_void, -1, &mut out) });
assert_eq!(buf.finish(), None);
}
#[test]
fn string_alloc_empty_string_len_1_finishes_to_some_empty() {
let mut buf = StringBuf::new();
let mut out: *mut c_char = std::ptr::null_mut();
assert!(unsafe { string_alloc(&mut buf as *mut StringBuf as *mut c_void, 1, &mut out) });
assert!(!out.is_null());
assert_eq!(buf.finish().as_deref(), Some(""));
}
#[test]
fn string_alloc_accepts_valid_len_and_nul_terminates() {
let mut buf = StringBuf::new();
let mut out: *mut c_char = std::ptr::null_mut();
let ok = unsafe { string_alloc(&mut buf as *mut StringBuf as *mut c_void, 5, &mut out) };
assert!(ok);
assert!(!out.is_null());
{
let vec = unsafe { buf.s.as_mut_vec() };
b"ABCD".iter().enumerate().for_each(|(i, &b)| vec[i] = b);
vec[4] = 0;
}
assert_eq!(buf.finish().as_deref(), Some("ABCD"));
}
#[test]
fn string_alloc_preserves_embedded_nul() {
let mut buf = StringBuf::new();
let mut out: *mut c_char = std::ptr::null_mut();
let ok = unsafe { string_alloc(&mut buf as *mut StringBuf as *mut c_void, 4, &mut out) };
assert!(ok);
assert!(!out.is_null());
{
let vec = unsafe { buf.s.as_mut_vec() };
b"A\0B".iter().enumerate().for_each(|(i, &b)| vec[i] = b);
vec[3] = 0;
}
assert_eq!(buf.finish().as_deref(), Some("A\0B"));
}
fn balloc(length: i32) -> bool {
let mut buf = ByteBuf::new();
let mut out: *mut i8 = std::ptr::null_mut();
unsafe { byte_alloc(&mut buf as *mut ByteBuf as *mut c_void, length, &mut out) }
}
#[test]
fn byte_alloc_rejects_oversized() {
assert!(!balloc(MAX_BINDER_STRING_LEN as i32 + 1));
}
#[test]
fn byte_alloc_accepts_null_marker_rejects_other_negative() {
assert!(balloc(-1));
assert!(!balloc(-2));
}
#[test]
fn byte_alloc_null_marker_finishes_to_none() {
let mut buf = ByteBuf::new();
let mut out: *mut i8 = std::ptr::null_mut();
assert!(unsafe { byte_alloc(&mut buf as *mut ByteBuf as *mut c_void, -1, &mut out) });
assert_eq!(buf.finish(), None);
}
#[test]
fn byte_alloc_zero_len_finishes_to_some_empty() {
let mut buf = ByteBuf::new();
let mut out: *mut i8 = std::ptr::null_mut();
assert!(unsafe { byte_alloc(&mut buf as *mut ByteBuf as *mut c_void, 0, &mut out) });
assert!(!out.is_null());
assert_eq!(buf.finish(), Some(vec![]));
}
#[test]
fn byte_alloc_round_trips_bytes_including_nul() {
let data: &[u8] = &[0xff, 0x00, 0xfe, 0x80, 0x41];
let mut buf = ByteBuf::new();
let mut out: *mut i8 = std::ptr::null_mut();
let ok = unsafe {
byte_alloc(
&mut buf as *mut ByteBuf as *mut c_void,
data.len() as i32,
&mut out,
)
};
assert!(ok);
assert!(!out.is_null());
{
let v = buf.v.as_mut_slice();
v.copy_from_slice(data);
}
assert_eq!(buf.finish(), Some(data.to_vec()));
}
}