* [PATCH net] virtio_net: reject stats replies for queues outside the request
@ 2026-10-22 18:26 Lianjie Wang
2026-10-27 12:06 ` Paolo Abeni
0 siblings, 1 reply; 3+ messages in thread
From: Lianjie Wang @ 2026-10-22 18:26 UTC (permalink / raw)
To: Michael S. Tsirkin, Jason Wang, Eugenio Pérez, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: Lianjie Wang, Xuan Zhuo, netdev, virtualization, linux-kernel, stable
__virtnet_get_hw_stats() passes the vq_index of each record in the
device's statistics reply to virtnet_fill_stats() without checking it,
so the device decides where the driver writes the counters.
For ethtool -S, virtnet_fill_stats() uses vq_index as an offset into the
output array, which the ethtool core sized for curr_queue_pairs. A
record with the vq_index of a higher queue makes the driver write past
the end of the array.
For the per-queue netlink stats, the queue type also comes from
vq_index. A receive record in reply to a request for a transmit queue
makes the driver fill the struct netdev_queue_stats_tx on the stack as
if it were a struct netdev_queue_stats_rx. If the device offers
VIRTIO_NET_STATS_TYPE_RX_SPEED, this writes one counter to the eight
bytes after the tx struct.
Fix this by rejecting the reply when the vq_index of a record lies
outside the range of queues the request asked about.
Claude Opus 5.5 was used to review the driver code and draft this patch.
To reproduce, the virtio-net device in QEMU was modified to answer an
ethtool -S request on a one-pair device with a record for a receive
queue the device does not have. On an unmodified kernel, KASAN then
reports a write past the ethtool -S output array. With this patch, the
driver rejects the same reply with "Failed to get hw stats.", a
well-formed reply fills the statistics as before, and the
drivers/net/stats.py selftest passes.
Fixes: 941168f8b40e ("virtio_net: support device stats")
Fixes: d888f04c09bb ("virtio-net: support queue stat")
Cc: stable@vger.kernel.org
Signed-off-by: Lianjie Wang <karin0.zst@gmail.com>
---
drivers/net/virtio_net.c | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
index daab43ac92ce4..0473fa9947858 100644
--- a/drivers/net/virtio_net.c
+++ b/drivers/net/virtio_net.c
@@ -4955,7 +4955,8 @@ static void virtnet_fill_stats(struct virtnet_info *vi, u32 qid,
static int __virtnet_get_hw_stats(struct virtnet_info *vi,
struct virtnet_stats_ctx *ctx,
struct virtio_net_ctrl_queue_stats *req,
- int req_size, void *reply, int res_size)
+ int req_size, void *reply, int res_size,
+ int first_vq, int last_vq, bool enable_cvq)
{
struct virtio_net_stats_reply_hdr *hdr;
struct scatterlist sgs_in, sgs_out;
@@ -4976,6 +4977,10 @@ static int __virtnet_get_hw_stats(struct virtnet_info *vi,
for (p = reply; p - reply < res_size; p += le16_to_cpu(hdr->size)) {
hdr = p;
qid = le16_to_cpu(hdr->vq_index);
+ if ((qid < first_vq || qid > last_vq) &&
+ !(enable_cvq && qid == vi->max_queue_pairs * 2))
+ return -EINVAL;
+
virtnet_fill_stats(vi, qid, ctx, p, false, hdr->type);
}
@@ -5055,7 +5060,8 @@ static int virtnet_get_hw_stats(struct virtnet_info *vi,
if (enable_cvq)
virtnet_make_stat_req(vi, ctx, req, vi->max_queue_pairs * 2, &j);
- ok = __virtnet_get_hw_stats(vi, ctx, req, sizeof(*req) * j, reply, res_size);
+ ok = __virtnet_get_hw_stats(vi, ctx, req, sizeof(*req) * j, reply, res_size,
+ first_vq, last_vq, enable_cvq);
kfree(req);
kfree(reply);
base-commit: 7be4bc52fd9e0e55dc9a2e0232b89427d2c4b614
--
2.56.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net] virtio_net: reject stats replies for queues outside the request
2026-10-22 18:26 [PATCH net] virtio_net: reject stats replies for queues outside the request Lianjie Wang
@ 2026-10-27 12:06 ` Paolo Abeni
2026-10-27 13:53 ` Lianjie Wang
0 siblings, 1 reply; 3+ messages in thread
From: Paolo Abeni @ 2026-10-27 12:06 UTC (permalink / raw)
To: Lianjie Wang, Michael S. Tsirkin, Jason Wang, Eugenio Pérez,
Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski
Cc: Xuan Zhuo, netdev, virtualization, linux-kernel, stable
On 10/22/26 20:26, Lianjie Wang wrote:
> __virtnet_get_hw_stats() passes the vq_index of each record in the
> device's statistics reply to virtnet_fill_stats() without checking it,
> so the device decides where the driver writes the counters.
>
> For ethtool -S, virtnet_fill_stats() uses vq_index as an offset into the
> output array, which the ethtool core sized for curr_queue_pairs. A
> record with the vq_index of a higher queue makes the driver write past
> the end of the array.
>
> For the per-queue netlink stats, the queue type also comes from
> vq_index. A receive record in reply to a request for a transmit queue
> makes the driver fill the struct netdev_queue_stats_tx on the stack as
> if it were a struct netdev_queue_stats_rx. If the device offers
> VIRTIO_NET_STATS_TYPE_RX_SPEED, this writes one counter to the eight
> bytes after the tx struct.
>
> Fix this by rejecting the reply when the vq_index of a record lies
> outside the range of queues the request asked about.
>
> Claude Opus 5.5 was used to review the driver code and draft this patch.
> To reproduce, the virtio-net device in QEMU was modified to answer an
> ethtool -S request on a one-pair device with a record for a receive
> queue the device does not have. On an unmodified kernel, KASAN then
> reports a write past the ethtool -S output array. With this patch, the
> driver rejects the same reply with "Failed to get hw stats.", a
> well-formed reply fills the statistics as before, and the
> drivers/net/stats.py selftest passes.
>
> Fixes: 941168f8b40e ("virtio_net: support device stats")
> Fixes: d888f04c09bb ("virtio-net: support queue stat")
> Cc: stable@vger.kernel.org
> Signed-off-by: Lianjie Wang <karin0.zst@gmail.com>
> ---
> drivers/net/virtio_net.c | 10 ++++++++--
> 1 file changed, 8 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/net/virtio_net.c b/drivers/net/virtio_net.c
> index daab43ac92ce4..0473fa9947858 100644
> --- a/drivers/net/virtio_net.c
> +++ b/drivers/net/virtio_net.c
> @@ -4955,7 +4955,8 @@ static void virtnet_fill_stats(struct virtnet_info *vi, u32 qid,
> static int __virtnet_get_hw_stats(struct virtnet_info *vi,
> struct virtnet_stats_ctx *ctx,
> struct virtio_net_ctrl_queue_stats *req,
> - int req_size, void *reply, int res_size)
> + int req_size, void *reply, int res_size,
> + int first_vq, int last_vq, bool enable_cvq)
> {
> struct virtio_net_stats_reply_hdr *hdr;
> struct scatterlist sgs_in, sgs_out;
> @@ -4976,6 +4977,10 @@ static int __virtnet_get_hw_stats(struct virtnet_info *vi,
> for (p = reply; p - reply < res_size; p += le16_to_cpu(hdr->size)) {
> hdr = p;
> qid = le16_to_cpu(hdr->vq_index);
> + if ((qid < first_vq || qid > last_vq) &&
> + !(enable_cvq && qid == vi->max_queue_pairs * 2))
> + return -EINVAL;
I think the above check could be simplified to:
if (qid < first_vq || qid > last_vq)
return -EINVAL;
squashing the following in virtnet_get_hw_stats():
- if (enable_cvq)
+ if (enable_cvq) {
virtnet_make_stat_req(vi, ctx, req, vi->max_queue_pairs * 2, &j);
+ last_vq++;
+ }
(likely with some meaningful comment).
/P
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH net] virtio_net: reject stats replies for queues outside the request
2026-10-27 12:06 ` Paolo Abeni
@ 2026-10-27 13:53 ` Lianjie Wang
0 siblings, 0 replies; 3+ messages in thread
From: Lianjie Wang @ 2026-10-27 13:53 UTC (permalink / raw)
To: Paolo Abeni
Cc: Michael S. Tsirkin, Jason Wang, Eugenio Pérez, Andrew Lunn,
David S. Miller, Eric Dumazet, Jakub Kicinski, Xuan Zhuo, netdev,
virtualization, linux-kernel, stable
On Tue, Oct 27, 2026 at 01:06:41PM +0100, Paolo Abeni wrote:
> On 10/22/26 20:26, Lianjie Wang wrote:
> > [...]
> > @@ -4976,6 +4977,10 @@ static int __virtnet_get_hw_stats(struct virtnet_info *vi,
> > for (p = reply; p - reply < res_size; p += le16_to_cpu(hdr->size)) {
> > hdr = p;
> > qid = le16_to_cpu(hdr->vq_index);
> > + if ((qid < first_vq || qid > last_vq) &&
> > + !(enable_cvq && qid == vi->max_queue_pairs * 2))
> > + return -EINVAL;
>
> I think the above check could be simplified to:
> if (qid < first_vq || qid > last_vq)
> return -EINVAL;
>
> squashing the following in virtnet_get_hw_stats():
>
> - if (enable_cvq)
> + if (enable_cvq) {
> virtnet_make_stat_req(vi, ctx, req, vi->max_queue_pairs * 2, &j);
> + last_vq++;
> + }
>
> (likely with some meaningful comment).
Thanks for the review!
I looked into this, and I think last_vq + 1 matches the control vq
only when all queue pairs are in use. The control vq index is
max_queue_pairs * 2, while last_vq is curr_queue_pairs * 2 - 1 for
ethtool -S. After ethtool -L lowers the queue count, or when probe caps
curr_queue_pairs at num_online_cpus(), last_vq + 1 is the receive vq at
index curr_queue_pairs * 2, which is not in the request. The simplified
check would accept a record for it. Since the ethtool -S array has
slots for only curr_queue_pairs queue pairs, with the transmit slots at
its end, virtnet_fill_stats() would write the counters of that record
into the transmit slots, and can write past the end of the array.
Would you like me to keep the current check or change it in some
other way?
As for the comment, I would add the following above the check in v2:
/* The control vq comes after all max_queue_pairs pairs, so it
* exceeds last_vq + 1 when fewer pairs are in use.
*/
Thanks,
Lianjie Wang
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-10-27 13:53 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-22 18:26 [PATCH net] virtio_net: reject stats replies for queues outside the request Lianjie Wang
2026-10-27 12:06 ` Paolo Abeni
2026-10-27 13:53 ` Lianjie Wang
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®