Re: [PATCH v3] nfc: st-nci: Fix potential buffer overflows in EVT_TRANSACTION

From: Jakub Kicinski
Date: Wed Jan 12 2022 - 12:35:16 EST


On Tue, 11 Jan 2022 17:45:43 +0100 Jordy Zomer wrote:
> It appears that there are some buffer overflows in EVT_TRANSACTION.
> This happens because the length parameters that are passed to memcpy
> come directly from skb->data and are not guarded in any way.
>
> Signed-off-by: Jordy Zomer <jordy@pwning.systems>

This patch with more context:

> diff --git a/drivers/nfc/st-nci/se.c b/drivers/nfc/st-nci/se.c
> index 7764b1a4c3cf..cdb59ddff4e8 100644
> --- a/drivers/nfc/st-nci/se.c
> +++ b/drivers/nfc/st-nci/se.c
> @@ -333,18 +333,28 @@ static int st_nci_hci_connectivity_event_received(struct nci_dev *ndev,
> transaction = devm_kzalloc(dev, skb->len - 2, GFP_KERNEL);

What checks skb->len > 2 ?

> if (!transaction)
> return -ENOMEM;

Leaks skb ?

> transaction->aid_len = skb->data[1];
> +
> + /* Checking if the length of the AID is valid */
> + if (transaction->aid_len > sizeof(transaction->aid))
> + return -EINVAL;
>
> memcpy(transaction->aid, &skb->data[2], transaction->aid_len);

What checks skb->len > 2 + transaction->aid_len ?

> /* Check next byte is PARAMETERS tag (82) */
> if (skb->data[transaction->aid_len + 2] !=

.. make that skb->len > 2 + transaction->aid_len + 1

> NFC_EVT_TRANSACTION_PARAMS_TAG)
> return -EPROTO;

Leaks skb ? (btw devm_kmalloc() in message processing could probably as well be counted
as leak unless something guarantees attacker can't generate infinite messages of this type)

> transaction->params_len = skb->data[transaction->aid_len + 3];

.. skb->len > 2 + transaction->aid_len + 1 + 1

> + /* Total size is allocated (skb->len - 2) minus fixed array members */
> + if (transaction->params_len > ((skb->len - 2) - sizeof(struct nfc_evt_transaction)))

So this check makes sure we don't overflow transaction->params, right?
Again, does skb->len not have to be validated as well?

> + return -EINVAL;
> +
> memcpy(transaction->params, skb->data +
> transaction->aid_len + 4, transaction->params_len);
>
> r = nfc_se_transaction(ndev->nfc_dev, host, transaction);
> break;