Skip to content
Closed
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
cd35b5a
swagger: add APIs for getting guest memory info
bchalios Feb 5, 2026
2035236
snapshot: make memory path optional in snapshot creation
bchalios Feb 4, 2026
160c3af
api: implement API for getting guest memory mappings
bchalios Feb 5, 2026
cd3fe9a
api: implement API for resident and zero memory
bchalios Feb 5, 2026
92eacbd
api: implement API for dirty memory
bchalios Feb 5, 2026
e9febb1
feat: enable write-protection on guest memory
bchalios Feb 12, 2026
fff6fd9
ci: remove dependency changes test
bchalios Feb 14, 2026
a284adf
feat: make network device snapshots backwards compatible
bchalios Feb 24, 2026
7a2ef60
snapshot: add state types for previous versions
bchalios Feb 26, 2026
ee3f6fa
feat: allow loading older snapshots
bchalios Feb 27, 2026
458ca91
fix: compilation in aarch64
bchalios Mar 23, 2026
9b2c7b5
feat(memfd): allow using memfd to back guest memory
bchalios Apr 24, 2026
70d1aa9
refactor(block): expand ConfigSpace to full virtio-blk layout
kalyazin Apr 24, 2026
17f5f3c
refactor(block): add Discard request type and discard segment struct
kalyazin Apr 24, 2026
a07c32a
feat(block): add discard method to FileEngine using fallocate
kalyazin Apr 24, 2026
c3bd76a
style(seccomp): fix indentation and trailing whitespace in filter files
kalyazin Apr 24, 2026
432ca16
feat(seccomp): allow fallocate syscall in vmm thread filter
kalyazin Apr 24, 2026
545d2b3
feat(block): handle VIRTIO_BLK_T_DISCARD requests
kalyazin Apr 24, 2026
7662cac
chore(snapshot): fix ConfigSpace restore for VIRTIO_BLK_F_DISCARD
kalyazin Apr 24, 2026
3901fd5
feat(block): advertise VIRTIO_BLK_F_DISCARD for non-read-only devices
kalyazin Apr 24, 2026
6de4a4b
doc(block): document VIRTIO_BLK_F_DISCARD discard support
kalyazin Apr 24, 2026
92a22cf
test(block): add unit tests for VIRTIO_BLK_F_DISCARD
kalyazin Apr 24, 2026
772ad3c
test(block): add pytest integration tests for VIRTIO_BLK_F_DISCARD
kalyazin Apr 24, 2026
a4985d5
refactor(block): extend ConfigSpace with write-zeroes fields
kalyazin May 6, 2026
f70b108
refactor(block): add WriteZeroes request type and supporting variants
kalyazin May 6, 2026
7c2a7b5
feat(block): add write_zeroes method to FileEngine using fallocate
kalyazin May 6, 2026
a4f3364
feat(block): handle VIRTIO_BLK_T_WRITE_ZEROES requests
kalyazin May 6, 2026
0f4fd52
feat(block): advertise VIRTIO_BLK_F_WRITE_ZEROES for non-read-only de…
kalyazin May 6, 2026
2aee8e9
test(block): add unit tests for VIRTIO_BLK_F_WRITE_ZEROES
kalyazin May 6, 2026
ace2b4e
test(block): add pytest integration tests for VIRTIO_BLK_F_WRITE_ZEROES
kalyazin May 6, 2026
ab2399e
doc(block): document VIRTIO_BLK_F_WRITE_ZEROES support
kalyazin May 6, 2026
bc19655
feat(balloon): advertise VIRTIO_BALLOON_F_HINT_WAIT_ON_ACK with FPH
kalyazin May 8, 2026
1bfffbf
test(balloon): integration test for WAIT_ON_ACK negotiation
kalyazin May 8, 2026
f0a35a1
docs(balloon): document WAIT_ON_ACK feature
kalyazin May 8, 2026
639196c
fix: saving/restoring async IO engine transport state
Dec 15, 2025
1de011d
fix(tests): fix compilation in integration test
bchalios May 20, 2026
b222edc
fix(discard_range): require page size alignment for region
bchalios May 21, 2026
bd85e43
fix(memory): punch holes for shared discard ranges
ValentaTomas May 20, 2026
2c743d3
fix(io_uring): handle more robustly EINTR in io_uring_enter()
bchalios May 20, 2026
bfb1e40
fix(block): make prepare_save() more robust
bchalios May 20, 2026
3a19026
fix(machine_config): remove unused MachineConfigUpdate import in tests
kalyazin May 26, 2026
431f1fc
fix(memory): use madvise(MADV_REMOVE) to discard memfd ranges
kalyazin May 26, 2026
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
17 changes: 0 additions & 17 deletions .github/workflows/dependency_modification_check.yml

This file was deleted.

