Skip to content

Commit 87e857a

Browse files
committed
fix: update to cpu id validation during snapshot restoration
Previously `validate_cpu_vendor` and `validate_cpu_manufacturer_id` checks were giving errors only when they could not obtain needed ids from host or guest. The equality checks were only used to print `info` or `error` logs. Now both methods only print logs and can not error out. Signed-off-by: Egor Lazarchuk <yegorlz@amazon.co.uk>
1 parent f5c0017 commit 87e857a

File tree

3 files changed

+50
-209
lines changed

3 files changed

+50
-209
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
- Updated deserialization of `bitmap` for custom CPU templates to allow usage
1010
of '_' as a separator.
1111
- Changed the strip feature of `cpu-template-helper` tool to operate bitwise.
12+
- Better logs during validation of CPU ID in snapshot restoration path. Also
13+
Firecracker now does not fail if it can't get CPU ID from the host or
14+
can't find CPU ID in the snapshot.
1215

1316
### Fixed
1417

src/vmm/src/persist.rs

Lines changed: 47 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -356,18 +356,6 @@ pub fn get_snapshot_data_version(
356356
Ok(data_version)
357357
}
358358

359-
/// Error type for [`validate_cpu_vendor`].
360-
#[cfg(target_arch = "x86_64")]
361-
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
362-
pub enum ValidateCpuVendorError {
363-
/// Failed to read host vendor.
364-
#[error("Failed to read host vendor: {0}")]
365-
Host(#[from] crate::cpu_config::x86_64::cpuid::common::GetCpuidError),
366-
/// Failed to read snapshot vendor.
367-
#[error("Failed to read snapshot vendor")]
368-
Snapshot,
369-
}
370-
371359
/// Validates that snapshot CPU vendor matches the host CPU vendor.
372360
///
373361
/// # Errors
@@ -376,38 +364,32 @@ pub enum ValidateCpuVendorError {
376364
/// - Failed to read host vendor.
377365
/// - Failed to read snapshot vendor.
378366
#[cfg(target_arch = "x86_64")]
379-
pub fn validate_cpu_vendor(microvm_state: &MicrovmState) -> Result<bool, ValidateCpuVendorError> {
380-
let host_vendor_id = get_vendor_id_from_host()?;
381-
382-
let snapshot_vendor_id = microvm_state.vcpu_states[0]
383-
.cpuid
384-
.vendor_id()
385-
.ok_or(ValidateCpuVendorError::Snapshot)?;
386-
387-
if host_vendor_id == snapshot_vendor_id {
388-
info!("Snapshot CPU vendor id: {:?}", &snapshot_vendor_id);
389-
Ok(true)
390-
} else {
391-
error!(
392-
"Host CPU vendor id: {:?} differs from the snapshotted one: {:?}",
393-
&host_vendor_id, &snapshot_vendor_id
394-
);
395-
Ok(false)
367+
pub fn validate_cpu_vendor(microvm_state: &MicrovmState) {
368+
let host_vendor_id = get_vendor_id_from_host();
369+
let snapshot_vendor_id = microvm_state.vcpu_states[0].cpuid.vendor_id();
370+
match (host_vendor_id, snapshot_vendor_id) {
371+
(Ok(host_id), Some(snapshot_id)) => {
372+
info!("Host CPU vendor ID: {host_id:?}");
373+
info!("Snapshot CPU vendor ID: {snapshot_id:?}");
374+
if host_id != snapshot_id {
375+
warn!("Host CPU vendor ID differs from the snapshotted one",);
376+
}
377+
}
378+
(Ok(host_id), None) => {
379+
info!("Host CPU vendor ID: {host_id:?}");
380+
warn!("Snapshot CPU vendor ID: couldn't get from the snapshot");
381+
}
382+
(Err(_), Some(snapshot_id)) => {
383+
warn!("Host CPU vendor ID: couldn't get from the host");
384+
info!("Snapshot CPU vendor ID: {snapshot_id:?}");
385+
}
386+
(Err(_), None) => {
387+
warn!("Host CPU vendor ID: couldn't get from the host");
388+
warn!("Snapshot CPU vendor ID: couldn't get from the snapshot");
389+
}
396390
}
397391
}
398392

399-
/// Error type for [`validate_cpu_manufacturer_id`].
400-
#[cfg(target_arch = "aarch64")]
401-
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
402-
pub enum ValidateCpuManufacturerIdError {
403-
/// Failed to read host vendor.
404-
#[error("Failed to get manufacturer ID from host: {0}")]
405-
Host(String),
406-
/// Failed to read host vendor.
407-
#[error("Failed to get manufacturer ID from state: {0}")]
408-
Snapshot(String),
409-
}
410-
411393
/// Validate that Snapshot Manufacturer ID matches
412394
/// the one from the Host
413395
///
@@ -418,27 +400,30 @@ pub enum ValidateCpuManufacturerIdError {
418400
/// - Failed to read host vendor.
419401
/// - Failed to read snapshot vendor.
420402
#[cfg(target_arch = "aarch64")]
421-
pub fn validate_cpu_manufacturer_id(
422-
microvm_state: &MicrovmState,
423-
) -> Result<bool, ValidateCpuManufacturerIdError> {
424-
let host_man_id = get_manufacturer_id_from_host()
425-
.map_err(|err| ValidateCpuManufacturerIdError::Host(err.to_string()))?;
426-
427-
for state in &microvm_state.vcpu_states {
428-
let state_man_id = get_manufacturer_id_from_state(&state.regs)
429-
.map_err(|err| ValidateCpuManufacturerIdError::Snapshot(err.to_string()))?;
430-
431-
if host_man_id != state_man_id {
432-
error!(
433-
"Host CPU manufacturer ID: {} differs from snapshotted one: {}",
434-
&host_man_id, &state_man_id
435-
);
436-
return Ok(false);
437-
} else {
438-
info!("Snapshot CPU manufacturer ID: {:?}", &state_man_id);
403+
pub fn validate_cpu_manufacturer_id(microvm_state: &MicrovmState) {
404+
let host_cpu_id = get_manufacturer_id_from_host();
405+
let snapshot_cpu_id = get_manufacturer_id_from_state(&microvm_state.vcpu_states[0].regs);
406+
match (host_cpu_id, snapshot_cpu_id) {
407+
(Ok(host_id), Ok(snapshot_id)) => {
408+
info!("Host CPU manufacturer ID: {host_id:?}");
409+
info!("Snapshot CPU manufacturer ID: {snapshot_id:?}");
410+
if host_id != snapshot_id {
411+
warn!("Host CPU manufacturer ID differs from the snapshotted one",);
412+
}
413+
}
414+
(Ok(host_id), Err(_)) => {
415+
info!("Host CPU manufacturer ID: {host_id:?}");
416+
warn!("Snapshot CPU manufacturer ID: couldn't get from the snapshot");
417+
}
418+
(Err(_), Ok(snapshot_id)) => {
419+
warn!("Host CPU manufacturer ID: couldn't get from the host");
420+
info!("Snapshot CPU manufacturer ID: {snapshot_id:?}");
421+
}
422+
(Err(_), Err(_)) => {
423+
warn!("Host CPU manufacturer ID: couldn't get from the host");
424+
warn!("Snapshot CPU manufacturer ID: couldn't get from the snapshot");
439425
}
440426
}
441-
Ok(true)
442427
}
443428
/// Error type for [`snapshot_state_sanity_check`].
444429
#[derive(Debug, thiserror::Error, PartialEq, Eq)]
@@ -449,14 +434,6 @@ pub enum SnapShotStateSanityCheckError {
449434
/// No memory region defined.
450435
#[error("No memory region defined.")]
451436
NoMemory,
452-
/// Failed to validate vCPU vendor.
453-
#[cfg(target_arch = "x86_64")]
454-
#[error("Failed to validate vCPU vendor: {0}")]
455-
ValidateCpuVendor(#[from] ValidateCpuVendorError),
456-
/// Failed to validate vCPU manufacturer id.
457-
#[error("Failed to validate vCPU manufacturer id: {0}")]
458-
#[cfg(target_arch = "aarch64")]
459-
ValidateCpuManufacturerId(#[from] ValidateCpuManufacturerIdError),
460437
}
461438

462439
/// Performs sanity checks against the state file and returns specific errors.
@@ -478,9 +455,9 @@ pub fn snapshot_state_sanity_check(
478455
}
479456

480457
#[cfg(target_arch = "x86_64")]
481-
validate_cpu_vendor(microvm_state)?;
458+
validate_cpu_vendor(microvm_state);
482459
#[cfg(target_arch = "aarch64")]
483-
validate_cpu_manufacturer_id(microvm_state)?;
460+
validate_cpu_manufacturer_id(microvm_state);
484461

485462
Ok(())
486463
}

src/vmm/tests/integration_tests.rs

Lines changed: 0 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -354,142 +354,3 @@ fn get_microvm_state_from_snapshot() -> MicrovmState {
354354
)
355355
.unwrap()
356356
}
357-
358-
#[cfg(target_arch = "x86_64")]
359-
#[test]
360-
fn test_snapshot_cpu_vendor() {
361-
use vmm::persist::validate_cpu_vendor;
362-
let microvm_state = get_microvm_state_from_snapshot();
363-
364-
// Check if the snapshot created above passes validation since
365-
// the snapshot was created locally.
366-
assert!(validate_cpu_vendor(&microvm_state).is_ok());
367-
}
368-
369-
#[cfg(target_arch = "x86_64")]
370-
#[test]
371-
fn test_snapshot_cpu_vendor_mismatch() {
372-
use vmm::persist::validate_cpu_vendor;
373-
let mut microvm_state = get_microvm_state_from_snapshot();
374-
375-
// Check if the snapshot created above passes validation since
376-
// the snapshot was created locally.
377-
assert_eq!(validate_cpu_vendor(&microvm_state), Ok(true));
378-
379-
// Modify the vendor id in CPUID.
380-
for entry in microvm_state.vcpu_states[0].cpuid.as_mut_slice().iter_mut() {
381-
if entry.function == 0 && entry.index == 0 {
382-
// Fail if vendor id is NULL as this needs furhter investigation.
383-
assert_ne!(entry.ebx, 0);
384-
assert_ne!(entry.ecx, 0);
385-
assert_ne!(entry.edx, 0);
386-
entry.ebx = 0;
387-
break;
388-
}
389-
}
390-
391-
// It succeeds in checking if the CPU vendor is valid, in this process it discovers the CPU
392-
// vendor not valid.
393-
assert_eq!(validate_cpu_vendor(&microvm_state), Ok(false));
394-
395-
// Negative test: remove the vendor id from cpuid.
396-
for entry in microvm_state.vcpu_states[0].cpuid.as_mut_slice().iter_mut() {
397-
if entry.function == 0 && entry.index == 0 {
398-
entry.function = 1234;
399-
}
400-
}
401-
402-
// It succeeds in checking if the CPU vendor is valid, in this process it discovers the CPU
403-
// vendor not valid.
404-
assert_eq!(
405-
validate_cpu_vendor(&microvm_state),
406-
Err(vmm::persist::ValidateCpuVendorError::Snapshot)
407-
);
408-
}
409-
410-
#[cfg(target_arch = "x86_64")]
411-
#[test]
412-
fn test_snapshot_cpu_vendor_missing() {
413-
use vmm::persist::validate_cpu_vendor;
414-
let mut microvm_state = get_microvm_state_from_snapshot();
415-
416-
// Check if the snapshot created above passes validation since
417-
// the snapshot was created locally.
418-
assert!(validate_cpu_vendor(&microvm_state).is_ok());
419-
420-
// Negative test: remove the vendor id from cpuid.
421-
for entry in microvm_state.vcpu_states[0].cpuid.as_mut_slice().iter_mut() {
422-
if entry.function == 0 && entry.index == 0 {
423-
entry.function = 1234;
424-
}
425-
}
426-
427-
// This must fail as the cpu vendor entry does not exist.
428-
assert!(validate_cpu_vendor(&microvm_state).is_err());
429-
}
430-
431-
#[cfg(target_arch = "aarch64")]
432-
#[test]
433-
fn test_snapshot_cpu_vendor() {
434-
use vmm::persist::validate_cpu_manufacturer_id;
435-
436-
let microvm_state = get_microvm_state_from_snapshot();
437-
438-
// Check if the snapshot created above passes validation since
439-
// the snapshot was created locally.
440-
assert!(validate_cpu_manufacturer_id(&microvm_state).is_ok());
441-
}
442-
443-
#[cfg(target_arch = "aarch64")]
444-
#[test]
445-
fn test_snapshot_cpu_vendor_missing() {
446-
use vmm::arch::aarch64::regs::{Aarch64RegisterVec, MIDR_EL1};
447-
use vmm::persist::{validate_cpu_manufacturer_id, ValidateCpuManufacturerIdError};
448-
449-
let mut microvm_state = get_microvm_state_from_snapshot();
450-
451-
// Check if the snapshot created above passes validation since
452-
// the snapshot was created locally.
453-
assert_eq!(validate_cpu_manufacturer_id(&microvm_state), Ok(true));
454-
455-
// Manufacturer id is stored in the MIDR_EL1 register. For this test we
456-
// remove it from the state.
457-
for state in microvm_state.vcpu_states.iter_mut() {
458-
let mut new_regs = Aarch64RegisterVec::default();
459-
// Removing MIDR_EL1 register.
460-
for reg in state.regs.iter() {
461-
if reg.id != MIDR_EL1 {
462-
new_regs.push(reg);
463-
}
464-
}
465-
state.regs = new_regs;
466-
}
467-
assert!(matches!(
468-
validate_cpu_manufacturer_id(&microvm_state),
469-
Err(ValidateCpuManufacturerIdError::Snapshot(_))
470-
));
471-
}
472-
473-
#[cfg(target_arch = "aarch64")]
474-
#[test]
475-
fn test_snapshot_cpu_vendor_mismatch() {
476-
use vmm::arch::aarch64::regs::MIDR_EL1;
477-
use vmm::persist::validate_cpu_manufacturer_id;
478-
479-
let mut microvm_state = get_microvm_state_from_snapshot();
480-
481-
// Check if the snapshot created above passes validation since
482-
// the snapshot was created locally.
483-
assert_eq!(validate_cpu_manufacturer_id(&microvm_state), Ok(true));
484-
485-
// Change the MIDR_EL1 value from the VCPU states, to contain an
486-
// invalid manufacturer ID
487-
for state in microvm_state.vcpu_states.as_mut_slice().iter_mut() {
488-
for mut reg in state.regs.iter_mut() {
489-
if reg.id == MIDR_EL1 {
490-
reg.set_value::<u64, 8>(0x710FD081);
491-
}
492-
}
493-
}
494-
assert_eq!(validate_cpu_manufacturer_id(&microvm_state), Ok(false));
495-
}

0 commit comments

Comments
 (0)