Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Drivers: hv: vmbus: Prevent load re-ordering when reading ring buffer

When reading a packet from a host-to-guest ring buffer, there is no
memory barrier between reading the write index (to see if there is
a packet to read) and reading the contents of the packet. The Hyper-V
host uses store-release when updating the write index to ensure that
writes of the packet data are completed first. On the guest side,
the processor can reorder and read the packet data before the write
index, and sometimes get stale packet data. Getting such stale packet
data has been observed in a reproducible case in a VM on ARM64.

Fix this by using virt_load_acquire() to read the write index,
ensuring that reads of the packet data cannot be reordered
before it. Preventing such reordering is logically correct, and
with this change, getting stale data can no longer be reproduced.

Signed-off-by: Michael Kelley <mikelley@microsoft.com>
Reviewed-by: Andrea Parri (Microsoft) <parri.andrea@gmail.com>
Link: https://lore.kernel.org/r/1648394710-33480-1-git-send-email-mikelley@microsoft.com
Signed-off-by: Wei Liu <wei.liu@kernel.org>

authored by

Michael Kelley and committed by
Wei Liu
b6cae15b 8d217324

+10 -1
+10 -1
drivers/hv/ring_buffer.c
··· 439 439 static u32 hv_pkt_iter_avail(const struct hv_ring_buffer_info *rbi) 440 440 { 441 441 u32 priv_read_loc = rbi->priv_read_index; 442 - u32 write_loc = READ_ONCE(rbi->ring_buffer->write_index); 442 + u32 write_loc; 443 + 444 + /* 445 + * The Hyper-V host writes the packet data, then uses 446 + * store_release() to update the write_index. Use load_acquire() 447 + * here to prevent loads of the packet data from being re-ordered 448 + * before the read of the write_index and potentially getting 449 + * stale data. 450 + */ 451 + write_loc = virt_load_acquire(&rbi->ring_buffer->write_index); 443 452 444 453 if (write_loc >= priv_read_loc) 445 454 return write_loc - priv_read_loc;