summaryrefslogtreecommitdiffstats
path: root/main
diff options
context:
space:
mode:
authorNatanael Copa <ncopa@alpinelinux.org>2013-02-05 15:59:04 +0000
committerNatanael Copa <ncopa@alpinelinux.org>2013-02-05 16:04:20 +0000
commit03f76f442fe7e358e07f507978eea46777af4268 (patch)
treed07069ef7259bc0b21a0f848ca8c08392860bd44 /main
parent90e8b091cd1484e0a7b28ee905bd792e4b12d56c (diff)
main/linux-grsec: various xen security fixes
Diffstat (limited to 'main')
-rw-r--r--main/linux-grsec/APKBUILD12
-rw-r--r--main/linux-grsec/xsa39-pvops-0001-xen-netback-shutdown-the-ring-if-it-contains-garbage.patch253
-rw-r--r--main/linux-grsec/xsa39-pvops-0002-xen-netback-don-t-leak-pages-on-failure-in-xen_netbk.patch132
-rw-r--r--main/linux-grsec/xsa39-pvops-0003-xen-netback-free-already-allocated-memory-on-failure.patch47
-rw-r--r--main/linux-grsec/xsa39-pvops-0004-netback-correct-netbk_tx_err-to-handle-wrap-around.patch27
5 files changed, 470 insertions, 1 deletions
diff --git a/main/linux-grsec/APKBUILD b/main/linux-grsec/APKBUILD
index c0b6f84f974..cb719e4f8a7 100644
--- a/main/linux-grsec/APKBUILD
+++ b/main/linux-grsec/APKBUILD
@@ -4,7 +4,7 @@ _flavor=grsec
pkgname=linux-${_flavor}
pkgver=3.6.11
_kernver=3.6
-pkgrel=9
+pkgrel=10
pkgdesc="Linux kernel with grsecurity"
url=http://grsecurity.net
depends="mkinitfs linux-firmware"
@@ -23,6 +23,12 @@ source="http://ftp.kernel.org/pub/linux/kernel/v3.x/linux-$_kernver.tar.xz
0001-r8169-remove-the-obsolete-and-incorrect-AMD-workarou.patch
r8169-fix-vlan-tag-reordering.patch
+ xsa39-pvops-0001-xen-netback-shutdown-the-ring-if-it-contains-garbage.patch
+ xsa39-pvops-0002-xen-netback-don-t-leak-pages-on-failure-in-xen_netbk.patch
+ xsa39-pvops-0003-xen-netback-free-already-allocated-memory-on-failure.patch
+ xsa39-pvops-0004-netback-correct-netbk_tx_err-to-handle-wrap-around.patch
+
+
kernelconfig.x86
kernelconfig.x86_64
"
@@ -152,5 +158,9 @@ daf2cbb558588c49c138fe9ca2482b64 r8169-num-rx-desc.patch
d9b4a528e722d10ba53034ebd440c31b ipv4-remove-output-route-check-in-ipv4_mtu.patch
63468b44e34fa19237e0a2a1f6737b14 0001-r8169-remove-the-obsolete-and-incorrect-AMD-workarou.patch
44a37e1289e1056300574848aea8bd31 r8169-fix-vlan-tag-reordering.patch
+706652ed6c17c5f7bb46a6c8318f9e75 xsa39-pvops-0001-xen-netback-shutdown-the-ring-if-it-contains-garbage.patch
+286101482a2e4b7d8c0dff16af36b3e9 xsa39-pvops-0002-xen-netback-don-t-leak-pages-on-failure-in-xen_netbk.patch
+89dbb0886c9d17c3c4a5ff4f1443e936 xsa39-pvops-0003-xen-netback-free-already-allocated-memory-on-failure.patch
+bce9f08c86570a0a86ef36f1d2e7a2dd xsa39-pvops-0004-netback-correct-netbk_tx_err-to-handle-wrap-around.patch
373db5888708938c6b1baed6da781fcb kernelconfig.x86
190788fb10e79abce9d570d5e87ec3b4 kernelconfig.x86_64"
diff --git a/main/linux-grsec/xsa39-pvops-0001-xen-netback-shutdown-the-ring-if-it-contains-garbage.patch b/main/linux-grsec/xsa39-pvops-0001-xen-netback-shutdown-the-ring-if-it-contains-garbage.patch
new file mode 100644
index 00000000000..3f983028f26
--- /dev/null
+++ b/main/linux-grsec/xsa39-pvops-0001-xen-netback-shutdown-the-ring-if-it-contains-garbage.patch
@@ -0,0 +1,253 @@
+From 7dd7ce44593a8c4c715fa665027af8e07245c8cf Mon Sep 17 00:00:00 2001
+From: Ian Campbell <ian.campbell@citrix.com>
+Date: Fri, 11 Jan 2013 14:26:29 +0000
+Subject: [PATCH 1/4] xen/netback: shutdown the ring if it contains garbage.
+
+A buggy or malicious frontend should not be able to confuse netback.
+If we spot anything which is not as it should be then shutdown the
+device and don't try to continue with the ring in a potentially
+hostile state. Well behaved and non-hostile frontends will not be
+penalised.
+
+As well as making the existing checks for such errors fatal also add a
+new check that ensures that there isn't an insane number of requests
+on the ring (i.e. more than would fit in the ring). If the ring
+contains garbage then previously is was possible to loop over this
+insane number, getting an error each time and therefore not generating
+any more pending requests and therefore not exiting the loop in
+xen_netbk_tx_build_gops for an externded period.
+
+Also turn various netdev_dbg calls which no precipitate a fatal error
+into netdev_err, they are rate limited because the device is shutdown
+afterwards.
+
+This fixes at least one known DoS/softlockup of the backend domain.
+
+Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
+Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+Acked-by: Jan Beulich <JBeulich@suse.com>
+---
+ drivers/net/xen-netback/common.h | 3 ++
+ drivers/net/xen-netback/interface.c | 23 ++++++++-----
+ drivers/net/xen-netback/netback.c | 63 +++++++++++++++++++++++++---------
+ 3 files changed, 63 insertions(+), 26 deletions(-)
+
+diff --git a/drivers/net/xen-netback/common.h b/drivers/net/xen-netback/common.h
+index 94b79c3..9d7f172 100644
+--- a/drivers/net/xen-netback/common.h
++++ b/drivers/net/xen-netback/common.h
+@@ -151,6 +151,9 @@ void xen_netbk_queue_tx_skb(struct xenvif *vif, struct sk_buff *skb);
+ /* Notify xenvif that ring now has space to send an skb to the frontend */
+ void xenvif_notify_tx_completion(struct xenvif *vif);
+
++/* Prevent the device from generating any further traffic. */
++void xenvif_carrier_off(struct xenvif *vif);
++
+ /* Returns number of ring slots required to send an skb to the frontend */
+ unsigned int xen_netbk_count_skb_slots(struct xenvif *vif, struct sk_buff *skb);
+
+diff --git a/drivers/net/xen-netback/interface.c b/drivers/net/xen-netback/interface.c
+index b7d41f8..b8c5193 100644
+--- a/drivers/net/xen-netback/interface.c
++++ b/drivers/net/xen-netback/interface.c
+@@ -343,17 +343,22 @@ err:
+ return err;
+ }
+
+-void xenvif_disconnect(struct xenvif *vif)
++void xenvif_carrier_off(struct xenvif *vif)
+ {
+ struct net_device *dev = vif->dev;
+- if (netif_carrier_ok(dev)) {
+- rtnl_lock();
+- netif_carrier_off(dev); /* discard queued packets */
+- if (netif_running(dev))
+- xenvif_down(vif);
+- rtnl_unlock();
+- xenvif_put(vif);
+- }
++
++ rtnl_lock();
++ netif_carrier_off(dev); /* discard queued packets */
++ if (netif_running(dev))
++ xenvif_down(vif);
++ rtnl_unlock();
++ xenvif_put(vif);
++}
++
++void xenvif_disconnect(struct xenvif *vif)
++{
++ if (netif_carrier_ok(vif->dev))
++ xenvif_carrier_off(vif);
+
+ atomic_dec(&vif->refcnt);
+ wait_event(vif->waiting_to_free, atomic_read(&vif->refcnt) == 0);
+diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
+index f2d6b78..1a449f9 100644
+--- a/drivers/net/xen-netback/netback.c
++++ b/drivers/net/xen-netback/netback.c
+@@ -888,6 +888,13 @@ static void netbk_tx_err(struct xenvif *vif,
+ xenvif_put(vif);
+ }
+
++static void netbk_fatal_tx_err(struct xenvif *vif)
++{
++ netdev_err(vif->dev, "fatal error; disabling device\n");
++ xenvif_carrier_off(vif);
++ xenvif_put(vif);
++}
++
+ static int netbk_count_requests(struct xenvif *vif,
+ struct xen_netif_tx_request *first,
+ struct xen_netif_tx_request *txp,
+@@ -901,19 +908,22 @@ static int netbk_count_requests(struct xenvif *vif,
+
+ do {
+ if (frags >= work_to_do) {
+- netdev_dbg(vif->dev, "Need more frags\n");
++ netdev_err(vif->dev, "Need more frags\n");
++ netbk_fatal_tx_err(vif);
+ return -frags;
+ }
+
+ if (unlikely(frags >= MAX_SKB_FRAGS)) {
+- netdev_dbg(vif->dev, "Too many frags\n");
++ netdev_err(vif->dev, "Too many frags\n");
++ netbk_fatal_tx_err(vif);
+ return -frags;
+ }
+
+ memcpy(txp, RING_GET_REQUEST(&vif->tx, cons + frags),
+ sizeof(*txp));
+ if (txp->size > first->size) {
+- netdev_dbg(vif->dev, "Frags galore\n");
++ netdev_err(vif->dev, "Frag is bigger than frame.\n");
++ netbk_fatal_tx_err(vif);
+ return -frags;
+ }
+
+@@ -921,8 +931,9 @@ static int netbk_count_requests(struct xenvif *vif,
+ frags++;
+
+ if (unlikely((txp->offset + txp->size) > PAGE_SIZE)) {
+- netdev_dbg(vif->dev, "txp->offset: %x, size: %u\n",
++ netdev_err(vif->dev, "txp->offset: %x, size: %u\n",
+ txp->offset, txp->size);
++ netbk_fatal_tx_err(vif);
+ return -frags;
+ }
+ } while ((txp++)->flags & XEN_NETTXF_more_data);
+@@ -1095,7 +1106,8 @@ static int xen_netbk_get_extras(struct xenvif *vif,
+
+ do {
+ if (unlikely(work_to_do-- <= 0)) {
+- netdev_dbg(vif->dev, "Missing extra info\n");
++ netdev_err(vif->dev, "Missing extra info\n");
++ netbk_fatal_tx_err(vif);
+ return -EBADR;
+ }
+
+@@ -1104,8 +1116,9 @@ static int xen_netbk_get_extras(struct xenvif *vif,
+ if (unlikely(!extra.type ||
+ extra.type >= XEN_NETIF_EXTRA_TYPE_MAX)) {
+ vif->tx.req_cons = ++cons;
+- netdev_dbg(vif->dev,
++ netdev_err(vif->dev,
+ "Invalid extra type: %d\n", extra.type);
++ netbk_fatal_tx_err(vif);
+ return -EINVAL;
+ }
+
+@@ -1121,13 +1134,15 @@ static int netbk_set_skb_gso(struct xenvif *vif,
+ struct xen_netif_extra_info *gso)
+ {
+ if (!gso->u.gso.size) {
+- netdev_dbg(vif->dev, "GSO size must not be zero.\n");
++ netdev_err(vif->dev, "GSO size must not be zero.\n");
++ netbk_fatal_tx_err(vif);
+ return -EINVAL;
+ }
+
+ /* Currently only TCPv4 S.O. is supported. */
+ if (gso->u.gso.type != XEN_NETIF_GSO_TYPE_TCPV4) {
+- netdev_dbg(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type);
++ netdev_err(vif->dev, "Bad GSO type %d.\n", gso->u.gso.type);
++ netbk_fatal_tx_err(vif);
+ return -EINVAL;
+ }
+
+@@ -1264,9 +1279,26 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
+
+ /* Get a netif from the list with work to do. */
+ vif = poll_net_schedule_list(netbk);
++ /*
++ * This can sometimes happen because the test of
++ * list_empty(net_schedule_list) at the top of the
++ * loop is unlocked. Just go back and have another
++ * look.
++ */
+ if (!vif)
+ continue;
+
++ if (vif->tx.sring->req_prod - vif->tx.req_cons >
++ XEN_NETIF_TX_RING_SIZE) {
++ netdev_err(vif->dev,
++ "Impossible number of requests. "
++ "req_prod %d, req_cons %d, size %ld\n",
++ vif->tx.sring->req_prod, vif->tx.req_cons,
++ XEN_NETIF_TX_RING_SIZE);
++ netbk_fatal_tx_err(vif);
++ continue;
++ }
++
+ RING_FINAL_CHECK_FOR_REQUESTS(&vif->tx, work_to_do);
+ if (!work_to_do) {
+ xenvif_put(vif);
+@@ -1294,17 +1326,14 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
+ work_to_do = xen_netbk_get_extras(vif, extras,
+ work_to_do);
+ idx = vif->tx.req_cons;
+- if (unlikely(work_to_do < 0)) {
+- netbk_tx_err(vif, &txreq, idx);
++ if (unlikely(work_to_do < 0))
+ continue;
+- }
+ }
+
+ ret = netbk_count_requests(vif, &txreq, txfrags, work_to_do);
+- if (unlikely(ret < 0)) {
+- netbk_tx_err(vif, &txreq, idx - ret);
++ if (unlikely(ret < 0))
+ continue;
+- }
++
+ idx += ret;
+
+ if (unlikely(txreq.size < ETH_HLEN)) {
+@@ -1316,11 +1345,11 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
+
+ /* No crossing a page as the payload mustn't fragment. */
+ if (unlikely((txreq.offset + txreq.size) > PAGE_SIZE)) {
+- netdev_dbg(vif->dev,
++ netdev_err(vif->dev,
+ "txreq.offset: %x, size: %u, end: %lu\n",
+ txreq.offset, txreq.size,
+ (txreq.offset&~PAGE_MASK) + txreq.size);
+- netbk_tx_err(vif, &txreq, idx);
++ netbk_fatal_tx_err(vif);
+ continue;
+ }
+
+@@ -1348,8 +1377,8 @@ static unsigned xen_netbk_tx_build_gops(struct xen_netbk *netbk)
+ gso = &extras[XEN_NETIF_EXTRA_TYPE_GSO - 1];
+
+ if (netbk_set_skb_gso(vif, skb, gso)) {
++ /* Failure in netbk_set_skb_gso is fatal. */
+ kfree_skb(skb);
+- netbk_tx_err(vif, &txreq, idx);
+ continue;
+ }
+ }
+--
+1.7.2.5
+
diff --git a/main/linux-grsec/xsa39-pvops-0002-xen-netback-don-t-leak-pages-on-failure-in-xen_netbk.patch b/main/linux-grsec/xsa39-pvops-0002-xen-netback-don-t-leak-pages-on-failure-in-xen_netbk.patch
new file mode 100644
index 00000000000..686f38bb7ab
--- /dev/null
+++ b/main/linux-grsec/xsa39-pvops-0002-xen-netback-don-t-leak-pages-on-failure-in-xen_netbk.patch
@@ -0,0 +1,132 @@
+From 90420631d2b78aca28c94beb66b25447e57a8dd4 Mon Sep 17 00:00:00 2001
+From: Ian Campbell <ian.campbell@citrix.com>
+Date: Mon, 14 Jan 2013 12:20:04 +0000
+Subject: [PATCH 2/4] xen/netback: don't leak pages on failure in xen_netbk_tx_check_gop.
+
+Signed-off-by: Matthew Daley <mattjd@gmail.com>
+Reviewed-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
+Acked-by: Ian Campbell <ian.campbell@citrix.com>
+Acked-by: Jan Beulich <JBeulich@suse.com>
+---
+ drivers/net/xen-netback/netback.c | 38 ++++++++++++------------------------
+ 1 files changed, 13 insertions(+), 25 deletions(-)
+
+diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
+index 1a449f9..975241e 100644
+--- a/drivers/net/xen-netback/netback.c
++++ b/drivers/net/xen-netback/netback.c
+@@ -147,7 +147,8 @@ void xen_netbk_remove_xenvif(struct xenvif *vif)
+ atomic_dec(&netbk->netfront_count);
+ }
+
+-static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx);
++static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx,
++ u8 status);
+ static void make_tx_response(struct xenvif *vif,
+ struct xen_netif_tx_request *txp,
+ s8 st);
+@@ -1007,30 +1008,20 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
+ {
+ struct gnttab_copy *gop = *gopp;
+ u16 pending_idx = *((u16 *)skb->data);
+- struct pending_tx_info *pending_tx_info = netbk->pending_tx_info;
+- struct xenvif *vif = pending_tx_info[pending_idx].vif;
+- struct xen_netif_tx_request *txp;
+ struct skb_shared_info *shinfo = skb_shinfo(skb);
+ int nr_frags = shinfo->nr_frags;
+ int i, err, start;
+
+ /* Check status of header. */
+ err = gop->status;
+- if (unlikely(err)) {
+- pending_ring_idx_t index;
+- index = pending_index(netbk->pending_prod++);
+- txp = &pending_tx_info[pending_idx].req;
+- make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR);
+- netbk->pending_ring[index] = pending_idx;
+- xenvif_put(vif);
+- }
++ if (unlikely(err))
++ xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR);
+
+ /* Skip first skb fragment if it is on same page as header fragment. */
+ start = (frag_get_pending_idx(&shinfo->frags[0]) == pending_idx);
+
+ for (i = start; i < nr_frags; i++) {
+ int j, newerr;
+- pending_ring_idx_t index;
+
+ pending_idx = frag_get_pending_idx(&shinfo->frags[i]);
+
+@@ -1039,16 +1030,12 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
+ if (likely(!newerr)) {
+ /* Had a previous error? Invalidate this fragment. */
+ if (unlikely(err))
+- xen_netbk_idx_release(netbk, pending_idx);
++ xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ continue;
+ }
+
+ /* Error on this fragment: respond to client with an error. */
+- txp = &netbk->pending_tx_info[pending_idx].req;
+- make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR);
+- index = pending_index(netbk->pending_prod++);
+- netbk->pending_ring[index] = pending_idx;
+- xenvif_put(vif);
++ xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR);
+
+ /* Not the first error? Preceding frags already invalidated. */
+ if (err)
+@@ -1056,10 +1043,10 @@ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
+
+ /* First error: invalidate header and preceding fragments. */
+ pending_idx = *((u16 *)skb->data);
+- xen_netbk_idx_release(netbk, pending_idx);
++ xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ for (j = start; j < i; j++) {
+ pending_idx = frag_get_pending_idx(&shinfo->frags[j]);
+- xen_netbk_idx_release(netbk, pending_idx);
++ xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ }
+
+ /* Remember the error: invalidate all subsequent fragments. */
+@@ -1093,7 +1080,7 @@ static void xen_netbk_fill_frags(struct xen_netbk *netbk, struct sk_buff *skb)
+
+ /* Take an extra reference to offset xen_netbk_idx_release */
+ get_page(netbk->mmap_pages[pending_idx]);
+- xen_netbk_idx_release(netbk, pending_idx);
++ xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ }
+ }
+
+@@ -1477,7 +1464,7 @@ static void xen_netbk_tx_submit(struct xen_netbk *netbk)
+ txp->size -= data_len;
+ } else {
+ /* Schedule a response immediately. */
+- xen_netbk_idx_release(netbk, pending_idx);
++ xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_OKAY);
+ }
+
+ if (txp->flags & XEN_NETTXF_csum_blank)
+@@ -1529,7 +1516,8 @@ static void xen_netbk_tx_action(struct xen_netbk *netbk)
+ xen_netbk_tx_submit(netbk);
+ }
+
+-static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx)
++static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx,
++ u8 status)
+ {
+ struct xenvif *vif;
+ struct pending_tx_info *pending_tx_info;
+@@ -1543,7 +1531,7 @@ static void xen_netbk_idx_release(struct xen_netbk *netbk, u16 pending_idx)
+
+ vif = pending_tx_info->vif;
+
+- make_tx_response(vif, &pending_tx_info->req, XEN_NETIF_RSP_OKAY);
++ make_tx_response(vif, &pending_tx_info->req, status);
+
+ index = pending_index(netbk->pending_prod++);
+ netbk->pending_ring[index] = pending_idx;
+--
+1.7.2.5
+
diff --git a/main/linux-grsec/xsa39-pvops-0003-xen-netback-free-already-allocated-memory-on-failure.patch b/main/linux-grsec/xsa39-pvops-0003-xen-netback-free-already-allocated-memory-on-failure.patch
new file mode 100644
index 00000000000..1c718019584
--- /dev/null
+++ b/main/linux-grsec/xsa39-pvops-0003-xen-netback-free-already-allocated-memory-on-failure.patch
@@ -0,0 +1,47 @@
+From b6b1f17aa44acfe1024968bafb1d1fe7704a749a Mon Sep 17 00:00:00 2001
+From: Ian Campbell <ian.campbell@citrix.com>
+Date: Mon, 14 Jan 2013 12:51:22 +0000
+Subject: [PATCH 3/4] xen/netback: free already allocated memory on failure in xen_netbk_get_requests
+
+Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
+---
+ drivers/net/xen-netback/netback.c | 16 +++++++++++++++-
+ 1 files changed, 15 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
+index 975241e..1a99288 100644
+--- a/drivers/net/xen-netback/netback.c
++++ b/drivers/net/xen-netback/netback.c
+@@ -978,7 +978,7 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
+ pending_idx = netbk->pending_ring[index];
+ page = xen_netbk_alloc_page(netbk, skb, pending_idx);
+ if (!page)
+- return NULL;
++ goto err;
+
+ gop->source.u.ref = txp->gref;
+ gop->source.domid = vif->domid;
+@@ -1000,6 +1000,20 @@ static struct gnttab_copy *xen_netbk_get_requests(struct xen_netbk *netbk,
+ }
+
+ return gop;
++err:
++ /*
++ * Unwind, freeing all pages and sending error
++ * reponses.
++ */
++ while (i-- > start) {
++ xen_netbk_idx_release(netbk, frag_get_pending_idx(&frags[i]),
++ XEN_NETIF_RSP_ERROR);
++ }
++ /* The head too, if necessary. */
++ if (start)
++ xen_netbk_idx_release(netbk, pending_idx, XEN_NETIF_RSP_ERROR);
++
++ return NULL;
+ }
+
+ static int xen_netbk_tx_check_gop(struct xen_netbk *netbk,
+--
+1.7.2.5
+
diff --git a/main/linux-grsec/xsa39-pvops-0004-netback-correct-netbk_tx_err-to-handle-wrap-around.patch b/main/linux-grsec/xsa39-pvops-0004-netback-correct-netbk_tx_err-to-handle-wrap-around.patch
new file mode 100644
index 00000000000..c76a2c40eb5
--- /dev/null
+++ b/main/linux-grsec/xsa39-pvops-0004-netback-correct-netbk_tx_err-to-handle-wrap-around.patch
@@ -0,0 +1,27 @@
+From ea5e3c1e8fd9ffe6080e01af7769a9fa420cc62e Mon Sep 17 00:00:00 2001
+From: Ian Campbell <ian.campbell@citrix.com>
+Date: Mon, 14 Jan 2013 13:32:31 +0000
+Subject: [PATCH 4/4] netback: correct netbk_tx_err to handle wrap around.
+
+Signed-off-by: Ian Campbell <ian.campbell@citrix.com>
+Acked-by: Jan Beulich <JBeulich@suse.com>
+---
+ drivers/net/xen-netback/netback.c | 2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c
+index 1a99288..28d5e06 100644
+--- a/drivers/net/xen-netback/netback.c
++++ b/drivers/net/xen-netback/netback.c
+@@ -880,7 +880,7 @@ static void netbk_tx_err(struct xenvif *vif,
+
+ do {
+ make_tx_response(vif, txp, XEN_NETIF_RSP_ERROR);
+- if (cons >= end)
++ if (cons == end)
+ break;
+ txp = RING_GET_REQUEST(&vif->tx, cons++);
+ } while (1);
+--
+1.7.2.5
+