Skip to main content

native_ipc_platform/
windows.rs

1//! Windows unnamed-section mappings, pipe identity, and Job containment.
2
3use std::ffi::{OsStr, OsString};
4use std::fmt;
5use std::mem::{size_of, zeroed};
6use std::os::windows::ffi::OsStrExt;
7use std::path::Path;
8use std::ptr::NonNull;
9use std::time::{Duration, Instant};
10
11use native_ipc_core::layout::{RegionSetLayout, ValidatedRegionLayout, ValidationExpectations};
12use native_ipc_core::mapping::{
13    BindingError, ReadOnlyMapping, ReaderRegion, SoleWriterMapping, WriterRegion,
14};
15use windows_sys::Win32::Foundation::{
16    CloseHandle, DuplicateHandle, ERROR_NO_DATA, ERROR_PIPE_CONNECTED, ERROR_PIPE_LISTENING,
17    GENERIC_READ, GENERIC_WRITE, GetLastError, HANDLE, INVALID_HANDLE_VALUE, WAIT_OBJECT_0,
18    WAIT_TIMEOUT,
19};
20use windows_sys::Win32::Security::Cryptography::{
21    BCRYPT_USE_SYSTEM_PREFERRED_RNG, BCryptGenRandom,
22};
23use windows_sys::Win32::Storage::FileSystem::{
24    CreateFileW, FILE_FLAG_FIRST_PIPE_INSTANCE, OPEN_EXISTING, PIPE_ACCESS_DUPLEX, ReadFile,
25    WriteFile,
26};
27use windows_sys::Win32::System::JobObjects::{
28    AssignProcessToJobObject, CreateJobObjectW, JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE,
29    JOBOBJECT_EXTENDED_LIMIT_INFORMATION, JobObjectExtendedLimitInformation,
30    SetInformationJobObject,
31};
32use windows_sys::Win32::System::Memory::{
33    CreateFileMappingW, FILE_MAP_READ, FILE_MAP_WRITE, MEMORY_MAPPED_VIEW_ADDRESS, MapViewOfFile,
34    PAGE_READWRITE, SEC_COMMIT, UnmapViewOfFile,
35};
36use windows_sys::Win32::System::Pipes::{
37    ConnectNamedPipe, CreateNamedPipeW, GetNamedPipeClientProcessId, GetNamedPipeServerProcessId,
38    PIPE_NOWAIT, PIPE_READMODE_MESSAGE, PIPE_REJECT_REMOTE_CLIENTS, PIPE_TYPE_MESSAGE,
39    SetNamedPipeHandleState,
40};
41use windows_sys::Win32::System::SystemInformation::{GetSystemInfo, SYSTEM_INFO};
42use windows_sys::Win32::System::Threading::{
43    CREATE_SUSPENDED, CREATE_UNICODE_ENVIRONMENT, CreateProcessW, GetCurrentProcess,
44    GetCurrentProcessId, GetExitCodeProcess, PROCESS_INFORMATION, ResumeThread, STARTUPINFOW,
45    TerminateProcess, WaitForSingleObject,
46};
47
48use crate::BackendStatus;
49use crate::protocol::{CONTROL_FRAME_LEN, ManifestEntry, PeerAccess, TransferManifest};
50
51/// Windows section, bootstrap, lifecycle, or binding failure.
52#[derive(Debug)]
53pub enum WindowsError {
54    /// Win32 API failed with a captured `GetLastError` value.
55    Os {
56        /// Bounded Win32 operation name.
57        operation: &'static str,
58        /// Captured `GetLastError` value.
59        code: u32,
60    },
61    /// Mapping size is zero or cannot be page-rounded.
62    InvalidSize(usize),
63    /// Named-pipe peer PID differs from the held expected process.
64    WrongPeer,
65    /// Received or duplicated handle is invalid for this process.
66    InvalidHandle,
67    /// Quiescent layout validation failed.
68    Layout(native_ipc_core::layout::LayoutError),
69    /// Audited core binding failed.
70    Binding(BindingError),
71    /// Bootstrap environment or authenticated handshake was malformed.
72    InvalidBootstrap,
73    /// A sole-writer capability was already duplicated from this preparation.
74    CapabilityAlreadyTransferred,
75    /// A bounded bootstrap or lifecycle operation reached its deadline.
76    TimedOut(&'static str),
77    /// The exact helper exited unsuccessfully.
78    ChildExit(u32),
79}
80
81impl fmt::Display for WindowsError {
82    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
83        write!(formatter, "Windows transport failed: {self:?}")
84    }
85}
86impl std::error::Error for WindowsError {}
87impl From<native_ipc_core::layout::LayoutError> for WindowsError {
88    fn from(value: native_ipc_core::layout::LayoutError) -> Self {
89        Self::Layout(value)
90    }
91}
92impl From<BindingError> for WindowsError {
93    fn from(value: BindingError) -> Self {
94        Self::Binding(value)
95    }
96}
97
98/// Generates a nonzero 256-bit bootstrap nonce from the system RNG.
99pub fn session_nonce() -> Result<[u8; 32], WindowsError> {
100    let mut nonce = [0_u8; 32];
101    // SAFETY: output buffer is valid; null algorithm selects system-preferred RNG.
102    let status = unsafe {
103        BCryptGenRandom(
104            std::ptr::null_mut(),
105            nonce.as_mut_ptr(),
106            nonce.len() as u32,
107            BCRYPT_USE_SYSTEM_PREFERRED_RNG,
108        )
109    };
110    if status < 0 || nonce == [0; 32] {
111        Err(last_os("BCryptGenRandom"))
112    } else {
113        Ok(nonce)
114    }
115}
116
117/// Verifies the connected client of a private named-pipe server instance.
118///
119/// # Safety
120///
121/// `pipe` must be a live connected named-pipe server handle owned by the caller.
122pub unsafe fn authenticate_pipe_client(
123    pipe: HANDLE,
124    expected_pid: u32,
125) -> Result<(), WindowsError> {
126    let mut actual = 0;
127    // SAFETY: caller supplies a live pipe and output pointer is valid.
128    if unsafe { GetNamedPipeClientProcessId(pipe, &mut actual) } == 0 {
129        return Err(last_os("GetNamedPipeClientProcessId"));
130    }
131    if actual == expected_pid {
132        Ok(())
133    } else {
134        Err(WindowsError::WrongPeer)
135    }
136}
137
138/// Verifies the connected server of a private named-pipe client instance.
139///
140/// # Safety
141///
142/// `pipe` must be a live connected named-pipe client handle owned by the caller.
143pub unsafe fn authenticate_pipe_server(
144    pipe: HANDLE,
145    expected_pid: u32,
146) -> Result<(), WindowsError> {
147    let mut actual = 0;
148    // SAFETY: caller supplies a live pipe and output pointer is valid.
149    if unsafe { GetNamedPipeServerProcessId(pipe, &mut actual) } == 0 {
150        return Err(last_os("GetNamedPipeServerProcessId"));
151    }
152    if actual == expected_pid {
153        Ok(())
154    } else {
155        Err(WindowsError::WrongPeer)
156    }
157}
158
159/// Quiescent unnamed paging-file section and exclusive initialization view.
160pub struct QuiescentRegion {
161    section: OwnedHandle,
162    view: View,
163    logical_len: usize,
164}
165
166impl QuiescentRegion {
167    /// Allocates a page-rounded unnamed, non-executable section.
168    pub fn new(logical_len: usize) -> Result<Self, WindowsError> {
169        let len = page_align(logical_len)?;
170        let size = len as u64;
171        // SAFETY: paging-file sentinel, null security/name, and checked size are valid.
172        let section = unsafe {
173            CreateFileMappingW(
174                INVALID_HANDLE_VALUE,
175                std::ptr::null(),
176                PAGE_READWRITE | SEC_COMMIT,
177                (size >> 32) as u32,
178                size as u32,
179                std::ptr::null(),
180            )
181        };
182        let section = OwnedHandle::new(section)?;
183        let view = View::map(section.0, len, FILE_MAP_WRITE)?;
184        // SAFETY: newly created unnamed section view is exclusive and writable.
185        unsafe { std::slice::from_raw_parts_mut(view.base.as_ptr(), len) }.fill(0);
186        Ok(Self {
187            section,
188            view,
189            logical_len,
190        })
191    }
192    /// Exact page-rounded capability size.
193    pub const fn len(&self) -> usize {
194        self.view.len
195    }
196    /// Returns whether the capability is empty (always false for valid values).
197    pub const fn is_empty(&self) -> bool {
198        false
199    }
200    /// Requested logical layout length.
201    pub const fn logical_len(&self) -> usize {
202        self.logical_len
203    }
204    /// Full quiescent initialization range.
205    pub fn as_bytes(&self) -> &[u8] {
206        // SAFETY: no duplicated handle or second view exists in this typestate.
207        unsafe { std::slice::from_raw_parts(self.view.base.as_ptr(), self.view.len) }
208    }
209    /// Mutable full quiescent initialization range.
210    pub fn as_bytes_mut(&mut self) -> &mut [u8] {
211        // SAFETY: `&mut self` and typestate provide exclusivity.
212        unsafe { std::slice::from_raw_parts_mut(self.view.base.as_ptr(), self.view.len) }
213    }
214
215    /// Validates a future local-writer region before attenuated duplication.
216    pub fn prepare_local_writer(
217        self,
218        expected: ValidationExpectations,
219        topology: RegionSetLayout,
220    ) -> Result<PreparedLocalWriter, WindowsError> {
221        // SAFETY: section is quiescent and complete capability range is borrowed.
222        let layout =
223            unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
224        let len = self.view.len;
225        let entry = ManifestEntry::validated(expected, len, PeerAccess::ReadOnly)
226            .ok_or(WindowsError::InvalidBootstrap)?;
227        Ok(PreparedLocalWriter {
228            section: self.section,
229            runtime: WriterRegion::new(WindowsWriterMapping { view: self.view }, layout, topology)?,
230            entry,
231            len,
232            reader_duplicated: false,
233        })
234    }
235
236    /// Validates a future remote-writer region before remapping local read-only.
237    pub fn prepare_remote_writer(
238        self,
239        expected: ValidationExpectations,
240        topology: RegionSetLayout,
241    ) -> Result<PreparedRemoteWriter, WindowsError> {
242        // SAFETY: section is quiescent and complete capability range is borrowed.
243        let layout =
244            unsafe { ValidatedRegionLayout::validate(self.as_bytes(), expected, &topology) }?;
245        let len = self.view.len;
246        drop(self.view);
247        let view = View::map(self.section.0, len, FILE_MAP_READ)?;
248        let entry = ManifestEntry::validated(expected, len, PeerAccess::SoleWriter)
249            .ok_or(WindowsError::InvalidBootstrap)?;
250        Ok(PreparedRemoteWriter {
251            section: self.section,
252            runtime: ReaderRegion::new(WindowsReaderMapping { view }, layout, topology)?,
253            entry,
254            len,
255            writer_duplicated: false,
256        })
257    }
258}
259
260/// Target-process handle value produced by exact-rights duplication.
261#[derive(Clone, Copy, Debug, Eq, PartialEq)]
262pub struct RemoteHandle(pub usize);
263
264/// Local unique writer awaiting a read-only peer handle and READY barrier.
265///
266/// ```compile_fail
267/// use native_ipc_platform::windows::PreparedLocalWriter;
268/// fn publish_early(mut pending: PreparedLocalWriter) {
269///     pending.publish(0, 1, None, b"too early").unwrap();
270/// }
271/// ```
272pub struct PreparedLocalWriter {
273    section: OwnedHandle,
274    runtime: WriterRegion<WindowsWriterMapping>,
275    entry: ManifestEntry,
276    len: usize,
277    reader_duplicated: bool,
278}
279impl PreparedLocalWriter {
280    /// Duplicates exactly `FILE_MAP_READ` into a held authenticated target process.
281    ///
282    /// # Safety
283    ///
284    /// `target_process` must be the held live process authenticated by the pipe.
285    unsafe fn duplicate_reader_to(
286        &mut self,
287        target_process: HANDLE,
288    ) -> Result<RemoteHandle, WindowsError> {
289        if self.reader_duplicated {
290            return Err(WindowsError::CapabilityAlreadyTransferred);
291        }
292        let handle = duplicate_to(self.section.0, target_process, FILE_MAP_READ)?;
293        self.reader_duplicated = true;
294        Ok(handle)
295    }
296}
297
298/// Local read-only view awaiting the sole remote-writer handle and READY barrier.
299pub struct PreparedRemoteWriter {
300    section: OwnedHandle,
301    runtime: ReaderRegion<WindowsReaderMapping>,
302    entry: ManifestEntry,
303    len: usize,
304    writer_duplicated: bool,
305}
306impl PreparedRemoteWriter {
307    /// Duplicates exactly one `FILE_MAP_WRITE` handle into a held authenticated target.
308    ///
309    /// # Safety
310    ///
311    /// `target_process` must be the held live process authenticated by the pipe.
312    unsafe fn duplicate_writer_to(
313        &mut self,
314        target_process: HANDLE,
315    ) -> Result<RemoteHandle, WindowsError> {
316        if self.writer_duplicated {
317            return Err(WindowsError::CapabilityAlreadyTransferred);
318        }
319        let handle = duplicate_to(self.section.0, target_process, FILE_MAP_WRITE)?;
320        self.writer_duplicated = true;
321        Ok(handle)
322    }
323}
324
325/// Validated imported reader withheld until the creator acknowledges READY.
326///
327/// ```compile_fail
328/// use native_ipc_platform::windows::PendingImportedReader;
329/// fn read_early(pending: PendingImportedReader) {
330///     let _ = pending.copy_payload(0, 1);
331/// }
332/// ```
333pub struct PendingImportedReader {
334    runtime: ReaderRegion<WindowsReaderMapping>,
335    entry: ManifestEntry,
336}
337
338/// Validated imported writer withheld until the creator acknowledges READY.
339pub struct PendingImportedWriter {
340    runtime: WriterRegion<WindowsWriterMapping>,
341    entry: ManifestEntry,
342}
343
344/// Imports a duplicated section handle using an exact desired mapping access.
345///
346/// # Safety
347///
348/// `handle` must have arrived over the authenticated bootstrap, be owned by this
349/// process, have exactly the manifest rights, and not have been previously closed.
350pub unsafe fn import_reader(
351    handle: usize,
352    len: usize,
353    expected: ValidationExpectations,
354    topology: RegionSetLayout,
355) -> Result<PendingImportedReader, WindowsError> {
356    let section = OwnedHandle::new(handle as HANDLE)?;
357    let view = View::map(section.0, len, FILE_MAP_READ)?;
358    // SAFETY: READY protocol keeps view quiescent; access is read-only.
359    let bytes = unsafe { std::slice::from_raw_parts(view.base.as_ptr(), len) };
360    let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
361    let entry = ManifestEntry::validated(expected, len, PeerAccess::ReadOnly)
362        .ok_or(WindowsError::InvalidBootstrap)?;
363    drop(section);
364    Ok(PendingImportedReader {
365        runtime: ReaderRegion::new(WindowsReaderMapping { view }, layout, topology)?,
366        entry,
367    })
368}
369
370/// Imports the sole duplicated writer handle.
371///
372/// # Safety
373///
374/// Same authenticated ownership requirements as [`import_reader`], plus the
375/// manifest/creator must guarantee no other writable handle or view exists.
376pub unsafe fn import_writer(
377    handle: usize,
378    len: usize,
379    expected: ValidationExpectations,
380    topology: RegionSetLayout,
381) -> Result<PendingImportedWriter, WindowsError> {
382    let section = OwnedHandle::new(handle as HANDLE)?;
383    let view = View::map(section.0, len, FILE_MAP_WRITE)?;
384    // SAFETY: READY protocol keeps the sole writer quiescent during validation.
385    let bytes = unsafe { std::slice::from_raw_parts(view.base.as_ptr(), len) };
386    let layout = unsafe { ValidatedRegionLayout::validate(bytes, expected, &topology) }?;
387    let entry = ManifestEntry::validated(expected, len, PeerAccess::SoleWriter)
388        .ok_or(WindowsError::InvalidBootstrap)?;
389    drop(section);
390    Ok(PendingImportedWriter {
391        runtime: WriterRegion::new(WindowsWriterMapping { view }, layout, topology)?,
392        entry,
393    })
394}
395
396/// Kill-on-last-handle Job Object used to contain an exact spawned helper tree.
397pub struct ChildJob(OwnedHandle);
398impl ChildJob {
399    /// Creates an unnamed non-inheritable kill-on-close job.
400    pub fn new() -> Result<Self, WindowsError> {
401        // SAFETY: null security/name create an unnamed non-inheritable job.
402        let handle = unsafe { CreateJobObjectW(std::ptr::null(), std::ptr::null()) };
403        let handle = OwnedHandle::new(handle)?;
404        let mut information: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = unsafe { zeroed() };
405        information.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
406        // SAFETY: buffer type/size match the requested information class.
407        if unsafe {
408            SetInformationJobObject(
409                handle.0,
410                JobObjectExtendedLimitInformation,
411                (&information as *const JOBOBJECT_EXTENDED_LIMIT_INFORMATION).cast(),
412                size_of::<JOBOBJECT_EXTENDED_LIMIT_INFORMATION>() as u32,
413            )
414        } == 0
415        {
416            return Err(last_os("SetInformationJobObject"));
417        }
418        Ok(Self(handle))
419    }
420    /// Assigns a still-suspended exact child before any untrusted code runs.
421    ///
422    /// # Safety
423    ///
424    /// `process` must be the live suspended child handle returned by CreateProcess.
425    pub unsafe fn assign_suspended(&self, process: HANDLE) -> Result<(), WindowsError> {
426        // SAFETY: caller proves process handle/lifecycle; job handle is live.
427        if unsafe { AssignProcessToJobObject(self.0.0, process) } == 0 {
428            Err(last_os("AssignProcessToJobObject"))
429        } else {
430            Ok(())
431        }
432    }
433}
434
435const PIPE_ENV: &str = "NATIVE_IPC_WINDOWS_PIPE";
436const NONCE_ENV: &str = "NATIVE_IPC_WINDOWS_NONCE";
437const PARENT_ENV: &str = "NATIVE_IPC_PARENT_PID";
438const BOOTSTRAP_MAGIC: [u8; 8] = *b"NIPCWIN1";
439const AUTH_MAGIC: [u8; 8] = *b"NIPCAUT1";
440const READY_MAGIC: [u8; 8] = *b"NIPCRDY1";
441const COMMIT_MAGIC: [u8; 8] = *b"NIPCCMT1";
442const CAPABILITY_MAGIC: [u8; 8] = *b"NIPCCAP1";
443#[cfg(not(test))]
444const WAIT_MS: u32 = 10_000;
445#[cfg(test)]
446const WAIT_MS: u32 = 1_000;
447
448#[repr(C)]
449#[derive(Clone, Copy)]
450struct BootstrapFrame {
451    magic: [u8; 8],
452    nonce: [u8; 32],
453    parent_pid: u32,
454    child_pid: u32,
455}
456
457const CAPABILITY_FRAME_LEN: usize = 40 + CONTROL_FRAME_LEN;
458
459fn encode_capability_frame(
460    reader: RemoteHandle,
461    reader_len: usize,
462    writer: RemoteHandle,
463    writer_len: usize,
464    transcript: &[u8; CONTROL_FRAME_LEN],
465) -> Result<[u8; CAPABILITY_FRAME_LEN], WindowsError> {
466    let mut frame = [0_u8; CAPABILITY_FRAME_LEN];
467    frame[..8].copy_from_slice(&CAPABILITY_MAGIC);
468    frame[8..16].copy_from_slice(
469        &u64::try_from(reader.0)
470            .map_err(|_| WindowsError::InvalidBootstrap)?
471            .to_le_bytes(),
472    );
473    frame[16..24].copy_from_slice(
474        &u64::try_from(writer.0)
475            .map_err(|_| WindowsError::InvalidBootstrap)?
476            .to_le_bytes(),
477    );
478    frame[24..32].copy_from_slice(
479        &u64::try_from(reader_len)
480            .map_err(|_| WindowsError::InvalidBootstrap)?
481            .to_le_bytes(),
482    );
483    frame[32..40].copy_from_slice(
484        &u64::try_from(writer_len)
485            .map_err(|_| WindowsError::InvalidBootstrap)?
486            .to_le_bytes(),
487    );
488    frame[40..].copy_from_slice(transcript);
489    Ok(frame)
490}
491
492/// Parent-owned exact helper, private pipe, process handle, and kill-on-close job.
493pub struct ChildSession {
494    pipe: OwnedHandle,
495    process: OwnedHandle,
496    _job: ChildJob,
497    pid: u32,
498    nonce: [u8; 32],
499    reaped: bool,
500    next_transfer_id: u64,
501    pending_manifest: Option<TransferManifest>,
502}
503
504impl ChildSession {
505    /// Creates a one-instance local pipe and launches the helper suspended.
506    pub fn spawn(path: &Path, arguments: &[OsString]) -> Result<Self, WindowsError> {
507        let nonce = session_nonce()?;
508        let name = format!(r"\\.\pipe\native-ipc-{}", hex(&nonce));
509        let pipe_name = wide_null(OsStr::new(&name));
510        // SAFETY: name is terminated; null security creates a non-inheritable handle.
511        let pipe = unsafe {
512            CreateNamedPipeW(
513                pipe_name.as_ptr(),
514                PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE,
515                PIPE_TYPE_MESSAGE
516                    | PIPE_READMODE_MESSAGE
517                    | PIPE_NOWAIT
518                    | PIPE_REJECT_REMOTE_CLIENTS,
519                1,
520                4096,
521                4096,
522                WAIT_MS,
523                std::ptr::null(),
524            )
525        };
526        let pipe = OwnedHandle::new(pipe)?;
527        let job = ChildJob::new()?;
528
529        let application = wide_null(path.as_os_str());
530        let mut command = command_line(path.as_os_str(), arguments);
531        let parent_pid = unsafe { GetCurrentProcessId() };
532        let environment = environment_block(&[
533            (PIPE_ENV, name),
534            (NONCE_ENV, hex(&nonce)),
535            (PARENT_ENV, parent_pid.to_string()),
536        ]);
537        let mut startup: STARTUPINFOW = unsafe { zeroed() };
538        startup.cb = size_of::<STARTUPINFOW>() as u32;
539        let mut information: PROCESS_INFORMATION = unsafe { zeroed() };
540        // SAFETY: all UTF-16 buffers and output structures remain live; no handles inherit.
541        if unsafe {
542            CreateProcessW(
543                application.as_ptr(),
544                command.as_mut_ptr(),
545                std::ptr::null(),
546                std::ptr::null(),
547                0,
548                CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT,
549                environment.as_ptr().cast(),
550                std::ptr::null(),
551                &startup,
552                &mut information,
553            )
554        } == 0
555        {
556            return Err(last_os("CreateProcessW"));
557        }
558        let process = OwnedHandle::new(information.hProcess)?;
559        let thread = OwnedHandle::new(information.hThread)?;
560        // SAFETY: CreateProcessW returned this exact child still suspended.
561        if let Err(error) = unsafe { job.assign_suspended(process.0) } {
562            // SAFETY: exact held child is still suspended.
563            let _ = unsafe { TerminateProcess(process.0, 127) };
564            return Err(error);
565        }
566        // SAFETY: thread is the exact suspended primary thread.
567        if unsafe { ResumeThread(thread.0) } == u32::MAX {
568            let error = last_os("ResumeThread");
569            let _ = unsafe { TerminateProcess(process.0, 127) };
570            return Err(error);
571        }
572        drop(thread);
573        connect_pipe(pipe.0, process.0)?;
574        // SAFETY: the server pipe is connected and the expected PID is held live.
575        unsafe { authenticate_pipe_client(pipe.0, information.dwProcessId)? };
576        let hello = BootstrapFrame {
577            magic: BOOTSTRAP_MAGIC,
578            nonce,
579            parent_pid,
580            child_pid: information.dwProcessId,
581        };
582        write_frame(pipe.0, &hello)?;
583        let ready = read_frame(pipe.0)?;
584        if ready.magic != AUTH_MAGIC
585            || ready.nonce != nonce
586            || ready.parent_pid != parent_pid
587            || ready.child_pid != information.dwProcessId
588        {
589            let _ = unsafe { TerminateProcess(process.0, 127) };
590            return Err(WindowsError::InvalidBootstrap);
591        }
592        Ok(Self {
593            pipe,
594            process,
595            _job: job,
596            pid: information.dwProcessId,
597            nonce,
598            reaped: false,
599            next_transfer_id: 1,
600            pending_manifest: None,
601        })
602    }
603
604    /// Exact live process handle used only for attenuated handle duplication.
605    pub const fn process_handle(&self) -> HANDLE {
606        self.process.0
607    }
608    /// Kernel-created child process ID authenticated on the private pipe.
609    pub const fn pid(&self) -> u32 {
610        self.pid
611    }
612    /// Sends the two exact-rights handle values and their complete mapped lengths.
613    fn send_capabilities(
614        &mut self,
615        reader_handle: RemoteHandle,
616        reader: &PreparedLocalWriter,
617        writer_handle: RemoteHandle,
618        remote_writer: &PreparedRemoteWriter,
619    ) -> Result<(), WindowsError> {
620        if self.pending_manifest.is_some() {
621            return Err(WindowsError::InvalidBootstrap);
622        }
623        let manifest = TransferManifest::new(
624            self.nonce,
625            unsafe { GetCurrentProcessId() },
626            self.pid,
627            self.next_transfer_id,
628            vec![reader.entry, remote_writer.entry],
629        )
630        .ok_or(WindowsError::InvalidBootstrap)?;
631        let frame = encode_capability_frame(
632            reader_handle,
633            reader.len,
634            writer_handle,
635            remote_writer.len,
636            &manifest.encode(CAPABILITY_MAGIC),
637        )?;
638        write_pod(self.pipe.0, &frame)?;
639        self.pending_manifest = Some(manifest);
640        Ok(())
641    }
642    /// Consumes prepared mappings after authenticated READY and sends COMMIT.
643    ///
644    /// This method owns capability duplication, the remote-handle cleanup
645    /// ledger, exact manifest transfer, READY validation, and COMMIT. It returns
646    /// `(local_writer, local_reader)` only after successful COMMIT.
647    ///
648    /// # Errors
649    ///
650    /// Returns an error for duplication, pipe, transcript, timeout, or process
651    /// failures. Ambiguous failure terminates and reaps the exact held child.
652    ///
653    /// ```compile_fail
654    /// use native_ipc_platform::windows::{
655    ///     ChildSession, PreparedLocalWriter, PreparedRemoteWriter,
656    /// };
657    /// fn commit_twice(
658    ///     session: &mut ChildSession,
659    ///     writer: PreparedLocalWriter,
660    ///     reader: PreparedRemoteWriter,
661    /// ) {
662    ///     let _ = session.commit_transfers(writer, reader);
663    ///     let _ = session.commit_transfers(writer, reader);
664    /// }
665    /// ```
666    pub fn commit_transfers(
667        &mut self,
668        writer: PreparedLocalWriter,
669        reader: PreparedRemoteWriter,
670    ) -> Result<
671        (
672            WriterRegion<WindowsWriterMapping>,
673            ReaderRegion<WindowsReaderMapping>,
674        ),
675        WindowsError,
676    > {
677        let result = self.commit_transfers_inner(writer, reader);
678        if result.is_err() {
679            self.abort_child();
680        }
681        result
682    }
683
684    fn commit_transfers_inner(
685        &mut self,
686        mut writer: PreparedLocalWriter,
687        mut reader: PreparedRemoteWriter,
688    ) -> Result<
689        (
690            WriterRegion<WindowsWriterMapping>,
691            ReaderRegion<WindowsReaderMapping>,
692        ),
693        WindowsError,
694    > {
695        // SAFETY: this session owns the exact authenticated live child handle.
696        let reader_handle = unsafe { writer.duplicate_reader_to(self.process.0)? };
697        // SAFETY: same held child; the preparation enforces one writer duplicate.
698        let writer_handle = unsafe { reader.duplicate_writer_to(self.process.0)? };
699        self.send_capabilities(reader_handle, &writer, writer_handle, &reader)?;
700        let manifest = self
701            .pending_manifest
702            .as_ref()
703            .ok_or(WindowsError::InvalidBootstrap)?;
704        let ready: [u8; CONTROL_FRAME_LEN] = read_pod(self.pipe.0)?;
705        if !manifest.matches_frame(READY_MAGIC, &ready) {
706            return Err(WindowsError::InvalidBootstrap);
707        }
708        write_pod(self.pipe.0, &manifest.encode(COMMIT_MAGIC))?;
709        drop(writer.section);
710        drop(reader.section);
711        self.pending_manifest = None;
712        self.next_transfer_id = self
713            .next_transfer_id
714            .checked_add(1)
715            .ok_or(WindowsError::InvalidBootstrap)?;
716        Ok((writer.runtime, reader.runtime))
717    }
718
719    fn abort_child(&mut self) {
720        if !self.reaped {
721            // SAFETY: this session owns the exact authenticated child handle.
722            let _ = unsafe { TerminateProcess(self.process.0, 127) };
723            // SAFETY: same held process; bounded wait completes cleanup.
724            let _ = unsafe { WaitForSingleObject(self.process.0, WAIT_MS) };
725            self.reaped = true;
726        }
727    }
728    /// Waits for a normal helper exit after protocol completion.
729    pub fn wait(mut self) -> Result<(), WindowsError> {
730        // SAFETY: process is held live for this session.
731        match unsafe { WaitForSingleObject(self.process.0, WAIT_MS) } {
732            WAIT_OBJECT_0 => {
733                let mut code = 0;
734                // SAFETY: held process is signaled and output pointer is valid.
735                if unsafe { GetExitCodeProcess(self.process.0, &mut code) } == 0 {
736                    return Err(last_os("GetExitCodeProcess"));
737                }
738                if code != 0 {
739                    return Err(WindowsError::ChildExit(code));
740                }
741            }
742            WAIT_TIMEOUT => return Err(WindowsError::TimedOut("helper exit")),
743            _ => return Err(last_os("WaitForSingleObject")),
744        }
745        self.reaped = true;
746        Ok(())
747    }
748}
749
750impl Drop for ChildSession {
751    fn drop(&mut self) {
752        if !self.reaped {
753            // SAFETY: exact held child; job close remains the backstop for descendants.
754            let _ = unsafe { TerminateProcess(self.process.0, 127) };
755            let _ = unsafe { WaitForSingleObject(self.process.0, WAIT_MS) };
756        }
757    }
758}
759
760/// Connects a spawned helper from its authenticated bootstrap environment.
761pub fn connect_spawned_helper() -> Result<ChildChannel, WindowsError> {
762    let name = std::env::var_os(PIPE_ENV).ok_or(WindowsError::InvalidBootstrap)?;
763    let nonce =
764        parse_nonce(&std::env::var(NONCE_ENV).map_err(|_| WindowsError::InvalidBootstrap)?)?;
765    let parent_pid = std::env::var(PARENT_ENV)
766        .map_err(|_| WindowsError::InvalidBootstrap)?
767        .parse::<u32>()
768        .map_err(|_| WindowsError::InvalidBootstrap)?;
769    let name = wide_null(&name);
770    // SAFETY: terminated name; no sharing or inheritance and existing pipe only.
771    let pipe = unsafe {
772        CreateFileW(
773            name.as_ptr(),
774            GENERIC_READ | GENERIC_WRITE,
775            0,
776            std::ptr::null(),
777            OPEN_EXISTING,
778            0,
779            std::ptr::null_mut(),
780        )
781    };
782    let pipe = OwnedHandle::new(pipe)?;
783    let mode = PIPE_READMODE_MESSAGE | PIPE_NOWAIT;
784    // SAFETY: connected client pipe and mode pointer are valid.
785    if unsafe { SetNamedPipeHandleState(pipe.0, &mode, std::ptr::null(), std::ptr::null()) } == 0 {
786        return Err(last_os("SetNamedPipeHandleState"));
787    }
788    // SAFETY: connected pipe client and exact expected parent from spawn environment.
789    unsafe { authenticate_pipe_server(pipe.0, parent_pid)? };
790    let hello = read_frame(pipe.0)?;
791    let child_pid = unsafe { GetCurrentProcessId() };
792    if hello.magic != BOOTSTRAP_MAGIC
793        || hello.nonce != nonce
794        || hello.parent_pid != parent_pid
795        || hello.child_pid != child_pid
796    {
797        return Err(WindowsError::InvalidBootstrap);
798    }
799    write_frame(
800        pipe.0,
801        &BootstrapFrame {
802            magic: AUTH_MAGIC,
803            ..hello
804        },
805    )?;
806    Ok(ChildChannel {
807        pipe,
808        parent_pid,
809        nonce,
810        next_transfer_id: 1,
811        pending_transcript: None,
812    })
813}
814
815/// Authenticated child endpoint retained for the lifetime of imported capabilities.
816pub struct ChildChannel {
817    pipe: OwnedHandle,
818    parent_pid: u32,
819    nonce: [u8; 32],
820    next_transfer_id: u64,
821    pending_transcript: Option<[u8; CONTROL_FRAME_LEN]>,
822}
823impl ChildChannel {
824    /// Held authenticated parent PID.
825    pub const fn parent_pid(&self) -> u32 {
826        self.parent_pid
827    }
828    /// Raw pipe handle for a bounded manifest protocol owned by the caller.
829    pub const fn pipe_handle(&self) -> HANDLE {
830        self.pipe.0
831    }
832    /// Receives exact-rights handle values only after pipe PID authentication.
833    ///
834    /// The tuple is `(reader_handle, reader_len, writer_handle, writer_len)`.
835    /// Lengths are exact page-rounded capability sizes. Handles remain pending
836    /// and must be imported and passed to [`Self::commit_imports`].
837    ///
838    /// # Errors
839    ///
840    /// Returns an error for duplicate receipt, timeout, truncated or oversized
841    /// frames, invalid fixed-width values, or a malformed capability envelope.
842    pub fn receive_capabilities(
843        &mut self,
844    ) -> Result<(RemoteHandle, usize, RemoteHandle, usize), WindowsError> {
845        if self.pending_transcript.is_some() {
846            return Err(WindowsError::InvalidBootstrap);
847        }
848        let frame: [u8; CAPABILITY_FRAME_LEN] = read_pod(self.pipe.0)?;
849        if frame[..8] != CAPABILITY_MAGIC {
850            return Err(WindowsError::InvalidBootstrap);
851        }
852        let reader_handle = usize::try_from(u64::from_le_bytes(
853            frame[8..16].try_into().expect("fixed range"),
854        ))
855        .map_err(|_| WindowsError::InvalidBootstrap)?;
856        let writer_handle = usize::try_from(u64::from_le_bytes(
857            frame[16..24].try_into().expect("fixed range"),
858        ))
859        .map_err(|_| WindowsError::InvalidBootstrap)?;
860        let reader_len = usize::try_from(u64::from_le_bytes(
861            frame[24..32].try_into().expect("fixed range"),
862        ))
863        .map_err(|_| WindowsError::InvalidBootstrap)?;
864        let writer_len = usize::try_from(u64::from_le_bytes(
865            frame[32..40].try_into().expect("fixed range"),
866        ))
867        .map_err(|_| WindowsError::InvalidBootstrap)?;
868        if reader_handle == 0 || writer_handle == 0 || reader_len == 0 || writer_len == 0 {
869            return Err(WindowsError::InvalidBootstrap);
870        }
871        let mut transcript = [0; CONTROL_FRAME_LEN];
872        transcript.copy_from_slice(&frame[40..]);
873        self.pending_transcript = Some(transcript);
874        Ok((
875            RemoteHandle(reader_handle),
876            reader_len,
877            RemoteHandle(writer_handle),
878            writer_len,
879        ))
880    }
881    /// Signals validation, waits for COMMIT, then exposes imported capabilities.
882    ///
883    /// Returns `(imported_reader, imported_writer)` in manifest order.
884    ///
885    /// # Errors
886    ///
887    /// Returns an error if the imported entries do not match the capability
888    /// transcript, READY cannot be sent, or COMMIT is malformed or stale.
889    pub fn commit_imports(
890        &mut self,
891        reader: PendingImportedReader,
892        writer: PendingImportedWriter,
893    ) -> Result<
894        (
895            ReaderRegion<WindowsReaderMapping>,
896            WriterRegion<WindowsWriterMapping>,
897        ),
898        WindowsError,
899    > {
900        let manifest = TransferManifest::new(
901            self.nonce,
902            self.parent_pid,
903            unsafe { GetCurrentProcessId() },
904            self.next_transfer_id,
905            vec![reader.entry, writer.entry],
906        )
907        .ok_or(WindowsError::InvalidBootstrap)?;
908        let transcript = self
909            .pending_transcript
910            .as_ref()
911            .ok_or(WindowsError::InvalidBootstrap)?;
912        if !manifest.matches_frame(CAPABILITY_MAGIC, transcript) {
913            return Err(WindowsError::InvalidBootstrap);
914        }
915        write_pod(self.pipe.0, &manifest.encode(READY_MAGIC))?;
916        let commit: [u8; CONTROL_FRAME_LEN] = read_pod(self.pipe.0)?;
917        if !manifest.matches_frame(COMMIT_MAGIC, &commit) {
918            return Err(WindowsError::InvalidBootstrap);
919        }
920        self.pending_transcript = None;
921        self.next_transfer_id = self
922            .next_transfer_id
923            .checked_add(1)
924            .ok_or(WindowsError::InvalidBootstrap)?;
925        Ok((reader.runtime, writer.runtime))
926    }
927}
928
929/// Platform-minted unique writable unnamed-section view.
930pub struct WindowsWriterMapping {
931    view: View,
932}
933// SAFETY: constructors consume the full creator handle and retain the sole RW view.
934unsafe impl SoleWriterMapping for WindowsWriterMapping {
935    fn base(&self) -> NonNull<u8> {
936        self.view.base
937    }
938    fn len(&self) -> usize {
939        self.view.len
940    }
941}
942/// Platform-minted read-only unnamed-section view.
943pub struct WindowsReaderMapping {
944    view: View,
945}
946// SAFETY: constructors map only FILE_MAP_READ and retain the view lifetime.
947unsafe impl ReadOnlyMapping for WindowsReaderMapping {
948    fn base(&self) -> NonNull<u8> {
949        self.view.base
950    }
951    fn len(&self) -> usize {
952        self.view.len
953    }
954}
955
956struct OwnedHandle(HANDLE);
957impl OwnedHandle {
958    fn new(handle: HANDLE) -> Result<Self, WindowsError> {
959        if handle.is_null() || handle == INVALID_HANDLE_VALUE {
960            Err(WindowsError::InvalidHandle)
961        } else {
962            Ok(Self(handle))
963        }
964    }
965}
966impl Drop for OwnedHandle {
967    fn drop(&mut self) {
968        // SAFETY: this value uniquely owns a real non-pseudo handle.
969        let _ = unsafe { CloseHandle(self.0) };
970    }
971}
972
973struct View {
974    base: NonNull<u8>,
975    len: usize,
976}
977impl View {
978    fn map(section: HANDLE, len: usize, access: u32) -> Result<Self, WindowsError> {
979        // SAFETY: section handle is live; access/offset/length are checked.
980        let address = unsafe { MapViewOfFile(section, access, 0, 0, len) };
981        let base = NonNull::new(address.Value.cast()).ok_or_else(|| last_os("MapViewOfFile"))?;
982        Ok(Self { base, len })
983    }
984}
985impl Drop for View {
986    fn drop(&mut self) {
987        // SAFETY: this object uniquely owns the mapped view.
988        let _ = unsafe {
989            UnmapViewOfFile(MEMORY_MAPPED_VIEW_ADDRESS {
990                Value: self.base.as_ptr().cast(),
991            })
992        };
993    }
994}
995
996fn duplicate_to(source: HANDLE, target: HANDLE, access: u32) -> Result<RemoteHandle, WindowsError> {
997    let mut remote: HANDLE = std::ptr::null_mut();
998    // SAFETY: source/current/target handles are live; no SAME_ACCESS option is used.
999    if unsafe {
1000        DuplicateHandle(
1001            GetCurrentProcess(),
1002            source,
1003            target,
1004            &mut remote,
1005            access,
1006            0,
1007            0,
1008        )
1009    } == 0
1010    {
1011        return Err(last_os("DuplicateHandle"));
1012    }
1013    if remote.is_null() {
1014        Err(WindowsError::InvalidHandle)
1015    } else {
1016        Ok(RemoteHandle(remote as usize))
1017    }
1018}
1019
1020fn page_align(size: usize) -> Result<usize, WindowsError> {
1021    if size == 0 {
1022        return Err(WindowsError::InvalidSize(size));
1023    }
1024    let mut information: SYSTEM_INFO = unsafe { zeroed() };
1025    // SAFETY: output pointer is valid.
1026    unsafe { GetSystemInfo(&mut information) };
1027    let page = information.dwPageSize as usize;
1028    if page == 0 || !page.is_power_of_two() {
1029        return Err(WindowsError::InvalidSize(size));
1030    }
1031    size.checked_add(page - 1)
1032        .map(|value| value & !(page - 1))
1033        .filter(|value| *value <= isize::MAX as usize)
1034        .ok_or(WindowsError::InvalidSize(size))
1035}
1036
1037fn wide_null(value: &OsStr) -> Vec<u16> {
1038    value.encode_wide().chain(std::iter::once(0)).collect()
1039}
1040
1041fn quote_argument(value: &OsStr, output: &mut Vec<u16>) {
1042    let units: Vec<u16> = value.encode_wide().collect();
1043    let needs_quotes = units.is_empty()
1044        || units
1045            .iter()
1046            .any(|unit| *unit == b' ' as u16 || *unit == b'\t' as u16);
1047    if !needs_quotes {
1048        output.extend(units);
1049        return;
1050    }
1051    output.push(b'"' as u16);
1052    let mut slashes = 0;
1053    for unit in units {
1054        if unit == b'\\' as u16 {
1055            slashes += 1;
1056        } else if unit == b'"' as u16 {
1057            output.extend(std::iter::repeat_n(b'\\' as u16, slashes * 2 + 1));
1058            output.push(unit);
1059            slashes = 0;
1060        } else {
1061            output.extend(std::iter::repeat_n(b'\\' as u16, slashes));
1062            output.push(unit);
1063            slashes = 0;
1064        }
1065    }
1066    output.extend(std::iter::repeat_n(b'\\' as u16, slashes * 2));
1067    output.push(b'"' as u16);
1068}
1069
1070fn command_line(path: &OsStr, arguments: &[OsString]) -> Vec<u16> {
1071    let mut result = Vec::new();
1072    quote_argument(path, &mut result);
1073    for argument in arguments {
1074        result.push(b' ' as u16);
1075        quote_argument(argument, &mut result);
1076    }
1077    result.push(0);
1078    result
1079}
1080
1081fn environment_block(overrides: &[(&str, String)]) -> Vec<u16> {
1082    let mut values: Vec<(OsString, OsString)> = std::env::vars_os()
1083        .filter(|(key, _)| {
1084            !overrides
1085                .iter()
1086                .any(|(name, _)| key.to_string_lossy().eq_ignore_ascii_case(name))
1087        })
1088        .collect();
1089    values.extend(
1090        overrides
1091            .iter()
1092            .map(|(key, value)| (OsString::from(key), OsString::from(value))),
1093    );
1094    values.sort_by(|left, right| {
1095        left.0
1096            .to_string_lossy()
1097            .to_ascii_lowercase()
1098            .cmp(&right.0.to_string_lossy().to_ascii_lowercase())
1099    });
1100    let mut block = Vec::new();
1101    for (key, value) in values {
1102        block.extend(key.encode_wide());
1103        block.push(b'=' as u16);
1104        block.extend(value.encode_wide());
1105        block.push(0);
1106    }
1107    block.push(0);
1108    block
1109}
1110
1111fn pod_bytes<T>(value: &T) -> &[u8] {
1112    // SAFETY: callers use repr(C), fully initialized integer-only protocol records.
1113    unsafe { std::slice::from_raw_parts((value as *const T).cast(), size_of::<T>()) }
1114}
1115
1116fn write_frame(pipe: HANDLE, frame: &BootstrapFrame) -> Result<(), WindowsError> {
1117    write_pod(pipe, frame)
1118}
1119
1120fn write_pod<T>(pipe: HANDLE, value: &T) -> Result<(), WindowsError> {
1121    let bytes = pod_bytes(value);
1122    let deadline = Instant::now() + Duration::from_millis(WAIT_MS.into());
1123    loop {
1124        let mut written = 0;
1125        // SAFETY: pipe is live, bytes are valid, and nonblocking operation is synchronous.
1126        if unsafe {
1127            WriteFile(
1128                pipe,
1129                bytes.as_ptr(),
1130                bytes.len() as u32,
1131                &mut written,
1132                std::ptr::null_mut(),
1133            )
1134        } != 0
1135        {
1136            return if written as usize == bytes.len() {
1137                Ok(())
1138            } else {
1139                Err(WindowsError::InvalidBootstrap)
1140            };
1141        }
1142        let code = unsafe { GetLastError() };
1143        if code != ERROR_NO_DATA && code != ERROR_PIPE_LISTENING {
1144            return Err(WindowsError::Os {
1145                operation: "WriteFile",
1146                code,
1147            });
1148        }
1149        wait_retry(deadline, "pipe write")?;
1150    }
1151}
1152
1153fn read_frame(pipe: HANDLE) -> Result<BootstrapFrame, WindowsError> {
1154    read_pod(pipe)
1155}
1156
1157fn read_pod<T>(pipe: HANDLE) -> Result<T, WindowsError> {
1158    let deadline = Instant::now() + Duration::from_millis(WAIT_MS.into());
1159    loop {
1160        let mut value: T = unsafe { zeroed() };
1161        let mut read = 0;
1162        // SAFETY: frame output range is valid and nonblocking operation is synchronous.
1163        if unsafe {
1164            ReadFile(
1165                pipe,
1166                (&mut value as *mut T).cast(),
1167                size_of::<T>() as u32,
1168                &mut read,
1169                std::ptr::null_mut(),
1170            )
1171        } != 0
1172        {
1173            return if read as usize == size_of::<T>() {
1174                Ok(value)
1175            } else {
1176                Err(WindowsError::InvalidBootstrap)
1177            };
1178        }
1179        let code = unsafe { GetLastError() };
1180        if code != ERROR_NO_DATA && code != ERROR_PIPE_LISTENING {
1181            return Err(WindowsError::Os {
1182                operation: "ReadFile",
1183                code,
1184            });
1185        }
1186        wait_retry(deadline, "pipe read")?;
1187    }
1188}
1189
1190fn connect_pipe(pipe: HANDLE, process: HANDLE) -> Result<(), WindowsError> {
1191    let deadline = Instant::now() + Duration::from_millis(WAIT_MS.into());
1192    loop {
1193        // SAFETY: server pipe is nonblocking and no OVERLAPPED operation is requested.
1194        if unsafe { ConnectNamedPipe(pipe, std::ptr::null_mut()) } != 0 {
1195            return Ok(());
1196        }
1197        let code = unsafe { GetLastError() };
1198        if code == ERROR_PIPE_CONNECTED {
1199            return Ok(());
1200        }
1201        if code != ERROR_PIPE_LISTENING && code != ERROR_NO_DATA {
1202            return Err(WindowsError::Os {
1203                operation: "ConnectNamedPipe",
1204                code,
1205            });
1206        }
1207        // SAFETY: exact child process handle is held throughout bootstrap.
1208        if unsafe { WaitForSingleObject(process, 0) } == WAIT_OBJECT_0 {
1209            let mut exit = 0;
1210            // SAFETY: process is signaled and output is valid.
1211            if unsafe { GetExitCodeProcess(process, &mut exit) } != 0 {
1212                return Err(WindowsError::ChildExit(exit));
1213            }
1214            return Err(last_os("GetExitCodeProcess"));
1215        }
1216        wait_retry(deadline, "pipe connect")?;
1217    }
1218}
1219
1220fn wait_retry(deadline: Instant, operation: &'static str) -> Result<(), WindowsError> {
1221    if Instant::now() >= deadline {
1222        Err(WindowsError::TimedOut(operation))
1223    } else {
1224        std::thread::sleep(Duration::from_millis(1));
1225        Ok(())
1226    }
1227}
1228
1229fn hex(bytes: &[u8; 32]) -> String {
1230    let mut output = String::with_capacity(64);
1231    for byte in bytes {
1232        use std::fmt::Write as _;
1233        write!(&mut output, "{byte:02x}").expect("String writes are infallible");
1234    }
1235    output
1236}
1237
1238fn parse_nonce(value: &str) -> Result<[u8; 32], WindowsError> {
1239    if value.len() != 64 {
1240        return Err(WindowsError::InvalidBootstrap);
1241    }
1242    let mut nonce = [0; 32];
1243    for (index, output) in nonce.iter_mut().enumerate() {
1244        *output = u8::from_str_radix(&value[index * 2..index * 2 + 2], 16)
1245            .map_err(|_| WindowsError::InvalidBootstrap)?;
1246    }
1247    if nonce == [0; 32] {
1248        return Err(WindowsError::InvalidBootstrap);
1249    }
1250    Ok(nonce)
1251}
1252
1253fn last_os(operation: &'static str) -> WindowsError {
1254    // SAFETY: GetLastError has no preconditions.
1255    WindowsError::Os {
1256        operation,
1257        code: unsafe { GetLastError() },
1258    }
1259}
1260
1261/// Reports the native backend's enforced capability policy.
1262pub const fn status() -> BackendStatus {
1263    BackendStatus::Available
1264}
1265
1266#[cfg(test)]
1267mod tests {
1268    use super::*;
1269    use native_ipc_core::layout::{
1270        AcknowledgementRouteSpec, Endpoint, LayoutLimits, RegionSpec, RoleId,
1271    };
1272    use std::time::Duration;
1273    use windows_sys::Win32::System::Memory::{PAGE_EXECUTE_READWRITE, VirtualProtect};
1274
1275    fn topology() -> (RegionSetLayout, RoleId, RoleId) {
1276        let producer = RoleId::new(1).unwrap();
1277        let peer = RoleId::new(2).unwrap();
1278        let specs = [
1279            RegionSpec {
1280                role: producer,
1281                writer: Endpoint::Initiator,
1282                slot_count: 1,
1283                payload_bytes: 32,
1284                acknowledgement_count: 1,
1285            },
1286            RegionSpec {
1287                role: peer,
1288                writer: Endpoint::Responder,
1289                slot_count: 1,
1290                payload_bytes: 32,
1291                acknowledgement_count: 1,
1292            },
1293        ];
1294        let routes = [
1295            AcknowledgementRouteSpec {
1296                owner: peer,
1297                target: producer,
1298                slot_index: 0,
1299                cell_index: 0,
1300            },
1301            AcknowledgementRouteSpec {
1302                owner: producer,
1303                target: peer,
1304                slot_index: 0,
1305                cell_index: 0,
1306            },
1307        ];
1308        let topology = RegionSetLayout::calculate(
1309            [7; 32],
1310            23,
1311            &specs,
1312            &routes,
1313            LayoutLimits {
1314                maximum_mapping_size: 1 << 20,
1315                maximum_slot_count: 2,
1316                maximum_acknowledgement_count: 2,
1317                maximum_payload_bytes: 64,
1318            },
1319        )
1320        .unwrap();
1321        (topology, producer, peer)
1322    }
1323
1324    fn expected(topology: &RegionSetLayout, role: RoleId, len: usize) -> ValidationExpectations {
1325        let region = topology.region(role).unwrap();
1326        ValidationExpectations {
1327            schema_id: [7; 32],
1328            generation: 23,
1329            role,
1330            writer: region.writer(),
1331            maximum_mapping_size: len as u64,
1332        }
1333    }
1334
1335    #[test]
1336    fn nonce_is_nonzero_and_job_is_constructible() {
1337        assert_ne!(session_nonce().unwrap(), [0; 32]);
1338        let _job = ChildJob::new().unwrap();
1339    }
1340
1341    #[test]
1342    fn unnamed_section_is_page_rounded_and_zeroed() {
1343        let region = QuiescentRegion::new(37).unwrap();
1344        assert!(region.len() >= 37);
1345        assert!(region.as_bytes().iter().all(|byte| *byte == 0));
1346        let mut prior = 0;
1347        // SAFETY: the complete live view and protection output are valid.
1348        assert_eq!(
1349            unsafe {
1350                VirtualProtect(
1351                    region.view.base.as_ptr().cast(),
1352                    region.len(),
1353                    PAGE_EXECUTE_READWRITE,
1354                    &mut prior,
1355                )
1356            },
1357            0
1358        );
1359    }
1360
1361    #[test]
1362    fn read_only_duplicate_rejects_writable_mapping() {
1363        let region = QuiescentRegion::new(4096).unwrap();
1364        let duplicate = duplicate_to(
1365            region.section.0,
1366            unsafe { GetCurrentProcess() },
1367            FILE_MAP_READ,
1368        )
1369        .unwrap();
1370        let duplicate = OwnedHandle::new(duplicate.0 as HANDLE).unwrap();
1371        // SAFETY: exact read-only section handle is live; the denied result is not owned.
1372        let denied = unsafe { MapViewOfFile(duplicate.0, FILE_MAP_WRITE, 0, 0, region.len()) };
1373        assert!(denied.Value.is_null());
1374    }
1375
1376    #[test]
1377    fn spawned_helper_is_pid_authenticated_and_job_owned() {
1378        let (topology, producer, peer) = topology();
1379        let producer_layout = topology.region(producer).unwrap();
1380        let mut producer_region =
1381            QuiescentRegion::new(producer_layout.total_size() as usize).unwrap();
1382        producer_layout
1383            .encode_into(producer_region.as_bytes_mut())
1384            .unwrap();
1385        let producer_expected = expected(&topology, producer, producer_region.len());
1386        let prepared_producer = producer_region
1387            .prepare_local_writer(producer_expected, topology.clone())
1388            .unwrap();
1389        let peer_layout = topology.region(peer).unwrap();
1390        let mut peer_region = QuiescentRegion::new(peer_layout.total_size() as usize).unwrap();
1391        peer_layout.encode_into(peer_region.as_bytes_mut()).unwrap();
1392        let peer_expected = expected(&topology, peer, peer_region.len());
1393        let prepared_peer = peer_region
1394            .prepare_remote_writer(peer_expected, topology.clone())
1395            .unwrap();
1396        let executable = std::env::current_exe().unwrap();
1397        let arguments = [
1398            OsString::from("--exact"),
1399            OsString::from("windows::tests::spawned_helper_entry"),
1400            OsString::from("--ignored"),
1401            OsString::from("--nocapture"),
1402        ];
1403        let mut child = ChildSession::spawn(&executable, &arguments).unwrap();
1404        assert_ne!(child.pid(), unsafe { GetCurrentProcessId() });
1405        let (mut writer, reader) = child
1406            .commit_transfers(prepared_producer, prepared_peer)
1407            .unwrap();
1408        writer
1409            .publish(0, 1, None, b"cross-process-windows")
1410            .unwrap();
1411        for _ in 0..10_000 {
1412            if let Ok(payload) = reader.copy_payload(0, 1) {
1413                assert_eq!(payload, b"child-windows-writer");
1414                child.wait().unwrap();
1415                return;
1416            }
1417            std::thread::sleep(Duration::from_millis(1));
1418        }
1419        panic!("child never published payload");
1420    }
1421
1422    #[test]
1423    fn helper_exit_before_connect_is_bounded() {
1424        let executable = std::env::current_exe().unwrap();
1425        let arguments = [
1426            OsString::from("--exact"),
1427            OsString::from("windows::tests::exit_before_connect_entry"),
1428            OsString::from("--ignored"),
1429        ];
1430        let result = ChildSession::spawn(&executable, &arguments);
1431        assert!(matches!(
1432            result,
1433            Err(WindowsError::ChildExit(0) | WindowsError::Os { .. })
1434        ));
1435    }
1436
1437    #[test]
1438    fn helper_stall_before_auth_is_bounded() {
1439        let executable = std::env::current_exe().unwrap();
1440        let arguments = [
1441            OsString::from("--exact"),
1442            OsString::from("windows::tests::stall_before_auth_entry"),
1443            OsString::from("--ignored"),
1444        ];
1445        let result = ChildSession::spawn(&executable, &arguments);
1446        assert!(matches!(result, Err(WindowsError::TimedOut("pipe read"))));
1447    }
1448
1449    #[test]
1450    #[ignore = "spawned only by the exact lifecycle test"]
1451    fn spawned_helper_entry() {
1452        let (topology, producer, peer) = topology();
1453        let mut channel = connect_spawned_helper().unwrap();
1454        assert_ne!(channel.parent_pid(), unsafe { GetCurrentProcessId() });
1455        let (reader_handle, reader_len, writer_handle, writer_len) =
1456            channel.receive_capabilities().unwrap();
1457        // SAFETY: exact handles arrived from authenticated parent on private pipe.
1458        let reader = unsafe {
1459            import_reader(
1460                reader_handle.0,
1461                reader_len,
1462                expected(&topology, producer, reader_len),
1463                topology.clone(),
1464            )
1465            .unwrap()
1466        };
1467        // SAFETY: manifest designates this exact handle as the sole writer.
1468        let writer = unsafe {
1469            import_writer(
1470                writer_handle.0,
1471                writer_len,
1472                expected(&topology, peer, writer_len),
1473                topology,
1474            )
1475            .unwrap()
1476        };
1477        let (reader, mut writer) = channel.commit_imports(reader, writer).unwrap();
1478        for _ in 0..10_000 {
1479            if let Ok(payload) = reader.copy_payload(0, 1) {
1480                assert_eq!(payload, b"cross-process-windows");
1481                writer.publish(0, 1, None, b"child-windows-writer").unwrap();
1482                return;
1483            }
1484            std::thread::sleep(Duration::from_millis(1));
1485        }
1486        panic!("parent never published payload");
1487    }
1488
1489    #[test]
1490    #[ignore = "spawned only by the exit-before-connect lifecycle test"]
1491    fn exit_before_connect_entry() {}
1492
1493    #[test]
1494    #[ignore = "spawned only by the stalled-auth lifecycle test"]
1495    fn stall_before_auth_entry() {
1496        let name = wide_null(&std::env::var_os(PIPE_ENV).unwrap());
1497        // SAFETY: terminated private pipe name and existing-only open are valid.
1498        let pipe = unsafe {
1499            CreateFileW(
1500                name.as_ptr(),
1501                GENERIC_READ | GENERIC_WRITE,
1502                0,
1503                std::ptr::null(),
1504                OPEN_EXISTING,
1505                0,
1506                std::ptr::null_mut(),
1507            )
1508        };
1509        let _pipe = OwnedHandle::new(pipe).unwrap();
1510        std::thread::sleep(Duration::from_secs(5));
1511    }
1512}