Describe the bug
In crates/bux-qcow2/src/overlay.rs, create_overlay allocates a fixed 256 KiB buffer (4 clusters of 64 KiB each) to construct the minimal QCOW2 v3 overlay header and tables. However, when writing the backing file path into the header via write_backing_file_name, it copies the bytes directly using slice indexing (buf[off..off + backing_bytes.len()]) without validating the length of backing_file.
If backing_file exceeds the available buffer capacity (e.g. an expanded deep path or malicious input > 260 KiB), the slice indexing triggers an out-of-bounds Panic (range end index out of range for slice of length 262144). This unhandled panic crashes the host daemon/worker process and leaves orphaned temporary files (*.qcow2.tmp) on the disk.
Furthermore, if backing_file is between ~65 KiB and 256 KiB, the slice index does not panic, but silently overflows Cluster 0 and clobbers Cluster 1 (the L1 routing table), causing catastrophic silent disk corruption when the VM boots.
To Reproduce
Steps to reproduce the behavior:
- Call
bux_qcow2::create_overlay (or invoke via DiskManager::create_overlay) passing a backing_file path longer than 260 KiB (e.g. run test repro_bug_2a_create_overlay_long_path_panics).
- Observe thread panic at
crates/bux-qcow2/src/overlay.rs:164:
thread 'repro_bug_2a_create_overlay_long_path_panics' panicked at crates/bux-qcow2/src/overlay.rs:164:8:
range end index 300128 out of range for slice of length 262144
- Inspect
vms_dir: notice that a half-created *.qcow2.tmp file is leaked on the host filesystem without being renamed or cleaned up.
Expected behavior
create_overlay should defensively validate backing_file.len() up-front:
- Ensure the backing path does not exceed the remaining capacity of Cluster 0 (
CLUSTER_SIZE - header_ext_len), preventing both slice index panics and silent corruption of the L1 table in Cluster 1.
- Return a typed error (e.g.
Error::BackingPathTooLong) rather than panicking.
- Callers should ensure temporary files are properly cleaned up on failure.
Screenshots
N/A (CLI / headless library)
Desktop (please complete the following information):
- OS: macOS 26.6.2 (Darwin arm64)
- Rust Version: rustc 1.97.1
- Shell: zsh
Smartphone (please complete the following information):
- Device: N/A
- OS: N/A
- Browser: N/A
- Version: N/A
Additional context
- Offending code location:
crates/bux-qcow2/src/overlay.rs:164 in write_backing_file_name.
- The warning was previously suppressed via
#[allow(clippy::indexing_slicing, reason = "buffer sizing invariant maintained by create_overlay (4 clusters)")], which bypassed Clippy's bounds checking lint under an unvalidated assumption.
- Upstream caller in
crates/bux/src/disk/mod.rs:207-214 creates a temporary file before calling create_overlay, which is orphaned on panic.
Describe the bug
In
crates/bux-qcow2/src/overlay.rs,create_overlayallocates a fixed 256 KiB buffer (4 clusters of 64 KiB each) to construct the minimal QCOW2 v3 overlay header and tables. However, when writing the backing file path into the header viawrite_backing_file_name, it copies the bytes directly using slice indexing (buf[off..off + backing_bytes.len()]) without validating the length ofbacking_file.If
backing_fileexceeds the available buffer capacity (e.g. an expanded deep path or malicious input > 260 KiB), the slice indexing triggers an out-of-bounds Panic (range end index out of range for slice of length 262144). This unhandled panic crashes the host daemon/worker process and leaves orphaned temporary files (*.qcow2.tmp) on the disk.Furthermore, if
backing_fileis between ~65 KiB and 256 KiB, the slice index does not panic, but silently overflows Cluster 0 and clobbers Cluster 1 (the L1 routing table), causing catastrophic silent disk corruption when the VM boots.To Reproduce
Steps to reproduce the behavior:
bux_qcow2::create_overlay(or invoke viaDiskManager::create_overlay) passing abacking_filepath longer than 260 KiB (e.g. run testrepro_bug_2a_create_overlay_long_path_panics).crates/bux-qcow2/src/overlay.rs:164:vms_dir: notice that a half-created*.qcow2.tmpfile is leaked on the host filesystem without being renamed or cleaned up.Expected behavior
create_overlayshould defensively validatebacking_file.len()up-front:CLUSTER_SIZE - header_ext_len), preventing both slice index panics and silent corruption of the L1 table in Cluster 1.Error::BackingPathTooLong) rather than panicking.Screenshots
N/A (CLI / headless library)
Desktop (please complete the following information):
Smartphone (please complete the following information):
Additional context
crates/bux-qcow2/src/overlay.rs:164inwrite_backing_file_name.#[allow(clippy::indexing_slicing, reason = "buffer sizing invariant maintained by create_overlay (4 clusters)")], which bypassed Clippy's bounds checking lint under an unvalidated assumption.crates/bux/src/disk/mod.rs:207-214creates a temporary file before callingcreate_overlay, which is orphaned on panic.