Linux USB
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Oliver Neukum" <oneukum@suse.com>, linux-usb@vger.kernel.org
Subject: Re: [PATCH net 1/1] net: usb: rtl8150: break cycle between tasklet and URB
Date: Fri, 13 Nov 2026 10:46:05 +0000	[thread overview]
Message-ID: <sashiko-outbox-212634@kernel.org> (raw)
In-Reply-To: <20261112104548.693456-1-oneukum@suse.com>

Thank you for your contribution! Sashiko AI review found 4 potential
issues to consider:
- [High] net: usb: rtl8150: infinite tasklet loop on -EPERM in
  rx_fixup()
- [High] net: usb: rtl8150: double free of dev->rx_skb on unplug in
  read_bulk_callback()
- [Medium] net: usb: rtl8150: false dev_err() on res == 0 in
  intr_callback()
- [Medium] net: usb: rtl8150: unbalanced URB reject count on error in
  rtl8150_open()
It also noticed 1 related pre-existing issue this patch does not
introduce:
- [High] net: usb: rtl8150: dangling rx_skb pointer in
  read_bulk_callback() [1]
--

commit bed5b8274af4c5f050a94925def40d4d88f1a
Author: Oliver Neukum <oneukum@suse.com>

net: usb: rtl8150: break cycle between tasklet and URB

This commit replaces usb_kill_urb() with usb_poison_urb() in
unlink_all_urbs(), blocks URBs at allocation time and unpoisons them on
open, and checks RTL8150_UNPLUG in read_bulk_callback() to break the
resubmission cycle between dev->rx_urb and dev->tl on disconnect.

> diff --git a/drivers/net/usb/rtl8150.c b/drivers/net/usb/rtl8150.c
> index d51e43170e033..846e6329d571c 100644
> --- a/drivers/net/usb/rtl8150.c
> +++ b/drivers/net/usb/rtl8150.c
> @@ -343,6 +343,13 @@ static int alloc_all_urbs(rtl8150_t * dev)
>  		return 0;
>  	}
>  
> +	/*
> +	 * They need to be blocked so that open()
> +	 * can unblock them
> +	 */
> +	usb_block_urb(dev->rx_urb);
> +	usb_block_urb(dev->tx_urb);
> +	usb_block_urb(dev->intr_urb);
>  	return 1;
>  }
>  
> @@ -353,11 +360,18 @@ static void free_all_urbs(rtl8150_t * dev)
>  	usb_free_urb(dev->intr_urb);
>  }
>  
> +static void reenable_all_urbs(rtl8150_t *dev)
> +{
> +	usb_unpoison_urb(dev->rx_urb);
> +	usb_unpoison_urb(dev->tx_urb);
> +	usb_unpoison_urb(dev->intr_urb);
> +}
> +
>  static void unlink_all_urbs(rtl8150_t * dev)
>  {
> -	usb_kill_urb(dev->rx_urb);
> -	usb_kill_urb(dev->tx_urb);
> -	usb_kill_urb(dev->intr_urb);
> +	usb_poison_urb(dev->rx_urb);
> +	usb_poison_urb(dev->tx_urb);
> +	usb_poison_urb(dev->intr_urb);
>  }

[Severity: High]
Can poisoning dev->rx_urb cause rx_fixup() to spin in an infinite tasklet
loop on -EPERM?

Once unlink_all_urbs() poisons dev->rx_urb in rtl8150_close() or
rtl8150_disconnect(), usb_submit_urb(dev->rx_urb, GFP_ATOMIC) returns -EPERM.
Neither read_bulk_callback() nor rx_fixup() ignores -EPERM, and rx_fixup()
never checks RTL8150_UNPLUG:

drivers/net/usb/rtl8150.c:rx_fixup() {
    ...
	if (test_bit(RX_URB_FAIL, &dev->flags))
		if (dev->rx_skb)
			goto try_again;
    ...
try_again:
	status = usb_submit_urb(dev->rx_urb, GFP_ATOMIC);
	if (status == -ENODEV) {
		netif_device_detach(dev->netdev);
	} else if (status) {
		set_bit(RX_URB_FAIL, &dev->flags);
		goto tlsched;
	}
    ...
tlsched:
	tasklet_schedule(&dev->tl);
}

If dev->tl is scheduled when unlink_all_urbs() poisons dev->rx_urb (or if
read_bulk_callback() hits -EPERM at goon: during rtl8150_close() where
RTL8150_UNPLUG is not set), rx_fixup() sets RX_URB_FAIL and reschedules
dev->tl in a tight softirq loop, locking up softirq processing on Close and
hanging tasklet_kill(&dev->tl) in rtl8150_disconnect().

