cloudfox-coreshift-core 2.18.2

Low-level Linux and Android systems primitives for CoreShift (CloudFox)
Documentation
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/

//! Host-runnable byte model of the NDK parcel wire format used by the handoff
//! `sendBinder` bundle.
//!
//! Compiled on **all** targets so the byte math is exercised by `cargo test`
//! on every host commit, not only on Android where `AParcel` exists. The
//! length the write path emits is *derived* here by running the same write
//! sequence against a byte counter, so the constant can never drift from what
//! is actually written (finding 28).
//!
//! Wire facts (verified against AOSP android-14.0.0_r1):
//! - `AParcel_writeString` (`system/libbinder_ndk/parcel.cpp:318`) writes
//!   `writeInt32(len16)` (UTF-16 code units) then
//!   `writeInplace((len16 + 1) * sizeof(char16_t))` — chars plus the NUL
//!   terminator, padded to 4 by `Parcel::writeInplace` (`pad_size`). So
//!   `String16 "binder"` (6 units) = 4 + pad(14) = 4 + 16 = 20, and
//!   `String16 "exec"` (4 units) = 4 + pad(10) = 4 + 12 = 16. A null string
//!   is a single `writeInt32(-1)`.
//! - `writeStrongBinder` = `flattenBinder` = 24-byte `flat_binder_object`
//!   (`writeObject`) + 4-byte stability int32 (`finishFlattenBinder`) = 28.
//! - `BaseBundle.writeToParcelInner` (`writeInt(length)` + `BUNDLE_MAGIC` +
//!   body): `startPos` is captured after the magic, so `length = endPos -
//!   startPos` counts the map body only (N + key/value entries) — not the
//!   length int itself nor the magic.

use crate::CoreError;
use std::os::raw::c_void;

/// `BaseBundle.BUNDLE_MAGIC` — the int `Parcel.writeBundle` writes before the
/// array-map payload. The handoff walk validates it while skipping a bundle.
pub(crate) const BUNDLE_MAGIC: i32 = 0x4C444E42;
/// `Parcel.writeValue` tag for an `IBinder` (`VAL_IBINDER`). 15, not 22.
pub(crate) const VAL_IBINDER: i32 = 15;
/// Bundle key under which the app reads the handed service binder.
pub(crate) const BUNDLE_KEY: &str = "binder";
/// Bundle key under which the app reads the handed exec binder (A1 delivery:
/// `{"binder": watch, "exec": exec}`). Optional — its absence must never
/// invalidate `"binder"`, so an old manager that only knows `"binder"` still
/// receives the watch service.
pub(crate) const EXEC_KEY: &str = "exec";

/// The parcel-write surface the bundle body is written through. Implemented
/// by the real `ParcelWriter` on Android and by the host `ByteCounter` in
/// tests, so the exact same write sequence runs against both.
pub(crate) trait BundleSink {
    fn write_i32(&self, v: i32) -> Result<(), CoreError>;
    fn write_string(&self, s: Option<&str>) -> Result<(), CoreError>;
    fn write_strong_binder(&self, b: *mut c_void) -> Result<(), CoreError>;
}

/// Write the `extras` map body of the `sendBinder` bundle (the bytes
/// `BaseBundle.writeToParcelInner` counts after the magic): `N` entries of
/// `writeString(key)` + `writeValue(binder)`.
///
/// This is the single source of truth for the bundle's wire shape — both the
/// on-device transaction and the host byte test run it, so the emitted length
/// (from [`bundle_length`]) and the emitted bytes cannot disagree.
pub(crate) fn write_bundle_body(
    w: &impl BundleSink,
    has_exec: bool,
    service_binder: *mut c_void,
    exec_binder: *mut c_void,
) -> Result<(), CoreError> {
    w.write_i32(if has_exec { 2 } else { 1 })?;
    w.write_string(Some(BUNDLE_KEY))?;
    w.write_i32(VAL_IBINDER)?;
    w.write_strong_binder(service_binder)?;
    if has_exec {
        w.write_string(Some(EXEC_KEY))?;
        w.write_i32(VAL_IBINDER)?;
        w.write_strong_binder(exec_binder)?;
    }
    Ok(())
}

