Sophie

Sophie

distrib > Scientific%20Linux > 5x > x86_64 > by-pkgid > 27922b4260f65d317aabda37e42bbbff > files > 2972

kernel-2.6.18-238.el5.src.rpm

From: Jiri Pirko <jpirko@redhat.com>
Date: Tue, 26 Aug 2008 16:12:18 +0200
Subject: [net] pppoe: check packet length on all receive paths
Message-id: 20080826161218.0baca88f@psychotron.englab.brq.redhat.com
O-Subject: [RHEL5.3 patch] BZ457013 pppoe: Check packet length on all receive paths [rhel-5.3]
Bugzilla: 457013
RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com>
RH-Acked-by: David Miller <davem@redhat.com>
RH-Acked-by: Eugene Teo <eteo@redhat.com>

BZ457013

Description:
The length field in the PPPOE header wasn't checked completely. It lacks of a
lower-bound check. Also, call skb_copy_datagram_iovec instead of memcpy_toiovec
so that paged packets (rare for PPPOE) are handled properly.

Upstream status:
http://git.kernel.org/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commitdiff;h=392fdb0e35055b96faa9c1cd6ab537805337cdce

Brew build:
https://brewweb.devel.redhat.com/taskinfo?taskID=1439219

Test status:
Booted on x86_64.

Jirka

diff --git a/drivers/net/pppoe.c b/drivers/net/pppoe.c
index e2cacb4..be1a4fd 100644
--- a/drivers/net/pppoe.c
+++ b/drivers/net/pppoe.c
@@ -335,12 +335,6 @@ static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
 	struct pppox_sock *relay_po = NULL;
 
 	if (sk->sk_state & PPPOX_BOUND) {
-		struct pppoe_hdr *ph = (struct pppoe_hdr *) skb->nh.raw;
-		int len = ntohs(ph->length);
-		skb_pull_rcsum(skb, sizeof(struct pppoe_hdr));
-		if (pskb_trim_rcsum(skb, len))
-			goto abort_kfree;
-
 		ppp_input(&po->chan, skb);
 	} else if (sk->sk_state & PPPOX_RELAY) {
 		relay_po = get_item_by_addr(&po->pppoe_relay);
@@ -351,7 +345,6 @@ static int pppoe_rcv_core(struct sock *sk, struct sk_buff *skb)
 		if ((sk_pppox(relay_po)->sk_state & PPPOX_CONNECTED) == 0)
 			goto abort_put;
 
-		skb_pull(skb, sizeof(struct pppoe_hdr));
 		if (!__pppoe_xmit(sk_pppox(relay_po), skb))
 			goto abort_put;
 	} else {
@@ -382,6 +375,7 @@ static int pppoe_rcv(struct sk_buff *skb,
 {
 	struct pppoe_hdr *ph;
 	struct pppox_sock *po;
+	int len;
 
 	if (!(skb = skb_share_check(skb, GFP_ATOMIC))) 
 		goto out;
@@ -390,10 +384,21 @@ static int pppoe_rcv(struct sk_buff *skb,
 		goto drop;
 
 	ph = (struct pppoe_hdr *) skb->nh.raw;
+	len = ntohs(ph->length);
+
+	skb_pull_rcsum(skb, sizeof(*ph));
+	if (skb->len < len)
+		goto drop;
 
 	po = get_item((unsigned long) ph->sid, eth_hdr(skb)->h_source);
-	if (po != NULL) 
-		return sk_receive_skb(sk_pppox(po), skb);
+	if (!po)
+		goto drop;
+
+	if (pskb_trim_rcsum(skb, len))
+		goto drop;
+
+	return sk_receive_skb(sk_pppox(po), skb);
+
 drop:
 	kfree_skb(skb);
 out:
@@ -917,8 +922,6 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock,
 	struct sock *sk = sock->sk;
 	struct sk_buff *skb = NULL;
 	int error = 0;
-	int len;
-	struct pppoe_hdr *ph = NULL;
 
 	if (sk->sk_state & PPPOX_BOUND) {
 		error = -EIO;
@@ -935,17 +938,12 @@ static int pppoe_recvmsg(struct kiocb *iocb, struct socket *sock,
 	m->msg_namelen = 0;
 
 	if (skb) {
-		error = 0;
-		ph = (struct pppoe_hdr *) skb->nh.raw;
-		len = ntohs(ph->length);
-
-		error = memcpy_toiovec(m->msg_iov, (unsigned char *) &ph->tag[0], len);
-		if (error < 0)
-			goto do_skb_free;
-		error = len;
+		total_len = min(total_len, skb->len);
+		error = skb_copy_datagram_iovec(skb, 0, m->msg_iov, total_len);
+		if (error == 0)
+			error = total_len;
 	}
 
-do_skb_free:
 	if (skb)
 		kfree_skb(skb);
 end: