Commit ea8420e9 authored by Simon Arlott's avatar Simon Arlott Committed by David S. Miller

ppp_generic: pull 2 bytes so that PPP_PROTO(skb) is valid

In ppp_input(), PPP_PROTO(skb) may refer to invalid data in the skb.

If this happens and (proto >= 0xc000 || proto == PPP_CCPFRAG) then
the packet is passed directly to pppd.

This occurs frequently when using PPPoE with an interface MTU
greater than 1500 because the skb is more likely to be non-linear.

The next 2 bytes need to be pulled in ppp_input(). The pull of 2
bytes in ppp_receive_frame() has been removed as it is no longer
required.
Signed-off-by: default avatarSimon Arlott <simon@fire.lp0.eu>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 1183f383
...@@ -1567,13 +1567,22 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) ...@@ -1567,13 +1567,22 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
struct channel *pch = chan->ppp; struct channel *pch = chan->ppp;
int proto; int proto;
if (!pch || skb->len == 0) { if (!pch) {
kfree_skb(skb); kfree_skb(skb);
return; return;
} }
proto = PPP_PROTO(skb);
read_lock_bh(&pch->upl); read_lock_bh(&pch->upl);
if (!pskb_may_pull(skb, 2)) {
kfree_skb(skb);
if (pch->ppp) {
++pch->ppp->dev->stats.rx_length_errors;
ppp_receive_error(pch->ppp);
}
goto done;
}
proto = PPP_PROTO(skb);
if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) { if (!pch->ppp || proto >= 0xc000 || proto == PPP_CCPFRAG) {
/* put it on the channel queue */ /* put it on the channel queue */
skb_queue_tail(&pch->file.rq, skb); skb_queue_tail(&pch->file.rq, skb);
...@@ -1585,6 +1594,8 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb) ...@@ -1585,6 +1594,8 @@ ppp_input(struct ppp_channel *chan, struct sk_buff *skb)
} else { } else {
ppp_do_recv(pch->ppp, skb, pch); ppp_do_recv(pch->ppp, skb, pch);
} }
done:
read_unlock_bh(&pch->upl); read_unlock_bh(&pch->upl);
} }
...@@ -1617,7 +1628,8 @@ ppp_input_error(struct ppp_channel *chan, int code) ...@@ -1617,7 +1628,8 @@ ppp_input_error(struct ppp_channel *chan, int code)
static void static void
ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
{ {
if (pskb_may_pull(skb, 2)) { /* note: a 0-length skb is used as an error indication */
if (skb->len > 0) {
#ifdef CONFIG_PPP_MULTILINK #ifdef CONFIG_PPP_MULTILINK
/* XXX do channel-level decompression here */ /* XXX do channel-level decompression here */
if (PPP_PROTO(skb) == PPP_MP) if (PPP_PROTO(skb) == PPP_MP)
...@@ -1625,15 +1637,10 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch) ...@@ -1625,15 +1637,10 @@ ppp_receive_frame(struct ppp *ppp, struct sk_buff *skb, struct channel *pch)
else else
#endif /* CONFIG_PPP_MULTILINK */ #endif /* CONFIG_PPP_MULTILINK */
ppp_receive_nonmp_frame(ppp, skb); ppp_receive_nonmp_frame(ppp, skb);
return; } else {
kfree_skb(skb);
ppp_receive_error(ppp);
} }
if (skb->len > 0)
/* note: a 0-length skb is used as an error indication */
++ppp->dev->stats.rx_length_errors;
kfree_skb(skb);
ppp_receive_error(ppp);
} }
static void static void
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment