net/nfc: fix use-after-free llcp_sock_bind/connect
authorOr Cohen <orcohen@paloaltonetworks.com>
Tue, 4 May 2021 07:16:46 +0000 (10:16 +0300)
committerDavid S. Miller <davem@davemloft.net>
Tue, 4 May 2021 18:59:48 +0000 (11:59 -0700)
Commits 8a4cd82d ("nfc: fix refcount leak in llcp_sock_connect()")
and c33b1cc62 ("nfc: fix refcount leak in llcp_sock_bind()")
fixed a refcount leak bug in bind/connect but introduced a
use-after-free if the same local is assigned to 2 different sockets.

This can be triggered by the following simple program:
    int sock1 = socket( AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP );
    int sock2 = socket( AF_NFC, SOCK_STREAM, NFC_SOCKPROTO_LLCP );
    memset( &addr, 0, sizeof(struct sockaddr_nfc_llcp) );
    addr.sa_family = AF_NFC;
    addr.nfc_protocol = NFC_PROTO_NFC_DEP;
    bind( sock1, (struct sockaddr*) &addr, sizeof(struct sockaddr_nfc_llcp) )
    bind( sock2, (struct sockaddr*) &addr, sizeof(struct sockaddr_nfc_llcp) )
    close(sock1);
    close(sock2);

Fix this by assigning NULL to llcp_sock->local after calling
nfc_llcp_local_put.

This addresses CVE-2021-23134.

Reported-by: Or Cohen <orcohen@paloaltonetworks.com>
Reported-by: Nadav Markus <nmarkus@paloaltonetworks.com>
Fixes: c33b1cc62 ("nfc: fix refcount leak in llcp_sock_bind()")
Signed-off-by: Or Cohen <orcohen@paloaltonetworks.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
net/nfc/llcp_sock.c

index a3b46f8..53dbe73 100644 (file)
@@ -109,12 +109,14 @@ static int llcp_sock_bind(struct socket *sock, struct sockaddr *addr, int alen)
                                          GFP_KERNEL);
        if (!llcp_sock->service_name) {
                nfc_llcp_local_put(llcp_sock->local);
+               llcp_sock->local = NULL;
                ret = -ENOMEM;
                goto put_dev;
        }
        llcp_sock->ssap = nfc_llcp_get_sdp_ssap(local, llcp_sock);
        if (llcp_sock->ssap == LLCP_SAP_MAX) {
                nfc_llcp_local_put(llcp_sock->local);
+               llcp_sock->local = NULL;
                kfree(llcp_sock->service_name);
                llcp_sock->service_name = NULL;
                ret = -EADDRINUSE;
@@ -709,6 +711,7 @@ static int llcp_sock_connect(struct socket *sock, struct sockaddr *_addr,
        llcp_sock->ssap = nfc_llcp_get_local_ssap(local);
        if (llcp_sock->ssap == LLCP_SAP_MAX) {
                nfc_llcp_local_put(llcp_sock->local);
+               llcp_sock->local = NULL;
                ret = -ENOMEM;
                goto put_dev;
        }
@@ -756,6 +759,7 @@ sock_unlink:
 sock_llcp_release:
        nfc_llcp_put_ssap(local, llcp_sock->ssap);
        nfc_llcp_local_put(llcp_sock->local);
+       llcp_sock->local = NULL;
 
 put_dev:
        nfc_put_device(dev);