Skip to content

fix(checkpoint): restore file-backed mappings and fds under a chroot#142

Merged
congwang-mk merged 1 commit into
mainfrom
fix/checkpoint-chroot-restore
Jul 15, 2026
Merged

fix(checkpoint): restore file-backed mappings and fds under a chroot#142
congwang-mk merged 1 commit into
mainfrom
fix/checkpoint-chroot-restore

Conversation

@congwang-mk

Copy link
Copy Markdown
Contributor

Problem

Checkpoint/restore of a confined (imaged/chrooted) workload failed at restore: sandlock_restore_interactive returned NULL and the process never resumed. A non-chrooted (host) workload restored fine, so the bug was specific to the chroot path.

Root cause

Two manifestations of the same mismatch between the paths the checkpoint records and what restore can open:

  1. Host-recorded paths, opened under chroot. Capture reads /proc/<pid>/maps and /proc/<pid>/fd, whose entries are resolved in the mount namespace and therefore ignore the process's chroot. A file the workload sees at /lib/ld-musl-x86_64.so.1 is recorded as its host path <rootfs>/lib/ld-musl-x86_64.so.1. The open()s injected during restore run in the already-confined child, where that host path does not resolve, so the remap/reopen fails.

  2. memfd-backed program text. An imaged binary is exec'd from a sealed memfd, so its code region is backed by /memfd:sandlock-exec (deleted) — unreopenable by path. Capture only dumped writable regions, leaving the read-only code to be remapped from a file that cannot be reopened.

Fix

  1. restore_into translates each recorded host path back to the child's in-chroot view via host_to_virtual (over the chroot root and mounts) before the injected open. Non-chroot restores pass paths through unchanged.
  2. Capture also dumps private regions backed by a memfd or a (deleted) path, so restore reconstructs them from bytes instead of reopening a vanished file.

Testing

Verified end to end via sandlock-server on a real host (Linux 7.x, Landlock ABI 10): a service checkpointed while running in an imaged (alpine) sandbox now restores and resumes (running=True), where it previously failed with a 500. cargo test -p sandlock-core checkpoint::resume passes.

Known limitation (pre-existing, not addressed here)

Active workloads (e.g. a busy shell loop) can still segfault shortly after resume — the documented injection-restore limitation (cached vDSO pointers / unswept stub mappings). This affects direct library restores too and is independent of this fix; a passive process (blocked in a syscall) resumes cleanly.

🤖 Generated with Claude Code

Checkpoint/restore of a confined (imaged/chrooted) workload failed at
restore: sandlock_restore_interactive returned NULL and the process
never came back. Two causes, both a mismatch between the paths the
checkpoint records and what restore can open:

1. Capture reads /proc/<pid>/maps and /proc/<pid>/fd, whose entries are
   resolved in the mount namespace and so ignore the process's chroot: a
   file the workload sees at /lib/ld-musl... is recorded as its host path
   <rootfs>/lib/ld-musl... The open()s injected during restore run in the
   already-confined child, where that host path does not resolve, so the
   remap/reopen fails. restore_into now translates each recorded host path
   back to the child's in-chroot view (host_to_virtual over the chroot
   root and mounts) before opening. Non-chroot restores are unchanged.

2. An imaged binary is exec'd from a sealed memfd, so its code region is
   backed by "/memfd:sandlock-exec (deleted)" -- unreopenable by path.
   Capture only dumped writable regions, leaving the read-only code to be
   remapped from a file that cannot be reopened. Capture now also dumps
   private regions backed by a memfd or a "(deleted)" path so restore
   reconstructs them from bytes.

With both fixes a checkpointed service in an imaged sandbox restores and
resumes.

Signed-off-by: Cong Wang <cwang@multikernel.io>
@congwang-mk congwang-mk force-pushed the fix/checkpoint-chroot-restore branch from af3724d to 1f5b03b Compare July 14, 2026 23:07
@congwang-mk congwang-mk merged commit 970c3ae into main Jul 15, 2026
13 checks passed
@congwang-mk congwang-mk deleted the fix/checkpoint-chroot-restore branch July 15, 2026 20:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant