Skip to content

Commit d38614d

Browse files
committed
fix(uffd): propagate Removed pages into DiffMetadata.Empty
Read the Removed bitmap from PageTracker and emit it as DiffMetadata.Empty so REMOVE'd pages become uuid.Nil mappings in the snapshot header (read as zero on resume). Defensively AndNot the empty set out of dirty: settle drains make these disjoint in practice (Removed pages have no PTE, WP-async only sees present pages with WP cleared), but if the invariant ever breaks the guest's last intent for a Removed page is "free, read zero on restore" — so empty must win, not stale dirty content.
1 parent 980aa79 commit d38614d

1 file changed

Lines changed: 24 additions & 1 deletion

File tree

  • packages/orchestrator/pkg/sandbox/uffd

packages/orchestrator/pkg/sandbox/uffd/uffd.go

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,30 @@ func (u *Uffd) Exit() *utils.ErrorOnce {
217217
//
218218
// It *MUST* be only called after the sandbox was successfully paused via API and after the snapshot endpoint was called.
219219
func (u *Uffd) DiffMetadata(ctx context.Context, f *fc.Process) (*header.DiffMetadata, error) {
220-
return f.DirtyMemory(ctx, u.memfile.BlockSize())
220+
handler, err := u.handler.WaitWithContext(ctx)
221+
if err != nil {
222+
return nil, fmt.Errorf("failed to get uffd: %w", err)
223+
}
224+
225+
diff, err := f.DirtyMemory(ctx, u.memfile.BlockSize())
226+
if err != nil {
227+
return nil, fmt.Errorf("failed to get dirty memory: %w", err)
228+
}
229+
230+
_, empty := handler.PageTracker.Export()
231+
232+
// dirty ∩ empty should be empty after settle (Removed pages have no PTE,
233+
// FC's WP-async dirty scan only sees present pages with WP cleared), but
234+
// AndNot here means "empty wins" if the invariant ever breaks: the guest's
235+
// most recent intent for a Removed page is "free, read zero on restore",
236+
// so we must not let stale dirty content shadow it in this diff layer.
237+
diff.Dirty.AndNot(empty)
238+
239+
return &header.DiffMetadata{
240+
BlockSize: diff.BlockSize,
241+
Dirty: diff.Dirty,
242+
Empty: empty,
243+
}, nil
221244
}
222245

223246
// PrefetchData returns page fault data for prefetch mapping.

0 commit comments

Comments
 (0)