Skip to main content

detcore/
random.rs

1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 * This source code is licensed under the BSD-style license found in the
4 * LICENSE file in the root directory of this source tree.
5 */
6
7//! Shared guest random-state and memory operations, independent of a backend.
8//! These synchronous operations preserve draws even when a later write fails.
9
10use std::collections::hash_map::DefaultHasher;
11use std::hash::Hash;
12use std::hash::Hasher;
13
14use rand::RngExt as _;
15use rand::SeedableRng as _;
16use rand_pcg::Pcg64Mcg;
17use reverie::Error;
18use reverie::syscalls::AddrMut;
19use reverie::syscalls::Errno;
20use reverie::syscalls::Getrandom;
21use reverie::syscalls::MemoryAccess;
22use serde::Deserialize;
23use serde::Serialize;
24use sha2::Digest as _;
25use sha2::Sha256;
26
27use crate::detlog;
28use crate::types::DetTid;
29
30pub(crate) const RANDOM_FILL_CHUNK_BYTES: usize = 4096;
31
32/// SplitMix64's output function (Steele, Lea and Flood, "Fast Splittable
33/// Pseudorandom Number Generators", OOPSLA 2014): every input bit changes
34/// about half of the output bits.
35pub(crate) fn splitmix64(mut z: u64) -> u64 {
36    z = z.wrapping_add(0x9e37_79b9_7f4a_7c15);
37    z = (z ^ (z >> 30)).wrapping_mul(0xbf58_476d_1ce4_e5b9);
38    z = (z ^ (z >> 27)).wrapping_mul(0x94d0_49bb_1331_11eb);
39    z ^ (z >> 31)
40}
41
42/// A user-access random copy failed for a reason other than a guest fault.
43///
44/// Memory may already contain a copied prefix, or even the entire attempted
45/// write. This is a failed run, not a guest errno or a successful short read.
46#[derive(Debug)]
47pub struct RandomCopyFailure {
48    errno: Errno,
49}
50
51impl RandomCopyFailure {
52    /// The exact error returned by the backend's user-access copy.
53    pub fn errno(&self) -> Errno {
54        self.errno
55    }
56}
57
58impl std::fmt::Display for RandomCopyFailure {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        write!(f, "random user-access copy failed: {}", self.errno)?;
61        if self.errno == Errno::EPERM {
62            f.write_str(
63                "; for a non-dumpable ptrace guest, retry with Hermit's default namespace \
64                 configuration (omit --no-namespace); embedders must check tracing permissions \
65                 in the guest's user namespace",
66            )?;
67        }
68        Ok(())
69    }
70}
71
72impl std::error::Error for RandomCopyFailure {}
73
74pub(crate) fn copy_error(error: Errno) -> Error {
75    match error {
76        Errno::EFAULT => Error::Errno(error),
77        errno => Error::Tool(anyhow::Error::new(RandomCopyFailure { errno })),
78    }
79}
80
81pub(crate) fn is_copy_failure(error: &Error) -> bool {
82    matches!(error, Error::Tool(inner) if inner.is::<RandomCopyFailure>())
83}
84
85/// Construct the root guest stream from its configured seed, without creating
86/// a thread or discovering any process metadata.
87pub fn root_prng(seed: u64) -> Pcg64Mcg {
88    Pcg64Mcg::seed_from_u64(seed)
89}
90
91/// Fixed maximum for the backend-independent initial random-state handoff.
92pub const MAX_INITIAL_STATE_BYTES: usize = 4096;
93
94/// Version 2 carries the AT_RANDOM bytes so that post-exec emits their record.
95/// Version 3 also carries the loader's getrandom fills, for the same reason.
96/// Version 4 carries its resource-limit reads too, in one ordered list with the
97/// fills ([`EarlyRequest`]).
98const INITIAL_STATE_VERSION: u32 = 4;
99
100/// Most getrandom fills a loader may serve before post-exec. glibc's early
101/// initialization makes one (the malloc tcache key); the bound keeps the
102/// handoff within [`MAX_INITIAL_STATE_BYTES`].
103pub const MAX_EARLY_GETRANDOM: usize = 32;
104
105/// Most resource-limit reads a loader may serve before post-exec. glibc's
106/// startup makes one (RLIMIT_STACK, for the default thread stack size); the
107/// bound keeps the handoff within [`MAX_INITIAL_STATE_BYTES`].
108pub const MAX_EARLY_LIMIT_READS: usize = 4;
109
110/// The syscall that made an early resource-limit read. Their records differ,
111/// as when Detcore's own handlers serve them: `prlimit64` records the read,
112/// `getrlimit` records nothing.
113#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
114#[serde(deny_unknown_fields)]
115pub enum LimitReadCall {
116    /// `prlimit64(pid, resource, NULL, old)`.
117    Prlimit64 {
118        /// The `pid` argument, as its record shows it.
119        pid: i32,
120    },
121    /// `getrlimit(resource, limit)`.
122    Getrlimit,
123}
124
125/// One request a loader served before post-exec. None emits a record when
126/// served, because Detcore's root thread does not exist yet: post-exec emits
127/// their records after the AT_RANDOM record, in the order they were served.
128#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
129#[serde(deny_unknown_fields)]
130pub enum EarlyRequest {
131    /// A getrandom fill ([`getrandom_unrecorded`]).
132    Getrandom(RandomFill),
133    /// A resource-limit read, answered from Detcore's initial limits
134    /// ([`crate::initial_resource_limit`]).
135    LimitRead {
136        /// The syscall that read the limit.
137        call: LimitReadCall,
138        /// The resource read.
139        resource: u32,
140        /// The soft limit returned.
141        current: u64,
142        /// The hard limit returned.
143        maximum: u64,
144    },
145}
146
147/// Whether `requests` is within the bounds a loader's handoff may carry.
148fn early_requests_within_bounds(requests: &[EarlyRequest]) -> bool {
149    let fills = requests
150        .iter()
151        .filter(|request| matches!(request, EarlyRequest::Getrandom(_)))
152        .count();
153    fills <= MAX_EARLY_GETRANDOM && requests.len() - fills <= MAX_EARLY_LIMIT_READS
154}
155
156/// One fill of guest memory from the stream: the bytes written and, in debug
157/// builds, the hash its record logs (zero in release builds, which log none).
158#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
159#[serde(deny_unknown_fields)]
160pub struct RandomFill {
161    /// Bytes written to guest memory.
162    pub written: usize,
163    /// Hash of the bytes written.
164    pub hash: u64,
165}
166
167/// Identity of the sole initial image whose real auxv was already written.
168/// Backends must authenticate this identity before constructing a handoff.
169#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
170#[serde(deny_unknown_fields)]
171pub struct InitialImage {
172    /// Initial physical process ID in the backend's guest PID namespace.
173    pub pid: i32,
174    /// Linux process generation from field22 of this process's proc stat.
175    pub start_time_ticks: u64,
176    /// Actual writable16-byte target from the authenticated initial auxv.
177    pub at_random: usize,
178}
179
180impl InitialImage {
181    fn validate(self) -> Result<(), Errno> {
182        if self.pid <= 0
183            || self.start_time_ticks == 0
184            || self.at_random == 0
185            || self.at_random.checked_add(16).is_none()
186        {
187            return Err(Errno::EPROTO);
188        }
189        Ok(())
190    }
191}
192
193#[derive(Serialize, Deserialize)]
194#[serde(deny_unknown_fields)]
195struct InitialRandomState {
196    version: u32,
197    configuration: [u8; 32],
198    image: InitialImage,
199    state: LoaderState,
200}
201
202/// Authenticated loader result. Continuations carry no random state and must
203/// leave the ordinary newly constructed thread completely unchanged.
204#[derive(Serialize, Deserialize)]
205#[serde(deny_unknown_fields)]
206pub enum LoaderState {
207    /// Initial dynamic image: the actual auxv write and early requests ran.
208    InitialRandom {
209        /// Stream after the real auxv and getrandom operations.
210        prng: Pcg64Mcg,
211        /// The bytes the real auxv write stored at AT_RANDOM. That write emits
212        /// no record; the thread's post-exec callback emits it from these
213        /// bytes (`record_initial_auxv`), where every backend emits it.
214        at_random_value: [u8; 16],
215        /// The loader's getrandom fills and resource-limit reads, in order.
216        /// They emit no record either: post-exec emits theirs after the
217        /// AT_RANDOM record, as on a backend whose own handlers serve them.
218        early_requests: Vec<EarlyRequest>,
219    },
220    /// A later real kernel exec observed in this owned process lineage.
221    ObservedExecContinuation,
222    /// The held initial program takes the existing static-loader path.
223    InitialStaticLegacy,
224}
225
226fn configuration_identity(config: &crate::Config) -> Result<[u8; 32], Errno> {
227    struct HashWriter(Sha256);
228    impl std::io::Write for HashWriter {
229        fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
230            self.0.update(bytes);
231            Ok(bytes.len())
232        }
233        fn flush(&mut self) -> std::io::Result<()> {
234            Ok(())
235        }
236    }
237    let mut writer = HashWriter(Sha256::new());
238    writer.0.update(b"hermit-initial-random-state-v1\0");
239    writer.0.update(crate::config_wire_fingerprint());
240    // Include actual effective values, not merely Config's type fingerprint.
241    // Stream into the digest instead of allocating a second config copy.
242    serde_json::to_writer(&mut writer, config).map_err(|_| Errno::EPROTO)?;
243    Ok(writer.0.finalize().into())
244}
245
246/// Encode only the actual PRNG, the completed auxv identity, the bytes
247/// written there ([`write_initial_auxv`]) and the loader's early requests
248/// ([`EarlyRequest`]), whose records are still to be emitted. No clock,
249/// metadata, chaos RNG or scheduler state is transferred.
250pub fn encode_initial_state(
251    config: &crate::Config,
252    image: InitialImage,
253    prng: &Pcg64Mcg,
254    at_random_value: [u8; 16],
255    early_requests: &[EarlyRequest],
256) -> Result<Vec<u8>, Errno> {
257    image.validate()?;
258    if !early_requests_within_bounds(early_requests) {
259        return Err(Errno::EOVERFLOW);
260    }
261    let value = InitialRandomState {
262        version: INITIAL_STATE_VERSION,
263        configuration: configuration_identity(config)?,
264        image,
265        state: LoaderState::InitialRandom {
266            prng: prng.clone(),
267            at_random_value,
268            early_requests: early_requests.to_vec(),
269        },
270    };
271    encode_state(value)
272}
273
274/// Encode a supervisor-authenticated legacy path without transferring RNG,
275/// clock, metadata or any auxiliary-vector completion fact.
276pub fn encode_continuation(
277    config: &crate::Config,
278    image: InitialImage,
279    state: LoaderState,
280) -> Result<Vec<u8>, Errno> {
281    image.validate()?;
282    if matches!(state, LoaderState::InitialRandom { .. }) {
283        return Err(Errno::EPROTO);
284    }
285    encode_state(InitialRandomState {
286        version: INITIAL_STATE_VERSION,
287        configuration: configuration_identity(config)?,
288        image,
289        state,
290    })
291}
292
293fn encode_state(value: InitialRandomState) -> Result<Vec<u8>, Errno> {
294    let bytes = serde_json::to_vec(&value).map_err(|_| Errno::EPROTO)?;
295    if bytes.is_empty() || bytes.len() > MAX_INITIAL_STATE_BYTES {
296        return Err(Errno::EOVERFLOW);
297    }
298    Ok(bytes)
299}
300
301/// Decode an exact canonical, configuration- and image-bound loader result.
302/// The backend must obtain these bytes only from its authenticated callback.
303pub fn decode_loader_state(
304    bytes: &[u8],
305    config: &crate::Config,
306    expected: InitialImage,
307) -> Result<LoaderState, Errno> {
308    expected.validate()?;
309    if bytes.is_empty() || bytes.len() > MAX_INITIAL_STATE_BYTES {
310        return Err(Errno::EPROTO);
311    }
312    let value: InitialRandomState = serde_json::from_slice(bytes).map_err(|_| Errno::EPROTO)?;
313    if value.version != INITIAL_STATE_VERSION
314        || value.configuration != configuration_identity(config)?
315        || value.image != expected
316        || serde_json::to_vec(&value).map_err(|_| Errno::EPROTO)? != bytes
317    {
318        // Re-encoding also rejects trailing whitespace/bytes and alternate
319        // representations. A rejected handoff is never replaced with a seed.
320        return Err(Errno::EPROTO);
321    }
322    Ok(value.state)
323}
324
325/// The decoded initial random state: the stream, the AT_RANDOM bytes and the
326/// loader's early requests whose records post-exec emits.
327pub(crate) type InitialRandom = (Pcg64Mcg, [u8; 16], Vec<EarlyRequest>);
328
329/// The records post-exec emits for a loader's early work: the AT_RANDOM bytes,
330/// then the early requests in order.
331pub(crate) type EarlyRandomRecords = ([u8; 16], Vec<EarlyRequest>);
332
333pub(crate) fn decode_initial_state(
334    bytes: &[u8],
335    config: &crate::Config,
336    expected: InitialImage,
337) -> Result<InitialRandom, Errno> {
338    match decode_loader_state(bytes, config, expected)? {
339        LoaderState::InitialRandom {
340            prng,
341            at_random_value,
342            early_requests,
343        } if early_requests_within_bounds(&early_requests) => {
344            Ok((prng, at_random_value, early_requests))
345        }
346        LoaderState::InitialRandom { .. } => Err(Errno::EPROTO),
347        LoaderState::ObservedExecContinuation | LoaderState::InitialStaticLegacy => {
348            Err(Errno::EPROTO)
349        }
350    }
351}
352
353const GETRANDOM_ALLOWED_FLAGS: u32 = libc::GRND_NONBLOCK | libc::GRND_RANDOM | libc::GRND_INSECURE;
354
355// AUTONOMOUS-BOT-IMPLEMENTED
356// TODO-HUMAN-REVIEW(#545): Confirm getrandom flag, stream, and fault semantics.
357pub(crate) fn validate_getrandom_flags(flags: usize) -> Result<(), Errno> {
358    let flags = flags as u32;
359    let random = flags & libc::GRND_RANDOM != 0;
360    let insecure = flags & libc::GRND_INSECURE != 0;
361
362    if flags & !GETRANDOM_ALLOWED_FLAGS != 0 || (random && insecure) {
363        Err(Errno::EINVAL)
364    } else {
365        Ok(())
366    }
367}
368
369/// Linux's import_ubuf clamps a getrandom request to MAX_RW_COUNT.
370pub(crate) fn getrandom_request_len(requested: usize) -> usize {
371    requested.min(crate::page::max_rw_count())
372}
373
374pub(crate) fn write_random_chunk(
375    memory: &mut impl MemoryAccess,
376    remote_buf: AddrMut<u8>,
377    local_buf: &[u8],
378) -> Result<usize, Errno> {
379    const PTRACE_WORD_SPLIT: usize = std::mem::size_of::<u64>() / 2;
380
381    if local_buf.len() != std::mem::size_of::<u64>() {
382        return memory.write_with_user_access(remote_buf, local_buf);
383    }
384
385    // Preserve the existing eight-byte split and its prefix semantics. Every
386    // length now uses the explicit user-access capability; debugger writes can
387    // bypass protection for other sizes too (including the KVM backend).
388    let first = memory.write_with_user_access(remote_buf, &local_buf[..PTRACE_WORD_SPLIT])?;
389    if first < PTRACE_WORD_SPLIT {
390        return Ok(first);
391    }
392    let Some(second_buf) = remote_buf
393        .as_raw()
394        .checked_add(PTRACE_WORD_SPLIT)
395        .and_then(AddrMut::<u8>::from_raw)
396    else {
397        return Ok(first);
398    };
399    match memory.write_with_user_access(second_buf, &local_buf[PTRACE_WORD_SPLIT..]) {
400        Ok(second) => Ok(first + second),
401        Err(Errno::EFAULT) => Ok(first),
402        Err(error) => Err(error),
403    }
404}
405
406/// Fill guest memory from the same stream/chunk/write algorithm used by the
407/// normal Detcore handler. No syscall/scheduler accounting is performed here.
408pub fn fill_bytes(
409    prng: &mut Pcg64Mcg,
410    memory: impl MemoryAccess,
411    remote_buf: AddrMut<u8>,
412    len: usize,
413    dettid: DetTid,
414    source: &str,
415) -> Result<usize, Error> {
416    let fill = fill_bytes_unrecorded(prng, memory, remote_buf, len)?;
417    record_fill(dettid, source, fill);
418    Ok(fill.written)
419}
420
421/// [`fill_bytes`] without its record, returning what the record would log.
422fn fill_bytes_unrecorded(
423    prng: &mut Pcg64Mcg,
424    mut memory: impl MemoryAccess,
425    remote_buf: AddrMut<u8>,
426    len: usize,
427) -> Result<RandomFill, Error> {
428    let mut local_words = [0_u64; RANDOM_FILL_CHUNK_BYTES / std::mem::size_of::<u64>()];
429    let mut hasher = DefaultHasher::new();
430    let mut written = 0;
431
432    while written < len {
433        let remote_chunk = match remote_buf
434            .as_raw()
435            .checked_add(written)
436            .and_then(AddrMut::<u8>::from_raw)
437        {
438            Some(address) => address,
439            None if written == 0 => return Err(Errno::EFAULT.into()),
440            None => break,
441        };
442        let chunk_len = (len - written).min(RANDOM_FILL_CHUNK_BYTES);
443        // Keep the existing aligned scratch and full attempted-chunk draw.
444        let local_buf = unsafe {
445            std::slice::from_raw_parts_mut(local_words.as_mut_ptr().cast::<u8>(), chunk_len)
446        };
447        prng.fill(local_buf);
448        let n = match write_random_chunk(&mut memory, remote_chunk, local_buf) {
449            Ok(n) => n,
450            Err(Errno::EFAULT) if written > 0 => break,
451            Err(error) => return Err(copy_error(error)),
452        };
453        if n == 0 {
454            if written == 0 {
455                return Err(Errno::EFAULT.into());
456            }
457            break;
458        }
459        if cfg!(debug_assertions) {
460            Hash::hash_slice(&local_buf[..n], &mut hasher);
461        }
462        written += n;
463        if n < chunk_len {
464            break;
465        }
466    }
467
468    Ok(RandomFill {
469        written,
470        hash: if cfg!(debug_assertions) {
471            hasher.finish()
472        } else {
473            0
474        },
475    })
476}
477
478/// The record of one fill of guest memory, logged in debug builds only.
479fn record_fill(dettid: DetTid, source: &str, fill: RandomFill) {
480    if cfg!(debug_assertions) {
481        detlog!(
482            "[dtid {}] USER RAND [{}] Filled guest memory with {} random bytes, hash of bytes: {}",
483            dettid,
484            source,
485            fill.written,
486            fill.hash
487        );
488    }
489}
490
491/// The record of a getrandom fill that a loader served before post-exec
492/// ([`getrandom_unrecorded`]), emitted by post-exec after the AT_RANDOM record.
493pub(crate) fn record_early_getrandom(dettid: DetTid, fill: RandomFill) {
494    record_fill(dettid, "getrandom", fill);
495}
496
497/// Apply getrandom's existing flag, length, null-buffer and fill semantics.
498pub fn getrandom(
499    prng: &mut Pcg64Mcg,
500    memory: impl MemoryAccess,
501    dettid: DetTid,
502    call: Getrandom,
503) -> Result<i64, Error> {
504    let (result, fill) = getrandom_unrecorded(prng, memory, call)?;
505    if let Some(fill) = fill {
506        record_fill(dettid, "getrandom", fill);
507    }
508    Ok(result)
509}
510
511/// [`getrandom`] for a loader that runs before the thread's post-exec
512/// callback: it emits no record and returns the fill, if any, whose record
513/// [`getrandom`] would have emitted. The loader hands the fills over with
514/// [`encode_initial_state`], and post-exec emits their records after the
515/// AT_RANDOM record, where every other backend's handler emits them.
516pub fn getrandom_unrecorded(
517    prng: &mut Pcg64Mcg,
518    memory: impl MemoryAccess,
519    call: Getrandom,
520) -> Result<(i64, Option<RandomFill>), Error> {
521    validate_getrandom_flags(call.flags())?;
522    let len = getrandom_request_len(call.buflen());
523    if len == 0 {
524        return Ok((0, None));
525    }
526    let buf = call.buf().ok_or(Errno::EFAULT)?;
527    let fill = fill_bytes_unrecorded(prng, memory, buf, len)?;
528    Ok((fill.written as i64, Some(fill)))
529}
530
531/// Draw and write the actual initial auxv bytes. A write failure preserves the
532/// consumed PRNG state, as in the normal post-exec callback.
533pub fn initialize_auxv(
534    prng: &mut Pcg64Mcg,
535    mut memory: impl MemoryAccess,
536    pointer: AddrMut<u8>,
537    dettid: DetTid,
538) -> Result<(), Errno> {
539    let bytes: [u8; 16] = prng.random();
540    record_initial_auxv(dettid, &bytes);
541    memory.write_value(pointer.cast::<[u8; 16]>(), &bytes)
542}
543
544/// Draw and write the initial auxv bytes before the thread's post-exec
545/// callback, emitting no record, and return them for the handoff
546/// ([`encode_initial_state`]). Post-exec then emits the record, so a run's
547/// records come in the same order whichever backend wrote the bytes, and the
548/// root thread's seeding records precede it on every backend.
549pub fn write_initial_auxv(
550    prng: &mut Pcg64Mcg,
551    mut memory: impl MemoryAccess,
552    pointer: AddrMut<u8>,
553) -> Result<[u8; 16], Errno> {
554    let bytes: [u8; 16] = prng.random();
555    memory.write_value(pointer.cast::<[u8; 16]>(), &bytes)?;
556    Ok(bytes)
557}
558
559/// The record of the initial auxv bytes, emitted once per initial image.
560pub(crate) fn record_initial_auxv(dettid: DetTid, bytes: &[u8; 16]) {
561    detlog!(
562        "[post_exec, dtid {}] init auxv AT_RANDOM value to {:?}",
563        dettid,
564        bytes
565    );
566}
567
568#[cfg(test)]
569mod tests {
570    include!("random/user_access_tests.rs");
571    use std::io::IoSlice;
572    use std::io::IoSliceMut;
573
574    use reverie::syscalls::Syscall;
575    use reverie::syscalls::SyscallArgs;
576    use reverie::syscalls::Sysno;
577
578    use super::*;
579
580    #[derive(Clone, Copy)]
581    struct OwnMemory;
582
583    impl MemoryAccess for OwnMemory {
584        fn write_with_user_access(
585            &mut self,
586            addr: AddrMut<u8>,
587            bytes: &[u8],
588        ) -> Result<usize, Errno> {
589            addr.as_raw()
590                .checked_add(bytes.len())
591                .ok_or(Errno::EFAULT)?;
592            if bytes.is_empty() {
593                return Ok(0);
594            }
595            let local = libc::iovec {
596                iov_base: bytes.as_ptr().cast_mut().cast(),
597                iov_len: bytes.len(),
598            };
599            let remote = libc::iovec {
600                iov_base: addr.as_raw() as *mut libc::c_void,
601                iov_len: bytes.len(),
602            };
603            let n = unsafe { libc::process_vm_writev(libc::getpid(), &local, 1, &remote, 1, 0) };
604            if n < 0 {
605                Err(Errno::last())
606            } else {
607                Ok(n as usize)
608            }
609        }
610        fn read_vectored(
611            &self,
612            remote: &[IoSlice],
613            local: &mut [IoSliceMut],
614        ) -> Result<usize, Errno> {
615            let n = unsafe {
616                libc::process_vm_readv(
617                    libc::getpid(),
618                    local.as_ptr().cast(),
619                    local.len() as _,
620                    remote.as_ptr().cast(),
621                    remote.len() as _,
622                    0,
623                )
624            };
625            if n < 0 {
626                Err(Errno::last())
627            } else {
628                Ok(n as usize)
629            }
630        }
631        fn write_vectored(
632            &mut self,
633            local: &[IoSlice],
634            remote: &mut [IoSliceMut],
635        ) -> Result<usize, Errno> {
636            let n = unsafe {
637                libc::process_vm_writev(
638                    libc::getpid(),
639                    local.as_ptr().cast(),
640                    local.len() as _,
641                    remote.as_ptr().cast(),
642                    remote.len() as _,
643                    0,
644                )
645            };
646            if n < 0 {
647                Err(Errno::last())
648            } else {
649                Ok(n as usize)
650            }
651        }
652    }
653
654    struct SecondHalfFailure {
655        error: Errno,
656        bytes: [u8; 8],
657        writes: Vec<(usize, Vec<u8>)>,
658    }
659
660    impl MemoryAccess for SecondHalfFailure {
661        fn read_vectored(
662            &self,
663            _remote: &[IoSlice],
664            _local: &mut [IoSliceMut],
665        ) -> Result<usize, Errno> {
666            panic!("random copying must not read guest memory")
667        }
668
669        fn write_vectored(
670            &mut self,
671            _local: &[IoSlice],
672            _remote: &mut [IoSliceMut],
673        ) -> Result<usize, Errno> {
674            panic!("random copying must use the user-access capability")
675        }
676
677        fn write_with_user_access(
678            &mut self,
679            addr: AddrMut<u8>,
680            buf: &[u8],
681        ) -> Result<usize, Errno> {
682            self.writes.push((addr.as_raw(), buf.to_vec()));
683            match self.writes.len() {
684                1 => {
685                    assert_eq!(addr.as_raw(), 0x1000);
686                    assert_eq!(buf.len(), 4);
687                    self.bytes[..4].copy_from_slice(buf);
688                    Ok(4)
689                }
690                2 => {
691                    assert_eq!(addr.as_raw(), 0x1004);
692                    assert_eq!(buf.len(), 4);
693                    Err(self.error)
694                }
695                _ => panic!("random copying retried a failed write"),
696            }
697        }
698    }
699
700    #[test]
701    fn eight_byte_random_copy_distinguishes_faults_from_backend_errors() {
702        for (error, expected) in [(Errno::EFAULT, Ok(4)), (Errno::EIO, Err(Errno::EIO))] {
703            let mut memory = SecondHalfFailure {
704                error,
705                bytes: [0xa5; 8],
706                writes: Vec::new(),
707            };
708            let address = AddrMut::from_raw(0x1000).unwrap();
709
710            assert_eq!(
711                write_random_chunk(&mut memory, address, &[1, 2, 3, 4, 5, 6, 7, 8]),
712                expected
713            );
714            assert_eq!(memory.bytes, [1, 2, 3, 4, 0xa5, 0xa5, 0xa5, 0xa5]);
715            assert_eq!(
716                memory.writes,
717                [(0x1000, vec![1, 2, 3, 4]), (0x1004, vec![5, 6, 7, 8])]
718            );
719        }
720    }
721
722    struct Pages {
723        address: *mut u8,
724        size: usize,
725    }
726    impl Pages {
727        fn new() -> Self {
728            let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
729            assert!(size >= 4096);
730            let address = unsafe {
731                libc::mmap(
732                    std::ptr::null_mut(),
733                    2 * size,
734                    libc::PROT_READ | libc::PROT_WRITE,
735                    libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
736                    -1,
737                    0,
738                )
739            };
740            assert_ne!(address, libc::MAP_FAILED);
741            unsafe {
742                std::ptr::write_bytes(address.cast::<u8>(), 0xa5, 2 * size);
743            }
744            assert_eq!(
745                unsafe { libc::mprotect(address.add(size), size, libc::PROT_READ) },
746                0
747            );
748            Self {
749                address: address.cast(),
750                size,
751            }
752        }
753        fn address(&self, offset: usize) -> AddrMut<'static, u8> {
754            assert!(offset < self.size * 2);
755            AddrMut::from_raw(self.address as usize + offset).unwrap()
756        }
757        fn bytes(&self, offset: usize, length: usize) -> Vec<u8> {
758            assert!(offset + length <= 2 * self.size);
759            unsafe { std::slice::from_raw_parts(self.address.add(offset), length) }.to_vec()
760        }
761    }
762    impl Drop for Pages {
763        fn drop(&mut self) {
764            assert_eq!(
765                unsafe { libc::munmap(self.address.cast(), 2 * self.size) },
766                0
767            );
768        }
769    }
770
771    fn call(buffer: usize, length: usize, flags: usize) -> Getrandom {
772        let Syscall::Getrandom(call) = Syscall::from_raw(
773            Sysno::getrandom,
774            SyscallArgs::new(buffer, length, flags, 0, 0, 0),
775        ) else {
776            unreachable!()
777        };
778        call
779    }
780    // Existing guest-errno cases must still be guest errnos. A terminal Tool
781    // error here is a failed assertion, never a successful errno projection.
782    fn guest_getrandom(
783        prng: &mut Pcg64Mcg,
784        memory: impl MemoryAccess,
785        tid: DetTid,
786        call: Getrandom,
787    ) -> Result<i64, Errno> {
788        super::getrandom(prng, memory, tid, call).map_err(|error| match error {
789            Error::Errno(errno) => errno,
790            other => panic!("unexpected terminal failure in guest-errno companion: {other:?}"),
791        })
792    }
793    fn same_state(a: &Pcg64Mcg, b: &Pcg64Mcg) {
794        assert_eq!(
795            serde_json::to_vec(a).unwrap(),
796            serde_json::to_vec(b).unwrap()
797        );
798    }
799
800    #[test]
801    fn initial_handoff_preserves_unrelated_state_and_consumes_only_auxv_fact() {
802        let pages = Pages::new();
803        let config = crate::Config::default();
804        let tid = DetTid::from_raw(3);
805        let image = InitialImage {
806            pid: 3,
807            start_time_ticks: 1234,
808            at_random: pages.address(0).as_raw(),
809        };
810        let mut stream = root_prng(config.rng_seed());
811        let written = write_initial_auxv(&mut stream, OwnMemory, pages.address(0)).unwrap();
812        assert_eq!(pages.bytes(0, 16), written);
813        // The early write draws exactly what post-exec's own write would have.
814        let mut ordinary = root_prng(config.rng_seed());
815        initialize_auxv(&mut ordinary, OwnMemory, pages.address(64), tid).unwrap();
816        assert_eq!(pages.bytes(64, 16), written);
817        // A loader's early getrandom draws and writes what the thread's own
818        // handler would have, and returns the fill that handler would have
819        // recorded, for post-exec to record instead.
820        let mut handler = stream.clone();
821        let mut recorded = stream.clone();
822        assert_eq!(
823            guest_getrandom(
824                &mut handler,
825                OwnMemory,
826                tid,
827                call(pages.address(48).as_raw(), 8, 1),
828            ),
829            Ok(8)
830        );
831        let (filled, fill) = getrandom_unrecorded(
832            &mut stream,
833            OwnMemory,
834            call(pages.address(32).as_raw(), 8, 1),
835        )
836        .unwrap();
837        same_state(&stream, &handler);
838        assert_eq!(pages.bytes(32, 8), pages.bytes(48, 8));
839        let fill = fill.expect("a nonempty getrandom fill returns its record");
840        assert_eq!(filled, 8);
841        assert_eq!(
842            fill_bytes_unrecorded(&mut recorded, OwnMemory, pages.address(96), 8).unwrap(),
843            fill,
844            "the fill is what the handler's own record logs"
845        );
846        let mut empty = stream.clone();
847        assert_eq!(
848            getrandom_unrecorded(&mut empty, OwnMemory, call(0, 0, 0)).unwrap(),
849            (0, None),
850            "an empty getrandom has no record"
851        );
852        same_state(&empty, &stream);
853        // An early stack-limit read, as glibc's startup makes, between two
854        // fills: the order survives the handoff.
855        let (stack_current, stack_maximum) =
856            crate::initial_resource_limit(reverie::syscalls::linux_abi::RLIMIT_STACK).unwrap();
857        let limit_read = EarlyRequest::LimitRead {
858            call: LimitReadCall::Prlimit64 { pid: 0 },
859            resource: reverie::syscalls::linux_abi::RLIMIT_STACK,
860            current: stack_current,
861            maximum: stack_maximum,
862        };
863        let requests = [
864            EarlyRequest::Getrandom(fill),
865            limit_read,
866            EarlyRequest::Getrandom(fill),
867        ];
868        let encoded = encode_initial_state(&config, image, &stream, written, &requests).unwrap();
869        let (decoded, decoded_value, decoded_requests) =
870            decode_initial_state(&encoded, &config, image).unwrap();
871        same_state(&decoded, &stream);
872        assert_eq!(decoded_value, written);
873        assert_eq!(decoded_requests, requests);
874        // The bounds keep the largest handoff within its limit, and neither
875        // side accepts more of either kind.
876        let widest_fill = EarlyRequest::Getrandom(RandomFill {
877            written: usize::MAX,
878            hash: u64::MAX,
879        });
880        let widest_read = EarlyRequest::LimitRead {
881            call: LimitReadCall::Prlimit64 { pid: i32::MIN },
882            resource: u32::MAX,
883            current: u64::MAX,
884            maximum: u64::MAX,
885        };
886        let widest: Vec<EarlyRequest> = std::iter::repeat_n(widest_fill, MAX_EARLY_GETRANDOM)
887            .chain(std::iter::repeat_n(widest_read, MAX_EARLY_LIMIT_READS))
888            .collect();
889        let full = encode_initial_state(&config, image, &stream, written, &widest).unwrap();
890        assert_eq!(
891            decode_initial_state(&full, &config, image).unwrap().2,
892            widest
893        );
894        for excess in [
895            vec![EarlyRequest::Getrandom(fill); MAX_EARLY_GETRANDOM + 1],
896            vec![limit_read; MAX_EARLY_LIMIT_READS + 1],
897        ] {
898            assert_eq!(
899                encode_initial_state(&config, image, &stream, written, &excess),
900                Err(Errno::EOVERFLOW)
901            );
902            let too_many = encode_state(InitialRandomState {
903                version: INITIAL_STATE_VERSION,
904                configuration: configuration_identity(&config).unwrap(),
905                image,
906                state: LoaderState::InitialRandom {
907                    prng: stream.clone(),
908                    at_random_value: written,
909                    early_requests: excess,
910                },
911            })
912            .unwrap();
913            assert!(matches!(
914                decode_initial_state(&too_many, &config, image),
915                Err(Errno::EPROTO)
916            ));
917        }
918        for bad in [
919            Vec::new(),
920            [encoded.as_slice(), b" "].concat(),
921            // Version 3 carried only getrandom fills, version 2 none and version
922            // 1 no AT_RANDOM bytes; neither they nor a later version is
923            // accepted.
924            String::from_utf8(encoded.clone())
925                .unwrap()
926                .replace("\"version\":4", "\"version\":1")
927                .into_bytes(),
928            String::from_utf8(encoded.clone())
929                .unwrap()
930                .replace("\"version\":4", "\"version\":2")
931                .into_bytes(),
932            String::from_utf8(encoded.clone())
933                .unwrap()
934                .replace("\"version\":4", "\"version\":3")
935                .into_bytes(),
936            String::from_utf8(encoded.clone())
937                .unwrap()
938                .replace("\"version\":4", "\"version\":5")
939                .into_bytes(),
940            // A body without the early requests, under the current version.
941            String::from_utf8(encoded.clone())
942                .unwrap()
943                .replace(
944                    &format!(
945                        ",\"early_requests\":{}",
946                        serde_json::to_string(&requests).unwrap()
947                    ),
948                    "",
949                )
950                .into_bytes(),
951        ] {
952            assert_ne!(bad, encoded);
953            assert!(decode_initial_state(&bad, &config, image).is_err());
954        }
955        for wrong in [
956            InitialImage { pid: 4, ..image },
957            InitialImage {
958                start_time_ticks: 1235,
959                ..image
960            },
961            InitialImage {
962                at_random: image.at_random + 16,
963                ..image
964            },
965        ] {
966            assert!(decode_initial_state(&encoded, &config, wrong).is_err());
967        }
968        let mut different = config.clone();
969        different.virtualize_time = !different.virtualize_time;
970        assert!(decode_initial_state(&encoded, &different, image).is_err());
971
972        for kind in [
973            LoaderState::ObservedExecContinuation,
974            LoaderState::InitialStaticLegacy,
975        ] {
976            let legacy = encode_continuation(&config, image, kind).unwrap();
977            assert!(matches!(
978                decode_loader_state(&legacy, &config, image).unwrap(),
979                LoaderState::ObservedExecContinuation | LoaderState::InitialStaticLegacy
980            ));
981            // Continuation is not a random state and cannot be applied through
982            // the initial-state API, even to an otherwise eligible normal root.
983            assert!(matches!(
984                decode_initial_state(&legacy, &config, image),
985                Err(Errno::EPROTO)
986            ));
987            let mut untouched = crate::tool_local::ThreadState::new(tid, &config, ());
988            let prng_before = serde_json::to_vec(&untouched.prng).unwrap();
989            let chaos_before = serde_json::to_vec(&untouched.chaos_prng).unwrap();
990            let clock_before = serde_json::to_vec(&untouched.thread_logical_time).unwrap();
991            let metadata_before = std::sync::Arc::clone(&untouched.file_metadata);
992            let memory_before = std::sync::Arc::clone(&untouched.memory_metadata);
993            assert_eq!(
994                untouched.apply_initial_random_state(&legacy, &config, image),
995                Err(Errno::EPROTO)
996            );
997            assert_eq!(serde_json::to_vec(&untouched.prng).unwrap(), prng_before);
998            assert_eq!(
999                serde_json::to_vec(&untouched.chaos_prng).unwrap(),
1000                chaos_before
1001            );
1002            assert_eq!(
1003                serde_json::to_vec(&untouched.thread_logical_time).unwrap(),
1004                clock_before
1005            );
1006            assert!(std::sync::Arc::ptr_eq(
1007                &untouched.file_metadata,
1008                &metadata_before
1009            ));
1010            assert!(std::sync::Arc::ptr_eq(
1011                &untouched.memory_metadata,
1012                &memory_before
1013            ));
1014            assert_eq!(
1015                untouched
1016                    .complete_initial_random_auxv(Some(image.at_random))
1017                    .unwrap(),
1018                None
1019            );
1020            assert!(matches!(
1021                decode_loader_state(
1022                    &legacy,
1023                    &config,
1024                    InitialImage {
1025                        start_time_ticks: 1235,
1026                        ..image
1027                    }
1028                ),
1029                Err(Errno::EPROTO)
1030            ));
1031        }
1032
1033        let mut state = crate::tool_local::ThreadState::new(tid, &config, ());
1034        let chaos = serde_json::to_vec(&state.chaos_prng).unwrap();
1035        let clock = serde_json::to_vec(&state.thread_logical_time).unwrap();
1036        let metadata = std::sync::Arc::clone(&state.file_metadata);
1037        let memory = std::sync::Arc::clone(&state.memory_metadata);
1038        let pedigree = state.pedigree.clone();
1039        state.committed_clock_value = 47;
1040        state
1041            .apply_initial_random_state(&encoded, &config, image)
1042            .unwrap();
1043        same_state(&state.prng, &stream);
1044        assert_eq!(serde_json::to_vec(&state.chaos_prng).unwrap(), chaos);
1045        assert_eq!(
1046            serde_json::to_vec(&state.thread_logical_time).unwrap(),
1047            clock
1048        );
1049        assert!(std::sync::Arc::ptr_eq(&state.file_metadata, &metadata));
1050        assert!(std::sync::Arc::ptr_eq(&state.memory_metadata, &memory));
1051        assert_eq!(state.pedigree.raw(), pedigree.raw());
1052        assert_eq!(state.committed_clock_value, 47);
1053        assert!(
1054            state
1055                .apply_initial_random_state(&encoded, &config, image)
1056                .is_err()
1057        );
1058        assert!(
1059            state
1060                .complete_initial_random_auxv(Some(image.at_random + 1))
1061                .is_err()
1062        );
1063        // Libc/guest writes after the early acknowledgement must survive the
1064        // normal late post-exec completion. Completion performs no memory I/O.
1065        OwnMemory
1066            .write_exact(pages.address(0), &[0x7c; 16])
1067            .unwrap();
1068        // handle_post_exec sets this before consuming the completion fact.
1069        state.past_global_first_execve = true;
1070        // It returns the bytes the early write stored, for post-exec's record,
1071        // not what the guest left there since, and the early requests, in
1072        // order, for the records post-exec emits after it.
1073        assert_eq!(
1074            state
1075                .complete_initial_random_auxv(Some(image.at_random))
1076                .unwrap(),
1077            Some((written, requests.to_vec()))
1078        );
1079        assert_eq!(pages.bytes(0, 16), [0x7c; 16]);
1080        same_state(&state.prng, &stream);
1081        assert_eq!(
1082            state
1083                .complete_initial_random_auxv(Some(image.at_random))
1084                .unwrap(),
1085            None
1086        );
1087        assert!(
1088            state
1089                .apply_initial_random_state(&encoded, &config, image)
1090                .is_err()
1091        );
1092    }
1093
1094    #[test]
1095    fn shared_random_preserves_auxv_and_fault_semantics() {
1096        let pages = Pages::new();
1097        let tid = DetTid::from_raw(3);
1098        let mut actual = root_prng(0);
1099        let mut expected = Pcg64Mcg::seed_from_u64(0);
1100        let auxv: [u8; 16] = expected.random();
1101        initialize_auxv(&mut actual, OwnMemory, pages.address(0), tid).unwrap();
1102        assert_eq!(pages.bytes(0, 16), auxv);
1103        same_state(&actual, &expected);
1104
1105        for (buffer, len, flags, result) in [
1106            (0, 0, 0, Ok(0)),
1107            (0, 8, 0, Err(Errno::EFAULT)),
1108            (
1109                pages.address(32).as_raw(),
1110                16,
1111                0x8000_0001,
1112                Err(Errno::EINVAL),
1113            ),
1114        ] {
1115            assert_eq!(
1116                guest_getrandom(&mut actual, OwnMemory, tid, call(buffer, len, flags)),
1117                result
1118            );
1119            same_state(&actual, &expected);
1120            assert_eq!(pages.bytes(32, 16), [0xa5; 16]);
1121        }
1122        for len in [8, 16, 32, 4096] {
1123            let mut bytes = vec![0; len];
1124            expected.fill(&mut bytes[..]);
1125            assert_eq!(
1126                guest_getrandom(
1127                    &mut actual,
1128                    OwnMemory,
1129                    tid,
1130                    call(pages.address(0).as_raw(), len, 1)
1131                ),
1132                Ok(len as i64)
1133            );
1134            assert_eq!(pages.bytes(0, len), bytes);
1135            same_state(&actual, &expected);
1136        }
1137        // A fully read-only destination consumes the generated chunk before
1138        // its first write fails; it must not use ptrace's protection bypass.
1139        let mut discarded = [0u8; 8];
1140        expected.fill(&mut discarded[..]);
1141        assert_eq!(
1142            guest_getrandom(
1143                &mut actual,
1144                OwnMemory,
1145                tid,
1146                call(pages.address(pages.size).as_raw(), 8, 0)
1147            ),
1148            Err(Errno::EFAULT)
1149        );
1150        assert_eq!(pages.bytes(pages.size, 8), [0xa5; 8]);
1151        same_state(&actual, &expected);
1152
1153        // A cross-page short write returns the writable prefix, but the PRNG
1154        // has generated the complete requested chunk, exactly as before.
1155        for len in [8, 16] {
1156            let mut bytes = vec![0; len];
1157            expected.fill(&mut bytes[..]);
1158            assert_eq!(
1159                guest_getrandom(
1160                    &mut actual,
1161                    OwnMemory,
1162                    tid,
1163                    call(pages.address(pages.size - 4).as_raw(), len, 0)
1164                ),
1165                Ok(4)
1166            );
1167            assert_eq!(pages.bytes(pages.size - 4, 4), bytes[..4]);
1168            assert_eq!(pages.bytes(pages.size, len - 4), vec![0xa5; len - 4]);
1169            same_state(&actual, &expected);
1170        }
1171    }
1172}