Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package firecracker for openSUSE:Factory checked in at 2026-03-04 21:09:04 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/firecracker (Old) and /work/SRC/openSUSE:Factory/.firecracker.new.561 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "firecracker" Wed Mar 4 21:09:04 2026 rev:20 rq:1336277 version:1.14.2 Changes: -------- --- /work/SRC/openSUSE:Factory/firecracker/firecracker.changes 2026-01-22 15:18:11.376256124 +0100 +++ /work/SRC/openSUSE:Factory/.firecracker.new.561/firecracker.changes 2026-03-04 21:10:07.872631417 +0100 @@ -1,0 +2,13 @@ +Fri Feb 27 07:16:00 UTC 2026 - Johannes Kastl <[email protected]> + +- Update to version 1.14.2: + * Fixed + - #5698: Fixed the possible ENXIO error which could occur + during file open operation if the underlying file is FIFO + without active readers already attached. + - #5705: Fixed a bug that caused Firecracker to corrupt the + memory files of differential snapshots for VMs with multiple + memory slots. This affected VMs using memory hot-plugging or + any x86 VMs with a memory size larger than 3GiB. + +------------------------------------------------------------------- @@ -6 +19,3 @@ - - #5631: Update binary copy process inside Jailer to disallow symlinks and hardlinks at the destination path and change ownership of the copied binary to the specified uid/gid. + - #5631: Update binary copy process inside Jailer to disallow + symlinks and hardlinks at the destination path and change + ownership of the copied binary to the specified uid/gid. Old: ---- firecracker-1.14.1.obscpio New: ---- firecracker-1.14.2.obscpio ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ firecracker.spec ++++++ --- /var/tmp/diff_new_pack.tLJGPM/_old 2026-03-04 21:10:08.896673741 +0100 +++ /var/tmp/diff_new_pack.tLJGPM/_new 2026-03-04 21:10:08.896673741 +0100 @@ -17,7 +17,7 @@ Name: firecracker -Version: 1.14.1 +Version: 1.14.2 Release: 0 Summary: Virtual Machine Monitor for creating microVMs License: Apache-2.0 ++++++ _service ++++++ --- /var/tmp/diff_new_pack.tLJGPM/_old 2026-03-04 21:10:08.936675394 +0100 +++ /var/tmp/diff_new_pack.tLJGPM/_new 2026-03-04 21:10:08.952676055 +0100 @@ -2,7 +2,7 @@ <service name="obs_scm" mode="manual"> <param name="url">https://github.com/firecracker-microvm/firecracker.git</param> <param name="scm">git</param> - <param name="revision">v1.14.1</param> + <param name="revision">v1.14.2</param> <param name="versionformat">@PARENT_TAG@</param> <param name="changesgenerate">enable</param> <param name="versionrewrite-pattern">v(.*)</param> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.tLJGPM/_old 2026-03-04 21:10:08.976677047 +0100 +++ /var/tmp/diff_new_pack.tLJGPM/_new 2026-03-04 21:10:08.976677047 +0100 @@ -1,6 +1,6 @@ <servicedata> <service name="tar_scm"> <param name="url">https://github.com/firecracker-microvm/firecracker.git</param> - <param name="changesrevision">37593df439aeb19fc70b292e618770108e563e7e</param></service></servicedata> + <param name="changesrevision">26b4c55f14f02fbb7dd3353769e608fa90d22445</param></service></servicedata> (No newline at EOF) ++++++ firecracker-1.14.1.obscpio -> firecracker-1.14.2.obscpio ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/CHANGELOG.md new/firecracker-1.14.2/CHANGELOG.md --- old/firecracker-1.14.1/CHANGELOG.md 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/CHANGELOG.md 2026-02-26 18:19:34.000000000 +0100 @@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [1.14.2] + +### Fixed + +- [#5698](https://github.com/firecracker-microvm/firecracker/pull/5698): Fixed + the possible ENXIO error which could occur during file open operation if the + underlying file is FIFO without active readers already attached. +- [#5705](https://github.com/firecracker-microvm/firecracker/pull/5705): Fixed a + bug that caused Firecracker to corrupt the memory files of differential + snapshots for VMs with multiple memory slots. This affected VMs using memory + hot-plugging or any x86 VMs with a memory size larger than 3GiB. + ## [1.14.1] ### Changed diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/CREDITS.md new/firecracker-1.14.2/CREDITS.md --- old/firecracker-1.14.1/CREDITS.md 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/CREDITS.md 2026-02-26 18:19:34.000000000 +0100 @@ -130,6 +130,7 @@ - huang-jl <[email protected]> - Iggy Jackson <[email protected]> - ihciah <[email protected]> +- Ilias Stamatis <[email protected]> - Ioana Chirca <[email protected]> - Ishwor Gurung <[email protected]> - Iulian Barbu <[email protected]> diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/Cargo.lock new/firecracker-1.14.2/Cargo.lock --- old/firecracker-1.14.1/Cargo.lock 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/Cargo.lock 2026-02-26 18:19:34.000000000 +0100 @@ -391,7 +391,7 @@ [[package]] name = "cpu-template-helper" -version = "1.14.1" +version = "1.14.2" dependencies = [ "clap", "displaydoc", @@ -554,7 +554,7 @@ [[package]] name = "firecracker" -version = "1.14.1" +version = "1.14.2" dependencies = [ "cargo_toml", "displaydoc", @@ -703,7 +703,7 @@ [[package]] name = "jailer" -version = "1.14.1" +version = "1.14.2" dependencies = [ "libc", "log-instrument", @@ -1078,7 +1078,7 @@ [[package]] name = "rebase-snap" -version = "1.14.1" +version = "1.14.2" dependencies = [ "displaydoc", "libc", @@ -1178,7 +1178,7 @@ [[package]] name = "seccompiler" -version = "1.14.1" +version = "1.14.2" dependencies = [ "bincode", "clap", @@ -1275,7 +1275,7 @@ [[package]] name = "snapshot-editor" -version = "1.14.1" +version = "1.14.2" dependencies = [ "clap", "clap-num", diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/docs/RELEASE_POLICY.md new/firecracker-1.14.2/docs/RELEASE_POLICY.md --- old/firecracker-1.14.1/docs/RELEASE_POLICY.md 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/docs/RELEASE_POLICY.md 2026-02-26 18:19:34.000000000 +0100 @@ -90,8 +90,8 @@ | Release | Release Date | Latest Patch | Min. end of support | Official end of Support | | ------: | -----------: | -----------: | ------------------: | :------------------------------ | -| v1.14 | 2025-12-17 | v1.14.0 | 2026-06-17 | Supported | -| v1.13 | 2025-08-28 | v1.13.1 | 2026-02-28 | Supported | +| v1.14 | 2025-12-17 | v1.14.2 | 2026-06-17 | Supported | +| v1.13 | 2025-08-28 | v1.13.2 | 2026-02-28 | Supported | | v1.12 | 2025-05-07 | v1.12.1 | 2025-11-07 | 2025-12-17 (v1.14 released) | | v1.11 | 2025-03-18 | v1.11.0 | 2025-09-18 | 2025-09-18 (end of 6mo support) | | v1.10 | 2024-11-07 | v1.10.1 | 2025-05-07 | 2025-05-07 (v1.12 released) | diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/cpu-template-helper/Cargo.toml new/firecracker-1.14.2/src/cpu-template-helper/Cargo.toml --- old/firecracker-1.14.1/src/cpu-template-helper/Cargo.toml 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/cpu-template-helper/Cargo.toml 2026-02-26 18:19:34.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "cpu-template-helper" -version = "1.14.1" +version = "1.14.2" authors = ["Amazon Firecracker team <[email protected]>"] edition = "2024" license = "Apache-2.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/firecracker/Cargo.toml new/firecracker-1.14.2/src/firecracker/Cargo.toml --- old/firecracker-1.14.1/src/firecracker/Cargo.toml 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/firecracker/Cargo.toml 2026-02-26 18:19:34.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "firecracker" -version = "1.14.1" +version = "1.14.2" authors = ["Amazon Firecracker team <[email protected]>"] edition = "2024" build = "build.rs" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/firecracker/swagger/firecracker.yaml new/firecracker-1.14.2/src/firecracker/swagger/firecracker.yaml --- old/firecracker-1.14.1/src/firecracker/swagger/firecracker.yaml 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/firecracker/swagger/firecracker.yaml 2026-02-26 18:19:34.000000000 +0100 @@ -5,7 +5,7 @@ The API is accessible through HTTP calls on specific URLs carrying JSON modeled data. The transport medium is a Unix Domain Socket. - version: 1.14.1 + version: 1.14.2 termsOfService: "" contact: email: "[email protected]" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/jailer/Cargo.toml new/firecracker-1.14.2/src/jailer/Cargo.toml --- old/firecracker-1.14.1/src/jailer/Cargo.toml 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/jailer/Cargo.toml 2026-02-26 18:19:34.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "jailer" -version = "1.14.1" +version = "1.14.2" authors = ["Amazon Firecracker team <[email protected]>"] edition = "2024" description = "Process for starting Firecracker in production scenarios; applies a cgroup/namespace isolation barrier and then drops privileges." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/rebase-snap/Cargo.toml new/firecracker-1.14.2/src/rebase-snap/Cargo.toml --- old/firecracker-1.14.1/src/rebase-snap/Cargo.toml 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/rebase-snap/Cargo.toml 2026-02-26 18:19:34.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "rebase-snap" -version = "1.14.1" +version = "1.14.2" authors = ["Amazon Firecracker team <[email protected]>"] edition = "2024" license = "Apache-2.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/seccompiler/Cargo.toml new/firecracker-1.14.2/src/seccompiler/Cargo.toml --- old/firecracker-1.14.1/src/seccompiler/Cargo.toml 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/seccompiler/Cargo.toml 2026-02-26 18:19:34.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "seccompiler" -version = "1.14.1" +version = "1.14.2" authors = ["Amazon Firecracker team <[email protected]>"] edition = "2024" description = "Program that compiles multi-threaded seccomp-bpf filters expressed as JSON into raw BPF programs, serializing them and outputting them to a file." diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/snapshot-editor/Cargo.toml new/firecracker-1.14.2/src/snapshot-editor/Cargo.toml --- old/firecracker-1.14.1/src/snapshot-editor/Cargo.toml 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/snapshot-editor/Cargo.toml 2026-02-26 18:19:34.000000000 +0100 @@ -1,6 +1,6 @@ [package] name = "snapshot-editor" -version = "1.14.1" +version = "1.14.2" authors = ["Amazon Firecracker team <[email protected]>"] edition = "2024" license = "Apache-2.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/vmm/src/device_manager/mod.rs new/firecracker-1.14.2/src/vmm/src/device_manager/mod.rs --- old/firecracker-1.14.1/src/vmm/src/device_manager/mod.rs 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/vmm/src/device_manager/mod.rs 2026-02-26 18:19:34.000000000 +0100 @@ -35,7 +35,7 @@ use crate::devices::virtio::transport::mmio::{IrqTrigger, MmioTransport}; use crate::resources::VmResources; use crate::snapshot::Persist; -use crate::utils::open_file_write_nonblock; +use crate::utils::open_file_nonblock; use crate::vstate::bus::BusError; use crate::vstate::memory::GuestMemoryMmap; use crate::{EmulateSerialInitError, EventManager, Vm}; @@ -125,7 +125,7 @@ output: Option<&PathBuf>, ) -> Result<Arc<Mutex<SerialDevice>>, std::io::Error> { let (serial_in, serial_out) = match output { - Some(path) => (None, open_file_write_nonblock(path).map(SerialOut::File)?), + Some(path) => (None, open_file_nonblock(path).map(SerialOut::File)?), None => { Self::set_stdout_nonblocking(); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/vmm/src/logger/logging.rs new/firecracker-1.14.2/src/vmm/src/logger/logging.rs --- old/firecracker-1.14.1/src/vmm/src/logger/logging.rs 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/vmm/src/logger/logging.rs 2026-02-26 18:19:34.000000000 +0100 @@ -13,7 +13,7 @@ use utils::time::LocalTime; use super::metrics::{IncMetric, METRICS}; -use crate::utils::open_file_write_nonblock; +use crate::utils::open_file_nonblock; /// Default level filter for logger matching the swagger specification /// (`src/firecracker/swagger/firecracker.yaml`). @@ -62,7 +62,7 @@ ); if let Some(log_path) = config.log_path { - let file = open_file_write_nonblock(&log_path).map_err(LoggerUpdateError)?; + let file = open_file_nonblock(&log_path).map_err(LoggerUpdateError)?; guard.target = Some(file); }; diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/vmm/src/utils/mod.rs new/firecracker-1.14.2/src/vmm/src/utils/mod.rs --- old/firecracker-1.14.1/src/vmm/src/utils/mod.rs 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/vmm/src/utils/mod.rs 2026-02-26 18:19:34.000000000 +0100 @@ -14,7 +14,6 @@ use std::num::Wrapping; use std::os::unix::fs::OpenOptionsExt; use std::path::Path; -use std::result::Result; use libc::O_NONBLOCK; @@ -76,14 +75,16 @@ addr & !(align - 1) } -/// Create and open a File for writing to it. -/// In case we open a FIFO, in order to not block the instance if nobody is consuming the message -/// that is flushed to it, we are opening it with `O_NONBLOCK` flag. -/// In this case, writing to a pipe will start failing when reaching 64K of unconsumed content. -pub fn open_file_write_nonblock(path: &Path) -> Result<File, std::io::Error> { +/// Create and open a file for both reading and writing to it with a O_NONBLOCK flag. +/// In case we open a FIFO, we need all READ, WRITE and O_NONBLOCK in order to not block the process +/// if nobody is consuming the message. Otherwise opening the FIFO with only WRITE and O_NONBLOCK +/// will fail with ENXIO if there is no reader already attached to it. +/// NOTE: writing to a pipe will start failing when reaching 64K of unconsumed content. +pub fn open_file_nonblock(path: &Path) -> Result<File, std::io::Error> { OpenOptions::new() .custom_flags(O_NONBLOCK) .create(true) + .read(true) .write(true) .open(path) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/vmm/src/vmm_config/metrics.rs new/firecracker-1.14.2/src/vmm/src/vmm_config/metrics.rs --- old/firecracker-1.14.1/src/vmm/src/vmm_config/metrics.rs 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/vmm/src/vmm_config/metrics.rs 2026-02-26 18:19:34.000000000 +0100 @@ -7,7 +7,7 @@ use serde::{Deserialize, Serialize}; use crate::logger::{FcLineWriter, METRICS}; -use crate::utils::open_file_write_nonblock; +use crate::utils::open_file_nonblock; /// Strongly typed structure used to describe the metrics system. #[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] @@ -26,7 +26,7 @@ /// Configures the metrics as described in `metrics_cfg`. pub fn init_metrics(metrics_cfg: MetricsConfig) -> Result<(), MetricsConfigError> { let writer = FcLineWriter::new( - open_file_write_nonblock(&metrics_cfg.metrics_path) + open_file_nonblock(&metrics_cfg.metrics_path) .map_err(|err| MetricsConfigError::InitializationFailure(err.to_string()))?, ); METRICS diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/vmm/src/vstate/interrupts.rs new/firecracker-1.14.2/src/vmm/src/vstate/interrupts.rs --- old/firecracker-1.14.1/src/vmm/src/vstate/interrupts.rs 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/vmm/src/vstate/interrupts.rs 2026-02-26 18:19:34.000000000 +0100 @@ -187,7 +187,7 @@ fn restore( constructor_args: Self::ConstructorArgs, state: &Self::State, - ) -> std::result::Result<Self, Self::Error> { + ) -> Result<Self, Self::Error> { let mut vectors = Vec::with_capacity(state.len()); for gsi in state { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/src/vmm/src/vstate/memory.rs new/firecracker-1.14.2/src/vmm/src/vstate/memory.rs --- old/firecracker-1.14.1/src/vmm/src/vstate/memory.rs 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/src/vmm/src/vstate/memory.rs 2026-02-26 18:19:34.000000000 +0100 @@ -59,6 +59,24 @@ Unaligned, /// Error protecting memory slot: {0} Mprotect(std::io::Error), + /// Size too large for i64 conversion + SlotSizeTooLarge, + /// Dirty bitmap not found for memory slot {0} + DirtyBitmapNotFound(u32), + /// Dirty bitmap is larger than the slot size + DirtyBitmapTooLarge, + /// Dirty bitmap is smaller than the slot size + DirtyBitmapTooSmall, + /// Seek error: {0} + SeekError(std::io::Error), + /// Volatile memory error: {0} + VolatileMemoryError(vm_memory::VolatileMemoryError), +} + +impl From<vm_memory::VolatileMemoryError> for MemoryError { + fn from(e: vm_memory::VolatileMemoryError) -> Self { + MemoryError::VolatileMemoryError(e) + } } /// Type of the guest region @@ -121,25 +139,47 @@ writer: &mut T, kvm_bitmap: &[u64], page_size: usize, - ) -> Result<(), GuestMemoryError> { + ) -> Result<(), MemoryError> { let firecracker_bitmap = self.slice.bitmap(); let mut write_size = 0; let mut skip_size = 0; let mut dirty_batch_start = 0; + let expected_bitmap_array_len = (self.slice.len() / page_size).div_ceil(64); + if kvm_bitmap.len() > expected_bitmap_array_len { + return Err(MemoryError::DirtyBitmapTooLarge); + } else if kvm_bitmap.len() < expected_bitmap_array_len { + return Err(MemoryError::DirtyBitmapTooSmall); + } + for (i, v) in kvm_bitmap.iter().enumerate() { for j in 0..64 { let is_kvm_page_dirty = ((v >> j) & 1u64) != 0u64; let page_offset = ((i * 64) + j) * page_size; let is_firecracker_page_dirty = firecracker_bitmap.dirty_at(page_offset); + // We process 64 pages at a time, however the number of pages + // in the slot might not be a multiple of 64. We need to break + // once we go past the last page that is actually part of the + // region. + if page_offset >= self.slice.len() { + // Ensure there are no more dirty bits after this point + if (v >> j) != 0 { + return Err(MemoryError::DirtyBitmapTooLarge); + } + break; + } + if is_kvm_page_dirty || is_firecracker_page_dirty { // We are at the start of a new batch of dirty pages. if skip_size > 0 { // Seek forward over the unmodified pages. + let offset = skip_size + .try_into() + .map_err(|_| MemoryError::SlotSizeTooLarge)?; writer - .seek(SeekFrom::Current(skip_size.try_into().unwrap())) - .unwrap(); + .seek(SeekFrom::Current(offset)) + .map_err(MemoryError::SeekError)?; dirty_batch_start = page_offset; skip_size = 0; } @@ -161,6 +201,14 @@ writer.write_all_volatile(&self.slice.subslice(dirty_batch_start, write_size)?)?; } + // Advance the cursor even if the trailing pages are clean, so that the + // next slot starts writing at the correct offset. + if skip_size > 0 { + writer + .seek(SeekFrom::Current(skip_size.try_into().unwrap())) + .map_err(MemoryError::SeekError)?; + } + Ok(()) } @@ -668,10 +716,15 @@ .flat_map(|region| region.slots()) .try_for_each(|(mem_slot, plugged)| { if !plugged { - let ilen = i64::try_from(mem_slot.slice.len()).unwrap(); - writer.seek(SeekFrom::Current(ilen)).unwrap(); + let ilen = i64::try_from(mem_slot.slice.len()) + .map_err(|_| MemoryError::SlotSizeTooLarge)?; + writer + .seek(SeekFrom::Current(ilen)) + .map_err(MemoryError::SeekError)?; } else { - let kvm_bitmap = dirty_bitmap.get(&mem_slot.slot).unwrap(); + let kvm_bitmap = dirty_bitmap + .get(&mem_slot.slot) + .ok_or(MemoryError::DirtyBitmapNotFound(mem_slot.slot))?; mem_slot.dump_dirty(writer, kvm_bitmap, page_size)?; } Ok(()) @@ -683,7 +736,7 @@ self.reset_dirty(); } - write_result.map_err(MemoryError::WriteMemory) + write_result } /// Resets all the memory region bitmaps @@ -814,6 +867,7 @@ use std::collections::HashMap; use std::io::{Read, Seek, Write}; + use std::os::unix::fs::MetadataExt; use vmm_sys_util::tempfile::TempFile; @@ -1123,17 +1177,23 @@ .write(&second_region, region_2_address) .unwrap(); + // Firecracker Dirty Bitmap after the writes: + // First region pages: [dirty, dirty] + // Second region pages: [dirty, dirty] + let memory_state = guest_memory.describe(); - // Dump only the dirty pages. + // KVM dirty bitmap: // First region pages: [dirty, clean] // Second region pages: [clean, dirty] - let mut dirty_bitmap: DirtyBitmap = HashMap::new(); - dirty_bitmap.insert(0, vec![0b01]); - dirty_bitmap.insert(1, vec![0b10]); + let mut kvm_dirty_bitmap: DirtyBitmap = HashMap::new(); + kvm_dirty_bitmap.insert(0, vec![0b01]); + kvm_dirty_bitmap.insert(1, vec![0b10]); let mut file = TempFile::new().unwrap().into_file(); - guest_memory.dump_dirty(&mut file, &dirty_bitmap).unwrap(); + guest_memory + .dump_dirty(&mut file, &kvm_dirty_bitmap) + .unwrap(); // We can restore from this because this is the first dirty dump. let restored_guest_memory = @@ -1158,18 +1218,25 @@ let ones = vec![1u8; page_size]; let twos = vec![2u8; page_size]; - // Firecracker Bitmap - // First region pages: [dirty, clean] + // Firecracker Dirty Bitmap: + // First region pages: [clean, dirty] // Second region pages: [clean, clean] guest_memory .write(&twos, GuestAddress(page_size as u64)) .unwrap(); + // KVM dirty bitmap: + // First region pages: [dirty, clean] + // Second region pages: [clean, dirty] + kvm_dirty_bitmap.insert(0, vec![0b01]); + kvm_dirty_bitmap.insert(1, vec![0b10]); - guest_memory.dump_dirty(&mut reader, &dirty_bitmap).unwrap(); + guest_memory + .dump_dirty(&mut reader, &kvm_dirty_bitmap) + .unwrap(); // Check that only the dirty regions are dumped. let mut diff_file_content = Vec::new(); - let expected_first_region = [ + let expected_file_contents = [ ones.as_slice(), twos.as_slice(), zeros.as_slice(), @@ -1178,7 +1245,71 @@ .concat(); reader.seek(SeekFrom::Start(0)).unwrap(); reader.read_to_end(&mut diff_file_content).unwrap(); - assert_eq!(expected_first_region, diff_file_content); + assert_eq!(expected_file_contents, diff_file_content); + + // Take a 3rd snapshot + + // Firecracker Dirty Bitmap: + // First region pages: [dirty, clean] + // Second region pages: [dirty, clean] + guest_memory.write(&twos, region_1_address).unwrap(); + guest_memory.write(&ones, region_2_address).unwrap(); + // KVM dirty bitmap: + // First region pages: [clean, clean] + // Second region pages: [clean, clean] + kvm_dirty_bitmap.insert(0, vec![0b00]); + kvm_dirty_bitmap.insert(1, vec![0b00]); + + let file = TempFile::new().unwrap(); + let logical_size = page_size as u64 * 4; + file.as_file().set_len(logical_size).unwrap(); + + let mut reader = file.into_file(); + guest_memory + .dump_dirty(&mut reader, &kvm_dirty_bitmap) + .unwrap(); + + // Check that only the dirty regions are dumped. + let mut diff_file_content = Vec::new(); + // The resulting file is a sparse file with holes. + let expected_file_contents = [ + twos.as_slice(), + zeros.as_slice(), // hole + ones.as_slice(), + zeros.as_slice(), // hole + ] + .concat(); + reader.seek(SeekFrom::Start(0)).unwrap(); + reader.read_to_end(&mut diff_file_content).unwrap(); + + assert_eq!(expected_file_contents, diff_file_content); + + // Make sure that only 2 of the pages are written in the file and the + // other two are holes. + let metadata = reader.metadata().unwrap(); + let physical_size = metadata.blocks() * 512; + assert_eq!(physical_size, 2 * page_size as u64); + assert_ne!(physical_size, logical_size); + + // Test with bitmaps that are too large or too small + kvm_dirty_bitmap.insert(0, vec![0b1, 0b01]); + kvm_dirty_bitmap.insert(1, vec![0b10]); + assert!(matches!( + guest_memory.dump_dirty(&mut reader, &kvm_dirty_bitmap), + Err(MemoryError::DirtyBitmapTooLarge) + )); + kvm_dirty_bitmap.insert(0, vec![0b01]); + kvm_dirty_bitmap.insert(1, vec![0b110]); + assert!(matches!( + guest_memory.dump_dirty(&mut reader, &kvm_dirty_bitmap), + Err(MemoryError::DirtyBitmapTooLarge) + )); + kvm_dirty_bitmap.insert(0, vec![]); + kvm_dirty_bitmap.insert(1, vec![0b10]); + assert!(matches!( + guest_memory.dump_dirty(&mut reader, &kvm_dirty_bitmap), + Err(MemoryError::DirtyBitmapTooSmall) + )); } #[test] diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/tests/integration_tests/functional/test_snapshot_basic.py new/firecracker-1.14.2/tests/integration_tests/functional/test_snapshot_basic.py --- old/firecracker-1.14.1/tests/integration_tests/functional/test_snapshot_basic.py 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/tests/integration_tests/functional/test_snapshot_basic.py 2026-02-26 18:19:34.000000000 +0100 @@ -410,14 +410,15 @@ # process would have been taken down. -def test_diff_snapshot_overlay(uvm_plain_any, microvm_factory): [email protected]("mem_size", [256, 4096]) +def test_diff_snapshot_overlay(uvm_plain_any, microvm_factory, mem_size): """ Tests that if we take a diff snapshot and direct firecracker to write it on top of an existing snapshot file, it will successfully merge them. """ basevm = uvm_plain_any basevm.spawn() - basevm.basic_config(track_dirty_pages=True) + basevm.basic_config(track_dirty_pages=True, mem_size_mib=mem_size) basevm.add_net_iface() basevm.start() diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/tools/devtool new/firecracker-1.14.2/tools/devtool --- old/firecracker-1.14.1/tools/devtool 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/tools/devtool 2026-02-26 18:19:34.000000000 +0100 @@ -133,6 +133,9 @@ # Container path to directory where we store built CI artifacts. CTR_CI_ARTIFACTS_PATH="${CTR_FC_ROOT_DIR}/resources/$(uname -m)" +# Lockfile used while modifying KVM modules +KVM_MODULE_LOCKFILE="/tmp/.kvm_module_lock" + # Check if Docker is available and exit if it's not. # Upon returning from this call, the caller can be certain Docker is available. # @@ -583,52 +586,127 @@ fi } -apply_linux_61_tweaks() { - KV=$(uname -r) - if [[ $KV != 6.1.* ]] || [ $(uname -m) != x86_64 ]; then - return - fi - say "Applying Linux 6.1 boot-time regression mitigations" - - KVM_VENDOR_MOD=$(lsmod |grep -P "^kvm_(amd|intel)" | awk '{print $1}') - ITLB_MULTIHIT=/sys/devices/system/cpu/vulnerabilities/itlb_multihit - NX_HUGEPAGES=/sys/module/kvm/parameters/nx_huge_pages - - # If m6a/m6i - if grep -q "Not affected" $ITLB_MULTIHIT; then - echo -e "CPU not vulnerable to iTLB multihit, using kvm.nx_huge_pages=never mitigation" - # we need a lock so another process is not running the same thing and to - # avoid race conditions. - lockfile="/tmp/.linux61_tweaks.lock" - set -C # noclobber - while true; do - if echo "$$" > "$lockfile"; then - echo "Successfully acquired lock" - if ! grep -q "never" $NX_HUGEPAGES; then - echo "Reloading KVM modules with nx_huge_pages=never" - sudo modprobe -r $KVM_VENDOR_MOD kvm - sudo modprobe kvm nx_huge_pages=never - sudo modprobe $KVM_VENDOR_MOD - fi - rm "$lockfile" - break - else - sleep 5s - fi - done - tail -v $ITLB_MULTIHIT $NX_HUGEPAGES - # else (m5d Skylake and CascadeLake) +# Acquire the KVM module lock and run the given command. +# Uses flock with a timeout for safe, automatic lock management. +# Usage: with_kvm_module_lock <command> [args...] +with_kvm_module_lock() { + local LOCK_TIMEOUT=120 + ( + if ! flock -w "$LOCK_TIMEOUT" 9; then + say_warn "Timed out waiting for KVM module lock after: ${LOCK_TIMEOUT}s" + exit 1 + fi + echo "Successfully acquired lock" + "$@" + ) 9>"$KVM_MODULE_LOCKFILE" +} + +# Reload KVM modules with the given vendor module and kvm params. +# Always enables avic=1 on AMD. Unloads first if already loaded. +# Usage: reload_kvm_modules <vendor_mod> [kvm_param...] +# e.g. reload_kvm_modules kvm_intel nx_huge_pages=never +reload_kvm_modules() { + local vendor_mod=$1; shift + + # Unload if already loaded + if lsmod | grep -qP "^kvm_(amd|intel)"; then + if ! sudo modprobe -r $vendor_mod kvm; then + say_warn "Failed to unload KVM modules (${vendor_mod}, kvm) (may be in use)" + return 1 + fi + fi + + if ! sudo modprobe kvm "$@"; then + say_warn "Failed to load kvm module" + return 1 + fi + if [[ $vendor_mod == "kvm_amd" ]]; then + if ! sudo modprobe kvm_amd avic=1; then + say_warn "Failed to load kvm_amd module" + return 1 + fi else - echo "CPU vulnerable to iTLB_multihit, checking if favordynmods is enabled" - mount |grep cgroup |grep -q favordynmods - if [ $? -ne 0 ]; then - say_warn "cgroups' favordynmods option not enabled; VM creation performance may be impacted" - else - echo "favordynmods is enabled" + if ! sudo modprobe $vendor_mod; then + say_warn "Failed to load $vendor_mod module" + return 1 fi fi } +# Determine the KVM vendor module for the current CPU. +kvm_vendor_mod() { + if grep -q "vmx" /proc/cpuinfo; then + echo kvm_intel + elif grep -q "svm" /proc/cpuinfo; then + echo kvm_amd + else + # aarch64 + echo kvm + fi +} + +# Ensure /dev/kvm is available and apply platform-specific KVM tweaks. +# - Loads KVM modules if not present +# - On Linux 6.1 x86_64: applies nx_huge_pages=never for non-vulnerable CPUs, +# checks favordynmods for vulnerable ones +# - On AMD: ensures AVIC is enabled +setup_kvm() { + local kernel_version=$(uname -r) + local arch=$(uname -m) + local vendor_mod=$(kvm_vendor_mod) + + local need_kvm_reload=0 + local kvm_extra_params=() + + # Load KVM if not already available + if [[ ! -c /dev/kvm ]]; then + need_kvm_reload=1 + fi + + local itlb_multihit=/sys/devices/system/cpu/vulnerabilities/itlb_multihit + local nx_huge_pages=/sys/module/kvm/parameters/nx_huge_pages + # Linux 6.1 x86_64: mitigate boot-time regression + if [[ $kernel_version == 6.1.* ]] && [[ $arch == x86_64 ]]; then + + say "Applying Linux 6.1 boot-time regression mitigations" + if grep -q "Not affected" $itlb_multihit; then + echo "CPU not vulnerable to iTLB multihit, using kvm.nx_huge_pages=never mitigation" + if ! grep -q "never" $nx_huge_pages 2>/dev/null; then + kvm_extra_params+=(nx_huge_pages=never) + need_kvm_reload=1 + fi + else + echo "CPU vulnerable to iTLB_multihit, checking if favordynmods is enabled" + if mount | grep cgroup | grep -q favordynmods; then + echo "favordynmods is enabled" + else + say_warn "cgroups' favordynmods option not enabled; VM creation performance may be impacted" + fi + fi + fi + + # AMD: ensure AVIC is enabled + local avic_param=/sys/module/kvm_amd/parameters/avic + if [[ $vendor_mod == "kvm_amd" ]]; then + if ! grep -q "Y\|1" $avic_param; then + echo "AVIC not enabled, will reload kvm_amd with avic=1" + need_kvm_reload=1 + fi + fi + + if [[ $need_kvm_reload -eq 1 ]]; then + echo "Reloading KVM modules" + reload_kvm_modules "$vendor_mod" "${kvm_extra_params[@]}" + ok_or_die "Could not reload kvm modules" + fi + + tail -v $itlb_multihit $nx_huge_pages + if [[ $vendor_mod == "kvm_amd" ]]; then + tail -v $avic_param + fi + + [[ -c /dev/kvm ]] || die "/dev/kvm not found. Aborting." +} # Modifies the processors CPU governor and P-state configuration (x86_64 only) for consistent performance. This means # - Disable turbo boost (Intel only) by writing 1 to /sys/devices/system/cpu/intel_pstate/no_turbo @@ -722,7 +800,7 @@ done # Check prerequisites. - [ $do_kvm_check != 0 ] && ensure_kvm + [ $do_kvm_check != 0 ] && with_kvm_module_lock setup_kvm ensure_devctr ensure_build_dir ensure_ci_artifacts @@ -734,8 +812,6 @@ fi fi - apply_linux_61_tweaks - # If we got to here, we've got all we need to continue. say "Kernel version: $(uname -r)" say "$(sed '/^processor.*: 0$/,/^processor.*: 1$/!d; /^processor.*: 1$/d' /proc/cpuinfo)" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/firecracker-1.14.1/tools/functions new/firecracker-1.14.2/tools/functions --- old/firecracker-1.14.1/tools/functions 2026-01-19 13:55:20.000000000 +0100 +++ new/firecracker-1.14.2/tools/functions 2026-02-26 18:19:34.000000000 +0100 @@ -125,15 +125,3 @@ die "Invalid version number: $version. Version should not contain \`wip\` or \`dirty\`." fi } - -######################### -# Firecracker functions # -######################### - -# Check if /dev/kvm exists. Exit if it doesn't. -# Upon returning from this call, the caller can be certain /dev/kvm is -# available. -# -ensure_kvm() { - [[ -c /dev/kvm ]] || die "/dev/kvm not found. Aborting." -} ++++++ firecracker.obsinfo ++++++ --- /var/tmp/diff_new_pack.tLJGPM/_old 2026-03-04 21:10:10.948758551 +0100 +++ /var/tmp/diff_new_pack.tLJGPM/_new 2026-03-04 21:10:10.960759047 +0100 @@ -1,5 +1,5 @@ name: firecracker -version: 1.14.1 -mtime: 1768827320 -commit: 37593df439aeb19fc70b292e618770108e563e7e +version: 1.14.2 +mtime: 1772126374 +commit: 26b4c55f14f02fbb7dd3353769e608fa90d22445 ++++++ vendor.tar.xz ++++++ /work/SRC/openSUSE:Factory/firecracker/vendor.tar.xz /work/SRC/openSUSE:Factory/.firecracker.new.561/vendor.tar.xz differ: char 15, line 1
