| From d2837e9ec891b7e18ffe1386a1a1d2a186deea6a Mon Sep 17 00:00:00 2001 |
| From: Sasha Levin <sashal@kernel.org> |
| Date: Sun, 22 Feb 2026 05:06:33 +0000 |
| Subject: net: usb: pegasus: enable basic endpoint checking |
| |
| From: Ziyi Guo <n7l8m4@u.northwestern.edu> |
| |
| [ 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 90b7f2961798 ("net: usb: rtl8150: enable basic endpoint checking") |
| - commit 9e7021d2aeae ("net: usb: catc: enable basic endpoint checking") |
| |
| Fixes: 1da177e4c3f4 ("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> |
| --- |
| drivers/net/usb/pegasus.c | 35 ++++++++++++++++++++++++++++++----- |
| 1 file changed, 30 insertions(+), 5 deletions(-) |
| |
| diff --git a/drivers/net/usb/pegasus.c b/drivers/net/usb/pegasus.c |
| index c514483134f05..0f16a133c75d1 100644 |
| --- a/drivers/net/usb/pegasus.c |
| +++ b/drivers/net/usb/pegasus.c |
| @@ -31,6 +31,17 @@ static const char driver_name[] = "pegasus"; |
| BMSR_100FULL | BMSR_ANEGCAPABLE) |
| #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 mii_mode; |
| static char *devid; |
| @@ -545,7 +556,7 @@ static void read_bulk_callback(struct urb *urb) |
| goto tl_sched; |
| goon: |
| 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, |
| read_bulk_callback, pegasus); |
| rx_status = usb_submit_urb(pegasus->rx_urb, GFP_ATOMIC); |
| @@ -585,7 +596,7 @@ static void rx_fixup(struct tasklet_struct *t) |
| return; |
| } |
| 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, |
| read_bulk_callback, pegasus); |
| 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); |
| skb_copy_from_linear_data(skb, pegasus->tx_buff + 2, skb->len); |
| 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, |
| write_bulk_callback, pegasus); |
| 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); |
| |
| 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, |
| read_bulk_callback, pegasus); |
| 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_rcvintpipe(pegasus->usb, 3), |
| + usb_rcvintpipe(pegasus->usb, PEGASUS_USB_EP_INT_IN), |
| pegasus->intr_buff, sizeof(pegasus->intr_buff), |
| intr_callback, pegasus, pegasus->intr_interval); |
| 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; |
| int dev_index = id - pegasus_ids; |
| 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)) |
| 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)); |
| if (!net) |
| goto out; |
| -- |
| 2.51.0 |
| |