Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions dev_tests/src/boilerplate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,13 +136,17 @@ const SKIP_FILES: &[&str] = &[
"litebox_runner_linux_on_macos_userland/tests/test-bins/hello_thread",
"litebox_runner_linux_on_macos_userland/tests/test-bins/hello_world_dyn",
"litebox_runner_linux_on_macos_userland/tests/test-bins/ld-linux-aarch64.so.1",
"litebox_runner_linux_on_windows_userland/tests/test-bins/fork_parent",
"litebox_runner_linux_on_windows_userland/tests/test-bins/fork_threads_parent",
"litebox_runner_linux_on_windows_userland/tests/test-bins/hello_exec_nolibc",
"litebox_runner_linux_on_windows_userland/tests/test-bins/hello_thread",
"litebox_runner_linux_on_windows_userland/tests/test-bins/hello_thread_static",
"litebox_runner_linux_on_windows_userland/tests/test-bins/hello_world_dyn",
"litebox_runner_linux_on_windows_userland/tests/test-bins/hello_world_static",
"litebox_runner_linux_on_windows_userland/tests/test-bins/pipe_broker",
"litebox_runner_linux_on_windows_userland/tests/test-bins/thread_static",
"litebox_runner_linux_on_windows_userland/tests/test-bins/vfork_exec_child",
"litebox_runner_linux_on_windows_userland/tests/test-bins/vfork_exec_parent",
"litebox_syscall_rewriter/tests/hello",
"litebox_syscall_rewriter/tests/hello-32",
"litebox_syscall_rewriter/tests/hello-aarch64",
Expand Down
15 changes: 8 additions & 7 deletions litebox_broker_core/src/process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -559,15 +559,15 @@ impl BrokerProcess {
/// Lets `write` store `length` bytes at `offset` in the memory image of
/// the pending child selected by `child_process_id`.
///
/// The first write creates the image with `create`. The image ends no
/// later than the broker's child image size limit, and all child images
/// together stay within the broker's total child image size limit.
/// The first write creates the image with `create`, given the broker's
/// child image size limit, which the image never grows past. All child
/// images together stay within the broker's total child image size limit.
pub fn write_child_memory<E: From<BrokerError>>(
&self,
child_process_id: ProcessId,
offset: u64,
length: u64,
create: impl FnOnce() -> Result<Box<dyn ProcessImage>>,
create: impl FnOnce(u64) -> Result<Box<dyn ProcessImage>>,
write: impl FnOnce(&mut dyn ProcessImage) -> core::result::Result<(), E>,
) -> core::result::Result<(), E> {
let limits = self.core.limits;
Expand All @@ -581,7 +581,7 @@ impl BrokerProcess {
let image = match &mut pending.image {
Some(image) => image,
None => pending.image.insert(ChildImage::new(
create()?,
create(limits.max_child_image_size)?,
Arc::clone(&self.core.reserved_child_image_size),
)),
};
Expand Down Expand Up @@ -2370,7 +2370,8 @@ mod tests {
.process_id;
let writes = Arc::new(std::sync::Mutex::new(Vec::new()));
let created = AtomicUsize::new(0);
let create = || {
let create = |capacity| {
assert_eq!(capacity, 8);
created.fetch_add(1, Ordering::Relaxed);
Ok(Box::new(TestImage(Arc::clone(&writes))) as Box<dyn ProcessImage>)
};
Expand Down Expand Up @@ -2448,7 +2449,7 @@ mod tests {
child,
offset,
length,
|| Ok(Box::new(TestImage)),
|_| Ok(Box::new(TestImage)),
|_| Ok(()),
)
};
Expand Down
11 changes: 8 additions & 3 deletions litebox_broker_host/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -898,10 +898,15 @@ pub trait ProcessLauncher: Send + Sync {
image: Option<ChildImage>,
) -> core::result::Result<(), BrokerError>;

/// Creates an empty memory image for a pending child.
/// Creates an empty memory image for a pending child, which never grows
/// past `capacity` bytes.
///
/// Platforms that cannot pass images to runners reject the request.
fn create_image(&self) -> core::result::Result<Box<dyn ProcessImage>, BrokerError> {
fn create_image(
&self,
capacity: u64,
) -> core::result::Result<Box<dyn ProcessImage>, BrokerError> {
let _ = capacity;
Err(BrokerError::UnsupportedOperation)
}
}
Expand Down Expand Up @@ -975,7 +980,7 @@ where
child_process_id,
offset,
u64::from(data.length()),
|| launcher.create_image(),
|capacity| launcher.create_image(capacity),
|image| {
let mut image_offset = offset;
for range in ranges {
Expand Down
32 changes: 19 additions & 13 deletions litebox_broker_local_userland/src/windows.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use litebox_broker_transport_windows_userland::control_ring::{
WindowsControlRingLocalCallChannel, WindowsControlRingLocalNotificationChannel,
};
use litebox_broker_transport_windows_userland::named_pipe::WindowsNamedPipeLocalSetupChannel;
use litebox_broker_transport_windows_userland::process_image::WindowsReceivedProcessImage;

const SETUP_TIMEOUT: Duration = Duration::from_secs(5);

Expand All @@ -24,6 +25,8 @@ pub struct BrokerConnection {
pub local: BrokerLocal<WindowsControlRingLocalCallChannel>,
/// Asynchronous broker notification channel.
pub notifications: BrokerNotifications<WindowsControlRingLocalNotificationChannel>,
/// Memory image this process starts from, if its parent wrote one.
pub process_image: Option<WindowsReceivedProcessImage>,
}

/// Connects to and negotiates an association with a Windows-userland broker.
Expand All @@ -37,23 +40,26 @@ pub fn connect(control_pipe: &OsStr) -> Result<(BrokerConnection, Option<Process
std::path::Path::new(control_pipe).display()
)
})?;
let (local, startup, notifications) = BrokerLocal::negotiate(setup, |mut setup| {
let shared_memory = Arc::new(setup.receive_shared_memory(SHARED_BUFFER_POOL_SIZE)?);
let control_memory = setup.receive_control_ring()?;
let control_ring = ControlRing::new(control_memory).map_err(|error| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("invalid broker control ring: {error:?}"),
)
})?;
let (calls, notifications) = setup.into_active(control_ring)?;
Ok((calls, shared_memory, notifications))
})
.context("broker negotiation failed")?;
let (local, startup, (notifications, process_image)) =
BrokerLocal::negotiate(setup, |mut setup| {
let shared_memory = Arc::new(setup.receive_shared_memory(SHARED_BUFFER_POOL_SIZE)?);
let control_memory = setup.receive_control_ring()?;
let process_image = setup.receive_process_image()?;
let control_ring = ControlRing::new(control_memory).map_err(|error| {
std::io::Error::new(
std::io::ErrorKind::InvalidData,
format!("invalid broker control ring: {error:?}"),
)
})?;
let (calls, notifications) = setup.into_active(control_ring)?;
Ok((calls, shared_memory, (notifications, process_image)))
})
.context("broker negotiation failed")?;
Ok((
BrokerConnection {
local,
notifications: BrokerNotifications::new(notifications),
process_image,
},
startup,
))
Expand Down
45 changes: 23 additions & 22 deletions litebox_broker_transport_windows_userland/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
[package]
name = "litebox_broker_transport_windows_userland"
version = "0.1.0"
edition = "2024"

[dependencies]
litebox_broker_protocol = { path = "../litebox_broker_protocol", version = "0.1.0" }
litebox_broker_transport = { path = "../litebox_broker_transport", version = "0.1.0" }

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.60.2", features = [
"Win32_Foundation",
"Win32_Security",
"Win32_Storage_FileSystem",
"Win32_System_Diagnostics_Debug",
"Win32_System_IO",
"Win32_System_Memory",
"Win32_System_Pipes",
"Win32_System_Threading",
] }

[lints]
[package]
name = "litebox_broker_transport_windows_userland"
version = "0.1.0"
edition = "2024"

[dependencies]
litebox_broker_protocol = { path = "../litebox_broker_protocol", version = "0.1.0" }
litebox_broker_transport = { path = "../litebox_broker_transport", version = "0.1.0" }

[target.'cfg(windows)'.dependencies]
windows-sys = { version = "0.60.2", features = [
"Win32_Foundation",
"Win32_Security",
"Win32_Storage_FileSystem",
"Win32_System_Diagnostics_Debug",
"Win32_System_IO",
"Win32_System_Memory",
"Win32_System_Pipes",
"Win32_System_SystemInformation",
"Win32_System_Threading",
] }

[lints]
workspace = true
37 changes: 37 additions & 0 deletions litebox_broker_transport_windows_userland/src/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use windows_sys::Win32::System::Threading::GetCurrentProcess;

use crate::control_ring::PipeLiveness;
use crate::named_pipe::{TRANSFER_FRAME_TAG, WindowsNamedPipeStream};
use crate::process_image::WindowsProcessImage;
use crate::setup::{
copy_io_error, invalid_data, read_frame, read_pipe_until_cancelled, ring_error, wire_error,
write_frame,
Expand Down Expand Up @@ -120,6 +121,42 @@ impl WindowsNamedPipeHostSetupChannel {
self.send_shared_memory(memory, unsafe { GetCurrentProcess() })
}

/// Duplicates a handle that can only read `image`, if any, into the runner and sends it.
///
/// The broker must not write `image` afterward. An empty image is sent as no image. The peer
/// must call
/// [`WindowsNamedPipeLocalSetupChannel::receive_process_image`](crate::named_pipe::WindowsNamedPipeLocalSetupChannel::receive_process_image)
/// at the same setup step, even when there is no image.
pub fn send_process_image(
&mut self,
image: Option<&WindowsProcessImage>,
runner_process: HANDLE,
) -> IoResult<()> {
let transfer = match image.filter(|image| !image.is_empty()) {
Some(image) => image.duplicate_to_process(runner_process)?,
None => TransferredSharedMemory {
length: 0,
handles: Vec::new(),
},
};
write_frame(
file_handle(&self.stream),
&encode_transfer(&transfer)?,
self.setup_deadline,
)
}

/// Duplicates a handle that can only read `image`, if any, into this process and sends it,
/// as [`Self::send_process_image`] does.
pub fn send_process_image_to_current_process(
&mut self,
image: Option<&WindowsProcessImage>,
) -> IoResult<()> {
// SAFETY: GetCurrentProcess returns a pseudo-handle that remains valid
// for the lifetime of this process and must not be closed.
self.send_process_image(image, unsafe { GetCurrentProcess() })
}

/// Activates host request, response, and notification control-ring endpoints.
pub fn into_active(
self,
Expand Down
1 change: 1 addition & 0 deletions litebox_broker_transport_windows_userland/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ mod host;
mod local;
pub mod named_pipe;
mod pending_calls;
pub mod process_image;
mod setup;
pub mod shared_memory;
17 changes: 17 additions & 0 deletions litebox_broker_transport_windows_userland/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use windows_sys::Win32::Storage::FileSystem::FILE_FLAG_OVERLAPPED;
use crate::control_ring::PipeLiveness;
use crate::named_pipe::{TRANSFER_FRAME_TAG, WindowsNamedPipeStream};
use crate::pending_calls::{PendingCalls, pending_calls_error};
use crate::process_image::WindowsReceivedProcessImage;
use crate::setup::{
copy_io_error, invalid_data, read_frame, read_pipe_until_cancelled, ring_error, wire_error,
write_frame,
Expand Down Expand Up @@ -137,6 +138,22 @@ impl WindowsNamedPipeLocalSetupChannel {
unsafe { WindowsSharedMemory::control_ring_from_transferred(transfer) }
}

/// Receives and maps the optional process image the broker sent with
/// [`WindowsNamedPipeHostSetupChannel::send_process_image`](crate::named_pipe::WindowsNamedPipeHostSetupChannel::send_process_image).
pub fn receive_process_image(&mut self) -> IoResult<Option<WindowsReceivedProcessImage>> {
let frame =
read_frame(file_handle(&self.stream), self.setup_deadline)?.ok_or_else(|| {
Error::new(
ErrorKind::UnexpectedEof,
"broker closed before transferring the process image",
)
})?;
let transfer = decode_transfer(&frame)?;
// SAFETY: The authenticated broker duplicated these handles into this process and encoded
// their target-process values in the setup frame.
unsafe { WindowsReceivedProcessImage::from_transferred(transfer) }
}

/// Activates calls and notifications over the transferred shared control ring.
pub fn into_active(
self,
Expand Down
Loading
Loading