diff mbox

[3.13.y] nfqueue: Orphan frags in nfqnl_zcopy and handle errors

Message ID 20140328142914.GB25962@hansolo.jdub.homelinux.org
State Not Applicable, archived
Delegated to: David Miller
Headers show

Commit Message

Josh Boyer March 28, 2014, 2:29 p.m. UTC
Backport of upstream commit 36d5fe6a0007 to 3.13.y

nfqnl_zcopy can copy elements of the frags array between skbs, but it doesn't
orphan them.  Also, it doesn't handle errors, so this patch takes care of that
as well, and modify the caller accordingly.  skb_tx_error() is also added to
the callers so they will signal the failed delivery towards the creator of the
skb.

Fixes CVE-2014-2568.

Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Josh Boyer <jwboyer@fedoraproject.org>
---
 net/netfilter/nfnetlink_queue_core.c | 31 ++++++++++++++++++++++---------
 1 file changed, 22 insertions(+), 9 deletions(-)

Comments

Ben Hutchings March 30, 2014, 11:56 p.m. UTC | #1
On Fri, 2014-03-28 at 10:29 -0400, Josh Boyer wrote:
> Backport of upstream commit 36d5fe6a0007 to 3.13.y
> 
> nfqnl_zcopy can copy elements of the frags array between skbs, but it doesn't
> orphan them.  Also, it doesn't handle errors, so this patch takes care of that
> as well, and modify the caller accordingly.  skb_tx_error() is also added to
> the callers so they will signal the failed delivery towards the creator of the
> skb.
> 
> Fixes CVE-2014-2568.
> 
> Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
> Signed-off-by: David S. Miller <davem@davemloft.net>
> Signed-off-by: Josh Boyer <jwboyer@fedoraproject.org>
[...]

FWIW, I applied the same change to Debian's 3.13.7-1, except for leaving
'from' as pointer-to-const.

Ben.
Josh Boyer April 1, 2014, 1:40 p.m. UTC | #2
On Sun, Mar 30, 2014 at 7:56 PM, Ben Hutchings <ben@decadent.org.uk> wrote:
> On Fri, 2014-03-28 at 10:29 -0400, Josh Boyer wrote:
>> Backport of upstream commit 36d5fe6a0007 to 3.13.y
>>
>> nfqnl_zcopy can copy elements of the frags array between skbs, but it doesn't
>> orphan them.  Also, it doesn't handle errors, so this patch takes care of that
>> as well, and modify the caller accordingly.  skb_tx_error() is also added to
>> the callers so they will signal the failed delivery towards the creator of the
>> skb.
>>
>> Fixes CVE-2014-2568.
>>
>> Signed-off-by: Zoltan Kiss <zoltan.kiss@citrix.com>
>> Signed-off-by: David S. Miller <davem@davemloft.net>
>> Signed-off-by: Josh Boyer <jwboyer@fedoraproject.org>
> [...]
>
> FWIW, I applied the same change to Debian's 3.13.7-1, except for leaving
> 'from' as pointer-to-const.

Thanks for the confirmation, Ben.  I noticed Dave already has the
original patch in his stable bundle on patchwork, so perhaps he's
going to do his own backport.  (Though he has v4 in the bundle and v5
is what went in-tree.)  I figured I would just send mine along in case
it was helpful.

josh
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/net/netfilter/nfnetlink_queue_core.c b/net/netfilter/nfnetlink_queue_core.c
index 21258cf..b241654 100644
--- a/net/netfilter/nfnetlink_queue_core.c
+++ b/net/netfilter/nfnetlink_queue_core.c
@@ -235,22 +235,23 @@  nfqnl_flush(struct nfqnl_instance *queue, nfqnl_cmpfn cmpfn, unsigned long data)
 	spin_unlock_bh(&queue->lock);
 }
 
-static void
-nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
+static int
+nfqnl_zcopy(struct sk_buff *to, struct sk_buff *from, int len, int hlen)
 {
 	int i, j = 0;
 	int plen = 0; /* length of skb->head fragment */
+	int ret;
 	struct page *page;
 	unsigned int offset;
 
 	/* dont bother with small payloads */
-	if (len <= skb_tailroom(to)) {
-		skb_copy_bits(from, 0, skb_put(to, len), len);
-		return;
-	}
+	if (len <= skb_tailroom(to))
+		return skb_copy_bits(from, 0, skb_put(to, len), len);
 
 	if (hlen) {
-		skb_copy_bits(from, 0, skb_put(to, hlen), hlen);
+		ret = skb_copy_bits(from, 0, skb_put(to, hlen), hlen);
+		if (unlikely(ret))
+			return ret;
 		len -= hlen;
 	} else {
 		plen = min_t(int, skb_headlen(from), len);
@@ -268,6 +269,11 @@  nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
 	to->len += len + plen;
 	to->data_len += len + plen;
 
+	if (unlikely(skb_orphan_frags(from, GFP_ATOMIC))) {
+		skb_tx_error(from);
+		return -ENOMEM;
+	}
+
 	for (i = 0; i < skb_shinfo(from)->nr_frags; i++) {
 		if (!len)
 			break;
@@ -278,6 +284,8 @@  nfqnl_zcopy(struct sk_buff *to, const struct sk_buff *from, int len, int hlen)
 		j++;
 	}
 	skb_shinfo(to)->nr_frags = j;
+
+	return 0;
 }
 
 static int
@@ -374,13 +382,16 @@  nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
 
 	skb = nfnetlink_alloc_skb(net, size, queue->peer_portid,
 				  GFP_ATOMIC);
-	if (!skb)
+	if (!skb) {
+		skb_tx_error(entskb);
 		return NULL;
+	}
 
 	nlh = nlmsg_put(skb, 0, 0,
 			NFNL_SUBSYS_QUEUE << 8 | NFQNL_MSG_PACKET,
 			sizeof(struct nfgenmsg), 0);
 	if (!nlh) {
+		skb_tx_error(entskb);
 		kfree_skb(skb);
 		return NULL;
 	}
@@ -504,13 +515,15 @@  nfqnl_build_packet_message(struct net *net, struct nfqnl_instance *queue,
 		nla->nla_type = NFQA_PAYLOAD;
 		nla->nla_len = nla_attr_size(data_len);
 
-		nfqnl_zcopy(skb, entskb, data_len, hlen);
+		if (nfqnl_zcopy(skb, entskb, data_len, hlen))
+			goto nla_put_failure;
 	}
 
 	nlh->nlmsg_len = skb->len;
 	return skb;
 
 nla_put_failure:
+	skb_tx_error(entskb);
 	kfree_skb(skb);
 	net_err_ratelimited("nf_queue: error creating packet message\n");
 	return NULL;