| From foo@baz Tue May 22 21:05:16 CEST 2018 |
| From: Willem de Bruijn <willemb@google.com> |
| Date: Fri, 11 May 2018 13:24:25 -0400 |
| Subject: packet: in packet_snd start writing at link layer allocation |
| |
| From: Willem de Bruijn <willemb@google.com> |
| |
| [ Upstream commit b84bbaf7a6c8cca24f8acf25a2c8e46913a947ba ] |
| |
| Packet sockets allow construction of packets shorter than |
| dev->hard_header_len to accommodate protocols with variable length |
| link layer headers. These packets are padded to dev->hard_header_len, |
| because some device drivers interpret that as a minimum packet size. |
| |
| packet_snd reserves dev->hard_header_len bytes on allocation. |
| SOCK_DGRAM sockets call skb_push in dev_hard_header() to ensure that |
| link layer headers are stored in the reserved range. SOCK_RAW sockets |
| do the same in tpacket_snd, but not in packet_snd. |
| |
| Syzbot was able to send a zero byte packet to a device with massive |
| 116B link layer header, causing padding to cross over into skb_shinfo. |
| Fix this by writing from the start of the llheader reserved range also |
| in the case of packet_snd/SOCK_RAW. |
| |
| Update skb_set_network_header to the new offset. This also corrects |
| it for SOCK_DGRAM, where it incorrectly double counted reserve due to |
| the skb_push in dev_hard_header. |
| |
| Fixes: 9ed988cd5915 ("packet: validate variable length ll headers") |
| Reported-by: syzbot+71d74a5406d02057d559@syzkaller.appspotmail.com |
| 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 | 4 +++- |
| 1 file changed, 3 insertions(+), 1 deletion(-) |
| |
| --- a/net/packet/af_packet.c |
| +++ b/net/packet/af_packet.c |
| @@ -2910,13 +2910,15 @@ static int packet_snd(struct socket *soc |
| if (skb == NULL) |
| goto out_unlock; |
| |
| - skb_set_network_header(skb, reserve); |
| + skb_reset_network_header(skb); |
| |
| err = -EINVAL; |
| if (sock->type == SOCK_DGRAM) { |
| offset = dev_hard_header(skb, dev, ntohs(proto), addr, NULL, len); |
| if (unlikely(offset < 0)) |
| goto out_free; |
| + } else if (reserve) { |
| + skb_push(skb, reserve); |
| } |
| |
| /* Returns -EFAULT on error */ |