Summary
Through a VirtioFS mount, symlinks are served with st_size = 0 once the virtiofs server first enumerates their directory on behalf of the guest — which, for a sandbox/boot, is how every pre-existing symlink gets its attributes cached. readlink(2) still returns the correct target, and the macOS host reports the correct size. Creating or touching the file on the host immediately restores the correct size in the guest (after the guest's ~1s attribute cache TTL); recreating the file from the guest also works. A symlink created on the host inside an already-enumerated directory reports correctly, and the poisoned state returns whenever the guest enumerates the containing directories again (observed with guest-side find walks) — both point at the directory-enumeration path in the server: its batch attribute fetch appears to drop symlink size.
Environment
- Docker Desktop 4.89.0, engine 29.7.2, macOS 26.6.2 (Apple Silicon)
- Docker Sandboxes (
sbx v0.39.0) microVM, guest kernel 7.0.12 aarch64, workspace mounted via virtiofs (host /Users/... virtiofs rw,relatime from /proc/mounts in the guest)
- Guest git 2.53.0;
statx is ENOSYS in the guest, so all stat traffic uses newfstatat
- Verified with python
os.lstat and coreutils stat inside the guest
Steps to reproduce
With any sbx sandbox whose workspace is a macOS host directory (paths are identical on both sides of the mount):
WS=~/path/to/workspace # the mounted workspace
mkdir -p "$WS/.sbx-probe/repro" # a directory NEW to the VM
ln -s a-target "$WS/.sbx-probe/repro/link"
stat -f %z "$WS/.sbx-probe/repro/link" # host -> 8 (target-string length, correct)
sbx exec <sandbox> -- sh -c "cd '$WS/.sbx-probe/repro' && readlink link && stat -c %s link"
# guest -> a-target (readlink works)
# guest -> 0 <- the bug
touch -h "$WS/.sbx-probe/repro/link" # host-side event
sleep 2 # guest attr-cache TTL
sbx exec <sandbox> -- sh -c "stat -c %s '$WS/.sbx-probe/repro/link'" # guest -> 8, fixed
Expected behavior
stat inside the guest returns the symlink's target-string length, matching the host.
Actual behavior
It returns 0 until a host-side event on the file (touch -h) or a guest-side create repopulates the attribute. Guest-side enumeration of the containing directory re-poisons it.
Impact
git inside the sandbox flags every pre-existing tracked symlink as modified and renders it as a deletion plus an empty file, because git (a) compares st_size against its index under every core.checkStat mode and (b) treats zero-size worktree entries as empty content in diff_populate_filespec without ever calling readlink. The phantom modifications cannot be cleared with git update-index --refresh, and they reappear after every VM boot. Any other tool that trusts st_size (rsync, tar, ...) is similarly affected. We currently work around it by touch -h-ing tracked symlinks from the host at every sandbox start.
Diagnostics: not uploaded — happy to provide on request.
Summary
Through a VirtioFS mount, symlinks are served with
st_size = 0once the virtiofs server first enumerates their directory on behalf of the guest — which, for a sandbox/boot, is how every pre-existing symlink gets its attributes cached.readlink(2)still returns the correct target, and the macOS host reports the correct size. Creating or touching the file on the host immediately restores the correct size in the guest (after the guest's ~1s attribute cache TTL); recreating the file from the guest also works. A symlink created on the host inside an already-enumerated directory reports correctly, and the poisoned state returns whenever the guest enumerates the containing directories again (observed with guest-sidefindwalks) — both point at the directory-enumeration path in the server: its batch attribute fetch appears to drop symlink size.Environment
sbxv0.39.0) microVM, guest kernel 7.0.12 aarch64, workspace mounted via virtiofs (host /Users/... virtiofs rw,relatimefrom/proc/mountsin the guest)statxis ENOSYS in the guest, so all stat traffic usesnewfstatatos.lstatand coreutilsstatinside the guestSteps to reproduce
With any sbx sandbox whose workspace is a macOS host directory (paths are identical on both sides of the mount):
Expected behavior
statinside the guest returns the symlink's target-string length, matching the host.Actual behavior
It returns 0 until a host-side event on the file (
touch -h) or a guest-side create repopulates the attribute. Guest-side enumeration of the containing directory re-poisons it.Impact
git inside the sandbox flags every pre-existing tracked symlink as modified and renders it as a deletion plus an empty file, because git (a) compares
st_sizeagainst its index under everycore.checkStatmode and (b) treats zero-size worktree entries as empty content indiff_populate_filespecwithout ever callingreadlink. The phantom modifications cannot be cleared withgit update-index --refresh, and they reappear after every VM boot. Any other tool that trustsst_size(rsync, tar, ...) is similarly affected. We currently work around it bytouch -h-ing tracked symlinks from the host at every sandbox start.Diagnostics: not uploaded — happy to provide on request.