1use 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
32pub(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#[derive(Debug)]
47pub struct RandomCopyFailure {
48 errno: Errno,
49}
50
51impl RandomCopyFailure {
52 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
85pub fn root_prng(seed: u64) -> Pcg64Mcg {
88 Pcg64Mcg::seed_from_u64(seed)
89}
90
91pub const MAX_INITIAL_STATE_BYTES: usize = 4096;
93
94const INITIAL_STATE_VERSION: u32 = 4;
99
100pub const MAX_EARLY_GETRANDOM: usize = 32;
104
105pub const MAX_EARLY_LIMIT_READS: usize = 4;
109
110#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
114#[serde(deny_unknown_fields)]
115pub enum LimitReadCall {
116 Prlimit64 {
118 pid: i32,
120 },
121 Getrlimit,
123}
124
125#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
129#[serde(deny_unknown_fields)]
130pub enum EarlyRequest {
131 Getrandom(RandomFill),
133 LimitRead {
136 call: LimitReadCall,
138 resource: u32,
140 current: u64,
142 maximum: u64,
144 },
145}
146
147fn 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#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
159#[serde(deny_unknown_fields)]
160pub struct RandomFill {
161 pub written: usize,
163 pub hash: u64,
165}
166
167#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
170#[serde(deny_unknown_fields)]
171pub struct InitialImage {
172 pub pid: i32,
174 pub start_time_ticks: u64,
176 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#[derive(Serialize, Deserialize)]
205#[serde(deny_unknown_fields)]
206pub enum LoaderState {
207 InitialRandom {
209 prng: Pcg64Mcg,
211 at_random_value: [u8; 16],
215 early_requests: Vec<EarlyRequest>,
219 },
220 ObservedExecContinuation,
222 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 serde_json::to_writer(&mut writer, config).map_err(|_| Errno::EPROTO)?;
243 Ok(writer.0.finalize().into())
244}
245
246pub 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
274pub 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
301pub 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 return Err(Errno::EPROTO);
321 }
322 Ok(value.state)
323}
324
325pub(crate) type InitialRandom = (Pcg64Mcg, [u8; 16], Vec<EarlyRequest>);
328
329pub(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
355pub(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
369pub(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 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
406pub 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
421fn 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 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
478fn 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
491pub(crate) fn record_early_getrandom(dettid: DetTid, fill: RandomFill) {
494 record_fill(dettid, "getrandom", fill);
495}
496
497pub 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
511pub 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
531pub 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
544pub 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
559pub(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 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 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 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 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 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 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 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 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 OwnMemory
1066 .write_exact(pages.address(0), &[0x7c; 16])
1067 .unwrap();
1068 state.past_global_first_execve = true;
1070 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 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 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}