lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

commit a83f1388a9f6b5a8538d87f8fcbc3dbd1bacf9d3
parent 5667824c6f254a3e7cf8ea0680a38ada9f32fc9e
Author: triesap <tyson@radroots.org>
Date:   Sun,  9 Aug 2026 18:56:38 +0000

mobile_ffi: duplicate host media descriptors

- avoid /dev/fd path reliance on sandboxed Unix hosts
- preserve caller descriptor ownership through immediate duplication
- reject descriptor values outside the platform range
- verify formatting, checks, clippy, and focused tests

Diffstat:
Mcrates/mobile_ffi/src/dto.rs | 31++++++++++++++++++++++++++++++-
1 file changed, 30 insertions(+), 1 deletion(-)

diff --git a/crates/mobile_ffi/src/dto.rs b/crates/mobile_ffi/src/dto.rs @@ -2,6 +2,8 @@ #[cfg(unix)] use std::os::unix::fs::FileExt; +#[cfg(unix)] +use std::os::{fd::BorrowedFd, unix::io::RawFd}; use radroots_blossom::{BlobDescriptor, MediaType, Sha256}; use radroots_event::{ @@ -1115,8 +1117,15 @@ fn read_media_file_descriptor( expected_size: u64, byte_size: usize, ) -> Result<Vec<u8>, RadrootsAppError> { - let file = std::fs::File::open(format!("/dev/fd/{file_descriptor}")) + let raw_file_descriptor = RawFd::try_from(file_descriptor) + .map_err(|_| RadrootsAppError::invalid_argument("media_handle_unavailable"))?; + // SAFETY: the host owns this descriptor for the duration of the synchronous + // FFI call. Duplicating it immediately gives Rust independent ownership + // without closing or otherwise consuming the host descriptor. + let borrowed = unsafe { BorrowedFd::borrow_raw(raw_file_descriptor) }; + let owned = rustix::io::dup(borrowed) .map_err(|_| RadrootsAppError::invalid_argument("media_handle_unavailable"))?; + let file = std::fs::File::from(owned); let metadata = file .metadata() .map_err(|_| RadrootsAppError::invalid_argument("media_handle_unavailable"))?; @@ -2182,6 +2191,26 @@ mod tests { media[0].url(), format!("http://127.0.0.1:3000/{digest}.png") ); + assert_eq!( + file.as_file().metadata().expect("caller-owned media").len(), + bytes.len() as u64 + ); + } + + #[test] + fn prepared_media_rejects_file_descriptors_outside_the_platform_range() { + let bytes = png(2, 2); + let blossom = blossom_slot(); + let input = photo_input(u64::MAX, &bytes, Sha256::digest(&bytes).to_hex()); + + assert_eq!( + input + .command_and_media(1_800_000_000, Some(&blossom)) + .expect_err("out-of-range descriptor") + .report() + .code, + "media_handle_unavailable" + ); } #[test]