$NetBSD: patch-CVE-2015-8550,v 1.1 2016/01/07 17:55:55 bouyer Exp $

patch for CVE-2015-8550 aka XSA-155 from
http://xenbits.xenproject.org/xsa/xsa155-xen-0001-xen-Add-RING_COPY_REQUEST.patch
http://xenbits.xenproject.org/xsa/xsa155-xen-0002-blktap2-Use-RING_COPY_REQUEST.patch
http://xenbits.xenproject.org/xsa/xsa155-qemut-qdisk-double-access.patch
http://xenbits.xenproject.org/xsa/xsa155-qemut-xenfb.patch

--- ../xen/include/public/io/ring.h.orig
+++ ../xen/include/public/io/ring.h
@@ -212,6 +212,20 @@ typedef struct __name##_back_ring __name##_back_ring_t
 #define RING_GET_REQUEST(_r, _idx)                                      \
     (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].req))
 
+/*
+ * Get a local copy of a request.
+ *
+ * Use this in preference to RING_GET_REQUEST() so all processing is
+ * done on a local copy that cannot be modified by the other end.
+ *
+ * Note that https://gcc.gnu.org/bugzilla/show_bug.cgi?id=58145 may cause this
+ * to be ineffective where _req is a struct which consists of only bitfields.
+ */
+#define RING_COPY_REQUEST(_r, _idx, _req) do {				\
+	/* Use volatile to force the copy into _req. */			\
+	*(_req) = *(volatile typeof(_req))RING_GET_REQUEST(_r, _idx);	\
+} while (0)
+
 #define RING_GET_RESPONSE(_r, _idx)                                     \
     (&((_r)->sring->ring[((_idx) & (RING_SIZE(_r) - 1))].rsp))

--- blktap2/drivers/block-log.c.orig
+++ blktap2/drivers/block-log.c
@@ -494,11 +494,12 @@ static int ctl_kick(struct tdlog_state* s, int fd)
   reqstart = s->bring.req_cons;
   reqend = s->sring->req_prod;
 
+  xen_mb();
   BDPRINTF("ctl: ring kicked (start = %u, end = %u)", reqstart, reqend);
 
   while (reqstart != reqend) {
     /* XXX actually submit these! */
-    memcpy(&req, RING_GET_REQUEST(&s->bring, reqstart), sizeof(req));
+    RING_COPY_REQUEST(&s->bring, reqstart, &req);
     BDPRINTF("ctl: read request %"PRIu64":%u", req.sector, req.count);
     s->bring.req_cons = ++reqstart;
 
--- blktap2/drivers/tapdisk-vbd.c.orig
+++ blktap2/drivers/tapdisk-vbd.c
@@ -1555,7 +1555,7 @@ tapdisk_vbd_pull_ring_requests(td_vbd_t *vbd)
 	int idx;
 	RING_IDX rp, rc;
 	td_ring_t *ring;
-	blkif_request_t *req;
+	blkif_request_t req;
 	td_vbd_request_t *vreq;
 
 	ring = &vbd->ring;
@@ -1566,16 +1566,16 @@ tapdisk_vbd_pull_ring_requests(td_vbd_t *vbd)
 	xen_rmb();
 
 	for (rc = ring->fe_ring.req_cons; rc != rp; rc++) {
-		req = RING_GET_REQUEST(&ring->fe_ring, rc);
+		RING_COPY_REQUEST(&ring->fe_ring, rc, &req);
 		++ring->fe_ring.req_cons;
 
-		idx  = req->id;
+		idx  = req.id;
 		vreq = &vbd->request_list[idx];
 
 		ASSERT(list_empty(&vreq->next));
 		ASSERT(vreq->secs_pending == 0);
 
-		memcpy(&vreq->req, req, sizeof(blkif_request_t));
+		memcpy(&vreq->req, &req, sizeof(blkif_request_t));
 		vbd->received++;
 		vreq->vbd = vbd;

--- ioemu-qemu-xen/hw/xen_blkif.h.orig
+++ ioemu-qemu-xen/hw/xen_blkif.h
@@ -79,8 +79,10 @@ static inline void blkif_get_x86_32_req(blkif_request_t *dst, blkif_x86_32_reque
 	dst->handle = src->handle;
 	dst->id = src->id;
 	dst->sector_number = src->sector_number;
-	if (n > src->nr_segments)
-		n = src->nr_segments;
+	/* prevent the compiler from optimizing the code and using src->nr_segments instead */
+	xen_mb();
+	if (n > dst->nr_segments)
+		n = dst->nr_segments;
 	for (i = 0; i < n; i++)
 		dst->seg[i] = src->seg[i];
 }
@@ -94,8 +96,10 @@ static inline void blkif_get_x86_64_req(blkif_request_t *dst, blkif_x86_64_reque
 	dst->handle = src->handle;
 	dst->id = src->id;
 	dst->sector_number = src->sector_number;
-	if (n > src->nr_segments)
-		n = src->nr_segments;
+	/* prevent the compiler from optimizing the code and using src->nr_segments instead */
+	xen_mb();
+	if (n > dst->nr_segments)
+		n = dst->nr_segments;
 	for (i = 0; i < n; i++)
 		dst->seg[i] = src->seg[i];
 }

--- ioemu-qemu-xen/hw/xenfb.c
+++ ioemu-qemu-xen/hw/xenfb.c
@@ -827,18 +827,20 @@ static void xenfb_invalidate(void *opaque)
 
 static void xenfb_handle_events(struct XenFB *xenfb)
 {
-    uint32_t prod, cons;
+    uint32_t prod, cons, out_cons;
     struct xenfb_page *page = xenfb->c.page;
 
     prod = page->out_prod;
-    if (prod == page->out_cons)
+    out_cons = page->out_cons;
+    if (prod == out_cons)
 	return;
     xen_rmb();		/* ensure we see ring contents up to prod */
-    for (cons = page->out_cons; cons != prod; cons++) {
+    for (cons = out_cons; cons != prod; cons++) {
 	union xenfb_out_event *event = &XENFB_OUT_RING_REF(page, cons);
+        uint8_t type = event->type;
 	int x, y, w, h;
 
-	switch (event->type) {
+	switch (type) {
 	case XENFB_TYPE_UPDATE:
 	    if (xenfb->up_count == UP_QUEUE)
 		xenfb->up_fullscreen = 1;
