Skip to content

Commit 401900a

Browse files
committed
1 parent 93971b0 commit 401900a

2 files changed

Lines changed: 28 additions & 5 deletions

File tree

Sources/Containerization/Mount.swift

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,13 @@ extension Mount {
163163

164164
extension VZDiskImageStorageDeviceAttachment {
165165
static func mountToVZAttachment(mount: Mount, options: [String]) throws -> VZDiskImageStorageDeviceAttachment {
166-
var cachingMode: VZDiskImageCachingMode = .automatic
166+
// Bug #57 (HIGH): VZDiskImageCachingMode.automatic causes read-after-write incoherence
167+
// with sparse files on the Apple Virtualization Framework VirtIO block backend: reads
168+
// may bypass the page cache and return stale zeros from APFS sparse holes instead of
169+
// recently written data, corrupting ext4 directories under memory pressure. Use .cached
170+
// so reads always go through the macOS page cache and see the most recent writes.
171+
// See: https://github.com/apple/container/pull/1041, https://github.com/utmapp/UTM/pull/5919
172+
var cachingMode: VZDiskImageCachingMode = .cached
167173
var synchronizationMode: VZDiskImageSynchronizationMode = .fsync
168174

169175
for option in options {
@@ -178,8 +184,9 @@ extension VZDiskImageStorageDeviceAttachment {
178184
switch key {
179185
case "vzDiskImageCachingMode":
180186
switch value {
181-
case "automatic":
182-
cachingMode = .automatic
187+
// Bug #57 (HIGH): "automatic" was removed; VZDiskImageCachingMode.automatic
188+
// causes filesystem corruption with sparse VirtIO block devices on Apple
189+
// Virtualization Framework and must not be used.
183190
case "cached":
184191
cachingMode = .cached
185192
case "uncached":

Sources/ContainerizationEXT4/BUGFIXES.md

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,16 +1074,32 @@ ext4-bugs X
10741074

10751075
---
10761076

1077+
## 57. HIGH: `VZDiskImageCachingMode.automatic` causes read-after-write incoherence on sparse VirtIO block devices, corrupting ext4 directories
1078+
1079+
**File:** `Sources/Containerization/Mount.swift:166`
1080+
**Bug:** The default `VZDiskImageCachingMode` for VirtIO block device attachments was `.automatic`. With sparse backing files and the Apple Virtualization Framework VirtIO block backend, `.automatic` can internally bypass the macOS page cache for reads while still buffering writes in it, creating a read-after-write hazard: a guest read of a just-written sparse block may return the old zeros from APFS storage instead of the newly written data. This corrupts ext4 directory blocks whose content has been written by the guest but whose backing storage is still a sparse hole:
1081+
```
1082+
EXT4-fs error (device vdb): ext4_readdir:262: inode #33249: block 131675: comm dnf:
1083+
path /usr/lib/python3.9/site-packages/dnf/module: bad entry in directory:
1084+
rec_len is smaller than minimal - offset=0, inode=0, rec_len=0, size=4096 fake=0
1085+
```
1086+
Confirmed by `lseek(SEEK_DATA/SEEK_HOLE)` analysis: the directory data block at APFS offset 131675 is a sparse hole (never materialized to APFS storage) while Python reads via the page cache return valid data. The bug manifests under memory pressure after long-running builds when page cache dirty pages are selectively evicted to APFS. The issue is a known defect in the Apple Virtualization Framework VirtIO block backend, independently confirmed and fixed in UTM (utmapp/UTM#5919) and apple/container (#1041).
1087+
**Fix:** Change the default to `.cached`, which ensures reads always go through the macOS page cache and see the most recently written data. Remove `.automatic` from the set of allowed override values to prevent callers from re-enabling the broken mode.
1088+
1089+
ext4-bugs X
1090+
1091+
---
1092+
10771093
## Summary
10781094

10791095
| Severity | Count |
10801096
|----------|-------|
10811097
| CRITICAL | 7 |
1082-
| HIGH | 17 |
1098+
| HIGH | 18 |
10831099
| MEDIUM | 16 |
10841100
| LOW | 12 |
10851101
| FALSE POSITIVE | 4 |
1086-
| **Total** | **56** |
1102+
| **Total** | **57** |
10871103

10881104
### Merged (4 pairs → 4 entries)
10891105
| Merged | Into | Reason |

0 commit comments

Comments
 (0)