fix CVE-2023-52901
Jimmy Hu (1): usb: xhci: Check endpoint is valid before dereferencing it
Mathias Nyman (1): xhci: add xhci_get_virt_ep() helper
drivers/usb/host/xhci-ring.c | 80 +++++++++++++++++++++++++----------- drivers/usb/host/xhci.h | 3 +- 2 files changed, 57 insertions(+), 26 deletions(-)
From: Mathias Nyman mathias.nyman@linux.intel.com
mainline inclusion from mainline-v5.12-rc1 commit b1adc42d440df3233255e313a45ab7e9b2b74096 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IALIMI CVE: CVE-2023-52901
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id=...
--------------------------------
In several event handlers we need to find the right endpoint structure from slot_id and ep_index in the event.
Add a helper for this, check that slot_id and ep_index are valid.
Signed-off-by: Mathias Nyman mathias.nyman@linux.intel.com Link: https://lore.kernel.org/r/20210129130044.206855-6-mathias.nyman@linux.intel.... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Conflicts: drivers/usb/host/xhci-ring.c drivers/usb/host/xhci.h [lc: adjust context] Signed-off-by: Liao Chen liaochen4@huawei.com --- drivers/usb/host/xhci-ring.c | 75 ++++++++++++++++++++++++------------ drivers/usb/host/xhci.h | 3 +- 2 files changed, 53 insertions(+), 25 deletions(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 6658a352e82f..0bff3f54049f 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -446,6 +446,27 @@ static void ring_doorbell_for_active_rings(struct xhci_hcd *xhci, } }
+static struct xhci_virt_ep *xhci_get_virt_ep(struct xhci_hcd *xhci, + unsigned int slot_id, + unsigned int ep_index) +{ + if (slot_id == 0 || slot_id >= MAX_HC_SLOTS) { + xhci_warn(xhci, "Invalid slot_id %u\n", slot_id); + return NULL; + } + if (ep_index >= EP_CTX_PER_DEV) { + xhci_warn(xhci, "Invalid endpoint index %u\n", ep_index); + return NULL; + } + if (!xhci->devs[slot_id]) { + xhci_warn(xhci, "No xhci virt device for slot_id %u\n", slot_id); + return NULL; + } + + return &xhci->devs[slot_id]->eps[ep_index]; +} + + /* Get the right ring for the given slot_id, ep_index and stream_id. * If the endpoint supports streams, boundary check the URB's stream ID. * If the endpoint doesn't support streams, return the singular endpoint ring. @@ -456,7 +477,10 @@ struct xhci_ring *xhci_triad_to_transfer_ring(struct xhci_hcd *xhci, { struct xhci_virt_ep *ep;
- ep = &xhci->devs[slot_id]->eps[ep_index]; + ep = xhci_get_virt_ep(xhci, slot_id, ep_index); + if (!ep) + return NULL; + /* Common case: no streams */ if (!(ep->ep_state & EP_HAS_STREAMS)) return ep->ring; @@ -726,11 +750,14 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, memset(&deq_state, 0, sizeof(deq_state)); ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3]));
- vdev = xhci->devs[slot_id]; + ep = xhci_get_virt_ep(xhci, slot_id, ep_index); + if (!ep) + return; + + vdev = ep->vdev; ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index); trace_xhci_handle_cmd_stop_ep(ep_ctx);
- ep = &xhci->devs[slot_id]->eps[ep_index]; last_unlinked_td = list_last_entry(&ep->cancelled_td_list, struct xhci_td, cancelled_td_list);
@@ -1044,17 +1071,17 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, unsigned int ep_index; unsigned int stream_id; struct xhci_ring *ep_ring; - struct xhci_virt_device *dev; struct xhci_virt_ep *ep; struct xhci_ep_ctx *ep_ctx; struct xhci_slot_ctx *slot_ctx;
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); stream_id = TRB_TO_STREAM_ID(le32_to_cpu(trb->generic.field[2])); - dev = xhci->devs[slot_id]; - ep = &dev->eps[ep_index]; + ep = xhci_get_virt_ep(xhci, slot_id, ep_index); + if (!ep) + return;
- ep_ring = xhci_stream_id_to_ring(dev, ep_index, stream_id); + ep_ring = xhci_stream_id_to_ring(ep->vdev, ep_index, stream_id); if (!ep_ring) { xhci_warn(xhci, "WARN Set TR deq ptr command for freed stream ID %u\n", stream_id); @@ -1062,8 +1089,8 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, goto cleanup; }
- ep_ctx = xhci_get_ep_ctx(xhci, dev->out_ctx, ep_index); - slot_ctx = xhci_get_slot_ctx(xhci, dev->out_ctx); + ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep_index); + slot_ctx = xhci_get_slot_ctx(xhci, ep->vdev->out_ctx); trace_xhci_handle_cmd_set_deq(slot_ctx); trace_xhci_handle_cmd_set_deq_ep(ep_ctx);
@@ -1116,7 +1143,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, /* Update the ring's dequeue segment and dequeue pointer * to reflect the new position. */ - update_ring_for_set_deq_completion(xhci, dev, + update_ring_for_set_deq_completion(xhci, ep->vdev, ep_ring, ep_index); } else { xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n"); @@ -1126,9 +1153,9 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, }
cleanup: - dev->eps[ep_index].ep_state &= ~SET_DEQ_PENDING; - dev->eps[ep_index].queued_deq_seg = NULL; - dev->eps[ep_index].queued_deq_ptr = NULL; + ep->ep_state &= ~SET_DEQ_PENDING; + ep->queued_deq_seg = NULL; + ep->queued_deq_ptr = NULL; /* Restart any rings with pending URBs */ ring_doorbell_for_active_rings(xhci, slot_id, ep_index); } @@ -1136,13 +1163,16 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, union xhci_trb *trb, u32 cmd_comp_code) { - struct xhci_virt_device *vdev; + struct xhci_virt_ep *ep; struct xhci_ep_ctx *ep_ctx; unsigned int ep_index;
ep_index = TRB_TO_EP_INDEX(le32_to_cpu(trb->generic.field[3])); - vdev = xhci->devs[slot_id]; - ep_ctx = xhci_get_ep_ctx(xhci, vdev->out_ctx, ep_index); + ep = xhci_get_virt_ep(xhci, slot_id, ep_index); + if (!ep) + return; + + ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep_index); trace_xhci_handle_cmd_reset_ep(ep_ctx);
/* This command will only fail if the endpoint wasn't halted, @@ -1170,7 +1200,7 @@ static void xhci_handle_cmd_reset_ep(struct xhci_hcd *xhci, int slot_id, xhci_ring_cmd_db(xhci); } else { /* Clear our internal halted state */ - xhci->devs[slot_id]->eps[ep_index].ep_state &= ~EP_HALTED; + ep->ep_state &= ~EP_HALTED; } }
@@ -2267,7 +2297,6 @@ static int process_bulk_intr_td(struct xhci_hcd *xhci, struct xhci_td *td, static int handle_tx_event(struct xhci_hcd *xhci, struct xhci_transfer_event *event) { - struct xhci_virt_device *xdev; struct xhci_virt_ep *ep; struct xhci_ring *ep_ring; unsigned int slot_id; @@ -2288,16 +2317,14 @@ static int handle_tx_event(struct xhci_hcd *xhci, trb_comp_code = GET_COMP_CODE(le32_to_cpu(event->transfer_len)); ep_trb_dma = le64_to_cpu(event->buffer);
- xdev = xhci->devs[slot_id]; - if (!xdev) { - xhci_err(xhci, "ERROR Transfer event pointed to bad slot %u\n", - slot_id); + ep = xhci_get_virt_ep(xhci, slot_id, ep_index); + if (!ep) { + xhci_err(xhci, "ERROR Invalid Transfer event\n"); goto err_out; }
- ep = &xdev->eps[ep_index]; ep_ring = xhci_dma_to_transfer_ring(ep, ep_trb_dma); - ep_ctx = xhci_get_ep_ctx(xhci, xdev->out_ctx, ep_index); + ep_ctx = xhci_get_ep_ctx(xhci, ep->vdev->out_ctx, ep_index);
if (GET_EP_CTX_STATE(ep_ctx) == EP_STATE_DISABLED) { xhci_err(xhci, diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h index 485ba36b566c..cabb56ec2e1a 100644 --- a/drivers/usb/host/xhci.h +++ b/drivers/usb/host/xhci.h @@ -991,6 +991,7 @@ struct xhci_interval_bw_table { unsigned int ss_bw_out; };
+#define EP_CTX_PER_DEV 31
struct xhci_virt_device { struct usb_device *udev; @@ -1005,7 +1006,7 @@ struct xhci_virt_device { struct xhci_container_ctx *out_ctx; /* Used for addressing devices and configuration changes */ struct xhci_container_ctx *in_ctx; - struct xhci_virt_ep eps[31]; + struct xhci_virt_ep eps[EP_CTX_PER_DEV]; u8 fake_port; u8 real_port; struct xhci_interval_bw_table *bw_table;
From: Jimmy Hu hhhuuu@google.com
mainline inclusion from mainline-v6.2-rc5 commit e8fb5bc76eb86437ab87002d4a36d6da02165654 category: bugfix bugzilla: https://gitee.com/src-openeuler/kernel/issues/IALIMI CVE: CVE-2023-52901
Reference: https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/commit/?id= e8fb5bc76eb86437ab87002d4a36d6da02165654
--------------------------------
When the host controller is not responding, all URBs queued to all endpoints need to be killed. This can cause a kernel panic if we dereference an invalid endpoint.
Fix this by using xhci_get_virt_ep() helper to find the endpoint and checking if the endpoint is valid before dereferencing it.
[233311.853271] xhci-hcd xhci-hcd.1.auto: xHCI host controller not responding, assume dead [233311.853393] Unable to handle kernel NULL pointer dereference at virtual address 00000000000000e8
[233311.853964] pc : xhci_hc_died+0x10c/0x270 [233311.853971] lr : xhci_hc_died+0x1ac/0x270
[233311.854077] Call trace: [233311.854085] xhci_hc_died+0x10c/0x270 [233311.854093] xhci_stop_endpoint_command_watchdog+0x100/0x1a4 [233311.854105] call_timer_fn+0x50/0x2d4 [233311.854112] expire_timers+0xac/0x2e4 [233311.854118] run_timer_softirq+0x300/0xabc [233311.854127] __do_softirq+0x148/0x528 [233311.854135] irq_exit+0x194/0x1a8 [233311.854143] __handle_domain_irq+0x164/0x1d0 [233311.854149] gic_handle_irq.22273+0x10c/0x188 [233311.854156] el1_irq+0xfc/0x1a8 [233311.854175] lpm_cpuidle_enter+0x25c/0x418 [msm_pm] [233311.854185] cpuidle_enter_state+0x1f0/0x764 [233311.854194] do_idle+0x594/0x6ac [233311.854201] cpu_startup_entry+0x7c/0x80 [233311.854209] secondary_start_kernel+0x170/0x198
Fixes: 50e8725e7c42 ("xhci: Refactor command watchdog and fix split string.") Cc: stable@vger.kernel.org Signed-off-by: Jimmy Hu hhhuuu@google.com Signed-off-by: Mathias Nyman mathias.nyman@linux.intel.com Message-ID: 0fe978ed-8269-9774-1c40-f8a98c17e838@linux.intel.com Link: https://lore.kernel.org/r/20230116142216.1141605-3-mathias.nyman@linux.intel... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Liao Chen liaochen4@huawei.com --- drivers/usb/host/xhci-ring.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index 0bff3f54049f..31dd916618ec 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -892,7 +892,10 @@ static void xhci_kill_endpoint_urbs(struct xhci_hcd *xhci, struct xhci_virt_ep *ep; struct xhci_ring *ring;
- ep = &xhci->devs[slot_id]->eps[ep_index]; + ep = xhci_get_virt_ep(xhci, slot_id, ep_index); + if (!ep) + return; + if ((ep->ep_state & EP_HAS_STREAMS) || (ep->ep_state & EP_GETTING_NO_STREAMS)) { int stream_id;
反馈: 您发送到kernel@openeuler.org的补丁/补丁集,已成功转换为PR! PR链接地址: https://gitee.com/openeuler/kernel/pulls/11149 邮件列表地址:https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/7...
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/11149 Mailing list address: https://mailweb.openeuler.org/hyperkitty/list/kernel@openeuler.org/message/7...