summaryrefslogtreecommitdiff
path: root/drivers/usb/host
diff options
context:
space:
mode:
authorJuergen Gross <jgross@suse.com>2022-03-11 13:35:09 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2022-03-16 11:05:59 +0300
commitaff477cb8f94613f501d386d10f20019e294bc35 (patch)
tree2ac514fde81db6bf0bd67887f1ab92ede848296d /drivers/usb/host
parent7f220d4a38e095ef4ee851ef4ff90f92ed9b778b (diff)
downloadlinux-aff477cb8f94613f501d386d10f20019e294bc35.tar.xz
xen/usb: harden xen_hcd against malicious backends
Make sure a malicious backend can't cause any harm other than wrong I/O data. Missing are verification of the request id in a response, sanitizing the reported actual I/O length, and protection against interrupt storms from the backend. Signed-off-by: Juergen Gross <jgross@suse.com> Link: https://lore.kernel.org/r/20220311103509.12908-1-jgross@suse.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host')
-rw-r--r--drivers/usb/host/xen-hcd.c57
1 files changed, 43 insertions, 14 deletions
diff --git a/drivers/usb/host/xen-hcd.c b/drivers/usb/host/xen-hcd.c
index 47ec092bd8e3..210f91bf661c 100644
--- a/drivers/usb/host/xen-hcd.c
+++ b/drivers/usb/host/xen-hcd.c
@@ -51,6 +51,7 @@ struct vdevice_status {
struct usb_shadow {
struct xenusb_urb_request req;
struct urb *urb;
+ bool in_flight;
};
struct xenhcd_info {
@@ -720,6 +721,12 @@ static void xenhcd_gnttab_done(struct xenhcd_info *info, unsigned int id)
int nr_segs = 0;
int i;
+ if (!shadow->in_flight) {
+ xenhcd_set_error(info, "Illegal request id");
+ return;
+ }
+ shadow->in_flight = false;
+
nr_segs = shadow->req.nr_buffer_segs;
if (xenusb_pipeisoc(shadow->req.pipe))
@@ -803,6 +810,7 @@ static int xenhcd_do_request(struct xenhcd_info *info, struct urb_priv *urbp)
info->urb_ring.req_prod_pvt++;
info->shadow[id].urb = urb;
+ info->shadow[id].in_flight = true;
RING_PUSH_REQUESTS_AND_CHECK_NOTIFY(&info->urb_ring, notify);
if (notify)
@@ -931,10 +939,27 @@ static int xenhcd_unlink_urb(struct xenhcd_info *info, struct urb_priv *urbp)
return ret;
}
-static int xenhcd_urb_request_done(struct xenhcd_info *info)
+static void xenhcd_res_to_urb(struct xenhcd_info *info,
+ struct xenusb_urb_response *res, struct urb *urb)
+{
+ if (unlikely(!urb))
+ return;
+
+ if (res->actual_length > urb->transfer_buffer_length)
+ urb->actual_length = urb->transfer_buffer_length;
+ else if (res->actual_length < 0)
+ urb->actual_length = 0;
+ else
+ urb->actual_length = res->actual_length;
+ urb->error_count = res->error_count;
+ urb->start_frame = res->start_frame;
+ xenhcd_giveback_urb(info, urb, res->status);
+}
+
+static int xenhcd_urb_request_done(struct xenhcd_info *info,
+ unsigned int *eoiflag)
{
struct xenusb_urb_response res;
- struct urb *urb;
RING_IDX i, rp;
__u16 id;
int more_to_do = 0;
@@ -961,16 +986,12 @@ static int xenhcd_urb_request_done(struct xenhcd_info *info)
xenhcd_gnttab_done(info, id);
if (info->error)
goto err;
- urb = info->shadow[id].urb;
- if (likely(urb)) {
- urb->actual_length = res.actual_length;
- urb->error_count = res.error_count;
- urb->start_frame = res.start_frame;
- xenhcd_giveback_urb(info, urb, res.status);
- }
+ xenhcd_res_to_urb(info, &res, info->shadow[id].urb);
}
xenhcd_add_id_to_freelist(info, id);
+
+ *eoiflag = 0;
}
info->urb_ring.rsp_cons = i;
@@ -988,7 +1009,7 @@ static int xenhcd_urb_request_done(struct xenhcd_info *info)
return 0;
}
-static int xenhcd_conn_notify(struct xenhcd_info *info)
+static int xenhcd_conn_notify(struct xenhcd_info *info, unsigned int *eoiflag)
{
struct xenusb_conn_response res;
struct xenusb_conn_request *req;
@@ -1033,6 +1054,8 @@ static int xenhcd_conn_notify(struct xenhcd_info *info)
info->conn_ring.req_prod_pvt);
req->id = id;
info->conn_ring.req_prod_pvt++;
+
+ *eoiflag = 0;
}
if (rc != info->conn_ring.req_prod_pvt)
@@ -1055,14 +1078,19 @@ static int xenhcd_conn_notify(struct xenhcd_info *info)
static irqreturn_t xenhcd_int(int irq, void *dev_id)
{
struct xenhcd_info *info = (struct xenhcd_info *)dev_id;
+ unsigned int eoiflag = XEN_EOI_FLAG_SPURIOUS;
- if (unlikely(info->error))
+ if (unlikely(info->error)) {
+ xen_irq_lateeoi(irq, XEN_EOI_FLAG_SPURIOUS);
return IRQ_HANDLED;
+ }
- while (xenhcd_urb_request_done(info) | xenhcd_conn_notify(info))
+ while (xenhcd_urb_request_done(info, &eoiflag) |
+ xenhcd_conn_notify(info, &eoiflag))
/* Yield point for this unbounded loop. */
cond_resched();
+ xen_irq_lateeoi(irq, eoiflag);
return IRQ_HANDLED;
}
@@ -1139,9 +1167,9 @@ static int xenhcd_setup_rings(struct xenbus_device *dev,
goto fail;
}
- err = bind_evtchn_to_irq(info->evtchn);
+ err = bind_evtchn_to_irq_lateeoi(info->evtchn);
if (err <= 0) {
- xenbus_dev_fatal(dev, err, "bind_evtchn_to_irq");
+ xenbus_dev_fatal(dev, err, "bind_evtchn_to_irq_lateeoi");
goto fail;
}
@@ -1494,6 +1522,7 @@ static struct usb_hcd *xenhcd_create_hcd(struct xenbus_device *dev)
for (i = 0; i < XENUSB_URB_RING_SIZE; i++) {
info->shadow[i].req.id = i + 1;
info->shadow[i].urb = NULL;
+ info->shadow[i].in_flight = false;
}
info->shadow[XENUSB_URB_RING_SIZE - 1].req.id = 0x0fff;