/// Byte length of the `sendBinder` extras bundle body — derived by running
/// [`write_bundle_body`] through the AOSP-verified byte counter, so it can
/// never disagree with what the write path actually emits. `56` for the
/// single-entry `{"binder": IBinder}` bundle, `104` for the two-entry
/// `{"binder": …, "exec": …}` bundle.
pub(crate) fn bundle_length(has_exec: bool) -> i32 {
    let c = ByteCounter::default();
    // ByteCounter writes never fail; the pointers are ignored.
    write_bundle_body(&c, has_exec, std::ptr::null_mut(), std::ptr::null_mut()).unwrap();
    c.bytes.get() as i32
}

/// Byte-accounting `BundleSink` that reproduces the AOSP parcel wire format
/// exactly (see the module docs for the derivation).
#[derive(Default)]
struct ByteCounter {
    bytes: std::cell::Cell<usize>,
}

fn pad4(n: usize) -> usize {
    (n + 3) & !3
}

/// `AParcel_writeString` byte size for an ASCII key: 4-byte UTF-16 length int
/// + `(len16 + 1) * 2` bytes (chars + NUL) padded to 4.
///
/// The bundle keys are ASCII, so UTF-8 byte length == UTF-16 code-unit count.
fn string16_size(s: &str) -> usize {
    4 + pad4((s.len() + 1) * 2)
}

impl BundleSink for ByteCounter {
    fn write_i32(&self, _v: i32) -> Result<(), CoreError> {
        self.bytes.set(self.bytes.get() + 4);
        Ok(())
    }
    fn write_string(&self, s: Option<&str>) -> Result<(), CoreError> {
        let n = match s {
            None => 4, // writeInt32(-1)
            Some(s) => string16_size(s),
        };
        self.bytes.set(self.bytes.get() + n);
        Ok(())
    }
    fn write_strong_binder(&self, _b: *mut c_void) -> Result<(), CoreError> {
        // 24-byte flat_binder_object + 4-byte stability int32.
        self.bytes.set(self.bytes.get() + 28);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn string16_sizes_match_aosp_write_path() {
        // "binder": 6 UTF-16 units → 4 + pad((6+1)*2)=pad(14)=16 → 20.
        assert_eq!(string16_size("binder"), 20);
        // "exec": 4 UTF-16 units → 4 + pad((4+1)*2)=pad(10)=12 → 16.
        // (The pre-test constant undercounted this as 12 — it dropped the NUL
        // terminator and the padding. Finding: BUNDLE_LENGTH_TWO was 100, and
        // the write path requires 104.)
        assert_eq!(string16_size("exec"), 16);
    }

    #[test]
    fn null_string_is_four_bytes() {
        let c = ByteCounter::default();
        c.write_string(None).unwrap();
        assert_eq!(c.bytes.get(), 4);
    }

    #[test]
    fn single_bundle_body_is_56() {
        assert_eq!(bundle_length(false), 56);
    }

    #[test]
    fn two_bundle_body_is_104() {
        // N=2 (4) + "binder" entry (20 + 4 + 28 = 52) + "exec" entry
        // (16 + 4 + 28 = 48) = 104.
        assert_eq!(bundle_length(true), 104);
    }

    #[test]
    fn write_path_emits_exactly_the_advertised_length() {
        // Construct the full bundle as the transaction writes it (length int,
        // magic, body) and assert the length field equals the body bytes.
        let c = ByteCounter::default();
        c.write_i32(bundle_length(true)).unwrap();
        c.write_i32(BUNDLE_MAGIC).unwrap();
        write_bundle_body(&c, true, std::ptr::null_mut(), std::ptr::null_mut()).unwrap();
        let total = c.bytes.get();
        // 4 (length int) + 4 (magic) + 104 (body).
        assert_eq!(total, 4 + 4 + 104);
        // And the body alone is exactly what the length field advertised.
        assert_eq!(c.bytes.get() - 8, bundle_length(true) as usize);
    }

    #[test]
    fn single_bundle_write_path_is_byte_identical_shape() {
        let c = ByteCounter::default();
        c.write_i32(bundle_length(false)).unwrap();
        c.write_i32(BUNDLE_MAGIC).unwrap();
        write_bundle_body(&c, false, std::ptr::null_mut(), std::ptr::null_mut()).unwrap();
        assert_eq!(c.bytes.get(), 4 + 4 + 56);
    }
}