> @@ -425,6 +439,11 @@ static void read_bulk_callback(struct urb *urb)
>  	netdev->stats.rx_bytes += pkt_len;
>  
>  	spin_lock_irqsave(&dev->rx_pool_lock, flags);
> +	/* prevent an eteral loop */
> +	if (test_bit(RTL8150_UNPLUG, &dev->flags)) {
> +		spin_unlock_irqrestore(&dev->rx_pool_lock, flags);
> +		return;
> +	}
>  	skb = pull_skb(dev);
>  	spin_unlock_irqrestore(&dev->rx_pool_lock, flags);
>  	if (!skb)

[Severity: High]
Does returning here when RTL8150_UNPLUG is set cause a double free of
dev->rx_skb?

Just before taking dev->rx_pool_lock, read_bulk_callback() passes
dev->rx_skb to netif_rx().  Returning without clearing dev->rx_skb to NULL
leaves the consumed skb pointer in dev->rx_skb, which rtl8150_disconnect()
then frees a second time via dev_kfree_skb(dev->rx_skb).

[Severity: High]
This problem wasn't introduced by this patch, but read_bulk_callback() also
leaves [1] dev->rx_skb holding a dangling pointer to the skb handed to
netif_rx() whenever pull_skb() returns NULL and jumps to resched:.  If the
device is disconnected before rx_fixup() replaces dev->rx_skb,
rtl8150_disconnect() double frees dev->rx_skb, and if the device is closed
and reopened or suspended and resumed in that state, rtl8150_open() or
rtl8150_resume() resubmits dev->rx_urb using the freed skb buffer.

> @@ -518,7 +537,7 @@ static void intr_callback(struct urb *urb)
>  	res = usb_submit_urb (urb, GFP_ATOMIC);
>  	if (res == -ENODEV)
>  		netif_device_detach(dev->netdev);
> -	else if (res)
> +	else if (res != -EPERM)
>  		dev_err(&dev->udev->dev,
>  			"can't resubmit intr, %s-%s/input0, status %d\n",
>  			dev->udev->bus->bus_name, dev->udev->devpath, res);

[Severity: Medium]
Doesn't "else if (res != -EPERM)" evaluate to true when usb_submit_urb()
succeeds with res == 0?  That logs a false dev_err() on every interrupt URB
completion unless the check is "else if (res && res != -EPERM)".

> @@ -751,6 +770,7 @@ static int rtl8150_open(struct net_device *netdev)
>  	if (!dev->rx_skb)
>  		return -ENOMEM;
>  
> +	reenable_all_urbs(dev);
>  	set_registers(dev, IDR, 6, netdev->dev_addr);
>  
>  	usb_fill_bulk_urb(dev->rx_urb, dev->udev, usb_rcvbulkpipe(dev->udev, 1),

[Severity: Medium]
What happens to the URB reject count if usb_submit_urb() fails in
rtl8150_open()?

reenable_all_urbs() decrements urb->reject from 1 to 0 on all three URBs,
and the error paths in rtl8150_open() return without calling
unlink_all_urbs() to re-poison them (only calling usb_kill_urb(dev->rx_urb)
when dev->intr_urb submission fails):

drivers/net/usb/rtl8150.c:rtl8150_open() {
    ...
	if ((res = usb_submit_urb(dev->rx_urb, GFP_KERNEL))) {
		if (res == -ENODEV)
			netif_device_detach(dev->netdev);
		dev_warn(&netdev->dev, "rx_urb submit failed: %d\n", res);
		return res;
	}
    ...
	if ((res = usb_submit_urb(dev->intr_urb, GFP_KERNEL))) {
		if (res == -ENODEV)
			netif_device_detach(dev->netdev);
		dev_warn(&netdev->dev, "intr_urb submit failed: %d\n", res);
		usb_kill_urb(dev->rx_urb);
		return res;
	}
    ...
}

Because rtl8150_close() is not called when rtl8150_open() fails, the next
rtl8150_open() call decrements urb->reject from 0 to -1, causing all
subsequent usb_submit_urb() calls to fail with -EPERM and causing a later
unlink_all_urbs() to increment urb->reject back to 0 instead of poisoning
the URBs.

[1] https://sashiko.dev/#/bug/linux-361a8ebc-4be5-4126-a2d9-b88d108064a9

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261112104548.693456-1-oneukum@suse.com?part=1

      parent reply	other threads:[~2026-11-13 10:46 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-11-12 10:45 [PATCH net 1/1] net: usb: rtl8150: break cycle between tasklet and URB Oliver Neukum
2026-11-12 10:52 ` netdev-bot+sinfo
2026-11-12 11:09   ` Oliver Neukum
2026-11-13 10:46 ` sashiko-bot [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=sashiko-outbox-212634@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=oneukum@suse.com \
    --cc=sashiko-reviews@lists.linux.dev \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox