[PATCH v2] um: vector: Possible underflow and races fixes

anton.ivanov at cambridgegreys.com anton.ivanov at cambridgegreys.com
Fri Nov 6 10:19:14 PST 2026


From: Anton Ivanov <anton.ivanov at cambridgegreys.com>

Michael S. Tsirkin while working on net virtio checksum hardening
discovered a number of issues in the uml vector driver. This patch
addresses these issues.

1. Use of header_size, not rx_header_size in some of the runt/underflow
checks. IPV6 raw socket API for protocols like GRE or L2TPV2 is asymmetric -
the header format and size in recv and send differ. All rx checks must use
the rx specific size.

2. virtio_net_header processing was not up to date with all the GSO
additions and changes and was skipping necessary processing.

3. Missing barrier in vector_send().

4. Incorrect timer destruction in net_close()

Signed-off-by: Anton Ivanov <anton.ivanov at cambridgegreys.com>
---
 arch/um/drivers/vector_kern.c       | 47 ++++++++++++++++++-----------
 arch/um/drivers/vector_kern.h       |  2 --
 arch/um/drivers/vector_transports.c |  3 --
 3 files changed, 29 insertions(+), 23 deletions(-)

diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c
index e902e78c5ff6..8008278ccee7 100644
--- a/arch/um/drivers/vector_kern.c
+++ b/arch/um/drivers/vector_kern.c
@@ -108,7 +108,7 @@ static void vector_reset_stats(struct vector_private *vp)
 	/* We reuse the existing queue locks for stats */
 
 	/* RX stats are modified with RX head_lock held
-	 * in vector_poll.
+	 * in vector_poll when using vector RX.
 	 */
 
 	if (vp->rx_queue)
@@ -119,18 +119,15 @@ static void vector_reset_stats(struct vector_private *vp)
 	vp->estats.rx_encaps_errors = 0;
 	vp->estats.sg_ok = 0;
 	vp->estats.sg_linearized = 0;
-
 	if (vp->rx_queue)
 		spin_unlock(&vp->rx_queue->head_lock);
 
-
 	/* TX stats are modified with TX head_lock held
-	 * in vector_send.
+	 * in vector_send when doing vector TX.
 	 */
 
 	if (vp->tx_queue)
 		spin_lock(&vp->tx_queue->head_lock);
-
 	vp->estats.tx_timeout_count = 0;
 	vp->estats.tx_restart_queue = 0;
 	vp->estats.tx_kicks = 0;
@@ -138,7 +135,6 @@ static void vector_reset_stats(struct vector_private *vp)
 	vp->estats.tx_flow_control_xoff = 0;
 	vp->estats.tx_queue_max = 0;
 	vp->estats.tx_queue_running_average = 0;
-
 	if (vp->tx_queue)
 		spin_unlock(&vp->tx_queue->head_lock);
 
@@ -236,6 +232,11 @@ static int get_transport_options(struct arglist *def)
 		}
 	}
 
+	if (get_depth(def) <= 0) {
+		vec_rx = 0;
+		vec_tx = 0;
+	}
+
 	if (get_bpf_flash(def))
 		result = VECTOR_BPF_FLASH;
 
