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
32#[derive(Debug)]
37pub struct RandomCopyFailure {
38 errno: Errno,
39}
40
41impl RandomCopyFailure {
42 pub fn errno(&self) -> Errno {
44 self.errno
45 }
46}
47
48impl std::fmt::Display for RandomCopyFailure {
49 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
50 write!(f, "random user-access copy failed: {}", self.errno)?;
51 if self.errno == Errno::EPERM {
52 f.write_str(
53 "; for a non-dumpable ptrace guest, retry with Hermit's default namespace \
54 configuration (omit --no-namespace); embedders must check tracing permissions \
55 in the guest's user namespace",
56 )?;
57 }
58 Ok(())
59 }
60}
61
62impl std::error::Error for RandomCopyFailure {}
63
64pub(crate) fn copy_error(error: Errno) -> Error {
65 match error {
66 Errno::EFAULT => Error::Errno(error),
67 errno => Error::Tool(anyhow::Error::new(RandomCopyFailure { errno })),
68 }
69}
70
71pub(crate) fn is_copy_failure(error: &Error) -> bool {
72 matches!(error, Error::Tool(inner) if inner.is::<RandomCopyFailure>())
73}
74
75pub fn root_prng(seed: u64) -> Pcg64Mcg {
78 Pcg64Mcg::seed_from_u64(seed)
79}
80
81pub const MAX_INITIAL_STATE_BYTES: usize = 4096;
83
84#[derive(Clone, Copy, Debug, Eq, PartialEq, Serialize, Deserialize)]
87#[serde(deny_unknown_fields)]
88pub struct InitialImage {
89 pub pid: i32,
91 pub start_time_ticks: u64,
93 pub at_random: usize,
95}
96
97impl InitialImage {
98 fn validate(self) -> Result<(), Errno> {
99 if self.pid <= 0
100 || self.start_time_ticks == 0
101 || self.at_random == 0
102 || self.at_random.checked_add(16).is_none()
103 {
104 return Err(Errno::EPROTO);
105 }
106 Ok(())
107 }
108}
109
110#[derive(Serialize, Deserialize)]
111#[serde(deny_unknown_fields)]
112struct InitialRandomState {
113 version: u32,
114 configuration: [u8; 32],
115 image: InitialImage,
116 state: LoaderState,
117}
118
119#[derive(Serialize, Deserialize)]
122#[serde(deny_unknown_fields)]
123pub enum LoaderState {
124 InitialRandom {
126 prng: Pcg64Mcg,
128 },
129 ObservedExecContinuation,
131 InitialStaticLegacy,
133}
134
135fn configuration_identity(config: &crate::Config) -> Result<[u8; 32], Errno> {
136 struct HashWriter(Sha256);
137 impl std::io::Write for HashWriter {
138 fn write(&mut self, bytes: &[u8]) -> std::io::Result<usize> {
139 self.0.update(bytes);
140 Ok(bytes.len())
141 }
142 fn flush(&mut self) -> std::io::Result<()> {
143 Ok(())
144 }
145 }
146 let mut writer = HashWriter(Sha256::new());
147 writer.0.update(b"hermit-initial-random-state-v1\0");
148 writer.0.update(crate::config_wire_fingerprint());
149 serde_json::to_writer(&mut writer, config).map_err(|_| Errno::EPROTO)?;
152 Ok(writer.0.finalize().into())
153}
154
155pub fn encode_initial_state(
158 config: &crate::Config,
159 image: InitialImage,
160 prng: &Pcg64Mcg,
161) -> Result<Vec<u8>, Errno> {
162 image.validate()?;
163 let value = InitialRandomState {
164 version: 1,
165 configuration: configuration_identity(config)?,
166 image,
167 state: LoaderState::InitialRandom { prng: prng.clone() },
168 };
169 encode_state(value)
170}
171
172pub fn encode_continuation(
175 config: &crate::Config,
176 image: InitialImage,
177 state: LoaderState,
178) -> Result<Vec<u8>, Errno> {
179 image.validate()?;
180 if matches!(state, LoaderState::InitialRandom { .. }) {
181 return Err(Errno::EPROTO);
182 }
183 encode_state(InitialRandomState {
184 version: 1,
185 configuration: configuration_identity(config)?,
186 image,
187 state,
188 })
189}
190
191fn encode_state(value: InitialRandomState) -> Result<Vec<u8>, Errno> {
192 let bytes = serde_json::to_vec(&value).map_err(|_| Errno::EPROTO)?;
193 if bytes.is_empty() || bytes.len() > MAX_INITIAL_STATE_BYTES {
194 return Err(Errno::EOVERFLOW);
195 }
196 Ok(bytes)
197}
198
199pub fn decode_loader_state(
202 bytes: &[u8],
203 config: &crate::Config,
204 expected: InitialImage,
205) -> Result<LoaderState, Errno> {
206 expected.validate()?;
207 if bytes.is_empty() || bytes.len() > MAX_INITIAL_STATE_BYTES {
208 return Err(Errno::EPROTO);
209 }
210 let value: InitialRandomState = serde_json::from_slice(bytes).map_err(|_| Errno::EPROTO)?;
211 if value.version != 1
212 || value.configuration != configuration_identity(config)?
213 || value.image != expected
214 || serde_json::to_vec(&value).map_err(|_| Errno::EPROTO)? != bytes
215 {
216 return Err(Errno::EPROTO);
219 }
220 Ok(value.state)
221}
222
223pub(crate) fn decode_initial_state(
224 bytes: &[u8],
225 config: &crate::Config,
226 expected: InitialImage,
227) -> Result<Pcg64Mcg, Errno> {
228 match decode_loader_state(bytes, config, expected)? {
229 LoaderState::InitialRandom { prng } => Ok(prng),
230 LoaderState::ObservedExecContinuation | LoaderState::InitialStaticLegacy => {
231 Err(Errno::EPROTO)
232 }
233 }
234}
235
236const GETRANDOM_ALLOWED_FLAGS: u32 = libc::GRND_NONBLOCK | libc::GRND_RANDOM | libc::GRND_INSECURE;
237
238pub(crate) fn validate_getrandom_flags(flags: usize) -> Result<(), Errno> {
241 let flags = flags as u32;
242 let random = flags & libc::GRND_RANDOM != 0;
243 let insecure = flags & libc::GRND_INSECURE != 0;
244
245 if flags & !GETRANDOM_ALLOWED_FLAGS != 0 || (random && insecure) {
246 Err(Errno::EINVAL)
247 } else {
248 Ok(())
249 }
250}
251
252pub(crate) const GETRANDOM_MAX_BYTES: usize = (i32::MAX as usize) & !4095;
254
255pub(crate) fn getrandom_request_len(requested: usize) -> usize {
256 requested.min(GETRANDOM_MAX_BYTES)
257}
258
259pub(crate) fn write_random_chunk(
260 memory: &mut impl MemoryAccess,
261 remote_buf: AddrMut<u8>,
262 local_buf: &[u8],
263) -> Result<usize, Errno> {
264 const PTRACE_WORD_SPLIT: usize = std::mem::size_of::<u64>() / 2;
265
266 if local_buf.len() != std::mem::size_of::<u64>() {
267 return memory.write_with_user_access(remote_buf, local_buf);
268 }
269
270 let first = memory.write_with_user_access(remote_buf, &local_buf[..PTRACE_WORD_SPLIT])?;
274 if first < PTRACE_WORD_SPLIT {
275 return Ok(first);
276 }
277 let Some(second_buf) = remote_buf
278 .as_raw()
279 .checked_add(PTRACE_WORD_SPLIT)
280 .and_then(AddrMut::<u8>::from_raw)
281 else {
282 return Ok(first);
283 };
284 match memory.write_with_user_access(second_buf, &local_buf[PTRACE_WORD_SPLIT..]) {
285 Ok(second) => Ok(first + second),
286 Err(Errno::EFAULT) => Ok(first),
287 Err(error) => Err(error),
288 }
289}
290
291pub fn fill_bytes(
294 prng: &mut Pcg64Mcg,
295 mut memory: impl MemoryAccess,
296 remote_buf: AddrMut<u8>,
297 len: usize,
298 dettid: DetTid,
299 source: &str,
300) -> Result<usize, Error> {
301 let mut local_words = [0_u64; RANDOM_FILL_CHUNK_BYTES / std::mem::size_of::<u64>()];
302 let mut hasher = DefaultHasher::new();
303 let mut written = 0;
304
305 while written < len {
306 let remote_chunk = match remote_buf
307 .as_raw()
308 .checked_add(written)
309 .and_then(AddrMut::<u8>::from_raw)
310 {
311 Some(address) => address,
312 None if written == 0 => return Err(Errno::EFAULT.into()),
313 None => break,
314 };
315 let chunk_len = (len - written).min(RANDOM_FILL_CHUNK_BYTES);
316 let local_buf = unsafe {
318 std::slice::from_raw_parts_mut(local_words.as_mut_ptr().cast::<u8>(), chunk_len)
319 };
320 prng.fill(local_buf);
321 let n = match write_random_chunk(&mut memory, remote_chunk, local_buf) {
322 Ok(n) => n,
323 Err(Errno::EFAULT) if written > 0 => break,
324 Err(error) => return Err(copy_error(error)),
325 };
326 if n == 0 {
327 if written == 0 {
328 return Err(Errno::EFAULT.into());
329 }
330 break;
331 }
332 if cfg!(debug_assertions) {
333 Hash::hash_slice(&local_buf[..n], &mut hasher);
334 }
335 written += n;
336 if n < chunk_len {
337 break;
338 }
339 }
340
341 if cfg!(debug_assertions) {
342 detlog!(
343 "[dtid {}] USER RAND [{}] Filled guest memory with {} random bytes, hash of bytes: {}",
344 dettid,
345 source,
346 written,
347 hasher.finish()
348 );
349 }
350 Ok(written)
351}
352
353pub fn getrandom(
355 prng: &mut Pcg64Mcg,
356 memory: impl MemoryAccess,
357 dettid: DetTid,
358 call: Getrandom,
359) -> Result<i64, Error> {
360 validate_getrandom_flags(call.flags())?;
361 let len = getrandom_request_len(call.buflen());
362 if len == 0 {
363 return Ok(0);
364 }
365 let buf = call.buf().ok_or(Errno::EFAULT)?;
366 fill_bytes(prng, memory, buf, len, dettid, "getrandom").map(|n| n as i64)
367}
368
369pub fn initialize_auxv(
372 prng: &mut Pcg64Mcg,
373 mut memory: impl MemoryAccess,
374 pointer: AddrMut<u8>,
375 dettid: DetTid,
376) -> Result<(), Errno> {
377 let bytes: [u8; 16] = prng.random();
378 detlog!(
379 "[post_exec, dtid {}] init auxv AT_RANDOM value to {:?}",
380 dettid,
381 bytes
382 );
383 memory.write_value(pointer.cast::<[u8; 16]>(), &bytes)
384}
385
386#[cfg(test)]
387mod tests {
388 include!("random/user_access_tests.rs");
389 use std::io::IoSlice;
390 use std::io::IoSliceMut;
391
392 use reverie::syscalls::Syscall;
393 use reverie::syscalls::SyscallArgs;
394 use reverie::syscalls::Sysno;
395
396 use super::*;
397
398 #[derive(Clone, Copy)]
399 struct OwnMemory;
400
401 impl MemoryAccess for OwnMemory {
402 fn write_with_user_access(
403 &mut self,
404 addr: AddrMut<u8>,
405 bytes: &[u8],
406 ) -> Result<usize, Errno> {
407 addr.as_raw()
408 .checked_add(bytes.len())
409 .ok_or(Errno::EFAULT)?;
410 if bytes.is_empty() {
411 return Ok(0);
412 }
413 let local = libc::iovec {
414 iov_base: bytes.as_ptr().cast_mut().cast(),
415 iov_len: bytes.len(),
416 };
417 let remote = libc::iovec {
418 iov_base: addr.as_raw() as *mut libc::c_void,
419 iov_len: bytes.len(),
420 };
421 let n = unsafe { libc::process_vm_writev(libc::getpid(), &local, 1, &remote, 1, 0) };
422 if n < 0 {
423 Err(Errno::last())
424 } else {
425 Ok(n as usize)
426 }
427 }
428 fn read_vectored(
429 &self,
430 remote: &[IoSlice],
431 local: &mut [IoSliceMut],
432 ) -> Result<usize, Errno> {
433 let n = unsafe {
434 libc::process_vm_readv(
435 libc::getpid(),
436 local.as_ptr().cast(),
437 local.len() as _,
438 remote.as_ptr().cast(),
439 remote.len() as _,
440 0,
441 )
442 };
443 if n < 0 {
444 Err(Errno::last())
445 } else {
446 Ok(n as usize)
447 }
448 }
449 fn write_vectored(
450 &mut self,
451 local: &[IoSlice],
452 remote: &mut [IoSliceMut],
453 ) -> Result<usize, Errno> {
454 let n = unsafe {
455 libc::process_vm_writev(
456 libc::getpid(),
457 local.as_ptr().cast(),
458 local.len() as _,
459 remote.as_ptr().cast(),
460 remote.len() as _,
461 0,
462 )
463 };
464 if n < 0 {
465 Err(Errno::last())
466 } else {
467 Ok(n as usize)
468 }
469 }
470 }
471
472 struct SecondHalfFailure {
473 error: Errno,
474 bytes: [u8; 8],
475 writes: Vec<(usize, Vec<u8>)>,
476 }
477
478 impl MemoryAccess for SecondHalfFailure {
479 fn read_vectored(
480 &self,
481 _remote: &[IoSlice],
482 _local: &mut [IoSliceMut],
483 ) -> Result<usize, Errno> {
484 panic!("random copying must not read guest memory")
485 }
486
487 fn write_vectored(
488 &mut self,
489 _local: &[IoSlice],
490 _remote: &mut [IoSliceMut],
491 ) -> Result<usize, Errno> {
492 panic!("random copying must use the user-access capability")
493 }
494
495 fn write_with_user_access(
496 &mut self,
497 addr: AddrMut<u8>,
498 buf: &[u8],
499 ) -> Result<usize, Errno> {
500 self.writes.push((addr.as_raw(), buf.to_vec()));
501 match self.writes.len() {
502 1 => {
503 assert_eq!(addr.as_raw(), 0x1000);
504 assert_eq!(buf.len(), 4);
505 self.bytes[..4].copy_from_slice(buf);
506 Ok(4)
507 }
508 2 => {
509 assert_eq!(addr.as_raw(), 0x1004);
510 assert_eq!(buf.len(), 4);
511 Err(self.error)
512 }
513 _ => panic!("random copying retried a failed write"),
514 }
515 }
516 }
517
518 #[test]
519 fn eight_byte_random_copy_distinguishes_faults_from_backend_errors() {
520 for (error, expected) in [(Errno::EFAULT, Ok(4)), (Errno::EIO, Err(Errno::EIO))] {
521 let mut memory = SecondHalfFailure {
522 error,
523 bytes: [0xa5; 8],
524 writes: Vec::new(),
525 };
526 let address = AddrMut::from_raw(0x1000).unwrap();
527
528 assert_eq!(
529 write_random_chunk(&mut memory, address, &[1, 2, 3, 4, 5, 6, 7, 8]),
530 expected
531 );
532 assert_eq!(memory.bytes, [1, 2, 3, 4, 0xa5, 0xa5, 0xa5, 0xa5]);
533 assert_eq!(
534 memory.writes,
535 [(0x1000, vec![1, 2, 3, 4]), (0x1004, vec![5, 6, 7, 8])]
536 );
537 }
538 }
539
540 struct Pages {
541 address: *mut u8,
542 size: usize,
543 }
544 impl Pages {
545 fn new() -> Self {
546 let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
547 assert!(size >= 4096);
548 let address = unsafe {
549 libc::mmap(
550 std::ptr::null_mut(),
551 2 * size,
552 libc::PROT_READ | libc::PROT_WRITE,
553 libc::MAP_PRIVATE | libc::MAP_ANONYMOUS,
554 -1,
555 0,
556 )
557 };
558 assert_ne!(address, libc::MAP_FAILED);
559 unsafe {
560 std::ptr::write_bytes(address.cast::<u8>(), 0xa5, 2 * size);
561 }
562 assert_eq!(
563 unsafe { libc::mprotect(address.add(size), size, libc::PROT_READ) },
564 0
565 );
566 Self {
567 address: address.cast(),
568 size,
569 }
570 }
571 fn address(&self, offset: usize) -> AddrMut<'static, u8> {
572 assert!(offset < self.size * 2);
573 AddrMut::from_raw(self.address as usize + offset).unwrap()
574 }
575 fn bytes(&self, offset: usize, length: usize) -> Vec<u8> {
576 assert!(offset + length <= 2 * self.size);
577 unsafe { std::slice::from_raw_parts(self.address.add(offset), length) }.to_vec()
578 }
579 }
580 impl Drop for Pages {
581 fn drop(&mut self) {
582 assert_eq!(
583 unsafe { libc::munmap(self.address.cast(), 2 * self.size) },
584 0
585 );
586 }
587 }
588
589 fn call(buffer: usize, length: usize, flags: usize) -> Getrandom {
590 let Syscall::Getrandom(call) = Syscall::from_raw(
591 Sysno::getrandom,
592 SyscallArgs::new(buffer, length, flags, 0, 0, 0),
593 ) else {
594 unreachable!()
595 };
596 call
597 }
598 fn guest_getrandom(
601 prng: &mut Pcg64Mcg,
602 memory: impl MemoryAccess,
603 tid: DetTid,
604 call: Getrandom,
605 ) -> Result<i64, Errno> {
606 super::getrandom(prng, memory, tid, call).map_err(|error| match error {
607 Error::Errno(errno) => errno,
608 other => panic!("unexpected terminal failure in guest-errno companion: {other:?}"),
609 })
610 }
611 fn same_state(a: &Pcg64Mcg, b: &Pcg64Mcg) {
612 assert_eq!(
613 serde_json::to_vec(a).unwrap(),
614 serde_json::to_vec(b).unwrap()
615 );
616 }
617
618 #[test]
619 fn initial_handoff_preserves_unrelated_state_and_consumes_only_auxv_fact() {
620 let pages = Pages::new();
621 let config = crate::Config::default();
622 let tid = DetTid::from_raw(3);
623 let image = InitialImage {
624 pid: 3,
625 start_time_ticks: 1234,
626 at_random: pages.address(0).as_raw(),
627 };
628 let mut stream = root_prng(config.rng_seed());
629 initialize_auxv(&mut stream, OwnMemory, pages.address(0), tid).unwrap();
630 guest_getrandom(
631 &mut stream,
632 OwnMemory,
633 tid,
634 call(pages.address(32).as_raw(), 8, 1),
635 )
636 .unwrap();
637 let encoded = encode_initial_state(&config, image, &stream).unwrap();
638 same_state(
639 &decode_initial_state(&encoded, &config, image).unwrap(),
640 &stream,
641 );
642 for bad in [
643 Vec::new(),
644 [encoded.as_slice(), b" "].concat(),
645 String::from_utf8(encoded.clone())
646 .unwrap()
647 .replace("\"version\":1", "\"version\":2")
648 .into_bytes(),
649 ] {
650 assert!(decode_initial_state(&bad, &config, image).is_err());
651 }
652 for wrong in [
653 InitialImage { pid: 4, ..image },
654 InitialImage {
655 start_time_ticks: 1235,
656 ..image
657 },
658 InitialImage {
659 at_random: image.at_random + 16,
660 ..image
661 },
662 ] {
663 assert!(decode_initial_state(&encoded, &config, wrong).is_err());
664 }
665 let mut different = config.clone();
666 different.virtualize_time = !different.virtualize_time;
667 assert!(decode_initial_state(&encoded, &different, image).is_err());
668
669 for kind in [
670 LoaderState::ObservedExecContinuation,
671 LoaderState::InitialStaticLegacy,
672 ] {
673 let legacy = encode_continuation(&config, image, kind).unwrap();
674 assert!(matches!(
675 decode_loader_state(&legacy, &config, image).unwrap(),
676 LoaderState::ObservedExecContinuation | LoaderState::InitialStaticLegacy
677 ));
678 assert!(matches!(
681 decode_initial_state(&legacy, &config, image),
682 Err(Errno::EPROTO)
683 ));
684 let mut untouched = crate::tool_local::ThreadState::new(tid, &config, ());
685 let prng_before = serde_json::to_vec(&untouched.prng).unwrap();
686 let chaos_before = serde_json::to_vec(&untouched.chaos_prng).unwrap();
687 let clock_before = serde_json::to_vec(&untouched.thread_logical_time).unwrap();
688 let metadata_before = std::sync::Arc::clone(&untouched.file_metadata);
689 let memory_before = std::sync::Arc::clone(&untouched.memory_metadata);
690 assert_eq!(
691 untouched.apply_initial_random_state(&legacy, &config, image),
692 Err(Errno::EPROTO)
693 );
694 assert_eq!(serde_json::to_vec(&untouched.prng).unwrap(), prng_before);
695 assert_eq!(
696 serde_json::to_vec(&untouched.chaos_prng).unwrap(),
697 chaos_before
698 );
699 assert_eq!(
700 serde_json::to_vec(&untouched.thread_logical_time).unwrap(),
701 clock_before
702 );
703 assert!(std::sync::Arc::ptr_eq(
704 &untouched.file_metadata,
705 &metadata_before
706 ));
707 assert!(std::sync::Arc::ptr_eq(
708 &untouched.memory_metadata,
709 &memory_before
710 ));
711 assert!(
712 !untouched
713 .complete_initial_random_auxv(Some(image.at_random))
714 .unwrap()
715 );
716 assert!(matches!(
717 decode_loader_state(
718 &legacy,
719 &config,
720 InitialImage {
721 start_time_ticks: 1235,
722 ..image
723 }
724 ),
725 Err(Errno::EPROTO)
726 ));
727 }
728
729 let mut state = crate::tool_local::ThreadState::new(tid, &config, ());
730 let chaos = serde_json::to_vec(&state.chaos_prng).unwrap();
731 let clock = serde_json::to_vec(&state.thread_logical_time).unwrap();
732 let metadata = std::sync::Arc::clone(&state.file_metadata);
733 let memory = std::sync::Arc::clone(&state.memory_metadata);
734 let pedigree = state.pedigree.clone();
735 state.committed_clock_value = 47;
736 state
737 .apply_initial_random_state(&encoded, &config, image)
738 .unwrap();
739 same_state(&state.prng, &stream);
740 assert_eq!(serde_json::to_vec(&state.chaos_prng).unwrap(), chaos);
741 assert_eq!(
742 serde_json::to_vec(&state.thread_logical_time).unwrap(),
743 clock
744 );
745 assert!(std::sync::Arc::ptr_eq(&state.file_metadata, &metadata));
746 assert!(std::sync::Arc::ptr_eq(&state.memory_metadata, &memory));
747 assert_eq!(state.pedigree.raw(), pedigree.raw());
748 assert_eq!(state.committed_clock_value, 47);
749 assert!(
750 state
751 .apply_initial_random_state(&encoded, &config, image)
752 .is_err()
753 );
754 assert!(
755 state
756 .complete_initial_random_auxv(Some(image.at_random + 1))
757 .is_err()
758 );
759 OwnMemory
762 .write_exact(pages.address(0), &[0x7c; 16])
763 .unwrap();
764 state.past_global_first_execve = true;
766 assert!(
767 state
768 .complete_initial_random_auxv(Some(image.at_random))
769 .unwrap()
770 );
771 assert_eq!(pages.bytes(0, 16), [0x7c; 16]);
772 same_state(&state.prng, &stream);
773 assert!(
774 !state
775 .complete_initial_random_auxv(Some(image.at_random))
776 .unwrap()
777 );
778 assert!(
779 state
780 .apply_initial_random_state(&encoded, &config, image)
781 .is_err()
782 );
783 }
784
785 #[test]
786 fn shared_random_preserves_auxv_and_fault_semantics() {
787 let pages = Pages::new();
788 let tid = DetTid::from_raw(3);
789 let mut actual = root_prng(0);
790 let mut expected = Pcg64Mcg::seed_from_u64(0);
791 let auxv: [u8; 16] = expected.random();
792 initialize_auxv(&mut actual, OwnMemory, pages.address(0), tid).unwrap();
793 assert_eq!(pages.bytes(0, 16), auxv);
794 same_state(&actual, &expected);
795
796 for (buffer, len, flags, result) in [
797 (0, 0, 0, Ok(0)),
798 (0, 8, 0, Err(Errno::EFAULT)),
799 (
800 pages.address(32).as_raw(),
801 16,
802 0x8000_0001,
803 Err(Errno::EINVAL),
804 ),
805 ] {
806 assert_eq!(
807 guest_getrandom(&mut actual, OwnMemory, tid, call(buffer, len, flags)),
808 result
809 );
810 same_state(&actual, &expected);
811 assert_eq!(pages.bytes(32, 16), [0xa5; 16]);
812 }
813 for len in [8, 16, 32, 4096] {
814 let mut bytes = vec![0; len];
815 expected.fill(&mut bytes[..]);
816 assert_eq!(
817 guest_getrandom(
818 &mut actual,
819 OwnMemory,
820 tid,
821 call(pages.address(0).as_raw(), len, 1)
822 ),
823 Ok(len as i64)
824 );
825 assert_eq!(pages.bytes(0, len), bytes);
826 same_state(&actual, &expected);
827 }
828 let mut discarded = [0u8; 8];
831 expected.fill(&mut discarded[..]);
832 assert_eq!(
833 guest_getrandom(
834 &mut actual,
835 OwnMemory,
836 tid,
837 call(pages.address(pages.size).as_raw(), 8, 0)
838 ),
839 Err(Errno::EFAULT)
840 );
841 assert_eq!(pages.bytes(pages.size, 8), [0xa5; 8]);
842 same_state(&actual, &expected);
843
844 for len in [8, 16] {
847 let mut bytes = vec![0; len];
848 expected.fill(&mut bytes[..]);
849 assert_eq!(
850 guest_getrandom(
851 &mut actual,
852 OwnMemory,
853 tid,
854 call(pages.address(pages.size - 4).as_raw(), len, 0)
855 ),
856 Ok(4)
857 );
858 assert_eq!(pages.bytes(pages.size - 4, 4), bytes[..4]);
859 assert_eq!(pages.bytes(pages.size, len - 4), vec![0xa5; len - 4]);
860 same_state(&actual, &expected);
861 }
862 }
863}