net: nfc: fix races in nfc_llcp_sock_get() and nfc_llcp_sock_get_sn()
authorEric Dumazet <edumazet@google.com>
Mon, 9 Oct 2023 12:31:10 +0000 (12:31 +0000)
committerJakub Kicinski <kuba@kernel.org>
Wed, 11 Oct 2023 02:44:44 +0000 (19:44 -0700)
Sili Luo reported a race in nfc_llcp_sock_get(), leading to UAF.

Getting a reference on the socket found in a lookup while
holding a lock should happen before releasing the lock.

nfc_llcp_sock_get_sn() has a similar problem.

Finally nfc_llcp_recv_snl() needs to make sure the socket
found by nfc_llcp_sock_from_sn() does not disappear.

Fixes: 8f50020ed9b8 ("NFC: LLCP late binding")
Reported-by: Sili Luo <rootlab@huawei.com>
Signed-off-by: Eric Dumazet <edumazet@google.com>
Cc: Willy Tarreau <w@1wt.eu>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Link: https://lore.kernel.org/r/20231009123110.3735515-1-edumazet@google.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
net/nfc/llcp_core.c

index 6705bb8..1dac281 100644 (file)
@@ -203,17 +203,13 @@ static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local,
 
                if (tmp_sock->ssap == ssap && tmp_sock->dsap == dsap) {
                        llcp_sock = tmp_sock;
+                       sock_hold(&llcp_sock->sk);
                        break;
                }
        }
 
        read_unlock(&local->sockets.lock);
 
-       if (llcp_sock == NULL)
-               return NULL;
-
-       sock_hold(&llcp_sock->sk);
-
        return llcp_sock;
 }
 
@@ -346,7 +342,8 @@ static int nfc_llcp_wks_sap(const char *service_name, size_t service_name_len)
 
 static
 struct nfc_llcp_sock *nfc_llcp_sock_from_sn(struct nfc_llcp_local *local,
-                                           const u8 *sn, size_t sn_len)
+                                           const u8 *sn, size_t sn_len,
+                                           bool needref)
 {
        struct sock *sk;
        struct nfc_llcp_sock *llcp_sock, *tmp_sock;
@@ -382,6 +379,8 @@ struct nfc_llcp_sock *nfc_llcp_sock_from_sn(struct nfc_llcp_local *local,
 
                if (memcmp(sn, tmp_sock->service_name, sn_len) == 0) {
                        llcp_sock = tmp_sock;
+                       if (needref)
+                               sock_hold(&llcp_sock->sk);
                        break;
                }
        }
@@ -423,7 +422,8 @@ u8 nfc_llcp_get_sdp_ssap(struct nfc_llcp_local *local,
                 * to this service name.
                 */
                if (nfc_llcp_sock_from_sn(local, sock->service_name,
-                                         sock->service_name_len) != NULL) {
+                                         sock->service_name_len,
+                                         false) != NULL) {
                        mutex_unlock(&local->sdp_lock);
 
                        return LLCP_SAP_MAX;
@@ -824,16 +824,7 @@ out:
 static struct nfc_llcp_sock *nfc_llcp_sock_get_sn(struct nfc_llcp_local *local,
                                                  const u8 *sn, size_t sn_len)
 {
-       struct nfc_llcp_sock *llcp_sock;
-
-       llcp_sock = nfc_llcp_sock_from_sn(local, sn, sn_len);
-
-       if (llcp_sock == NULL)
-               return NULL;
-
-       sock_hold(&llcp_sock->sk);
-
-       return llcp_sock;
+       return nfc_llcp_sock_from_sn(local, sn, sn_len, true);
 }
 
 static const u8 *nfc_llcp_connect_sn(const struct sk_buff *skb, size_t *sn_len)
@@ -1298,7 +1289,8 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
                        }
 
                        llcp_sock = nfc_llcp_sock_from_sn(local, service_name,
-                                                         service_name_len);
+                                                         service_name_len,
+                                                         true);
                        if (!llcp_sock) {
                                sap = 0;
                                goto add_snl;
@@ -1318,6 +1310,7 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
 
                                if (sap == LLCP_SAP_MAX) {
                                        sap = 0;
+                                       nfc_llcp_sock_put(llcp_sock);
                                        goto add_snl;
                                }
 
@@ -1335,6 +1328,7 @@ static void nfc_llcp_recv_snl(struct nfc_llcp_local *local,
 
                        pr_debug("%p %d\n", llcp_sock, sap);
 
+                       nfc_llcp_sock_put(llcp_sock);
 add_snl:
                        sdp = nfc_llcp_build_sdres_tlv(tid, sap);
                        if (sdp == NULL)