From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qk1-f169.google.com (mail-qk1-f169.google.com [209.85.222.169]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 3EABB315D33 for ; Thu, 18 Jun 2026 03:00:04 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.222.169 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781751606; cv=none; b=Ybiz+V7MqRcrCWJLN5RGd52kPrToYWM3SimWwN2eH/x6yCJs1YYw1y3U4tVAcs9AfaCJ5pKKDNRxrQSSQwG4q1RA5yoz93yZQM76Su7HbCYy+DegDzOPZjtNZ+nM8OMrBYmwPVI9LneQM3emjzZhTvZWW1WzlNi64mv9QBNUIe8= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781751606; c=relaxed/simple; bh=7pkU0cOlb8CHWqvrmOwnHQuJHOyjiix7BJuBTVSXnrQ=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Qs8teqM2Z3Q/lZAqzLF2gnPOOhEV5eGWk84QGL6GMFnvWdfEw3uUmdJsd5NLmimMPu/JydKIVFmuHX4Vgz4cUoZjmSxnSipgIDNc64a0pMxyU6G/tvU9b5mSDeCsPu5bFp6pHykK2MNxWm45rqtdVYw8uENq6uFi6rRaW8hm6rM= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=NTTz0frZ; arc=none smtp.client-ip=209.85.222.169 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="NTTz0frZ" Received: by mail-qk1-f169.google.com with SMTP id af79cd13be357-91591f19c30so70449985a.1 for ; Wed, 17 Jun 2026 20:00:04 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1781751603; x=1782356403; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=L7B6PdGJepmzFaqqAJQ2/2TD7NiohpiM3OoOluPcG1I=; b=NTTz0frZwI9ndUU9O1VoeAL8313EVTiA7SETHsuR0mbQ+H/aZn3Ik4ZRcFoOcBHNlh EGC9nFBCkNcOVNxWU8Ic7JUetv+BrD4hyfDyCf8ZYGdlZ7cRm2M/DwYTXGQKoAfQSLzC r5Fe9bu774Hg5RnaflKrAF3j0pKUkf5lQhWgV2mZh96cY1SkOmerVsRZQV2LghaE8Mdh GycAjFIlP2KqdHT/+HpQ1qNsMDMAaoX55u5Ov2rjI9jAmAFkvCZNxKlR4lhJxX0mCgJy orYReNbCGCHpXpOseEOAgX65+l0Tyq33HzGCcAa0LV96rVuMaRx/V9dnN6m2ri1EjjZ7 yK6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781751603; x=1782356403; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to; bh=L7B6PdGJepmzFaqqAJQ2/2TD7NiohpiM3OoOluPcG1I=; b=kjq93BQPNMKMuFNZXi+35vfEW+QBJi/XCjkIWbe2XnTf4FdXyIruxi8Ok3hVj6lFTo Ntkl2mXH3AiZngctF1lGmtnEOMXrKosSELPPzNGNgUP68mumXzpRlVt5OuafHOsKEd6o Y8JOegE5G22XiUCsyUWcNQsFQ2rdyO6QRNdqshUZS3Uiafq6R9O6QaOKq7jy75N4VsQ9 iV/qtrQ5u4Log3c3hBR0ntdgMWKCf+qT0G4mHdb8CdlnBeL335wObxwaQAKNMLqP+wCn lt2kBhmE9kJdhM/zb5JFePFOmgjWZAlvK59EHjhf1fJr6KF4cIs7ozjvi35aGA4B9dGr YAIw== X-Forwarded-Encrypted: i=1; AFNElJ8QCkGIraJa371VTErqBaij7qrWRvPmQAPv5M7vHAmHqaQ9vtXrIFQW4iNk1TgVgcgHeQuLfVOMvx0xUmE=@vger.kernel.org X-Gm-Message-State: AOJu0YxPJTS39H0BMBiS4A+3VLgR3CH6sRko5AzuhPqv6kmp21xo/WOQ ALK2WCTpXt8/YKLYckYKT5cO4jEoZ2zIBvpYzyE2gLswgLcWvx5IgLdV X-Gm-Gg: Acq92OFemzwjvEj8xxWUFvNx/KjmX/SA/gmfBRsrMoqKvRbnyCjFhO+FYtbO7rn8qYi kH6kSXUOyvaFC2CGNqDWSxbFwp4kLn3FPo1w0fKDkSlbwJlaAlbqMVOWfKBtVVtRDBOs6ls/G13 V+l4qjmP7aIbsP1D4WmIDszeMbX7pXs0A7QR4OPj5iCbhqZcFV99gUyjjEFLPUPSDHsEjM81/N2 odX4fwNgw6EKzPevXxBK+AblSvb3UgqwpwVNYcugf09A/1kFWLKAGhh3ZvyVLynpfDc/tEftO/r iyC0Kj4CRlhdLDLyyLMc2RFvwzs1S677HIoYqH8dRst5pu5+PK55C/3/Pr7JOpMjeTXaVNbkViB T+wpToEIt1TTySGfaJvaSM+lH+baicbzql8xIv11sf9/E2yEaYlkB9B7Do6Uab8kyh40Fup6B5K 4lWXmL6IJmfp/R5rnROpQVDr8BRUf1FXOUDRKUKxASwF4JeWgz9NVJLlCGP/nuFzjpDYQcFQDSK htcspa1xkLPmnapsLQihKz1hhkUTeCI X-Received: by 2002:a05:620a:458e:b0:912:8fd0:4a5d with SMTP id af79cd13be357-91f2e0a0eccmr256622085a.55.1781751603114; Wed, 17 Jun 2026 20:00:03 -0700 (PDT) Received: from server0.tail6e7dd.ts.net (c-68-48-65-54.hsd1.mi.comcast.net. [68.48.65.54]) by smtp.gmail.com with ESMTPSA id 6a1803df08f44-8db4ad6d976sm47519786d6.43.2026.06.17.20.00.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 17 Jun 2026 20:00:02 -0700 (PDT) From: Michael Bommarito To: Krzysztof Kozlowski , Dan Carpenter Cc: Jakub Kicinski , Kees Cook , linux-kernel@vger.kernel.org Subject: [PATCH 1/2] nfc: pn533: bound device-supplied lengths in the receive path Date: Wed, 17 Jun 2026 22:59:52 -0400 Message-ID: <20260618025953.1878757-2-michael.bommarito@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260618025953.1878757-1-michael.bommarito@gmail.com> References: <20260618025953.1878757-1-michael.bommarito@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 7bit The PN533 receive path trusts device-supplied length fields and reads past the received buffer when they exceed it. The rx skb is sized to the device's USB transfer length, so a malicious PN53x reader triggers out-of-bounds reads in the standard/ACR122 frame validators, in Type A target parsing (nfcid_len), and in the autopoll record walk. Thread the received length into the rx_is_frame_valid callbacks, reject ACR122 frames with datalen < 2, and add the missing nfcid and per-record bounds. This was found with static analysis and confirmed with the KUnit tests added in the following patch. Fixes: c46ee38620a2 ("NFC: pn533: add NXP pn533 nfc device driver") Fixes: 9815c7cf22da ("NFC: pn533: Separate physical layer from the core implementation") Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito --- drivers/nfc/pn533/pn533.c | 32 ++++++++++++++++++++++++++++++-- drivers/nfc/pn533/pn533.h | 3 ++- drivers/nfc/pn533/usb.c | 12 ++++++++++-- 3 files changed, 42 insertions(+), 5 deletions(-) diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c index d7bdbc82e2ba2..984a61a83feaf 100644 --- a/drivers/nfc/pn533/pn533.c +++ b/drivers/nfc/pn533/pn533.c @@ -274,11 +274,15 @@ static void pn533_std_tx_update_payload_len(void *_frame, int len) frame->datalen += len; } -static bool pn533_std_rx_frame_is_valid(void *_frame, struct pn533 *dev) +static bool pn533_std_rx_frame_is_valid(void *_frame, size_t frame_len, + struct pn533 *dev) { u8 checksum; struct pn533_std_frame *stdf = _frame; + if (frame_len < sizeof(struct pn533_std_frame)) + return false; + if (stdf->start_frame != cpu_to_be16(PN533_STD_FRAME_SOF)) return false; @@ -286,6 +290,10 @@ static bool pn533_std_rx_frame_is_valid(void *_frame, struct pn533 *dev) /* Standard frame code */ dev->ops->rx_header_len = PN533_STD_FRAME_HEADER_LEN; + if (frame_len < sizeof(struct pn533_std_frame) + + stdf->datalen + PN533_STD_FRAME_TAIL_LEN) + return false; + checksum = pn533_std_checksum(stdf->datalen); if (checksum != stdf->datalen_checksum) return false; @@ -299,6 +307,11 @@ static bool pn533_std_rx_frame_is_valid(void *_frame, struct pn533 *dev) dev->ops->rx_header_len = PN533_EXT_FRAME_HEADER_LEN; + if (frame_len < sizeof(struct pn533_ext_frame) || + frame_len < sizeof(struct pn533_ext_frame) + + be16_to_cpu(eif->datalen) + PN533_STD_FRAME_TAIL_LEN) + return false; + checksum = pn533_ext_checksum(be16_to_cpu(eif->datalen)); if (checksum != eif->datalen_checksum) return false; @@ -701,6 +714,10 @@ static bool pn533_target_type_a_is_valid(struct pn533_target_type_a *type_a, if (type_a->nfcid_len > NFC_NFCID1_MAXSIZE) return false; + if (sizeof(struct pn533_target_type_a) + type_a->nfcid_len > + (size_t)target_data_len) + return false; + return true; } @@ -1399,6 +1416,7 @@ static int pn533_autopoll_complete(struct pn533 *dev, void *arg, { struct pn532_autopoll_resp *apr; struct nfc_target nfc_tgt; + size_t remaining; u8 nbtg; int rc; @@ -1417,12 +1435,21 @@ static int pn533_autopoll_complete(struct pn533 *dev, void *arg, goto stop_poll; } + if (resp->len < 1) + return -EAGAIN; + nbtg = resp->data[0]; if ((nbtg > 2) || (nbtg <= 0)) return -EAGAIN; + remaining = resp->len - 1; apr = (struct pn532_autopoll_resp *)&resp->data[1]; while (nbtg--) { + if (remaining < sizeof(struct pn532_autopoll_resp) || + apr->ln < 1 || + remaining < (size_t)apr->ln + 2) + return -EAGAIN; + memset(&nfc_tgt, 0, sizeof(struct nfc_target)); switch (apr->type) { case PN532_AUTOPOLL_TYPE_ISOA: @@ -1468,6 +1495,7 @@ static int pn533_autopoll_complete(struct pn533 *dev, void *arg, } dev->tgt_available_prots = nfc_tgt.supported_protocols; + remaining -= (size_t)apr->ln + 2; apr = (struct pn532_autopoll_resp *) (apr->tgdata + (apr->ln - 1)); } @@ -2189,7 +2217,7 @@ void pn533_recv_frame(struct pn533 *dev, struct sk_buff *skb, int status) print_hex_dump_debug("PN533 RX: ", DUMP_PREFIX_NONE, 16, 1, skb->data, dev->ops->rx_frame_size(skb->data), false); - if (!dev->ops->rx_is_frame_valid(skb->data, dev)) { + if (!dev->ops->rx_is_frame_valid(skb->data, skb->len, dev)) { nfc_err(dev->dev, "Received an invalid frame\n"); dev->cmd->status = -EIO; } else if (!pn533_rx_frame_is_cmd_response(dev, skb->data)) { diff --git a/drivers/nfc/pn533/pn533.h b/drivers/nfc/pn533/pn533.h index 09e35b8693f5a..08804f88b38a3 100644 --- a/drivers/nfc/pn533/pn533.h +++ b/drivers/nfc/pn533/pn533.h @@ -201,7 +201,8 @@ struct pn533_frame_ops { int tx_header_len; int tx_tail_len; - bool (*rx_is_frame_valid)(void *frame, struct pn533 *dev); + bool (*rx_is_frame_valid)(void *frame, size_t frame_len, + struct pn533 *dev); bool (*rx_frame_is_ack)(void *frame); int (*rx_frame_size)(void *frame); int rx_header_len; diff --git a/drivers/nfc/pn533/usb.c b/drivers/nfc/pn533/usb.c index efb07f944fce2..1d50600609dd4 100644 --- a/drivers/nfc/pn533/usb.c +++ b/drivers/nfc/pn533/usb.c @@ -312,14 +312,22 @@ static void pn533_acr122_tx_update_payload_len(void *_frame, int len) frame->datalen += len; } -static bool pn533_acr122_is_rx_frame_valid(void *_frame, struct pn533 *dev) +static bool pn533_acr122_is_rx_frame_valid(void *_frame, size_t frame_len, + struct pn533 *dev) { struct pn533_acr122_rx_frame *frame = _frame; + if (frame_len < sizeof(struct pn533_acr122_rx_frame)) + return false; + if (frame->ccid.type != 0x83) return false; - if (!frame->ccid.datalen) + if (frame->ccid.datalen < 2) + return false; + + if (frame_len < sizeof(struct pn533_acr122_rx_frame) + + frame->ccid.datalen) return false; if (frame->data[frame->ccid.datalen - 2] == 0x63) -- 2.53.0