29 changes: 26 additions & 3 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions resources/seccomp/x86_64-unknown-linux-musl.json
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
{
"syscall": "mincore"
},
{
"syscall": "pread64"
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing pread64 syscall in aarch64 seccomp filter

High Severity

The pread64 syscall is added to the x86_64 seccomp filter but not to the aarch64 one. The new memory info features use libc::pread in pagemap.rs which runs on both architectures. Calling the /memory/dirty or /memory endpoints on aarch64 will trigger a seccomp violation and crash the VMM.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 81d5796. Configure here.

Comment thread
cursor[bot] marked this conversation as resolved.
{
"syscall": "writev",
"comment": "Used by the VirtIO net device to write to tap"
Expand Down
4 changes: 2 additions & 2 deletions src/firecracker/src/api_server/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,7 +275,7 @@ mod tests {
Box::new(VmmAction::CreateSnapshot(CreateSnapshotParams {
snapshot_type: SnapshotType::Diff,
snapshot_path: PathBuf::new(),
mem_file_path: PathBuf::new(),
mem_file_path: Some(PathBuf::new()),
})),
start_time_us,
);
Expand All @@ -288,7 +288,7 @@ mod tests {
Box::new(VmmAction::CreateSnapshot(CreateSnapshotParams {
snapshot_type: SnapshotType::Diff,
snapshot_path: PathBuf::new(),
mem_file_path: PathBuf::new(),
mem_file_path: Some(PathBuf::new()),
})),
start_time_us,
);
Expand Down
14 changes: 14 additions & 0 deletions src/firecracker/src/api_server/parsed_request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use super::request::vsock::parse_put_vsock;
use crate::api_server::request::hotplug::memory::{
parse_get_memory_hotplug, parse_patch_memory_hotplug, parse_put_memory_hotplug,
};
use crate::api_server::request::memory_info::parse_get_memory;
use crate::api_server::request::serial::parse_put_serial;

#[derive(Debug)]
Expand Down Expand Up @@ -91,6 +92,7 @@ impl TryFrom<&Request> for ParsedRequest {
(Method::Get, "hotplug", None) if path_tokens.next() == Some("memory") => {
parse_get_memory_hotplug()
}
(Method::Get, "memory", None) => parse_get_memory(path_tokens),
(Method::Get, _, Some(_)) => method_to_error(Method::Get),
(Method::Put, "actions", Some(body)) => parse_put_actions(body),
(Method::Put, "balloon", Some(body)) => parse_put_balloon(body),
Expand Down Expand Up @@ -196,6 +198,9 @@ impl ParsedRequest {
&serde_json::json!({ "firecracker_version": version.as_str() }),
),
VmmData::FullVmConfig(config) => Self::success_response_with_data(config),
VmmData::MemoryMappings(mappings) => Self::success_response_with_data(mappings),
VmmData::Memory(meminfo) => Self::success_response_with_data(meminfo),
VmmData::MemoryDirty(dirty) => Self::success_response_with_data(dirty),
},
Err(vmm_action_error) => {
let mut response = match vmm_action_error {
Expand Down Expand Up @@ -610,6 +615,15 @@ pub mod tests {
&serde_json::json!({ "firecracker_version": version.as_str() }).to_string(),
200,
),
VmmData::MemoryMappings(mappings) => {
http_response(&serde_json::to_string(mappings).unwrap(), 200)
}
VmmData::Memory(meminfo) => {
http_response(&serde_json::to_string(meminfo).unwrap(), 200)
}
VmmData::MemoryDirty(dirty) => {
http_response(&serde_json::to_string(dirty).unwrap(), 200)
}
};
let response = ParsedRequest::convert_to_response(&data);
response.write_all(&mut buf).unwrap();
Expand Down
19 changes: 19 additions & 0 deletions src/firecracker/src/api_server/request/memory_info.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use micro_http::Method;
use vmm::rpc_interface::VmmAction;

use crate::api_server::parsed_request::{ParsedRequest, RequestError};

pub(crate) fn parse_get_memory<'a, T>(mut path_tokens: T) -> Result<ParsedRequest, RequestError>
where
T: Iterator<Item = &'a str>,
{
match path_tokens.next() {
Some("mappings") => Ok(ParsedRequest::new_sync(VmmAction::GetMemoryMappings)),
Some("dirty") => Ok(ParsedRequest::new_sync(VmmAction::GetMemoryDirty)),
Some(unknown_path) => Err(RequestError::InvalidPathMethod(
format!("/memory/{}", unknown_path),
Method::Get,
)),
None => Ok(ParsedRequest::new_sync(VmmAction::GetMemory)),
}
}
1 change: 1 addition & 0 deletions src/firecracker/src/api_server/request/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod hotplug;
pub mod instance_info;
pub mod logger;
pub mod machine_configuration;
pub mod memory_info;
pub mod metrics;
pub mod mmds;
pub mod net;
Expand Down
58 changes: 55 additions & 3 deletions src/firecracker/src/api_server/request/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,13 +91,21 @@ fn parse_put_snapshot_load(body: &Body) -> Result<ParsedRequest, RequestError> {
// If `mem_file_path` is specified instead of `mem_backend`, we construct the
// `MemBackendConfig` object from the path specified, with `File` as backend type.
let mem_backend = match snapshot_config.mem_backend {
Some(backend_cfg) => backend_cfg,
Some(backend_cfg) => {
if backend_cfg.use_memfd && backend_cfg.backend_type != MemBackendType::Uffd {
return Err(RequestError::SerdeJson(serde_json::Error::custom(
"use_memfd is only valid when backend_type is Uffd",
)));
}
backend_cfg
}
None => {
MemBackendConfig {
// This is safe to unwrap() because we ensure above that one of the two:
// either `mem_file_path` or `mem_backend` field is always specified.
backend_path: snapshot_config.mem_file_path.unwrap(),
backend_type: MemBackendType::File,
use_memfd: false,
}
}
};
Expand Down Expand Up @@ -145,7 +153,7 @@ mod tests {
let expected_config = CreateSnapshotParams {
snapshot_type: SnapshotType::Diff,
snapshot_path: PathBuf::from("foo"),
mem_file_path: PathBuf::from("bar"),
mem_file_path: Some(PathBuf::from("bar")),
};
assert_eq!(
vmm_action_from_request(parse_put_snapshot(&Body::new(body), Some("create")).unwrap()),
Expand All @@ -159,7 +167,7 @@ mod tests {
let expected_config = CreateSnapshotParams {
snapshot_type: SnapshotType::Full,
snapshot_path: PathBuf::from("foo"),
mem_file_path: PathBuf::from("bar"),
mem_file_path: Some(PathBuf::from("bar")),
};
assert_eq!(
vmm_action_from_request(parse_put_snapshot(&Body::new(body), Some("create")).unwrap()),
Expand All @@ -184,6 +192,7 @@ mod tests {
mem_backend: MemBackendConfig {
backend_path: PathBuf::from("bar"),
backend_type: MemBackendType::File,
use_memfd: false,
},
track_dirty_pages: false,
resume_vm: false,
Expand Down Expand Up @@ -215,6 +224,7 @@ mod tests {
mem_backend: MemBackendConfig {
backend_path: PathBuf::from("bar"),
backend_type: MemBackendType::File,
use_memfd: false,
},
track_dirty_pages: true,
resume_vm: false,
Expand Down Expand Up @@ -246,6 +256,7 @@ mod tests {
mem_backend: MemBackendConfig {
backend_path: PathBuf::from("bar"),
backend_type: MemBackendType::Uffd,
use_memfd: false,
},
track_dirty_pages: false,
resume_vm: true,
Expand Down Expand Up @@ -283,6 +294,7 @@ mod tests {
mem_backend: MemBackendConfig {
backend_path: PathBuf::from("bar"),
backend_type: MemBackendType::Uffd,
use_memfd: false,
},
track_dirty_pages: false,
resume_vm: true,
Expand Down Expand Up @@ -314,6 +326,7 @@ mod tests {
mem_backend: MemBackendConfig {
backend_path: PathBuf::from("bar"),
backend_type: MemBackendType::File,
use_memfd: false,
},
track_dirty_pages: false,
resume_vm: true,
Expand Down Expand Up @@ -401,6 +414,45 @@ mod tests {
);
parse_put_snapshot(&Body::new(body), Some("invalid")).unwrap_err();
parse_put_snapshot(&Body::new(body), None).unwrap_err();

// use_memfd=true with Uffd backend must be accepted and propagated.
let body = r#"{
"snapshot_path": "foo",
"mem_backend": {
"backend_path": "bar",
"backend_type": "Uffd",
"use_memfd": true
}
}"#;
let expected_config = LoadSnapshotParams {
snapshot_path: PathBuf::from("foo"),
mem_backend: MemBackendConfig {
backend_path: PathBuf::from("bar"),
backend_type: MemBackendType::Uffd,
use_memfd: true,
},
track_dirty_pages: false,
resume_vm: false,
network_overrides: vec![],
clock_realtime: false,
};
let mut parsed_request = parse_put_snapshot(&Body::new(body), Some("load")).unwrap();
assert!(parsed_request.parsing_info().take_deprecation_message().is_none());
assert_eq!(
vmm_action_from_request(parsed_request),
VmmAction::LoadSnapshot(expected_config)
);

// use_memfd=true with File backend must be rejected.
let body = r#"{
"snapshot_path": "foo",
"mem_backend": {
"backend_path": "bar",
"backend_type": "File",
"use_memfd": true
}
}"#;
parse_put_snapshot(&Body::new(body), Some("load")).unwrap_err();
}

#[test]
Expand Down
Loading
Loading