A vm-admin in XAPI-based hypervisors (XenServer, XCP-ng) can permanently block VHD leaf coalesce operations on any VDI by setting VDI.other_config:leaf-coalesce to false. The SM garbage collector (cleanup.py) reads this key and skips leaf coalesce when the value is false. Snapshot chains grow unbounded, consuming storage until the SR reaches capacity or the VHD chain exceeds the maximum depth. The attack requires a single API call, produces no alert, and the effect persists indefinitely. The same mechanism allows setting gc=false or coalesce=false to disable all garbage collection on a VDI. The leaf-coalesce key has no per-key RBAC protection in datamodel.ml.
VDI.other_config is a Map(String, String) field writable by vm-admin. The leaf-coalesce key is consumed by the SM garbage collector at cleanup.py:2149-2245 to decide whether to perform leaf coalesce operations on a VDI.
The code path:
vm-admin calls VDI.add_to_other_config(vdi, "leaf-coalesce", "false")VDI.other_config during its periodic scancleanup.py:516 reads DB_LEAFCLSC = "leaf-coalesce" and checks its value"false" (LEAFCLSC_DISABLED), the GC skips leaf coalesce for this VDIAdditional GC-disabling keys in VDI.other_config:
gc=false - disables garbage collection entirely for the VDI (cleanup.py:514)coalesce=false - disables all coalesce operations (cleanup.py:515)All three keys are unprotected by map_keys_roles. The VDI.other_config field only protects folder and XenCenter.CustomFields.* at _R_VM_OP.
Missing RBAC protection. VDI.other_config has no map_keys_roles entry for leaf-coalesce, gc, or coalesce. All are writable by vm-admin.
Direct SM GC control from user-writable field. The garbage collector's behavior is controlled by keys in a user-writable map field with no intermediate validation layer.
No monitoring or alerting. When coalesce is disabled, no alert or audit event is generated. The condition is invisible to administrators until storage exhaustion occurs.
Persistent effect. The key persists in the XAPI database indefinitely. Coalesce blocking survives VM restarts, host reboots, and pool maintenance operations.
| Scenario | Impact | Pre-conditions | Status |
|---|---|---|---|
| Leaf coalesce blocking | Snapshot chains grow unbounded until SR fills | vm-admin, VDI with snapshots on LVHD SR | Source-traced |
| Full GC disablement | Orphan VDIs accumulate, consuming storage indefinitely | vm-admin, LVHD SR with VDI lifecycle operations | Source-traced |
| Silent storage exhaustion | SR reaches capacity, all VMs on the SR experience I/O errors | vm-admin, coalesce blocked on one or more VDIs | Modeled |
| Chain depth exhaustion | VHD chain exceeds maximum depth, VDI operations fail | vm-admin, repeated snapshots with coalesce blocked | Modeled |
VDI.other_config for leaf-coalesce=false, gc=false, or coalesce=false on production VDIsdisclosure/vendor-detection-guidance.mdVDI.other_config records for leaf-coalesce, gc, and coalesce keys set to falseAdd map_keys_roles. Protect leaf-coalesce, gc, and coalesce in datamodel.ml at _R_POOL_ADMIN to prevent vm-admin from controlling garbage collection behavior.
Separate GC policy from user-writable fields. Storage GC configuration should not be stored in other_config. Move GC policy to a dedicated, restricted configuration mechanism.
Upstream patches exist. They are held privately pending coordinated disclosure.
Disclosure:
datamodel.ml:6310-6315 (VDI.other_config field definition), cleanup.py:514-516 (GC key constants), cleanup.py:2149-2245 (leaf coalesce decision logic)disclosure/advisories/doc-security-advisory.md (DOC-5)research/investigations/vdi-other-config.mdDiscovered and reported by Jakob Wolffhechel, Moksha.