[<prev] [next>] [day] [month] [year] [list]
Message-Id: <20190424190742.178760-1-willemdebruijn.kernel@gmail.com>
Date: Wed, 24 Apr 2019 15:07:42 -0400
From: Willem de Bruijn <willemdebruijn.kernel@...il.com>
To: netdev@...r.kernel.org
Cc: davem@...emloft.net, David.Laight@...lab.com, idosch@...sch.org,
Willem de Bruijn <willemb@...gle.com>
Subject: [PATCH net] packet: validate msg_namelen in send directly
From: Willem de Bruijn <willemb@...gle.com>
Packet sockets in datagram mode take a link layer destination address
in sockaddr_ll.sll_addr. For some device types, address length exceeds
this field size, extending beyond the struct.
This addr argument is then passed to dev_hard_header without length
and assumed to be equal to dev->addr_len. Before commit 99137b7888f4
("packet: validate address length") it was possible to pass a shorter
msg_name and msg_namelen, causing an out of bound read.
After that and commit 6b8d95f1795c4 ("packet: validate address length
if non-zero") send() returns EINVAL if sockaddr_ll.sll_halen is
non-zero but less than dev->addr_len (zero length is allowed because
sockets in SOCK_RAW mode ignore sll_addr).
It turns out that legacy applications require more permissive input.
An otherwise legitimate Ethernet application that sets sll_halen > 0,
< ETH_ALEN sees EINVAL, even though ETH_ALEN fits in sockaddr_ll, so
cannot cause the out of bounds read.
Be more permissive. Ignore sll_halen, which is not used elsewhere.
Directly compare msg_namelen to dev->addr_len.
Fixes: 6b8d95f1795c4 ("packet: validate address length if non-zero")
Signed-off-by: Willem de Bruijn <willemb@...gle.com>
---
net/packet/af_packet.c | 13 +++++++++----
1 file changed, 9 insertions(+), 4 deletions(-)
diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 9419c5cf4de5e..9ae30bbd00913 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2616,6 +2616,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
proto = po->num;
addr = NULL;
} else {
+ const int sa_len = offsetof(struct sockaddr_ll, sll_addr);
+ const short sk_type = po->sk.sk_socket->type;
+
err = -EINVAL;
if (msg->msg_namelen < sizeof(struct sockaddr_ll))
goto out;
@@ -2624,9 +2627,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
sll_addr)))
goto out;
proto = saddr->sll_protocol;
- addr = saddr->sll_halen ? saddr->sll_addr : NULL;
+ addr = sk_type == SOCK_DGRAM ? saddr->sll_addr : NULL;
dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
- if (addr && dev && saddr->sll_halen < dev->addr_len)
+ if (addr && dev && msg->msg_namelen < (sa_len + dev->addr_len))
goto out_put;
}
@@ -2818,15 +2821,17 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
proto = po->num;
addr = NULL;
} else {
+ const int sa_len = offsetof(struct sockaddr_ll, sll_addr);
+
err = -EINVAL;
if (msg->msg_namelen < sizeof(struct sockaddr_ll))
goto out;
if (msg->msg_namelen < (saddr->sll_halen + offsetof(struct sockaddr_ll, sll_addr)))
goto out;
proto = saddr->sll_protocol;
- addr = saddr->sll_halen ? saddr->sll_addr : NULL;
+ addr = sock->type == SOCK_DGRAM ? saddr->sll_addr : NULL;
dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex);
- if (addr && dev && saddr->sll_halen < dev->addr_len)
+ if (addr && dev && msg->msg_namelen < (sa_len + dev->addr_len))
goto out_unlock;
}
--
2.21.0.593.g511ec345e18-goog
Powered by blists - more mailing lists