Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions src/vmm/src/arch/aarch64/regs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,9 @@ mod tests {
let mut buf = vec![0; 10000];

Snapshot::new(&v).save(&mut buf.as_mut_slice()).unwrap();
let restored: Aarch64RegisterVec = Snapshot::load(&mut buf.as_slice()).unwrap().data;
let restored: Aarch64RegisterVec = Snapshot::load_without_crc_check(buf.as_slice())
.unwrap()
.data;

for (old, new) in v.iter().zip(restored.iter()) {
assert_eq!(old, new);
Expand Down Expand Up @@ -576,7 +578,7 @@ mod tests {

// Total size of registers according IDs are 16 + 16 = 32,
// but actual data size is 8 + 16 = 24.
Snapshot::<Aarch64RegisterVec>::load(&mut buf.as_slice()).unwrap_err();
Snapshot::<Aarch64RegisterVec>::load_without_crc_check(buf.as_slice()).unwrap_err();
}

#[test]
Expand All @@ -598,7 +600,7 @@ mod tests {
Snapshot::new(v).save(&mut buf.as_mut_slice()).unwrap();

// 4096 bit wide registers are not supported.
Snapshot::<Aarch64RegisterVec>::load(&mut buf.as_slice()).unwrap_err();
Snapshot::<Aarch64RegisterVec>::load_without_crc_check(buf.as_slice()).unwrap_err();
}

#[test]
Expand Down
4 changes: 3 additions & 1 deletion src/vmm/src/arch/x86_64/vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,9 @@ mod tests {
Snapshot::new(state)
.save(&mut snapshot_data.as_mut_slice())
.unwrap();
let restored_state: VmState = Snapshot::load(&mut snapshot_data.as_slice()).unwrap().data;
let restored_state: VmState = Snapshot::load_without_crc_check(snapshot_data.as_slice())
.unwrap()
.data;

vm.restore_state(&restored_state).unwrap();
}
Expand Down
4 changes: 3 additions & 1 deletion src/vmm/src/device_manager/pci_mngr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -659,7 +659,9 @@ mod tests {
// object and calling default_vmm() is the easiest way to create one.
let vmm = default_vmm();
let device_manager_state: device_manager::DevicesState =
Snapshot::load(&mut buf.as_slice()).unwrap().data;
Snapshot::load_without_crc_check(buf.as_slice())
.unwrap()
.data;
let vm_resources = &mut VmResources::default();
let restore_args = PciDevicesConstructorArgs {
vm: vmm.vm.clone(),
Expand Down
4 changes: 3 additions & 1 deletion src/vmm/src/device_manager/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -680,7 +680,9 @@ mod tests {
let mut event_manager = EventManager::new().expect("Unable to create EventManager");
let vmm = default_vmm();
let device_manager_state: device_manager::DevicesState =
Snapshot::load(&mut buf.as_slice()).unwrap().data;
Snapshot::load_without_crc_check(buf.as_slice())
.unwrap()
.data;
let vm_resources = &mut VmResources::default();
let restore_args = MMIODevManagerConstructorArgs {
mem: vmm.vm.guest_memory(),
Expand Down
4 changes: 3 additions & 1 deletion src/vmm/src/devices/virtio/balloon/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,9 @@ mod tests {
mem: guest_mem,
restored_from_file: true,
},
&Snapshot::load(&mut mem.as_slice()).unwrap().data,
&Snapshot::load_without_crc_check(mem.as_slice())
.unwrap()
.data,
)
.unwrap();

Expand Down
4 changes: 3 additions & 1 deletion src/vmm/src/devices/virtio/block/virtio/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,9 @@ mod tests {
// Restore the block device.
let restored_block = VirtioBlock::restore(
BlockConstructorArgs { mem: guest_mem },
&Snapshot::load(&mut mem.as_slice()).unwrap().data,
&Snapshot::load_without_crc_check(mem.as_slice())
.unwrap()
.data,
)
.unwrap();

Expand Down
4 changes: 3 additions & 1 deletion src/vmm/src/devices/virtio/net/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,9 @@ mod tests {
mem: guest_mem,
mmds: mmds_ds,
},
&Snapshot::load(&mut mem.as_slice()).unwrap().data,
&Snapshot::load_without_crc_check(mem.as_slice())
.unwrap()
.data,
) {
Ok(restored_net) => {
// Test that virtio specific fields are the same.
Expand Down
17 changes: 13 additions & 4 deletions src/vmm/src/devices/virtio/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -366,8 +366,13 @@ mod tests {
mem,
is_activated: true,
};
let restored_queue =
Queue::restore(ca, &Snapshot::load(&mut bytes.as_slice()).unwrap().data).unwrap();
let restored_queue = Queue::restore(
ca,
&Snapshot::load_without_crc_check(bytes.as_slice())
.unwrap()
.data,
)
.unwrap();

assert_eq!(restored_queue, queue);
}
Expand All @@ -380,7 +385,9 @@ mod tests {
let state = VirtioDeviceState::from_device(&dummy);
Snapshot::new(&state).save(&mut mem.as_mut_slice()).unwrap();

let restored_state: VirtioDeviceState = Snapshot::load(&mut mem.as_slice()).unwrap().data;
let restored_state: VirtioDeviceState = Snapshot::load_without_crc_check(mem.as_slice())
.unwrap()
.data;
assert_eq!(restored_state, state);
}

Expand Down Expand Up @@ -419,7 +426,9 @@ mod tests {
};
let restored_mmio_transport = MmioTransport::restore(
restore_args,
&Snapshot::load(&mut buf.as_slice()).unwrap().data,
&Snapshot::load_without_crc_check(buf.as_slice())
.unwrap()
.data,
)
.unwrap();

Expand Down
4 changes: 3 additions & 1 deletion src/vmm/src/devices/virtio/rng/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ mod tests {
let guest_mem = create_virtio_mem();
let restored = Entropy::restore(
EntropyConstructorArgs { mem: guest_mem },
&Snapshot::load(&mut mem.as_slice()).unwrap().data,
&Snapshot::load_without_crc_check(mem.as_slice())
.unwrap()
.data,
)
.unwrap();

Expand Down
4 changes: 3 additions & 1 deletion src/vmm/src/devices/virtio/vsock/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,9 @@ pub(crate) mod tests {

Snapshot::new(&state).save(&mut mem.as_mut_slice()).unwrap();

let restored_state: VsockState = Snapshot::load(&mut mem.as_slice()).unwrap().data;
let restored_state: VsockState = Snapshot::load_without_crc_check(mem.as_slice())
.unwrap()
.data;
let mut restored_device = Vsock::restore(
VsockConstructorArgs {
mem: ctx.mem.clone(),
Expand Down
4 changes: 3 additions & 1 deletion src/vmm/src/mmds/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ mod tests {

let restored_ns = MmdsNetworkStack::restore(
Arc::new(Mutex::new(Mmds::default())),
&Snapshot::load(&mut mem.as_slice()).unwrap().data,
&Snapshot::load_without_crc_check(mem.as_slice())
.unwrap()
.data,
)
.unwrap();

Expand Down
5 changes: 3 additions & 2 deletions src/vmm/src/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -676,8 +676,9 @@ mod tests {
.save(&mut buf.as_mut_slice())
.unwrap();

let restored_microvm_state: MicrovmState =
Snapshot::load(&mut buf.as_slice()).unwrap().data;
let restored_microvm_state: MicrovmState = Snapshot::load_without_crc_check(buf.as_slice())
.unwrap()
.data;

assert_eq!(restored_microvm_state.vm_info, microvm_state.vm_info);
assert_eq!(
Expand Down
18 changes: 14 additions & 4 deletions src/vmm/src/rate_limiter/persist.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,13 @@ mod tests {
.save(&mut mem.as_mut_slice())
.unwrap();

let restored_tb =
TokenBucket::restore((), &Snapshot::load(&mut mem.as_slice()).unwrap().data).unwrap();
let restored_tb = TokenBucket::restore(
(),
&Snapshot::load_without_crc_check(mem.as_slice())
.unwrap()
.data,
)
.unwrap();
assert!(tb.partial_eq(&restored_tb));
}

Expand Down Expand Up @@ -197,8 +202,13 @@ mod tests {
Snapshot::new(rate_limiter.save())
.save(&mut mem.as_mut_slice())
.unwrap();
let restored_rate_limiter =
RateLimiter::restore((), &Snapshot::load(&mut mem.as_slice()).unwrap().data).unwrap();
let restored_rate_limiter = RateLimiter::restore(
(),
&Snapshot::load_without_crc_check(mem.as_slice())
.unwrap()
.data,
)
.unwrap();

assert!(
rate_limiter
Expand Down
71 changes: 53 additions & 18 deletions src/vmm/src/snapshot/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
pub mod crc;
mod persist;
use std::fmt::Debug;
use std::io::{Read, Write};
use std::io::{Read, Seek, SeekFrom, Write};

use bincode::config;
use bincode::config::{Configuration, Fixint, Limit, LittleEndian};
Expand All @@ -38,7 +38,7 @@ use serde::{Deserialize, Serialize};
use crate::persist::SNAPSHOT_VERSION;
use crate::snapshot::crc::{CRC64Reader, CRC64Writer};
pub use crate::snapshot::persist::Persist;
use crate::utils::mib_to_bytes;
use crate::utils::{mib_to_bytes, u64_to_usize};

#[cfg(target_arch = "x86_64")]
const SNAPSHOT_MAGIC_ID: u64 = 0x0710_1984_8664_0000u64;
Expand All @@ -64,10 +64,14 @@ pub enum SnapshotError {
InvalidFormatVersion(Version),
/// Magic value does not match arch: {0}
InvalidMagic(u64),
/// Snapshot file is not long enough to even contain the CRC
TooShort,
/// An error occured during bincode encoding: {0}
Encode(#[from] EncodeError),
/// An error occured during bincode decoding: {0}
Decode(#[from] DecodeError),
/// IO Error: {0}
Io(#[from] std::io::Error),
}

fn serialize<S: Serialize, W: Write>(data: &S, write: &mut W) -> Result<(), SnapshotError> {
Expand All @@ -86,8 +90,8 @@ struct SnapshotHdr {
}

impl SnapshotHdr {
fn load<R: Read>(reader: &mut R) -> Result<Self, SnapshotError> {
let hdr: SnapshotHdr = bincode::serde::decode_from_std_read(reader, BINCODE_CONFIG)?;
fn load(buf: &mut &[u8]) -> Result<Self, SnapshotError> {
let (hdr, bytes_read) = bincode::serde::decode_from_slice::<Self, _>(buf, BINCODE_CONFIG)?;

if hdr.magic != SNAPSHOT_MAGIC_ID {
return Err(SnapshotError::InvalidMagic(hdr.magic));
Expand All @@ -98,6 +102,8 @@ impl SnapshotHdr {
return Err(SnapshotError::InvalidFormatVersion(hdr.version));
}

*buf = &buf[bytes_read..];

Ok(hdr)
}
}
Expand Down Expand Up @@ -138,17 +144,27 @@ impl<Data> Snapshot<Data> {
}

impl<Data: DeserializeOwned> Snapshot<Data> {
fn load_without_crc_check<R: Read>(reader: &mut R) -> Result<Self, SnapshotError> {
let header = SnapshotHdr::load(reader)?;
let data = bincode::serde::decode_from_std_read(reader, BINCODE_CONFIG)?;
pub(crate) fn load_without_crc_check(mut buf: &[u8]) -> Result<Self, SnapshotError> {
let header = SnapshotHdr::load(&mut buf)?;
let data = bincode::serde::decode_from_slice(buf, BINCODE_CONFIG)?.0;
Ok(Self { header, data })
}

/// Loads a snapshot from the given [`Read`] instance, performing all validations
/// (CRC, snapshot magic value, snapshot version).
pub fn load<R: Read>(reader: &mut R) -> Result<Self, SnapshotError> {
pub fn load<R: Read + Seek>(reader: &mut R) -> Result<Self, SnapshotError> {
let snapshot_size = reader.seek(SeekFrom::End(0))?;
reader.seek(SeekFrom::Start(0))?;
// dont read the CRC yet.
let mut buf = vec![
0;
u64_to_usize(snapshot_size)
.checked_sub(size_of::<u64>())
.ok_or(SnapshotError::TooShort)?
];
let mut crc_reader = CRC64Reader::new(reader);
let snapshot = Self::load_without_crc_check(&mut crc_reader)?;
crc_reader.read_exact(buf.as_mut_slice())?;
let snapshot = Self::load_without_crc_check(buf.as_slice())?;
let computed_checksum = crc_reader.checksum();
let stored_checksum: u64 =
bincode::serde::decode_from_std_read(&mut crc_reader.reader, BINCODE_CONFIG)?;
Expand All @@ -171,7 +187,20 @@ impl<Data: Serialize> Snapshot<Data> {

#[cfg(test)]
mod tests {
use vmm_sys_util::tempfile::TempFile;

use super::*;
use crate::persist::MicrovmState;

#[test]
fn test_snapshot_restore() {
let state = MicrovmState::default();
let file = TempFile::new().unwrap();

Snapshot::new(state).save(&mut file.as_file()).unwrap();
file.as_file().seek(SeekFrom::Start(0)).unwrap();
Snapshot::<MicrovmState>::load(&mut file.as_file()).unwrap();
}

#[test]
fn test_parse_version_from_file() {
Expand Down Expand Up @@ -199,10 +228,16 @@ mod tests {
}
}

impl Seek for BadReader {
fn seek(&mut self, _pos: SeekFrom) -> std::io::Result<u64> {
Ok(9) // needs to be long enough to prevent to have a CRC
}
}

let mut reader = BadReader {};

assert!(
matches!(Snapshot::<()>::load(&mut reader), Err(SnapshotError::Decode(DecodeError::Io {inner, ..})) if inner.kind() == std::io::ErrorKind::InvalidInput)
matches!(Snapshot::<()>::load(&mut reader), Err(SnapshotError::Io(inner)) if inner.kind() == std::io::ErrorKind::InvalidInput)
);
}

Expand Down Expand Up @@ -239,7 +274,7 @@ mod tests {
serialize(&snapshot, &mut data.as_mut_slice()).unwrap();

assert!(matches!(
Snapshot::<()>::load(&mut data.as_slice()),
Snapshot::<()>::load(&mut std::io::Cursor::new(data.as_slice())),
Err(SnapshotError::Crc64(_))
));
}
Expand All @@ -254,7 +289,7 @@ mod tests {
snapshot.save(&mut data.as_mut_slice()).unwrap();

assert!(matches!(
Snapshot::<()>::load(&mut data.as_slice()),
Snapshot::<()>::load_without_crc_check(data.as_slice()),
Err(SnapshotError::InvalidFormatVersion(v)) if v.major == SNAPSHOT_VERSION.major + 1
));

Expand All @@ -263,36 +298,36 @@ mod tests {
snapshot.header.version.minor = SNAPSHOT_VERSION.minor + 1;
snapshot.save(&mut data.as_mut_slice()).unwrap();
assert!(matches!(
Snapshot::<()>::load(&mut data.as_slice()),
Snapshot::<()>::load_without_crc_check(data.as_slice()),
Err(SnapshotError::InvalidFormatVersion(v)) if v.minor == SNAPSHOT_VERSION.minor + 1
));

// But we can support minor versions smaller or equal to ours. We also support
// all patch versions within our supported major.minor version.
let snapshot = Snapshot::new(());
snapshot.save(&mut data.as_mut_slice()).unwrap();
Snapshot::<()>::load(&mut data.as_slice()).unwrap();
Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();

if SNAPSHOT_VERSION.minor != 0 {
let mut snapshot = Snapshot::new(());
snapshot.header.version.minor = SNAPSHOT_VERSION.minor - 1;
snapshot.save(&mut data.as_mut_slice()).unwrap();
Snapshot::<()>::load(&mut data.as_slice()).unwrap();
Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();
}

let mut snapshot = Snapshot::new(());
snapshot.header.version.patch = 0;
snapshot.save(&mut data.as_mut_slice()).unwrap();
Snapshot::<()>::load(&mut data.as_slice()).unwrap();
Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();

let mut snapshot = Snapshot::new(());
snapshot.header.version.patch = SNAPSHOT_VERSION.patch + 1;
snapshot.save(&mut data.as_mut_slice()).unwrap();
Snapshot::<()>::load(&mut data.as_slice()).unwrap();
Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();

let mut snapshot = Snapshot::new(());
snapshot.header.version.patch = 1024;
snapshot.save(&mut data.as_mut_slice()).unwrap();
Snapshot::<()>::load(&mut data.as_slice()).unwrap();
Snapshot::<()>::load_without_crc_check(data.as_slice()).unwrap();
}
}
Loading