mirror of
https://github.com/kvcache-ai/AgentENV.git
synced 2026-11-05 01:17:36 +00:00
[GH-ISSUE #302] bug(ublk): pooled devices can expose stale placeholder pages after image switch #81
Labels
No labels
bug
documentation
enhancement
enhancement
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
kvcache-ai/AgentENV#81
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @dushulin on GitHub (Sep 18, 2026).
Original GitHub issue: https://github.com/kvcache-ai/AgentENV/issues/302
Summary
A pooled ublk device can return cached zero pages from its idle placeholder after being rebound to a nonzero business image. In an isolated one-device experiment, buffered reads returned zeros while
O_DIRECTreads of the same offset returned the correct snapshot content. Flushing only that device's page cache restored correct buffered reads.We investigated this after intermittent guest kernel panics during concurrent snapshot restores, surfaced to the caller as
timed out waiting for envd. The stale-page mechanism is reproduced; its connection to the original guest panics is strongly supported but not fully proven. We have not identified the reader that populated the placeholder cache during the original failures or reproduced a complete VM panic in the reduced experiment.Affected component
ublk / ublk-daemon, specifically pooled device image switching and host block-device page-cache consistency.
AgentENV version
mainate330917f17d9f5103066df2aea5c2e172d6c6527.prepare_overlaybd_device,idle_released_device, andclear_page_cachefunctions are byte-for-byte identical to those in the upstream revision above. This is a source comparison, not a claim that the complete upstream revision was runtime-tested.Host environment
6.8.0-101-generic.6.1.175; Firecracker v1.15.1-based build./dev/ublkbN, used as Firecracker file-backed memory.filelower, emptyrepoBlobUrl), not a registry-backed source.Operation and configuration
The original operation was creating sandboxes from committed snapshots. The reduced experiment only starts a separate daemon with one private device; it does not start Firecracker, envd, or readiness probes.
Relevant isolated daemon options:
Use a separate socket and configuration/cache directory for the experiment. All flushes below target only the experiment's own device.
Steps to reproduce
Supply a valid read-only OverlayBD image with a known nonzero, 4096-byte-aligned page. We used a 2 GiB memory image and offset
0x7ffce000.acquire_overlaybd,access_mode: "shared",virtual_size: 2147483648). Read the selected page using alignedO_DIRECTI/O and save its content asoriginal; verify it is nonzero.release_overlaybd. It remains in the pool bound to the empty placeholder.O_DIRECTread now returns zeros. In the controlled experiment, wait 200 ms and issueBLKFLSBUFon this private device to discard any old business-image read-ahead; then perform a buffered read and verify it returns zeros. The test intentionally fills the placeholder page cache at this point.O_DIRECT: it returnsoriginal.BLKFLSBUFon this private device after the switch. The buffered read now returnsoriginalas well.The retained reader FD and explicit placeholder read are part of the reproducer, not an observation about which process was responsible in the original workload. The experiment does not assume a particular udev or block-probing implementation.
Expected behavior
After an acquire completes, buffered and direct reads must refer to the newly bound image. Cache pages populated while the device was idle must not be served as guest memory from a different image.
Actual behavior
At the same offset of the same private device:
O_DIRECT0x7ffcd0670x00x0(incorrect)O_DIRECT0x7ffcd067(correct)BLKFLSBUF0x7ffcd067(correct)The complete 4096-byte pages matched before the switch, on the direct-read control, and after the flush. The stale buffered page was entirely zero.
An earlier experiment encountered old business-image data while the device was already on the placeholder, so the final controlled experiment explicitly discarded that old cache before filling the placeholder page. Both observations indicate that changing the target alone does not establish a new page-cache identity.
Logs and diagnostics
Sanitized guest error excerpt from the original restore failures:
Several failures first faulted in
receive_mergeable; another first faulted inkfreeon an ACPI GED call path and subsequently faulted in multiple kernel paths. The common evidence is the missing page-table entry, not a proven virtio-net defect. The tested offset0x7ffce000is the PUD-table location indicated by this page-table walk (PUD index 0).In one representative case, snapshot load returned successfully in approximately 30 ms, the VM resumed, and Firecracker exited after the guest panic approximately 2.3 seconds later. The caller received the envd readiness timeout about 57.7 seconds after that exit. Snapshot load uses demand-backed memory, so its short duration alone does not prove all memory reads were complete.
Suspected mechanism in current upstream code
idle_released_deviceclears the page cache before switching to the idle placeholder.prepare_overlaybd_deviceswitches the target to the requested image and returns the device, without a post-switch cache invalidation.clear_page_cacheis currently a best-effortBLKFLSBUF.Clearing before release does not cover cache pages created during the placeholder phase and retained across checkout. Newly created/prewarmed placeholder devices also need a consistent handover; the issue is not necessarily limited to devices previously used by another VM.
A fix needs to consider old in-flight I/O, target switching, and cache invalidation together before the new image is exposed. The flush control in this report demonstrates the cache mismatch; it does not establish that adding one unsynchronized flush is a complete fix.
Reproduction frequency
Intermittent guest failures in the original workload. The reduced one-device experiment reproduced the incorrect buffered read with the explicit retained-reader/cache-fill sequence above. No claim is made that the original VM failure rate or trigger has been reproduced deterministically.
Regression information
Additional context
The reduced cache experiment does not depend on readiness probes. Separately,
EnvdInstance::wait_for_readyonly polls health with a deadline; observing Firecracker process exit during this wait would help report the original VM failure promptly instead of masking it as an envd timeout. That would improve diagnostics but would not fix the stale memory contents.The experiment's private daemon exited successfully and its device was removed. No global page-cache flush was used.
Pre-submission checklist
@NickNYU commented on GitHub (Oct 5, 2026):
Hi @dushulin
In order to understand this bug comprehensively, I just reproduced
main(5843159)UblkDaemonServerwith your pool shape (low=0, high=1, prewarm=false) — no Firecracker, envd, or external reader process involved.Results match your table exactly:
I have encoded the procedure as an
#[ignore]d regression test in #342 (fails on current code at the post-switch buffered assertion; meant to be un-ignored together with the fix). Details and validation results are in the PR.will follow up on the fix direction separately after looking into the in-flight I/O quiescing question you raised.