native-ipc 0.6.0

One safe API for least-authority native shared memory: sealed memfd on Linux, Mach memory entries on macOS, exact-rights sections on Windows
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
//! Private native backend implementations.
#![allow(
    dead_code,
    reason = "private role-scoped evidence remains unreachable until native session composition"
)]

use crate::negotiation::AcceptedTranscriptFacts;
use crate::protocol::{CapabilityFrame, NativeAuthorityProfile};
use crate::session::{AbsoluteDeadline, AtomicCapabilities, ProtocolVersion, SessionLimits};
use core::cell::Cell;
use core::marker::PhantomData;

/// Exact spawned-pair identities shared by role-scoped evidence constructors.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct SpawnIdentityFacts {
    parent_pid: u32,
    child_pid: u32,
    parent_uid: u32,
    parent_gid: u32,
    child_uid: u32,
    child_gid: u32,
    nonce: [u8; 32],
}

/// Exact accepted-session provenance and negotiated limits retained by the
/// inseparable dispatcher.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) struct AcceptedSessionParameters {
    facts: SpawnIdentityFacts,
    limits: SessionLimits,
    authority_profile: NativeAuthorityProfile,
    atomics: AtomicCapabilities,
    protocol_version: ProtocolVersion,
}

impl AcceptedSessionParameters {
    pub(crate) const fn facts(self) -> SpawnIdentityFacts {
        self.facts
    }

    pub(crate) const fn limits(self) -> SessionLimits {
        self.limits
    }

    pub(crate) const fn authority_profile(self) -> NativeAuthorityProfile {
        self.authority_profile
    }

    pub(crate) const fn atomics(self) -> AtomicCapabilities {
        self.atomics
    }

    pub(crate) const fn protocol_version(self) -> ProtocolVersion {
        self.protocol_version
    }
}

impl SpawnIdentityFacts {
    fn new(
        parent_pid: u32,
        child_pid: u32,
        parent_uid: u32,
        parent_gid: u32,
        child_uid: u32,
        child_gid: u32,
        nonce: [u8; 32],
    ) -> Option<Self> {
        if parent_pid == 0 || child_pid == 0 || parent_pid == child_pid || nonce == [0; 32] {
            return None;
        }
        Some(Self {
            parent_pid,
            child_pid,
            parent_uid,
            parent_gid,
            child_uid,
            child_gid,
            nonce,
        })
    }

    pub(crate) const fn parent_pid(self) -> u32 {
        self.parent_pid
    }

    pub(crate) const fn child_pid(self) -> u32 {
        self.child_pid
    }

    pub(crate) const fn parent_uid(self) -> u32 {
        self.parent_uid
    }

    pub(crate) const fn parent_gid(self) -> u32 {
        self.parent_gid
    }

    pub(crate) const fn child_uid(self) -> u32 {
        self.child_uid
    }

    pub(crate) const fn child_gid(self) -> u32 {
        self.child_gid
    }

    pub(crate) const fn nonce(self) -> [u8; 32] {
        self.nonce
    }
}

/// Coordinator-only evidence of the exact child-channel authentication flow.
pub(crate) struct CoordinatorChildChannelReceipt {
    facts: SpawnIdentityFacts,
}

impl CoordinatorChildChannelReceipt {
    /// # Safety
    ///
    /// `facts` must have been established by the backend's complete kernel
    /// endpoint-authentication and bootstrap-nonce state machine.
    unsafe fn from_verified_native(facts: SpawnIdentityFacts) -> Self {
        Self { facts }
    }
}

/// Coordinator-only evidence retaining the exact spawned child image owner.
pub(crate) struct CoordinatorChildImageReceipt {
    facts: SpawnIdentityFacts,
}

impl CoordinatorChildImageReceipt {
    /// # Safety
    ///
    /// The normative pre/post-spawn image identity must have been verified and
    /// its race-resistant native state must remain owned by the endpoint.
    unsafe fn from_verified_native(facts: SpawnIdentityFacts) -> Self {
        Self { facts }
    }
}

/// Coordinator evidence after exact child channel, image, and bilateral ACCEPT.
pub(crate) struct CoordinatorAcceptedEvidence {
    facts: SpawnIdentityFacts,
    transcript: AcceptedTranscriptFacts,
    not_sync: PhantomData<Cell<()>>,
}

