net/ieee802154: fix uninit value bug in dgram_sendmsg
authorHaimin Zhang <tcs.kernel@gmail.com>
Thu, 8 Sep 2022 12:19:27 +0000 (20:19 +0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 26 Oct 2022 11:16:52 +0000 (13:16 +0200)
[ Upstream commit 94160108a70c8af17fa1484a37e05181c0e094af ]

There is uninit value bug in dgram_sendmsg function in
net/ieee802154/socket.c when the length of valid data pointed by the
msg->msg_name isn't verified.

We introducing a helper function ieee802154_sockaddr_check_size to
check namelen. First we check there is addr_type in ieee802154_addr_sa.
Then, we check namelen according to addr_type.

Also fixed in raw_bind, dgram_bind, dgram_connect.

Signed-off-by: Haimin Zhang <tcs_kernel@tencent.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Signed-off-by: Sasha Levin <sashal@kernel.org>
include/net/ieee802154_netdev.h
net/ieee802154/socket.c

index c4b31601cd53629ef62781ea043fe54102f29ad3..fd1665baa17944c17139b774de503cd74d02d85e 100644 (file)
 #ifndef IEEE802154_NETDEVICE_H
 #define IEEE802154_NETDEVICE_H
 
+#define IEEE802154_REQUIRED_SIZE(struct_type, member) \
+       (offsetof(typeof(struct_type), member) + \
+       sizeof(((typeof(struct_type) *)(NULL))->member))
+
+#define IEEE802154_ADDR_OFFSET \
+       offsetof(typeof(struct sockaddr_ieee802154), addr)
+
+#define IEEE802154_MIN_NAMELEN (IEEE802154_ADDR_OFFSET + \
+       IEEE802154_REQUIRED_SIZE(struct ieee802154_addr_sa, addr_type))
+
+#define IEEE802154_NAMELEN_SHORT (IEEE802154_ADDR_OFFSET + \
+       IEEE802154_REQUIRED_SIZE(struct ieee802154_addr_sa, short_addr))
+
+#define IEEE802154_NAMELEN_LONG (IEEE802154_ADDR_OFFSET + \
+       IEEE802154_REQUIRED_SIZE(struct ieee802154_addr_sa, hwaddr))
+
 #include <net/af_ieee802154.h>
 #include <linux/netdevice.h>
 #include <linux/skbuff.h>
@@ -173,6 +189,27 @@ static inline void ieee802154_devaddr_to_raw(void *raw, __le64 addr)
        memcpy(raw, &temp, IEEE802154_ADDR_LEN);
 }
 
+static inline int
+ieee802154_sockaddr_check_size(struct sockaddr_ieee802154 *daddr, int len)
+{
+       struct ieee802154_addr_sa *sa;
+
+       sa = &daddr->addr;
+       if (len < IEEE802154_MIN_NAMELEN)
+               return -EINVAL;
+       switch (sa->addr_type) {
+       case IEEE802154_ADDR_SHORT:
+               if (len < IEEE802154_NAMELEN_SHORT)
+                       return -EINVAL;
+               break;
+       case IEEE802154_ADDR_LONG:
+               if (len < IEEE802154_NAMELEN_LONG)
+                       return -EINVAL;
+               break;
+       }
+       return 0;
+}
+
 static inline void ieee802154_addr_from_sa(struct ieee802154_addr *a,
                                           const struct ieee802154_addr_sa *sa)
 {
index 9d46d9462129d9ca917b453b59f4517bfb1b925a..16bf114118c30a9b7d9c734970cb50208228c192 100644 (file)
@@ -212,8 +212,9 @@ static int raw_bind(struct sock *sk, struct sockaddr *_uaddr, int len)
        int err = 0;
        struct net_device *dev = NULL;
 
-       if (len < sizeof(*uaddr))
-               return -EINVAL;
+       err = ieee802154_sockaddr_check_size(uaddr, len);
+       if (err < 0)
+               return err;
 
        uaddr = (struct sockaddr_ieee802154 *)_uaddr;
        if (uaddr->family != AF_IEEE802154)
@@ -506,7 +507,8 @@ static int dgram_bind(struct sock *sk, struct sockaddr *uaddr, int len)
 
        ro->bound = 0;
 
-       if (len < sizeof(*addr))
+       err = ieee802154_sockaddr_check_size(addr, len);
+       if (err < 0)
                goto out;
 
        if (addr->family != AF_IEEE802154)
@@ -577,8 +579,9 @@ static int dgram_connect(struct sock *sk, struct sockaddr *uaddr,
        struct dgram_sock *ro = dgram_sk(sk);
        int err = 0;
 
-       if (len < sizeof(*addr))
-               return -EINVAL;
+       err = ieee802154_sockaddr_check_size(addr, len);
+       if (err < 0)
+               return err;
 
        if (addr->family != AF_IEEE802154)
                return -EINVAL;
@@ -617,6 +620,7 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
        struct ieee802154_mac_cb *cb;
        struct dgram_sock *ro = dgram_sk(sk);
        struct ieee802154_addr dst_addr;
+       DECLARE_SOCKADDR(struct sockaddr_ieee802154*, daddr, msg->msg_name);
        int hlen, tlen;
        int err;
 
@@ -625,10 +629,20 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
                return -EOPNOTSUPP;
        }
 
-       if (!ro->connected && !msg->msg_name)
-               return -EDESTADDRREQ;
-       else if (ro->connected && msg->msg_name)
-               return -EISCONN;
+       if (msg->msg_name) {
+               if (ro->connected)
+                       return -EISCONN;
+               if (msg->msg_namelen < IEEE802154_MIN_NAMELEN)
+                       return -EINVAL;
+               err = ieee802154_sockaddr_check_size(daddr, msg->msg_namelen);
+               if (err < 0)
+                       return err;
+               ieee802154_addr_from_sa(&dst_addr, &daddr->addr);
+       } else {
+               if (!ro->connected)
+                       return -EDESTADDRREQ;
+               dst_addr = ro->dst_addr;
+       }
 
        if (!ro->bound)
                dev = dev_getfirstbyhwtype(sock_net(sk), ARPHRD_IEEE802154);
@@ -664,16 +678,6 @@ static int dgram_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
        cb = mac_cb_init(skb);
        cb->type = IEEE802154_FC_TYPE_DATA;
        cb->ackreq = ro->want_ack;
-
-       if (msg->msg_name) {
-               DECLARE_SOCKADDR(struct sockaddr_ieee802154*,
-                                daddr, msg->msg_name);
-
-               ieee802154_addr_from_sa(&dst_addr, &daddr->addr);
-       } else {
-               dst_addr = ro->dst_addr;
-       }
-
        cb->secen = ro->secen;
        cb->secen_override = ro->secen_override;
        cb->seclevel = ro->seclevel;