Public bug reported:
[Impact]
drive-mirror, blockdev-mirror, and active blockcommit can silently lose guest
writes issued during a short window at job startup.
The destination/base image keeps stale data and the job pivots with no error.
It's silent data corruption.
mirror_start_job() disables the block-layer dirty bitmap before the mirror
filter's own tracking is live, so writes in that window are tracked by neither
(block/mirror.c).
Regression since QEMU 8.0.0, introduced by commit 32125b14606a ('mirror: Fix
access of uninitialised fields during start').
Upstream issue, with MySQL/PostgreSQL corruption reported by production users
on the standard `virsh blockcommit --active --pivot` flow:
https://gitlab.com/qemu-project/qemu/-/issues/3273
Fixed upstream in 0f51f9c3420b, backported to the qemu-stable tree as
61f14858c159.
[Test Plan]
Not a guest test: QEMU runs with -machine none (no VM/OS booted).
The startup race is reproduced by injecting a controlled QMP/blkdebug/NBD
sequence, not by guest I/O.
# 1. active-commit chain: base.qcow2 <- top.qcow2 (64G sparse)
qemu-img create -f qcow2 base.qcow2 64G
qemu-img create -f qcow2 -F qcow2 -b base.qcow2 top.qcow2 64G
# 2. 2046 marker clusters (0x11) from 64M up, to keep mirror_dirty_init()
scanning past offset 0
for i in $(seq 0 2045); do echo "write -P 0x11 $((64 + i*32))M 64k"; done |
qemu-io -f qcow2 top.qcow2 >/dev/null
# 3. start QEMU with QMP on stdio: write JSON commands to the FIFO, read
replies/events from qmp.out
mkfifo qmp.in
qemu-system-x86_64 -nodefaults -machine none -display none -monitor none -qmp
stdio -drive
if=none,id=drive0,node-name=top,format=qcow2,file=blkdebug::"$PWD"/top.qcow2
<qmp.in >qmp.out 2>qemu.err &
qemu_pid=$!
exec 3>qmp.in
echo '{"execute":"qmp_capabilities"}' >&3
# 4. writable NBD export on the active node, BEFORE block-commit so the mirror
filter takes over its writes
echo
'{"execute":"nbd-server-start","arguments":{"addr":{"type":"unix","data":{"path":"'"$PWD"'/nbd.sock"}}}}'
>&3
echo
'{"execute":"block-export-add","arguments":{"id":"exp0","type":"nbd","node-name":"top","name":"exp0","writable":true}}'
>&3
# 5. arm the blkdebug breakpoint, then start the active commit
echo '{"execute":"human-monitor-command","arguments":{"command-line":"qemu-io
drive0 \"break l2_load A\""}}' >&3
# active commit of drive0's whole backing chain (no base/top passed)
echo
'{"execute":"block-commit","arguments":{"device":"drive0","job-id":"commit","filter-node-name":"commit-filter"}}'
>&3
# 6. wait until dirty_init hits l2_load (offset 0 already scanned); its
wait_break reply carries id "wb"
echo
'{"execute":"human-monitor-command","id":"wb","arguments":{"command-line":"qemu-io
drive0 \"wait_break A\""}}' >&3
until grep -q '"wb"' qmp.out; do sleep 0.2; done
# 7. write into the still-open startup window (job not installed yet); it
blocks until resume, so background it
qemu-io -f raw -c "write -P 0x7b 0 64k" "nbd+unix:///exp0?socket=$PWD/nbd.sock"
&
# 8. give the background write time to connect and block on the breakpoint,
then resume
sleep 1
echo '{"execute":"human-monitor-command","arguments":{"command-line":"qemu-io
drive0 \"resume A\""}}' >&3
wait $!
# 9. finish the active commit, pivot, quit
until grep -q BLOCK_JOB_READY qmp.out; do sleep 0.2; done
echo '{"execute":"block-job-complete","arguments":{"device":"commit"}}' >&3
until grep -q BLOCK_JOB_COMPLETED qmp.out; do sleep 0.2; done
echo '{"execute":"quit"}' >&3
wait "$qemu_pid"
# 10. check the committed base directly
qemu-io -f qcow2 -c "read -P 0x11 64M 64k" base.qcow2
qemu-io -f qcow2 -c "read -P 0x7b 0 64k" base.qcow2
The 0x11 read is a control (always passes: the commit copied normal data).
The 0x7b read at offset 0 is the verdict:
before patch: fails -- base reads 0x00, the window write was lost
after patch: succeeds -- the window write reached base
[Where problems could occur]
The change is in mirror_start_job() and the per-write hot path
bdrv_mirror_top_do_write(), shared by drive-mirror, blockdev-mirror and active
block-commit.
A regression would therefore affect any such job (libvirt
blockcommit/blockcopy, live storage migration), not only the startup window
being fixed.
- Bitmap lifecycle moved: the mirror bitmap is now created right after
bdrv_append() and released on the job-start failure path, so a mistake there
could leak the bitmap or free it twice.
- Bitmap create/disable now runs inside the drained section, reordered against
bdrv_append() and job creation; wrong ordering could race with in-flight
requests.
- An out-of-tree block driver reading the bitmap during the drain interval
would see the new ordering; no in-tree caller does.
- Noble (qemu 8.2.2) needs a manual backport because surrounding code shifted,
so divergence from upstream is the risk; it is re-verified with the same
deterministic Test Plan above.
[Other Info]
** Affects: qemu (Ubuntu)
Importance: Undecided
Assignee: Seyeong Kim (seyeongkim)
Status: New
** Affects: qemu (Ubuntu Noble)
Importance: Undecided
Assignee: Seyeong Kim (seyeongkim)
Status: New
** Affects: qemu (Ubuntu Questing)
Importance: Undecided
Assignee: Seyeong Kim (seyeongkim)
Status: New
** Affects: qemu (Ubuntu Resolute)
Importance: Undecided
Assignee: Seyeong Kim (seyeongkim)
Status: New
** Also affects: qemu (Ubuntu Noble)
Importance: Undecided
Status: New
** Also affects: qemu (Ubuntu Resolute)
Importance: Undecided
Status: New
** Also affects: qemu (Ubuntu Questing)
Importance: Undecided
Status: New
** Changed in: qemu (Ubuntu Noble)
Assignee: (unassigned) => Seyeong Kim (seyeongkim)
** Changed in: qemu (Ubuntu)
Assignee: (unassigned) => Seyeong Kim (seyeongkim)
** Changed in: qemu (Ubuntu Questing)
Assignee: (unassigned) => Seyeong Kim (seyeongkim)
** Changed in: qemu (Ubuntu Resolute)
Assignee: (unassigned) => Seyeong Kim (seyeongkim)
--
You received this bug notification because you are a member of Ubuntu
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2156307
Title:
drive-mirror/blockdev-mirror/active blockcommit silently lose guest
writes during job startup
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/qemu/+bug/2156307/+subscriptions
--
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs