mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2025-01-26 15:54:18 +08:00
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>
This commit is contained in:
parent
7f220d4a38
commit
aff477cb8f
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user