Skip to content

Commit 6e0d46f

Browse files
committed
Get test suite ready for miri
This commit brings the test suite into a state where "cargo miri test --features backend-mmap" reports all tests as passing. Some tests have been marked with `#[cfg(not(miri))]` due to exercising operations that miri does not support, and which cannot be reasonably mocked out (such as creating a file-backed mmap). Calls to `mmap` with `MAP_ANONYMOUS` have been mocked out with calls to `std::allow::alloc_zeroed`, due to miri not supporting `mmap`. Some tests ran into alignment issues that are not present on production systems, presumable because the system allocator always aligns sufficiently. Miri will not do that, and happily align everything to a 1-byte boundary. However, miri only exercises a _single_ execution path, so it is possible that on that single execution path, everything happened to be aligned sufficiently for a test to pass. That is why the adjustments made in this commit might not be sufficient. Signed-off-by: Patrick Roy <[email protected]>
1 parent ad5ee2d commit 6e0d46f

File tree

4 files changed

+35
-3
lines changed

4 files changed

+35
-3
lines changed

src/bytes.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@ use crate::volatile_memory::VolatileSlice;
2626
///
2727
/// A type `T` is `ByteValued` if and only if it can be initialized by reading its contents from a
2828
/// byte array. This is generally true for all plain-old-data structs. It is notably not true for
29-
/// any type that includes a reference.
29+
/// any type that includes a reference. It is generally also not safe for non-packed structs, as
30+
/// compiler-inserted padding is considered uninitialized memory, and thus reads/writing it will
31+
/// cause undefined behavior.
3032
///
3133
/// Implementing this trait guarantees that it is safe to instantiate the struct with random data.
3234
pub unsafe trait ByteValued: Copy + Default + Send + Sync {

src/guest_memory.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1057,6 +1057,7 @@ mod tests {
10571057
// has the same value (and thus it did not do a non-atomic access). The test succeeds if
10581058
// no mismatch is detected after performing accesses for a pre-determined amount of time.
10591059
#[cfg(feature = "backend-mmap")]
1060+
#[cfg(not(miri))] // This test simulates a race condition between guest and vmm
10601061
fn non_atomic_access_helper<T>()
10611062
where
10621063
T: ByteValued
@@ -1074,13 +1075,16 @@ mod tests {
10741075
#[derive(Clone, Copy, Debug, Default, PartialEq)]
10751076
struct Data<T> {
10761077
val: T,
1077-
some_bytes: [u8; 7],
1078+
some_bytes: [u8; 8],
10781079
}
10791080

10801081
// Some sanity checks.
10811082
assert_eq!(mem::align_of::<T>(), mem::align_of::<Data<T>>());
10821083
assert_eq!(mem::size_of::<T>(), mem::align_of::<T>());
10831084

1085+
// There must be no padding bytes, as otherwise implementing ByteValued is UB
1086+
assert_eq!(mem::size_of::<Data<T>>(), mem::size_of::<T>() + 8);
1087+
10841088
unsafe impl<T: ByteValued> ByteValued for Data<T> {}
10851089

10861090
// Start of first guest memory region.
@@ -1099,7 +1103,7 @@ mod tests {
10991103
// Need to clone this and move it into the new thread we create.
11001104
let mem2 = mem.clone();
11011105
// Just some bytes.
1102-
let some_bytes = [1u8, 2, 4, 16, 32, 64, 128];
1106+
let some_bytes = [1u8, 2, 4, 16, 32, 64, 128, 255];
11031107

11041108
let mut data = Data {
11051109
val: T::from(0u8),
@@ -1146,6 +1150,7 @@ mod tests {
11461150

11471151
#[cfg(feature = "backend-mmap")]
11481152
#[test]
1153+
#[cfg(not(miri))]
11491154
fn test_non_atomic_access() {
11501155
non_atomic_access_helper::<u16>()
11511156
}

src/mmap.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -892,6 +892,7 @@ mod tests {
892892
}
893893

894894
#[test]
895+
#[cfg(not(miri))] // Miri cannot mmap files
895896
fn mapped_file_read() {
896897
let mut f = TempFile::new().unwrap().into_file();
897898
let sample_buf = &[1, 2, 3, 4, 5];

src/mmap_unix.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ impl<B: Bitmap> MmapRegionBuilder<B> {
142142
(-1, 0)
143143
};
144144

145+
#[cfg(not(miri))]
145146
// SAFETY: This is safe because we're not allowing MAP_FIXED, and invalid parameters
146147
// cannot break Rust safety guarantees (things may change if we're mapping /dev/mem or
147148
// some wacky file).
@@ -156,10 +157,22 @@ impl<B: Bitmap> MmapRegionBuilder<B> {
156157
)
157158
};
158159

160+
#[cfg(not(miri))]
159161
if addr == libc::MAP_FAILED {
160162
return Err(Error::Mmap(io::Error::last_os_error()));
161163
}
162164

165+
#[cfg(miri)]
166+
if self.size == 0 {
167+
return Err(Error::Mmap(io::Error::from_raw_os_error(libc::EINVAL)));
168+
}
169+
170+
// Miri does not support the mmap syscall, so we use rust's allocator for miri tests
171+
#[cfg(miri)]
172+
let addr = unsafe {
173+
std::alloc::alloc_zeroed(std::alloc::Layout::from_size_align(self.size, 8).unwrap())
174+
};
175+
163176
Ok(MmapRegion {
164177
addr: addr as *mut u8,
165178
size: self.size,
@@ -412,7 +425,14 @@ impl<B> Drop for MmapRegion<B> {
412425
// SAFETY: This is safe because we mmap the area at addr ourselves, and nobody
413426
// else is holding a reference to it.
414427
unsafe {
428+
#[cfg(not(miri))]
415429
libc::munmap(self.addr as *mut libc::c_void, self.size);
430+
431+
#[cfg(miri)]
432+
std::alloc::dealloc(
433+
self.addr,
434+
std::alloc::Layout::from_size_align(self.size, 8).unwrap(),
435+
);
416436
}
417437
}
418438
}
@@ -499,6 +519,7 @@ mod tests {
499519
}
500520

501521
#[test]
522+
#[cfg(not(miri))] // Miri cannot mmap files
502523
fn test_mmap_region_from_file() {
503524
let mut f = TempFile::new().unwrap().into_file();
504525
let offset: usize = 0;
@@ -517,6 +538,7 @@ mod tests {
517538
}
518539

519540
#[test]
541+
#[cfg(not(miri))] // Miri cannot mmap files
520542
fn test_mmap_region_build() {
521543
let a = Arc::new(TempFile::new().unwrap().into_file());
522544

@@ -587,6 +609,7 @@ mod tests {
587609
}
588610

589611
#[test]
612+
#[cfg(not(miri))] // Causes warnings due to the pointer casts
590613
fn test_mmap_region_build_raw() {
591614
let addr = 0;
592615
let size = unsafe { libc::sysconf(libc::_SC_PAGESIZE) as usize };
@@ -605,6 +628,7 @@ mod tests {
605628
}
606629

607630
#[test]
631+
#[cfg(not(miri))] // Miri cannot mmap files
608632
fn test_mmap_region_fds_overlap() {
609633
let a = Arc::new(TempFile::new().unwrap().into_file());
610634
assert_eq!(unsafe { libc::ftruncate(a.as_raw_fd(), 1024 * 10) }, 0);

0 commit comments

Comments
 (0)