net: usb: pegasus: enable basic endpoint checking

[ Upstream commit 3d7e6ce34f4fcc7083510c28b17a7c36462a25d4 ]

pegasus_probe() fills URBs with hardcoded endpoint pipes without
verifying the endpoint descriptors:

  - usb_rcvbulkpipe(dev, 1) for RX data
  - usb_sndbulkpipe(dev, 2) for TX data
  - usb_rcvintpipe(dev, 3)  for status interrupts

A malformed USB device can present these endpoints with transfer types
that differ from what the driver assumes.

Add a pegasus_usb_ep enum for endpoint numbers, replacing magic
constants throughout. Add usb_check_bulk_endpoints() and
usb_check_int_endpoints() calls before any resource allocation to
verify endpoint types before use, rejecting devices with mismatched
descriptors at probe time, and avoid triggering assertion.

Similar fix to
- commit 90b7f29617 ("net: usb: rtl8150: enable basic endpoint checking")
- commit 9e7021d2aeae ("net: usb: catc: enable basic endpoint checking")

Fixes: 1da177e4c3 ("Linux-2.6.12-rc2")
Signed-off-by: Ziyi Guo <n7l8m4@u.northwestern.edu>
Reviewed-by: Simon Horman <horms@kernel.org>
Link: https://patch.msgid.link/20260222050633.410165-1-n7l8m4@u.northwestern.edu
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
Ziyi Guo 2026-02-22 05:06:33 +00:00 committed by Sasha Levin
parent f3e4cceafa
commit d2e7c898cc
1 changed files with 30 additions and 5 deletions

View File

@ -31,6 +31,17 @@ static const char driver_name[] = "pegasus";
BMSR_100FULL | BMSR_ANEGCAPABLE) BMSR_100FULL | BMSR_ANEGCAPABLE)
#define CARRIER_CHECK_DELAY (2 * HZ) #define CARRIER_CHECK_DELAY (2 * HZ)
/*
* USB endpoints.
*/
enum pegasus_usb_ep {
PEGASUS_USB_EP_CONTROL = 0,
PEGASUS_USB_EP_BULK_IN = 1,
PEGASUS_USB_EP_BULK_OUT = 2,
PEGASUS_USB_EP_INT_IN = 3,
};
static bool loopback; static bool loopback;
static bool mii_mode; static bool mii_mode;
static char *devid; static char *devid;
@ -545,7 +556,7 @@ static void read_bulk_callback(struct urb *urb)
goto tl_sched; goto tl_sched;
goon: goon:
usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
usb_rcvbulkpipe(pegasus->usb, 1), usb_rcvbulkpipe(pegasus->usb, PEGASUS_USB_EP_BULK_IN),
pegasus->rx_skb->data, PEGASUS_MTU, pegasus->rx_skb->data, PEGASUS_MTU,
read_bulk_callback, pegasus); read_bulk_callback, pegasus);
rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC);
@ -585,7 +596,7 @@ static void rx_fixup(struct tasklet_struct *t)
return; return;
} }
usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
usb_rcvbulkpipe(pegasus->usb, 1), usb_rcvbulkpipe(pegasus->usb, PEGASUS_USB_EP_BULK_IN),
pegasus->rx_skb->data, PEGASUS_MTU, pegasus->rx_skb->data, PEGASUS_MTU,
read_bulk_callback, pegasus); read_bulk_callback, pegasus);
try_again: try_again:
@ -713,7 +724,7 @@ static netdev_tx_t pegasus_start_xmit(struct sk_buff *skb,
((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16); ((__le16 *) pegasus->tx_buff)[0] = cpu_to_le16(l16);
skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len); skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len);
usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb, usb_fill_bulk_urb(pegasus->tx_urb, pegasus->usb,
usb_sndbulkpipe(pegasus->usb, 2), usb_sndbulkpipe(pegasus->usb, PEGASUS_USB_EP_BULK_OUT),
pegasus->tx_buff, count, pegasus->tx_buff, count,
write_bulk_callback, pegasus); write_bulk_callback, pegasus);
if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) { if ((res = usb_submit_urb(pegasus->tx_urb, GFP_ATOMIC))) {
@ -840,7 +851,7 @@ static int pegasus_open(struct net_device *net)
set_registers(pegasus, EthID, 6, net->dev_addr); set_registers(pegasus, EthID, 6, net->dev_addr);
usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb, usb_fill_bulk_urb(pegasus->rx_urb, pegasus->usb,
usb_rcvbulkpipe(pegasus->usb, 1), usb_rcvbulkpipe(pegasus->usb, PEGASUS_USB_EP_BULK_IN),
pegasus->rx_skb->data, PEGASUS_MTU, pegasus->rx_skb->data, PEGASUS_MTU,
read_bulk_callback, pegasus); read_bulk_callback, pegasus);
if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) { if ((res = usb_submit_urb(pegasus->rx_urb, GFP_KERNEL))) {
@ -851,7 +862,7 @@ static int pegasus_open(struct net_device *net)
} }
usb_fill_int_urb(pegasus->intr_urb, pegasus->usb, usb_fill_int_urb(pegasus->intr_urb, pegasus->usb,
usb_rcvintpipe(pegasus->usb, 3), usb_rcvintpipe(pegasus->usb, PEGASUS_USB_EP_INT_IN),
pegasus->intr_buff, sizeof(pegasus->intr_buff), pegasus->intr_buff, sizeof(pegasus->intr_buff),
intr_callback, pegasus, pegasus->intr_interval); intr_callback, pegasus, pegasus->intr_interval);
if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) { if ((res = usb_submit_urb(pegasus->intr_urb, GFP_KERNEL))) {
@ -1136,10 +1147,24 @@ static int pegasus_probe(struct usb_interface *intf,
pegasus_t *pegasus; pegasus_t *pegasus;
int dev_index = id - pegasus_ids; int dev_index = id - pegasus_ids;
int res = -ENOMEM; int res = -ENOMEM;
static const u8 bulk_ep_addr[] = {
PEGASUS_USB_EP_BULK_IN | USB_DIR_IN,
PEGASUS_USB_EP_BULK_OUT | USB_DIR_OUT,
0};
static const u8 int_ep_addr[] = {
PEGASUS_USB_EP_INT_IN | USB_DIR_IN,
0};
if (pegasus_blacklisted(dev)) if (pegasus_blacklisted(dev))
return -ENODEV; return -ENODEV;
/* Verify that all required endpoints are present */
if (!usb_check_bulk_endpoints(intf, bulk_ep_addr) ||
!usb_check_int_endpoints(intf, int_ep_addr)) {
dev_err(&intf->dev, "Missing or invalid endpoints\n");
return -ENODEV;
}
net = alloc_etherdev(sizeof(struct pegasus)); net = alloc_etherdev(sizeof(struct pegasus));
if (!net) if (!net)
goto out; goto out;