[PPP] generic: Fix receive path data clobbering & non-linear handling
Herbert Xu [Sun, 16 Sep 2007 23:22:13 +0000 (16:22 -0700)]
This patch adds missing pskb_may_pull calls to deal with non-linear
packets that may arrive from pppoe or pppol2tp.

It also copies cloned packets before writing over them.

Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>

drivers/net/ppp_generic.c

index 7e21342..4b49d0e 100644 (file)
@@ -1525,7 +1525,7 @@ ppp_input_error(struct ppp_channel *chan, int code)
 static void
 ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
 {
-       if (skb->len >= 2) {
+       if (pskb_may_pull(skb, 2)) {
 #ifdef CONFIG_PPP_MULTILINK
                /* XXX do channel-level decompression here */
                if (PPP_PROTO(skb) == PPP_MP)
@@ -1577,7 +1577,7 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
                if (ppp->vj == 0 || (ppp->flags & SC_REJ_COMP_TCP))
                        goto err;
 
-               if (skb_tailroom(skb) < 124) {
+               if (skb_tailroom(skb) < 124 || skb_cloned(skb)) {
                        /* copy to a new sk_buff with more tailroom */
                        ns = dev_alloc_skb(skb->len + 128);
                        if (ns == 0) {
@@ -1648,23 +1648,29 @@ ppp_receive_nonmp_frame(struct ppp *ppp, struct sk_buff *skb)
                /* check if the packet passes the pass and active filters */
                /* the filter instructions are constructed assuming
                   a four-byte PPP header on each packet */
-               *skb_push(skb, 2) = 0;
-               if (ppp->pass_filter
-                   && sk_run_filter(skb, ppp->pass_filter,
-                                    ppp->pass_len) == 0) {
-                       if (ppp->debug & 1)
-                               printk(KERN_DEBUG "PPP: inbound frame not passed\n");
-                       kfree_skb(skb);
-                       return;
-               }
-               if (!(ppp->active_filter
-                     && sk_run_filter(skb, ppp->active_filter,
-                                      ppp->active_len) == 0))
-                       ppp->last_recv = jiffies;
-               skb_pull(skb, 2);
-#else
-               ppp->last_recv = jiffies;
+               if (ppp->pass_filter || ppp->active_filter) {
+                       if (skb_cloned(skb) &&
+                           pskb_expand_head(skb, 0, 0, GFP_ATOMIC))
+                               goto err;
+
+                       *skb_push(skb, 2) = 0;
+                       if (ppp->pass_filter
+                           && sk_run_filter(skb, ppp->pass_filter,
+                                            ppp->pass_len) == 0) {
+                               if (ppp->debug & 1)
+                                       printk(KERN_DEBUG "PPP: inbound frame "
+                                              "not passed\n");
+                               kfree_skb(skb);
+                               return;
+                       }
+                       if (!(ppp->active_filter
+                             && sk_run_filter(skb, ppp->active_filter,
+                                              ppp->active_len) == 0))
+                               ppp->last_recv = jiffies;
+                       __skb_pull(skb, 2);
+               } else
 #endif /* CONFIG_PPP_FILTER */
+                       ppp->last_recv = jiffies;
 
                if ((ppp->dev->flags & IFF_UP) == 0
                    || ppp->npmode[npi] != NPMODE_PASS) {
@@ -1762,7 +1768,7 @@ ppp_receive_mp_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
        struct channel *ch;
        int mphdrlen = (ppp->flags & SC_MP_SHORTSEQ)? MPHDRLEN_SSN: MPHDRLEN;
 
-       if (!pskb_may_pull(skb, mphdrlen) || ppp->mrru == 0)
+       if (!pskb_may_pull(skb, mphdrlen + 1) || ppp->mrru == 0)
                goto err;               /* no good, throw it away */
 
        /* Decode sequence number and begin/end bits */