]> www.infradead.org Git - users/hch/misc.git/commitdiff
smc: Use __sk_dst_get() and dst_dev_rcu() in in smc_clc_prfx_set().
authorKuniyuki Iwashima <kuniyu@google.com>
Tue, 16 Sep 2025 21:47:20 +0000 (21:47 +0000)
committerJakub Kicinski <kuba@kernel.org>
Thu, 18 Sep 2025 01:10:21 +0000 (18:10 -0700)
smc_clc_prfx_set() is called during connect() and not under RCU
nor RTNL.

Using sk_dst_get(sk)->dev could trigger UAF.

Let's use __sk_dst_get() and dev_dst_rcu() under rcu_read_lock()
after kernel_getsockname().

Note that the returned value of smc_clc_prfx_set() is not used
in the caller.

While at it, we change the 1st arg of smc_clc_prfx_set[46]_rcu()
not to touch dst there.

Fixes: a046d57da19f ("smc: CLC handshake (incl. preparation steps)")
Signed-off-by: Kuniyuki Iwashima <kuniyu@google.com>
Reviewed-by: Eric Dumazet <edumazet@google.com>
Link: https://patch.msgid.link/20250916214758.650211-3-kuniyu@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/smc/smc_clc.c

index 08be56dfb3f24e90c81fca7de5e52e9bbbce06fa..976b2102bdfcd91a3898d7e81fad11714be57a51 100644 (file)
@@ -509,10 +509,10 @@ static bool smc_clc_msg_hdr_valid(struct smc_clc_msg_hdr *clcm, bool check_trl)
 }
 
 /* find ipv4 addr on device and get the prefix len, fill CLC proposal msg */
-static int smc_clc_prfx_set4_rcu(struct dst_entry *dst, __be32 ipv4,
+static int smc_clc_prfx_set4_rcu(struct net_device *dev, __be32 ipv4,
                                 struct smc_clc_msg_proposal_prefix *prop)
 {
-       struct in_device *in_dev = __in_dev_get_rcu(dst->dev);
+       struct in_device *in_dev = __in_dev_get_rcu(dev);
        const struct in_ifaddr *ifa;
 
        if (!in_dev)
@@ -530,12 +530,12 @@ static int smc_clc_prfx_set4_rcu(struct dst_entry *dst, __be32 ipv4,
 }
 
 /* fill CLC proposal msg with ipv6 prefixes from device */
-static int smc_clc_prfx_set6_rcu(struct dst_entry *dst,
+static int smc_clc_prfx_set6_rcu(struct net_device *dev,
                                 struct smc_clc_msg_proposal_prefix *prop,
                                 struct smc_clc_ipv6_prefix *ipv6_prfx)
 {
 #if IS_ENABLED(CONFIG_IPV6)
-       struct inet6_dev *in6_dev = __in6_dev_get(dst->dev);
+       struct inet6_dev *in6_dev = __in6_dev_get(dev);
        struct inet6_ifaddr *ifa;
        int cnt = 0;
 
@@ -564,41 +564,44 @@ static int smc_clc_prfx_set(struct socket *clcsock,
                            struct smc_clc_msg_proposal_prefix *prop,
                            struct smc_clc_ipv6_prefix *ipv6_prfx)
 {
-       struct dst_entry *dst = sk_dst_get(clcsock->sk);
        struct sockaddr_storage addrs;
        struct sockaddr_in6 *addr6;
        struct sockaddr_in *addr;
+       struct net_device *dev;
+       struct dst_entry *dst;
        int rc = -ENOENT;
 
-       if (!dst) {
-               rc = -ENOTCONN;
-               goto out;
-       }
-       if (!dst->dev) {
-               rc = -ENODEV;
-               goto out_rel;
-       }
        /* get address to which the internal TCP socket is bound */
        if (kernel_getsockname(clcsock, (struct sockaddr *)&addrs) < 0)
-               goto out_rel;
+               goto out;
+
        /* analyze IP specific data of net_device belonging to TCP socket */
        addr6 = (struct sockaddr_in6 *)&addrs;
+
        rcu_read_lock();
+
+       dst = __sk_dst_get(clcsock->sk);
+       dev = dst ? dst_dev_rcu(dst) : NULL;
+       if (!dev) {
+               rc = -ENODEV;
+               goto out_unlock;
+       }
+
        if (addrs.ss_family == PF_INET) {
                /* IPv4 */
                addr = (struct sockaddr_in *)&addrs;
-               rc = smc_clc_prfx_set4_rcu(dst, addr->sin_addr.s_addr, prop);
+               rc = smc_clc_prfx_set4_rcu(dev, addr->sin_addr.s_addr, prop);
        } else if (ipv6_addr_v4mapped(&addr6->sin6_addr)) {
                /* mapped IPv4 address - peer is IPv4 only */
-               rc = smc_clc_prfx_set4_rcu(dst, addr6->sin6_addr.s6_addr32[3],
+               rc = smc_clc_prfx_set4_rcu(dev, addr6->sin6_addr.s6_addr32[3],
                                           prop);
        } else {
                /* IPv6 */
-               rc = smc_clc_prfx_set6_rcu(dst, prop, ipv6_prfx);
+               rc = smc_clc_prfx_set6_rcu(dev, prop, ipv6_prfx);
        }
+
+out_unlock:
        rcu_read_unlock();
-out_rel:
-       dst_release(dst);
 out:
        return rc;
 }