diff options
Diffstat (limited to 'net/nfc/llcp_core.c')
| -rw-r--r-- | net/nfc/llcp_core.c | 39 | 
1 files changed, 36 insertions, 3 deletions
diff --git a/net/nfc/llcp_core.c b/net/nfc/llcp_core.c index 1dac28136e6a..18be13fb9b75 100644 --- a/net/nfc/llcp_core.c +++ b/net/nfc/llcp_core.c @@ -145,6 +145,13 @@ static void nfc_llcp_socket_release(struct nfc_llcp_local *local, bool device,  static struct nfc_llcp_local *nfc_llcp_local_get(struct nfc_llcp_local *local)  { +	/* Since using nfc_llcp_local may result in usage of nfc_dev, whenever +	 * we hold a reference to local, we also need to hold a reference to +	 * the device to avoid UAF. +	 */ +	if (!nfc_get_device(local->dev->idx)) +		return NULL; +  	kref_get(&local->ref);  	return local; @@ -177,10 +184,18 @@ static void local_release(struct kref *ref)  int nfc_llcp_local_put(struct nfc_llcp_local *local)  { +	struct nfc_dev *dev; +	int ret; +  	if (local == NULL)  		return 0; -	return kref_put(&local->ref, local_release); +	dev = local->dev; + +	ret = kref_put(&local->ref, local_release); +	nfc_put_device(dev); + +	return ret;  }  static struct nfc_llcp_sock *nfc_llcp_sock_get(struct nfc_llcp_local *local, @@ -959,8 +974,17 @@ static void nfc_llcp_recv_connect(struct nfc_llcp_local *local,  	}  	new_sock = nfc_llcp_sock(new_sk); -	new_sock->dev = local->dev; +  	new_sock->local = nfc_llcp_local_get(local); +	if (!new_sock->local) { +		reason = LLCP_DM_REJ; +		sock_put(&new_sock->sk); +		release_sock(&sock->sk); +		sock_put(&sock->sk); +		goto fail; +	} + +	new_sock->dev = local->dev;  	new_sock->rw = sock->rw;  	new_sock->miux = sock->miux;  	new_sock->nfc_protocol = sock->nfc_protocol; @@ -1597,7 +1621,16 @@ int nfc_llcp_register_device(struct nfc_dev *ndev)  	if (local == NULL)  		return -ENOMEM; -	local->dev = ndev; +	/* As we are going to initialize local's refcount, we need to get the +	 * nfc_dev to avoid UAF, otherwise there is no point in continuing. +	 * See nfc_llcp_local_get(). +	 */ +	local->dev = nfc_get_device(ndev->idx); +	if (!local->dev) { +		kfree(local); +		return -ENODEV; +	} +  	INIT_LIST_HEAD(&local->list);  	kref_init(&local->ref);  	mutex_init(&local->sdp_lock);  |