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: