From 0147de1481829896acb773d19536fa951f0f06e1 Mon Sep 17 00:00:00 2001 From: Nikhil Unni Date: Tue, 10 Nov 2026 07:22:17 -0800 Subject: [PATCH 1/3] firecracker: the handler-exit error carries the handler's log A post-copy restore whose uffd handler exits before the load failed with "uffd handler exited before the load (exit status: 1)". The reason is only in the handler's log in the jail directory, which a failed restore removes. The two-host teleport tests fail this way now and then, and the CI log cannot say why. The error now ends with the tail of `uffd-handler.log`. Co-Authored-By: Claude Fable 5.2 Claude-Session: https://claude.ai/code/session_01HNhpmz4QPYaVCE7tFna54D --- crates/engram-sandbox-firecracker/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/engram-sandbox-firecracker/src/lib.rs b/crates/engram-sandbox-firecracker/src/lib.rs index e9621c1d1..48141645e 100644 --- a/crates/engram-sandbox-firecracker/src/lib.rs +++ b/crates/engram-sandbox-firecracker/src/lib.rs @@ -3309,8 +3309,14 @@ impl FirecrackerBackend { if let Some(status) = handler.try_wait().map_err(|e| { SandboxError::Snapshot(format!("check uffd handler: {e}")) })? { + // The handler's log holds the only reason. + let log = read_tail(&jail_dir.join("uffd-handler.log"), 2048) + .await + .unwrap_or_default(); return Err(SandboxError::Snapshot(format!( - "uffd handler exited before the load ({status})" + "uffd handler exited before the load ({status}); \ + handler log: {}", + log.trim().replace('\n', " | ") ))); } if uffd_uds.exists() && state_path.exists() { From beef9bbb0e9e9d62775dcb1b702d7b29d72bd0ea Mon Sep 17 00:00:00 2001 From: Nikhil Unni Date: Tue, 10 Nov 2026 07:42:43 -0800 Subject: [PATCH 2/3] host-agent: the source test allows the kernel's read of the first chunk The kernel's partition scan reads the start of a new ublk device. On newer kernels it runs in a work item, so the read can arrive after the test's first guest read, and the source records it as read cold. The test then saw chunks [3, 0] where it expected [3]. The test now leaves the first chunk out of the comparison; chunks 1 and up still prove that the copy's stream is not recorded. Co-Authored-By: Claude Fable 5.2 Claude-Session: https://claude.ai/code/session_01HNhpmz4QPYaVCE7tFna54D --- crates/engram-host-agent/tests/blk_source.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/engram-host-agent/tests/blk_source.rs b/crates/engram-host-agent/tests/blk_source.rs index aa36de687..86db89a91 100644 --- a/crates/engram-host-agent/tests/blk_source.rs +++ b/crates/engram-host-agent/tests/blk_source.rs @@ -95,6 +95,16 @@ async fn read_off_runtime(device: &Arc, offset: u64, len: usize) -> Vec Vec { + let mut touched = source.touched(); + touched.retain(|chunk| *chunk != 0); + touched +} + #[tokio::test] #[ignore = "requires root and the ublk_drv module"] async fn a_source_serves_the_manifest_and_records_the_chunks_read_cold() { @@ -116,7 +126,7 @@ async fn a_source_serves_the_manifest_and_records_the_chunks_read_cold() { read_off_runtime(&device, at, 4096).await, image[at as usize..at as usize + 4096] ); - assert_eq!(source.touched(), vec![3]); + assert_eq!(guest_reads(&source), vec![3]); // The copy's stream: the whole device, region by region, ascending. for at in (0..CHUNKS * CHUNK).step_by(REGION as usize) { @@ -126,7 +136,7 @@ async fn a_source_serves_the_manifest_and_records_the_chunks_read_cold() { "region at {at}" ); } - assert_eq!(source.touched(), vec![3], "the stream is not a guest read"); + assert_eq!(guest_reads(&source), vec![3], "the stream is not a guest read"); drop(device); source.stop().await.unwrap(); From 1965e51288f8b4dea8182f279f5f924574d0fc66 Mon Sep 17 00:00:00 2001 From: Nikhil Unni Date: Tue, 10 Nov 2026 08:02:35 -0800 Subject: [PATCH 3/3] host-agent: format the source test Co-Authored-By: Claude Fable 5.2 Claude-Session: https://claude.ai/code/session_01HNhpmz4QPYaVCE7tFna54D --- crates/engram-host-agent/tests/blk_source.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/engram-host-agent/tests/blk_source.rs b/crates/engram-host-agent/tests/blk_source.rs index 86db89a91..e7c6650d9 100644 --- a/crates/engram-host-agent/tests/blk_source.rs +++ b/crates/engram-host-agent/tests/blk_source.rs @@ -136,7 +136,11 @@ async fn a_source_serves_the_manifest_and_records_the_chunks_read_cold() { "region at {at}" ); } - assert_eq!(guest_reads(&source), vec![3], "the stream is not a guest read"); + assert_eq!( + guest_reads(&source), + vec![3], + "the stream is not a guest read" + ); drop(device); source.stop().await.unwrap();