impl CoordinatorAcceptedEvidence {
    fn combine(
        channel: CoordinatorChildChannelReceipt,
        image: CoordinatorChildImageReceipt,
        transcript: AcceptedTranscriptFacts,
    ) -> Result<Self, SessionTransportError> {
        if channel.facts != image.facts || channel.facts.nonce != transcript.nonce() {
            return Err(SessionTransportError::IdentityMismatch);
        }
        Ok(Self {
            facts: channel.facts,
            transcript,
            not_sync: PhantomData,
        })
    }

    pub(crate) const fn facts(&self) -> SpawnIdentityFacts {
        self.facts
    }

    pub(crate) const fn session_parameters(
        &self,
        authority_profile: NativeAuthorityProfile,
    ) -> AcceptedSessionParameters {
        let (major, minor) = self.transcript.wire_version();
        AcceptedSessionParameters {
            facts: self.facts,
            limits: self.transcript.effective_limits(),
            authority_profile,
            atomics: AtomicCapabilities::from_accepted_offer(self.transcript.effective_atomics()),
            protocol_version: ProtocolVersion::new(major, minor),
        }
    }
}

/// Receiver-only evidence of the authenticated trusted spawning coordinator.
///
/// This deliberately carries no coordinator-owned child-image or pidfd proof.
pub(crate) struct ReceiverSpawnerEvidence {
    facts: SpawnIdentityFacts,
    transcript: AcceptedTranscriptFacts,
    not_sync: PhantomData<Cell<()>>,
}

impl ReceiverSpawnerEvidence {
    /// # Safety
    ///
    /// `facts` must come from the exact inherited endpoint's validated spawning
    /// parent credentials and the local child identity captured during HELLO.
    unsafe fn from_verified_native(
        facts: SpawnIdentityFacts,
        transcript: AcceptedTranscriptFacts,
    ) -> Result<Self, SessionTransportError> {
        if facts.nonce != transcript.nonce() {
            return Err(SessionTransportError::IdentityMismatch);
        }
        Ok(Self {
            facts,
            transcript,
            not_sync: PhantomData,
        })
    }

    pub(crate) const fn facts(&self) -> SpawnIdentityFacts {
        self.facts
    }

    pub(crate) const fn session_parameters(
        &self,
        authority_profile: NativeAuthorityProfile,
    ) -> AcceptedSessionParameters {
        let (major, minor) = self.transcript.wire_version();
        AcceptedSessionParameters {
            facts: self.facts,
            limits: self.transcript.effective_limits(),
            authority_profile,
            atomics: AtomicCapabilities::from_accepted_offer(self.transcript.effective_atomics()),
            protocol_version: ProtocolVersion::new(major, minor),
        }
    }
}

/// Peer lifecycle state observed without surrendering endpoint ownership.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum PeerState {
    Running,
    ExitedUnknown,
}

/// Bounded platform-neutral native session transport failure.
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub(crate) enum SessionTransportError {
    DeadlineExpired,
    PeerExited,
    MalformedRecord,
    RecordTooLarge,
    IdentityMismatch,
    Ambiguous,
    Poisoned,
    Native(Option<i32>),
}

pub(crate) mod sealed {
    pub(crate) trait Sealed {}
}

/// Private authenticated duplex zero-rights record transport.
///
/// Implementations must reject ancillary capability delivery, bound record
/// allocation by `maximum`, preserve one caller-derived absolute deadline
/// across every retry, and poison themselves after ambiguous transmission.
pub(crate) trait AuthenticatedZeroRightsTransport: sealed::Sealed {
    fn send_record(
        &mut self,
        bytes: &[u8],
        deadline: AbsoluteDeadline,
    ) -> Result<(), SessionTransportError>;

    fn receive_record(
        &mut self,
        maximum: usize,
        deadline: AbsoluteDeadline,
    ) -> Result<Vec<u8>, SessionTransportError>;

    /// Performs one nonblocking peer observation without inventing an exit
    /// code that the authenticated record transport cannot prove.
    fn try_poll_peer(&mut self) -> Result<PeerState, SessionTransportError>;

    /// Permanently invalidates the transport. Every later I/O operation must
    /// fail immediately without touching native state.
    fn poison(&mut self);
}

