Re: [PATCH] um: vector: fix NULL pointer derefs in queue-less transports

Anton Ivanov <[email protected]>
Newsgroups gmane.linux.uml.devel,gmane.linux.kernel,gmane.linux.kernel.stable
Organization Cambridge Greys
Message-ID <[email protected]>
On 10/04/2026 21:30, Michael Bommarito wrote:
> TAP transport sets neither VECTOR_RX nor VECTOR_TX, so
> vector_net_open() never allocates rx_queue or tx_queue.  HYBRID sets
> VECTOR_RX but not VECTOR_TX, so tx_queue is NULL there too.
>
> vector_reset_stats(), vector_poll(), vector_get_ethtool_stats(), and
> vector_get_ringparam() unconditionally deref these queue pointers,
> causing a NULL pointer crash on SMP or with any lock debugging option.
>
> Guard all queue pointer accesses with NULL checks.
>
> Fixes: 49da7e64f33e ("High Performance UML Vector Network Driver")
> Cc: [email protected]
> Cc: Anton Ivanov <[email protected]>
> Assisted-by: Claude:claude-opus-4-6
> Assisted-by: Codex:gpt-5-4
> Signed-off-by: Michael Bommarito <[email protected]>
> ---
> Found while enabling KCOV and lockdep on UML for a network-stack
> test lab.  Tested boot with SMP=y + PROVE_LOCKING + DEBUG_SPINLOCK +
> DEBUG_LOCK_ALLOC + LOCKDEP + KCOV, all with vec0:transport=tap.
>
> Without the fix, the same config panics at addr 0x18 (SMP, no debug),
> 0x1c (DEBUG_SPINLOCK), or 0x30 (lockdep) -- all offsets into a NULL
> vector_queue pointer.
>
>   arch/um/drivers/vector_kern.c | 48 +++++++++++++++++------------------
>   1 file changed, 24 insertions(+), 24 deletions(-)
>
> diff --git a/arch/um/drivers/vector_kern.c b/arch/um/drivers/vector_kern.c
> index 2cc90055499a5..6134c376e57be 100644
> --- a/arch/um/drivers/vector_kern.c
> +++ b/arch/um/drivers/vector_kern.c
> @@ -105,25 +105,18 @@ static const struct {
>   
>   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.
> -	 */
> -
> -	spin_lock(&vp->rx_queue->head_lock);
> +	if (vp->rx_queue)
> +		spin_lock(&vp->rx_queue->head_lock);
>   	vp->estats.rx_queue_max = 0;
>   	vp->estats.rx_queue_running_average = 0;
>   	vp->estats.rx_encaps_errors = 0;
>   	vp->estats.sg_ok = 0;
>   	vp->estats.sg_linearized = 0;
> -	spin_unlock(&vp->rx_queue->head_lock);
> -
> -	/* TX stats are modified with TX head_lock held
> -	 * in vector_send.
> -	 */
> +	if (vp->rx_queue)
> +		spin_unlock(&vp->rx_queue->head_lock);
>   
> -	spin_lock(&vp->tx_queue->head_lock);
> +	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;
> @@ -131,7 +124,8 @@ 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;
> -	spin_unlock(&vp->tx_queue->head_lock);
> +	if (vp->tx_queue)
> +		spin_unlock(&vp->tx_queue->head_lock);
>   }
>   
>   static int get_mtu(struct arglist *def)
> @@ -1163,7 +1157,8 @@ static int vector_poll(struct napi_struct *napi, int budget)
>   
>   	if ((vp->options & VECTOR_TX) != 0)
>   		tx_enqueued = (vector_send(vp->tx_queue) > 0);
> -	spin_lock(&vp->rx_queue->head_lock);
> +	if (vp->rx_queue)
> +		spin_lock(&vp->rx_queue->head_lock);
>   	if ((vp->options & VECTOR_RX) > 0)
>   		err = vector_mmsg_rx(vp, budget);
>   	else {
> @@ -1171,7 +1166,8 @@ static int vector_poll(struct napi_struct *napi, int budget)
>   		if (err > 0)
>   			err = 1;
>   	}
> -	spin_unlock(&vp->rx_queue->head_lock);
> +	if (vp->rx_queue)
> +		spin_unlock(&vp->rx_queue->head_lock);
>   	if (err > 0)
>   		work_done += err;
>   
> @@ -1421,10 +1417,10 @@ static void vector_get_ringparam(struct net_device *netdev,
>   {
>   	struct vector_private *vp = netdev_priv(netdev);
>   
> -	ring->rx_max_pending = vp->rx_queue->max_depth;
> -	ring->tx_max_pending = vp->tx_queue->max_depth;
> -	ring->rx_pending = vp->rx_queue->max_depth;
> -	ring->tx_pending = vp->tx_queue->max_depth;
> +	ring->rx_max_pending = vp->rx_queue ? vp->rx_queue->max_depth : 0;
> +	ring->tx_max_pending = vp->tx_queue ? vp->tx_queue->max_depth : 0;
> +	ring->rx_pending = ring->rx_max_pending;
> +	ring->tx_pending = ring->tx_max_pending;
>   }
>   
>   static void vector_get_strings(struct net_device *dev, u32 stringset, u8 *buf)
> @@ -1466,11 +1462,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,

Acked-By: Anton Ivanov <[email protected]>

-- 
Anton R. Ivanov
Cambridgegreys Limited. Registered in England. Company Number 10273661
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.