packet: validate msg_namelen in send directly [Linux 3.16.72]

This Linux kernel change "packet: validate msg_namelen in send directly" is included in the Linux 3.16.72 release. This change is authored by Willem de Bruijn <willemb [at] google.com> on Mon Apr 29 11:53:18 2019 -0400. The commit for this change in Linux stable tree is e4fa924 (patch) which is from upstream commit 486efdc. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 486efdc.

packet: validate msg_namelen in send directly

commit 486efdc8f6ce802b27e15921d2353cc740c55451 upstream.

Packet sockets in datagram mode take a destination address. Verify its
length before passing to dev_hard_header.

Prior to 2.6.14-rc3, the send code ignored sll_halen. This is
established behavior. Directly compare msg_namelen to dev->addr_len.

Change v1->v2: initialize addr in all paths

Fixes: 6b8d95f1795c4 ("packet: validate address length if non-zero")
Suggested-by: David Laight <[email protected]>
Signed-off-by: Willem de Bruijn <[email protected]>
Signed-off-by: David S. Miller <[email protected]>
Signed-off-by: Ben Hutchings <[email protected]>

There are 24 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 net/packet/af_packet.c | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index cba54f0..0777d98 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2278,8 +2278,8 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
    void *ph;
    DECLARE_SOCKADDR(struct sockaddr_ll *, saddr, msg->msg_name);
    bool need_wait = !(msg->msg_flags & MSG_DONTWAIT);
+   unsigned char *addr = NULL;
    int tp_len, size_max;
-   unsigned char *addr;
    int len_sum = 0;
    int status = TP_STATUS_AVAILABLE;
    int hlen, tlen;
@@ -2289,7 +2289,6 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
    if (likely(saddr == NULL)) {
        dev = packet_cached_dev_get(po);
        proto   = po->num;
-       addr    = NULL;
    } else {
        err = -EINVAL;
        if (msg->msg_namelen < sizeof(struct sockaddr_ll))
@@ -2299,10 +2298,13 @@ 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;
        dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
-       if (addr && dev && saddr->sll_halen < dev->addr_len)
-           goto out_put;
+       if (po->sk.sk_socket->type == SOCK_DGRAM) {
+           if (dev && msg->msg_namelen < dev->addr_len +
+                  offsetof(struct sockaddr_ll, sll_addr))
+               goto out_put;
+           addr = saddr->sll_addr;
+       }
    }

    err = -ENXIO;
@@ -2435,7 +2437,7 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
    struct sk_buff *skb;
    struct net_device *dev;
    __be16 proto;
-   unsigned char *addr;
+   unsigned char *addr = NULL;
    int err, reserve = 0;
    struct virtio_net_hdr vnet_hdr = { 0 };
    int offset = 0;
@@ -2453,7 +2455,6 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
    if (likely(saddr == NULL)) {
        dev = packet_cached_dev_get(po);
        proto   = po->num;
-       addr    = NULL;
    } else {
        err = -EINVAL;
        if (msg->msg_namelen < sizeof(struct sockaddr_ll))
@@ -2461,10 +2462,13 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
        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;
        dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex);
-       if (addr && dev && saddr->sll_halen < dev->addr_len)
-           goto out_unlock;
+       if (sock->type == SOCK_DGRAM) {
+           if (dev && msg->msg_namelen < dev->addr_len +
+                  offsetof(struct sockaddr_ll, sll_addr))
+               goto out_unlock;
+           addr = saddr->sll_addr;
+       }
    }

    err = -ENXIO;

Leave a Reply

Your email address will not be published. Required fields are marked *