/// Coordinator-only capability-record send operation on the accepted owner.
///
/// The associated value is backend-owned borrowed authority. Implementations
/// must send exactly one canonical capability record with 1..=16 native
/// capabilities under the supplied absolute deadline.
pub(crate) trait CoordinatorCapabilityTransport: AuthenticatedZeroRightsTransport {
    type Capabilities<'a>
    where
        Self: 'a;

    fn send_capability_record(
        &mut self,
        frame: &CapabilityFrame,
        capabilities: Self::Capabilities<'_>,
        deadline: AbsoluteDeadline,
    ) -> Result<(), SessionTransportError>;
}

/// Receiver-only capability-record receive operation on the accepted owner.
///
/// The returned backend value must immediately own every installed native
/// capability and keep it transaction-bound until that value is destroyed or
/// consumed by a later complete import state machine.
pub(crate) trait ReceiverCapabilityTransport: AuthenticatedZeroRightsTransport {
    type ReceivedCapabilities;

    fn receive_capability_record(
        &mut self,
        expected: &CapabilityFrame,
        deadline: AbsoluteDeadline,
    ) -> Result<Self::ReceivedCapabilities, SessionTransportError>;
}

/// Coordinator-only owned-child lifecycle operations.
pub(crate) trait OwnedChildLifecycle: sealed::Sealed {
    fn terminate_and_reap(
        &mut self,
        deadline: AbsoluteDeadline,
    ) -> Result<(), SessionTransportError>;
}

mod accepted_control;
mod reaper_ownership;

#[cfg(target_os = "linux")]
#[deny(dead_code)]
pub(crate) mod linux;
#[cfg(target_os = "linux")]
#[allow(dead_code)]
pub(crate) mod linux_vnext;
#[cfg(target_os = "macos")]
#[allow(dead_code)]
pub(crate) mod macos;
#[cfg(target_os = "windows")]
#[allow(dead_code)]
pub(crate) mod windows;

#[cfg(target_os = "linux")]
pub(crate) fn mint_incarnation() -> Result<[u8; 16], ()> {
    let mut bytes = [0_u8; 16];
    let mut filled = 0;
    while filled < bytes.len() {
        // SAFETY: the remaining byte slice is writable for the supplied length.
        let result = unsafe {
            libc::getrandom(bytes[filled..].as_mut_ptr().cast(), bytes.len() - filled, 0)
        };
        if result < 0 {
            if std::io::Error::last_os_error().kind() == std::io::ErrorKind::Interrupted {
                continue;
            }
            return Err(());
        }
        if result == 0 {
            return Err(());
        }
        filled += usize::try_from(result).map_err(|_| ())?;
    }
    (bytes != [0; 16]).then_some(bytes).ok_or(())
}

#[cfg(target_os = "macos")]
pub(crate) fn mint_incarnation() -> Result<[u8; 16], ()> {
    unsafe extern "C" {
        fn arc4random_buf(buffer: *mut core::ffi::c_void, length: usize);
    }
    let mut bytes = [0_u8; 16];
    // SAFETY: `bytes` is writable for exactly its length; arc4random_buf has no
    // failure return and fills caller-owned storage.
    unsafe { arc4random_buf(bytes.as_mut_ptr().cast(), bytes.len()) };
    (bytes != [0; 16]).then_some(bytes).ok_or(())
}

#[cfg(target_os = "windows")]
pub(crate) fn mint_incarnation() -> Result<[u8; 16], ()> {
    use windows_sys::Win32::Security::Cryptography::{
        BCRYPT_USE_SYSTEM_PREFERRED_RNG, BCryptGenRandom,
    };
    let mut bytes = [0_u8; 16];
    // SAFETY: the system-preferred RNG accepts a null algorithm handle and the
    // output buffer is writable for exactly the supplied length.
    let status = unsafe {
        BCryptGenRandom(
            core::ptr::null_mut(),
            bytes.as_mut_ptr(),
            bytes.len() as u32,
            BCRYPT_USE_SYSTEM_PREFERRED_RNG,
        )
    };
    if status != 0 || bytes == [0; 16] {
        return Err(());
    }
    Ok(bytes)
}

#[cfg(test)]
#[path = "mod_test.rs"]
mod receipt_tests;

#[cfg(test)]
#[path = "accepted_control_test.rs"]
mod accepted_control_tests;

#[cfg(all(test, loom))]
#[path = "reaper_ownership_test.rs"]
mod reaper_ownership_tests;