@@ -416,6 +417,7 @@ static int vector_send(struct vector_queue *qi)
 		/* update queue_depth to current value */
 		while (atomic_read(&qi->queue_depth) > 0) {
 			/* Calculate the start of the vector */
+			smp_rmb();
 			send_len = atomic_read(&qi->queue_depth);
 			send_from = qi->mmsg_vector;
 			send_from += qi->head;
@@ -872,18 +874,17 @@ static int vector_legacy_rx(struct vector_private *vp)
 	}
 
 	if (skb != NULL) {
-		if (pkt_len > vp->header_size) {
-			if (vp->header_size > 0) {
+		if (pkt_len > vp->rx_header_size) {
+			if (vp->rx_header_size > 0) {
 				header_check = vp->verify_header(
 					vp->header_rxbuffer, skb, vp);
 				if (header_check < 0) {
 					dev_kfree_skb_irq(skb);
 					vp->dev->stats.rx_dropped++;
-					vp->estats.rx_encaps_errors++;
+					dev_kfree_skb_irq(skb);
 					return 0;
 				}
 				if (header_check > 0) {
-					vp->estats.rx_csum_offload_good++;
 					skb->ip_summed = CHECKSUM_UNNECESSARY;
 				}
 			}
@@ -988,7 +989,7 @@ static int vector_mmsg_rx(struct vector_private *vp, int budget)
 
 	for (i = 0; i < packet_count; i++) {
 		skb = (*skbuff_vector);
-		if (mmsg_vector->msg_len > vp->header_size) {
+		if (mmsg_vector->msg_len > vp->rx_header_size) {
 			if (vp->header_size > 0) {
 				header_check = vp->verify_header(
 					mmsg_vector->msg_hdr.msg_iov->iov_base,
@@ -1119,7 +1120,8 @@ static int vector_net_close(struct net_device *dev)
 	struct vector_private *vp = netdev_priv(dev);
 
 	netif_stop_queue(dev);
-	timer_delete(&vp->tl);
+	if (vp->options & VECTOR_TX)
+		timer_shutdown_sync(&vp->tl);
 
 	vp->opened = false;
 
@@ -1250,9 +1252,13 @@ static int vector_net_open(struct net_device *dev)
 		if (vp->header_txbuffer == NULL)
 			goto out_close;
 	}
+	if ((vp->options & (VECTOR_TX | VECTOR_RX)) &&
+			(get_depth(vp->parsed) > (NAPI_POLL_WEIGHT << 2)))
+		netif_napi_add_weight(vp->dev, &vp->napi, vector_poll,
+				      get_depth(vp->parsed));
+	else
+		netif_napi_add_weight(vp->dev, &vp->napi, vector_poll, NAPI_POLL_WEIGHT);
 
-	netif_napi_add_weight(vp->dev, &vp->napi, vector_poll,
-			      get_depth(vp->parsed));
 	napi_enable(&vp->napi);
 
 	/* READ IRQ */
@@ -1310,6 +1316,7 @@ static int vector_net_open(struct net_device *dev)
 
 	if ((vp->options & VECTOR_TX) != 0)
 		add_timer(&vp->tl);
+
 	return 0;
 out_close:
 	vector_net_close(dev);
@@ -1477,11 +1484,15 @@ static void vector_get_ethtool_stats(struct net_device *dev,
 	 * to date.
 	 */
 
-	spin_lock(&vp->tx_queue->head_lock);
-	spin_lock(&vp->rx_queue->head_lock);
+	if (vp->tx_queue)
+		spin_lock(&vp->tx_queue->head_lock);
+	if (vp->rx_queue)
+		spin_lock(&vp->rx_queue->head_lock);
 	memcpy(tmp_stats, &vp->estats, sizeof(struct vector_estats));
-	spin_unlock(&vp->rx_queue->head_lock);
-	spin_unlock(&vp->tx_queue->head_lock);
+	if (vp->rx_queue)
+		spin_unlock(&vp->rx_queue->head_lock);
+	if (vp->tx_queue)
+		spin_unlock(&vp->tx_queue->head_lock);
 }
 
 static int vector_get_coalesce(struct net_device *netdev,
diff --git a/arch/um/drivers/vector_kern.h b/arch/um/drivers/vector_kern.h
index 417834793658..5f0ceb80779d 100644
--- a/arch/um/drivers/vector_kern.h
+++ b/arch/um/drivers/vector_kern.h
@@ -116,8 +116,6 @@ struct vector_private {
 	int (*verify_header)(uint8_t *header,
 		struct sk_buff *skb, struct vector_private *vp);
 
-	spinlock_t stats_lock;
-
 	bool rexmit_scheduled;
 	bool opened;
 	bool in_write_poll;
diff --git a/arch/um/drivers/vector_transports.c b/arch/um/drivers/vector_transports.c
index e1fb2a76fdff..786f6badc52d 100644
--- a/arch/um/drivers/vector_transports.c
+++ b/arch/um/drivers/vector_transports.c
@@ -206,9 +206,6 @@ static int raw_verify_header(
 				GSO_ERROR
 		);
 	}
-	if ((vheader->flags & VIRTIO_NET_HDR_F_DATA_VALID) > 0)
-		return 1;
-
 	skb_set_network_header(skb, ETH_HLEN);
 	virtio_net_hdr_to_skb(skb, vheader, virtio_legacy_is_little_endian());
 	return 0;
-- 
2.47.3




More information about the linux-um mailing list