| From foo@baz Sat Apr 16 09:15:18 PDT 2016 |
| From: Willem de Bruijn <willemb@google.com> |
| Date: Wed, 9 Mar 2016 21:58:34 -0500 |
| Subject: packet: validate variable length ll headers |
| |
| From: Willem de Bruijn <willemb@google.com> |
| |
| [ Upstream commit 9ed988cd591500c040b2a6257bc68543e08ceeef ] |
| |
| Replace link layer header validation check ll_header_truncate with |
| more generic dev_validate_header. |
| |
| Validation based on hard_header_len incorrectly drops valid packets |
| in variable length protocols, such as AX25. dev_validate_header |
| calls header_ops.validate for such protocols to ensure correctness |
| below hard_header_len. |
| |
| See also http://comments.gmane.org/gmane.linux.network/401064 |
| |
| Fixes 9c7077622dd9 ("packet: make packet_snd fail on len smaller than l2 header") |
| Signed-off-by: Willem de Bruijn <willemb@google.com> |
| Signed-off-by: David S. Miller <davem@davemloft.net> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| --- |
| net/packet/af_packet.c | 37 ++++++++++++++++--------------------- |
| 1 file changed, 16 insertions(+), 21 deletions(-) |
| |
| --- a/net/packet/af_packet.c |
| +++ b/net/packet/af_packet.c |
| @@ -1916,6 +1916,10 @@ retry: |
| goto retry; |
| } |
| |
| + if (!dev_validate_header(dev, skb->data, len)) { |
| + err = -EINVAL; |
| + goto out_unlock; |
| + } |
| if (len > (dev->mtu + dev->hard_header_len + extra_len) && |
| !packet_extra_vlan_len_allowed(dev, skb)) { |
| err = -EMSGSIZE; |
| @@ -2326,18 +2330,6 @@ static void tpacket_destruct_skb(struct |
| sock_wfree(skb); |
| } |
| |
| -static bool ll_header_truncated(const struct net_device *dev, int len) |
| -{ |
| - /* net device doesn't like empty head */ |
| - if (unlikely(len < dev->hard_header_len)) { |
| - net_warn_ratelimited("%s: packet size is too short (%d < %d)\n", |
| - current->comm, len, dev->hard_header_len); |
| - return true; |
| - } |
| - |
| - return false; |
| -} |
| - |
| static void tpacket_set_protocol(const struct net_device *dev, |
| struct sk_buff *skb) |
| { |
| @@ -2420,19 +2412,19 @@ static int tpacket_fill_skb(struct packe |
| if (unlikely(err < 0)) |
| return -EINVAL; |
| } else if (dev->hard_header_len) { |
| - if (ll_header_truncated(dev, tp_len)) |
| - return -EINVAL; |
| + int hdrlen = min_t(int, dev->hard_header_len, tp_len); |
| |
| skb_push(skb, dev->hard_header_len); |
| - err = skb_store_bits(skb, 0, data, |
| - dev->hard_header_len); |
| + err = skb_store_bits(skb, 0, data, hdrlen); |
| if (unlikely(err)) |
| return err; |
| + if (!dev_validate_header(dev, skb->data, hdrlen)) |
| + return -EINVAL; |
| if (!skb->protocol) |
| tpacket_set_protocol(dev, skb); |
| |
| - data += dev->hard_header_len; |
| - to_write -= dev->hard_header_len; |
| + data += hdrlen; |
| + to_write -= hdrlen; |
| } |
| |
| offset = offset_in_page(data); |
| @@ -2763,9 +2755,6 @@ static int packet_snd(struct socket *soc |
| offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len); |
| if (unlikely(offset < 0)) |
| goto out_free; |
| - } else { |
| - if (ll_header_truncated(dev, len)) |
| - goto out_free; |
| } |
| |
| /* Returns -EFAULT on error */ |
| @@ -2773,6 +2762,12 @@ static int packet_snd(struct socket *soc |
| if (err) |
| goto out_free; |
| |
| + if (sock->type == SOCK_RAW && |
| + !dev_validate_header(dev, skb->data, len)) { |
| + err = -EINVAL; |
| + goto out_free; |
| + } |
| + |
| sock_tx_timestamp(sk, &skb_shinfo(skb)->tx_flags); |
| |
| if (!gso_type && (len > dev->mtu + reserve + extra_len) && |