Commit aff477cb authored by Juergen Gross's avatar Juergen Gross Committed by Greg Kroah-Hartman
Browse files

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: default avatarJuergen Gross <jgross@suse.com>
Link: https://lore.kernel.org/r/20220311103509.12908-1-jgross@suse.com


Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 7f220d4a
Loading
Loading
Loading
Loading
+43 −14
Original line number Diff line number Diff line
@@ -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;