Alan Stern (1): USB: core: Add routines for endpoint checks in old drivers
Nikita Zhandarovich (1): wifi: carl9170: add a proper sanity check for endpoints
drivers/net/wireless/ath/carl9170/usb.c | 32 +++++++++++ drivers/usb/core/usb.c | 76 +++++++++++++++++++++++++ include/linux/usb.h | 5 ++ 3 files changed, 113 insertions(+)
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/9647 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/5...
FeedBack: The patch(es) which you have sent to kernel@openeuler.org mailing list has been converted to a pull request successfully! Pull request link: https://gitee.com/openeuler/kernel/pulls/9647 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/5...
From: Alan Stern stern@rowland.harvard.edu
stable inclusion from stable-v5.10.181 commit 4c260bbf356af7b5a0c236ffad4595e1923b6051 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA6S5Z CVE: CVE-2024-38567
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
commit 13890626501ffda22b18213ddaf7930473da5792 upstream.
Many of the older USB drivers in the Linux USB stack were written based simply on a vendor's device specification. They use the endpoint information in the spec and assume these endpoints will always be present, with the properties listed, in any device matching the given vendor and product IDs.
While that may have been true back then, with spoofing and fuzzing it is not true any more. More and more we are finding that those old drivers need to perform at least a minimum of checking before they try to use any endpoint other than ep0.
To make this checking as simple as possible, we now add a couple of utility routines to the USB core. usb_check_bulk_endpoints() and usb_check_int_endpoints() take an interface pointer together with a list of endpoint addresses (numbers and directions). They check that the interface's current alternate setting includes endpoints with those addresses and that each of these endpoints has the right type: bulk or interrupt, respectively.
Although we already have usb_find_common_endpoints() and related routines meant for a similar purpose, they are not well suited for this kind of checking. Those routines find endpoints of various kinds, but only one (either the first or the last) of each kind, and they don't verify that the endpoints' addresses agree with what the caller expects.
In theory the new routines could be more general: They could take a particular altsetting as their argument instead of always using the interface's current altsetting. In practice I think this won't matter too much; multiple altsettings tend to be used for transferring media (audio or visual) over isochronous endpoints, not bulk or interrupt. Drivers for such devices will generally require more sophisticated checking than these simplistic routines provide.
Signed-off-by: Alan Stern stern@rowland.harvard.edu Link: https://lore.kernel.org/r/dd2c8e8c-2c87-44ea-ba17-c64b97e201c9@rowland.harva... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Li Huafei lihuafei1@huawei.com --- drivers/usb/core/usb.c | 76 ++++++++++++++++++++++++++++++++++++++++++ include/linux/usb.h | 5 +++ 2 files changed, 81 insertions(+)
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index db4de5367737..c4cd9d46f9e3 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -207,6 +207,82 @@ int usb_find_common_endpoints_reverse(struct usb_host_interface *alt, } EXPORT_SYMBOL_GPL(usb_find_common_endpoints_reverse);
+/** + * usb_find_endpoint() - Given an endpoint address, search for the endpoint's + * usb_host_endpoint structure in an interface's current altsetting. + * @intf: the interface whose current altsetting should be searched + * @ep_addr: the endpoint address (number and direction) to find + * + * Search the altsetting's list of endpoints for one with the specified address. + * + * Return: Pointer to the usb_host_endpoint if found, %NULL otherwise. + */ +static const struct usb_host_endpoint *usb_find_endpoint( + const struct usb_interface *intf, unsigned int ep_addr) +{ + int n; + const struct usb_host_endpoint *ep; + + n = intf->cur_altsetting->desc.bNumEndpoints; + ep = intf->cur_altsetting->endpoint; + for (; n > 0; (--n, ++ep)) { + if (ep->desc.bEndpointAddress == ep_addr) + return ep; + } + return NULL; +} + +/** + * usb_check_bulk_endpoints - Check whether an interface's current altsetting + * contains a set of bulk endpoints with the given addresses. + * @intf: the interface whose current altsetting should be searched + * @ep_addrs: 0-terminated array of the endpoint addresses (number and + * direction) to look for + * + * Search for endpoints with the specified addresses and check their types. + * + * Return: %true if all the endpoints are found and are bulk, %false otherwise. + */ +bool usb_check_bulk_endpoints( + const struct usb_interface *intf, const u8 *ep_addrs) +{ + const struct usb_host_endpoint *ep; + + for (; *ep_addrs; ++ep_addrs) { + ep = usb_find_endpoint(intf, *ep_addrs); + if (!ep || !usb_endpoint_xfer_bulk(&ep->desc)) + return false; + } + return true; +} +EXPORT_SYMBOL_GPL(usb_check_bulk_endpoints); + +/** + * usb_check_int_endpoints - Check whether an interface's current altsetting + * contains a set of interrupt endpoints with the given addresses. + * @intf: the interface whose current altsetting should be searched + * @ep_addrs: 0-terminated array of the endpoint addresses (number and + * direction) to look for + * + * Search for endpoints with the specified addresses and check their types. + * + * Return: %true if all the endpoints are found and are interrupt, + * %false otherwise. + */ +bool usb_check_int_endpoints( + const struct usb_interface *intf, const u8 *ep_addrs) +{ + const struct usb_host_endpoint *ep; + + for (; *ep_addrs; ++ep_addrs) { + ep = usb_find_endpoint(intf, *ep_addrs); + if (!ep || !usb_endpoint_xfer_int(&ep->desc)) + return false; + } + return true; +} +EXPORT_SYMBOL_GPL(usb_check_int_endpoints); + /** * usb_find_alt_setting() - Given a configuration, find the alternate setting * for the given interface. diff --git a/include/linux/usb.h b/include/linux/usb.h index a093667991bb..efa136e2de9b 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -279,6 +279,11 @@ void usb_put_intf(struct usb_interface *intf); #define USB_MAXINTERFACES 32 #define USB_MAXIADS (USB_MAXINTERFACES/2)
+bool usb_check_bulk_endpoints( + const struct usb_interface *intf, const u8 *ep_addrs); +bool usb_check_int_endpoints( + const struct usb_interface *intf, const u8 *ep_addrs); + /* * USB Resume Timer: Every Host controller driver should drive the resume * signalling on the bus for the amount of time defined by this macro.
From: Nikita Zhandarovich n.zhandarovich@fintech.ru
stable inclusion from stable-v5.10.219 commit 8650725bb0a48b206d5a8ddad3a7488f9a5985b7 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IA6S5Z CVE: CVE-2024-3856
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
[ Upstream commit b6dd09b3dac89b45d1ea3e3bd035a3859c0369a0 ]
Syzkaller reports [1] hitting a warning which is caused by presence of a wrong endpoint type at the URB sumbitting stage. While there was a check for a specific 4th endpoint, since it can switch types between bulk and interrupt, other endpoints are trusted implicitly. Similar warning is triggered in a couple of other syzbot issues [2].
Fix the issue by doing a comprehensive check of all endpoints taking into account difference between high- and full-speed configuration.
[1] Syzkaller report: ... WARNING: CPU: 0 PID: 4721 at drivers/usb/core/urb.c:504 usb_submit_urb+0xed6/0x1880 drivers/usb/core/urb.c:504 ... Call Trace: <TASK> carl9170_usb_send_rx_irq_urb+0x273/0x340 drivers/net/wireless/ath/carl9170/usb.c:504 carl9170_usb_init_device drivers/net/wireless/ath/carl9170/usb.c:939 [inline] carl9170_usb_firmware_finish drivers/net/wireless/ath/carl9170/usb.c:999 [inline] carl9170_usb_firmware_step2+0x175/0x240 drivers/net/wireless/ath/carl9170/usb.c:1028 request_firmware_work_func+0x130/0x240 drivers/base/firmware_loader/main.c:1107 process_one_work+0x9bf/0x1710 kernel/workqueue.c:2289 worker_thread+0x669/0x1090 kernel/workqueue.c:2436 kthread+0x2e8/0x3a0 kernel/kthread.c:376 ret_from_fork+0x1f/0x30 arch/x86/entry/entry_64.S:308 </TASK>
[2] Related syzkaller crashes: Link: https://syzkaller.appspot.com/bug?extid=e394db78ae0b0032cb4d Link: https://syzkaller.appspot.com/bug?extid=9468df99cb63a4a4c4e1
Reported-and-tested-by: syzbot+0ae4804973be759fa420@syzkaller.appspotmail.com Fixes: a84fab3cbfdc ("carl9170: 802.11 rx/tx processing and usb backend") Signed-off-by: Nikita Zhandarovich n.zhandarovich@fintech.ru Acked-By: Christian Lamparter chunkeey@gmail.com Signed-off-by: Kalle Valo quic_kvalo@quicinc.com Link: https://msgid.link/20240422183355.3785-1-n.zhandarovich@fintech.ru Signed-off-by: Sasha Levin sashal@kernel.org Signed-off-by: Li Huafei lihuafei1@huawei.com --- drivers/net/wireless/ath/carl9170/usb.c | 32 +++++++++++++++++++++++++ 1 file changed, 32 insertions(+)
diff --git a/drivers/net/wireless/ath/carl9170/usb.c b/drivers/net/wireless/ath/carl9170/usb.c index e4eb666c6eea..a5265997b576 100644 --- a/drivers/net/wireless/ath/carl9170/usb.c +++ b/drivers/net/wireless/ath/carl9170/usb.c @@ -1069,6 +1069,38 @@ static int carl9170_usb_probe(struct usb_interface *intf, ar->usb_ep_cmd_is_bulk = true; }
+ /* Verify that all expected endpoints are present */ + if (ar->usb_ep_cmd_is_bulk) { + u8 bulk_ep_addr[] = { + AR9170_USB_EP_RX | USB_DIR_IN, + AR9170_USB_EP_TX | USB_DIR_OUT, + AR9170_USB_EP_CMD | USB_DIR_OUT, + 0}; + u8 int_ep_addr[] = { + AR9170_USB_EP_IRQ | USB_DIR_IN, + 0}; + if (!usb_check_bulk_endpoints(intf, bulk_ep_addr) || + !usb_check_int_endpoints(intf, int_ep_addr)) + err = -ENODEV; + } else { + u8 bulk_ep_addr[] = { + AR9170_USB_EP_RX | USB_DIR_IN, + AR9170_USB_EP_TX | USB_DIR_OUT, + 0}; + u8 int_ep_addr[] = { + AR9170_USB_EP_IRQ | USB_DIR_IN, + AR9170_USB_EP_CMD | USB_DIR_OUT, + 0}; + if (!usb_check_bulk_endpoints(intf, bulk_ep_addr) || + !usb_check_int_endpoints(intf, int_ep_addr)) + err = -ENODEV; + } + + if (err) { + carl9170_free(ar); + return err; + } + usb_set_intfdata(intf, ar); SET_IEEE80211_DEV(ar->hw, &intf->dev);