From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from mail-qv1-f53.google.com (mail-qv1-f53.google.com [209.85.219.53]) (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 25B9B314D16 for ; Thu, 18 Jun 2026 03:00:05 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.219.53 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781751606; cv=none; b=A2bNCGSFDo475zf5Rzm1BhhdnbmPD5C1zOhNBbne+ZXrQQqpSzVNHVyITkeAmwNGCJJZMYoOlx75NGHZrYktkFCLKT4mcr6VqrJpBbBWjFSOKd9elgeKIyvU1jKIC9o5xQXJn81MWtDxgOpk4hdbBOVi/NTGwUMwiQxn7UkW45Y= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1781751606; c=relaxed/simple; bh=MstXq7ZxnnlF8z9nR2664LYEPA8SQzasoH8QqF3G/PI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=cqOlDQoT2EVTfgPa3VIkOujvxYqz7dAd1uoC1RzIrL8KMxo2uxUmYXBBes0yEXIeBHctx2Q297t3S0Jf5M+VcYfc7C77nZs8oCQTlAZX8FaUZyw/tDOKfH2mi3WTKHGL7+QUuEz4zgrBGyLp/T/Asq8mu7BcNvWkI2brBWNiajc= 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=HglWgQ6g; arc=none smtp.client-ip=209.85.219.53 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="HglWgQ6g" Received: by mail-qv1-f53.google.com with SMTP id 6a1803df08f44-8ccf181a52bso4415766d6.3 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=1781751604; x=1782356404; 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=lB+itDcu/45dgEeBwzciVyutGfznJNyOzDcA+YTBaR0=; b=HglWgQ6ggnYc0jN5bKcTZqznS22ztOMnBe1prHVCsU1twqn8j1Ezd0Igt2F6y8hkGe AVrdZa1d4n8JS9RAfNVz8MJZ12+oqq6fJCHG/IocV4VF92eGhYWTL/lYMqXbZoPVi0iw 5u9tlky02KZJryFN876QkLmPNuPAGjSPPttifY9eYe2M25V7QhJy0PmkLvy04tO8SnQE qvmcYL3k+5nhXBYBhsEu49dlPIwzrYp/RtZ/664KNgaer/o0+MLzfqeBD9mcVH/R+WC/ WAJ/lu8P5sLw6/Ve7mv7AhG1G/Gbmq/WWf8QhhB9q0IOh9MM/FkrCyapahMsXD5o1YxE 9pQQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1781751604; x=1782356404; 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=lB+itDcu/45dgEeBwzciVyutGfznJNyOzDcA+YTBaR0=; b=Rb56p6dNozSZ0P4wvkqnSqqJFHx3NB6mG13tvbVJwmXVoKZuiwCh6bqEWvCqlLzwB9 HGmKmPxj+uJSvB5TkPOu7JxSIltlPYyyqM9AieR6Y7zKrTJmjj4ck45Mym8dNNpfteza TxmeQZSiZaWk8RQcMSx0iWU7xZEUQbc2AMgWMYHgKOPBmb9xGDtbMIoGRHiiXI7ZS7lD x7Ge0nM67N/2mE5jPhU98z4XrWJ887qqonUiGfRw0kqFLxTJ4FOlwMJnYrbhHjUSPgYg W5dZA0ia+ICl7ZcDzkj0THfVwBjK3UJoeNKJ8QM5jA0dGt2sPcn0TjNTQ589vMzTb8cG Cz/Q== X-Forwarded-Encrypted: i=1; AFNElJ8VrLZhyg/trbzxS6wjwuxYPj6PxWGsH8phhiSFCuyTw0zykDloCq2lSivXZ3HFP57jE3QdmzRZJHS4GKk=@vger.kernel.org X-Gm-Message-State: AOJu0YyhhFGDmQ6IvKfIeq9uanzkA1fsuHvhydgPwO0uJYO7oy5ctq/5 CHEwNzeuoCP+gRtDcw4Z1JfUPLyHPTZCY2go3+wwRpYEEPCWVxk9zATs X-Gm-Gg: AfdE7cn100xeDF4BgC+rlf37+FmsjsC1kw1xfetfHMUCqOV/xMbJm7tHfmnHsikgKTU bn0LwURO0JLafkvst1ko9C3wgDHr2ZnFVS+6bCQXktP9UxQLARD9YYPFe/G69INkWJCsyh/4+2x LfX1qqu9YuNGt73tNuv3xIkUO4jrckx8IbDxQ2dCQWvto6o8UyXiH8j5LKhxxPOIVRcZHdzwa+L jq6DKBDtDLmPAtrdYV4CqvgRHn+qQav1xLUOwP4IoqXnm0vBTpGAbvuGzMBvSxoS0TTzrlE0XHt DQx8LowFqKABaNUhd+lvvu3zI5dR5h0L4D1Cw9Q06BfvdDw1pYvm//9/hYmxWkJpAIWaEQOGqKs 5gEHOpUlOCLMpUTjYCWpgwGvI08n58YOFbl82MBOWiMoJCjungOppaxW+3sJhk5JENX97yGlZ9l cCWaVT8RZHVAaqL56Ya4r8JS4eMm0j+NLSBier+NMPkUqaCbtM+BFcasFvtwMqs4evQcCzsVc5x /sVvR2ZX3dKo9DjKnDGMttgb3gI6eySvS5xkz8i05Y= X-Received: by 2002:a05:6214:5909:b0:8da:eb2e:a113 with SMTP id 6a1803df08f44-8dd570d471emr4588886d6.34.1781751604023; Wed, 17 Jun 2026 20:00:04 -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.03 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 17 Jun 2026 20:00:03 -0700 (PDT) From: Michael Bommarito To: Krzysztof Kozlowski , Dan Carpenter Cc: Jakub Kicinski , Kees Cook , linux-kernel@vger.kernel.org Subject: [PATCH 2/2] nfc: pn533: add KUnit tests for receive-path bounds Date: Wed, 17 Jun 2026 22:59:53 -0400 Message-ID: <20260618025953.1878757-3-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 Two regression tests: a header-only standard frame declaring datalen = 8 (KASAN slab-out-of-bounds in pn533_std_rx_frame_is_valid on an unpatched tree), and a header-only Type A target with nfcid_len = 10 (pn533_target_found_type_a returns -EPROTO once bounded). Benign controls exercise the valid path. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Michael Bommarito --- drivers/nfc/pn533/Kconfig | 9 ++++ drivers/nfc/pn533/pn533.c | 93 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/drivers/nfc/pn533/Kconfig b/drivers/nfc/pn533/Kconfig index 31822b432d51c..a0ebbb75419b1 100644 --- a/drivers/nfc/pn533/Kconfig +++ b/drivers/nfc/pn533/Kconfig @@ -37,3 +37,12 @@ config NFC_PN532_UART If you choose to build a module, it'll be called pn532_uart. Say N if unsure. + +config NFC_PN533_KUNIT_TEST + bool "KUnit tests for the NFC PN533 driver" if !KUNIT_ALL_TESTS + depends on NFC_PN533=y && KUNIT=y + default KUNIT_ALL_TESTS + help + Enable the KUnit regression tests for the NFC PN533 driver, + covering bounds checking of device-supplied frame lengths. + If unsure, say N. diff --git a/drivers/nfc/pn533/pn533.c b/drivers/nfc/pn533/pn533.c index 984a61a83feaf..47d2b4b56bc2e 100644 --- a/drivers/nfc/pn533/pn533.c +++ b/drivers/nfc/pn533/pn533.c @@ -2883,3 +2883,96 @@ MODULE_AUTHOR("Waldemar Rymarkiewicz "); MODULE_DESCRIPTION("PN533 driver ver " VERSION); MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); + +#if IS_ENABLED(CONFIG_NFC_PN533_KUNIT_TEST) +#include + +/* + * Regression test for the out-of-bounds read in the PN533 standard-frame + * validator: pn533_std_rx_frame_is_valid() trusts the device-supplied + * datalen field and reads data[0 .. datalen-1] (the data checksum) without + * checking that the received buffer actually contains that many bytes. A + * malicious USB PN53x device returning a short transfer therefore makes the + * driver read past the receive buffer. + */ +static void pn533_std_rx_frame_short_oob_test(struct kunit *test) +{ + struct pn533 *dev; + struct pn533_frame_ops *ops; + struct pn533_std_frame *f; + const size_t hdr = sizeof(struct pn533_std_frame); /* header, no data[] */ + + dev = kunit_kzalloc(test, sizeof(*dev), GFP_KERNEL); + ops = kunit_kzalloc(test, sizeof(*ops), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, dev); + KUNIT_ASSERT_NOT_NULL(test, ops); + dev->ops = ops; + + /* benign control: a correctly sized, well-formed frame validates and + * does not read out of bounds (proves the trigger reaches the real + * data-checksum path). data is all zero: data checksum = 0, and the + * trailing frame-checksum byte data[datalen] is already 0. + */ + { + const u8 dl = 3; + const size_t full = hdr + dl + PN533_STD_FRAME_TAIL_LEN; + struct pn533_std_frame *gf = kunit_kzalloc(test, full, GFP_KERNEL); + + KUNIT_ASSERT_NOT_NULL(test, gf); + gf->start_frame = cpu_to_be16(PN533_STD_FRAME_SOF); + gf->datalen = dl; + gf->datalen_checksum = pn533_std_checksum(dl); + KUNIT_EXPECT_TRUE(test, + pn533_std_rx_frame_is_valid(gf, full, dev)); + } + + /* attack: a header-only buffer that declares datalen = 8. The data + * checksum loop reads data[0 .. 7] starting one byte past the + * allocation -> KASAN slab-out-of-bounds read on an unpatched tree. + */ + f = kunit_kzalloc(test, hdr, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, f); + f->start_frame = cpu_to_be16(PN533_STD_FRAME_SOF); + f->datalen = 8; + f->datalen_checksum = pn533_std_checksum(8); + pn533_std_rx_frame_is_valid(f, hdr, dev); +} + +static void pn533_target_type_a_nfcid_oob_test(struct kunit *test) +{ + struct nfc_target nfc_tgt; + struct pn533_target_type_a *ta; + const size_t hdr = sizeof(struct pn533_target_type_a); /* 4, no nfcid */ + + /* header-only target data, but nfcid_len claims the full 10 bytes -> + * pn533_target_found_type_a()'s memcpy reads nfcid_data[0..9], past the + * allocation -> KASAN slab-out-of-bounds on an unpatched tree. + */ + ta = kunit_kzalloc(test, hdr, GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, ta); + ta->sens_res = cpu_to_be16(0x0001); + ta->sel_res = 0; + ta->nfcid_len = NFC_NFCID1_MAXSIZE; /* 10 */ + + /* unpatched: is_valid accepts (nfcid_len only checked vs the + * destination) and the memcpy reads nfcid_data[0..9] past the 4-byte + * buffer. The fix bounds nfcid_len against the response buffer and + * rejects the frame, returning -EPROTO. + */ + KUNIT_EXPECT_EQ(test, + pn533_target_found_type_a(&nfc_tgt, (u8 *)ta, hdr), + -EPROTO); +} + +static struct kunit_case pn533_test_cases[] = { + KUNIT_CASE(pn533_std_rx_frame_short_oob_test), + KUNIT_CASE(pn533_target_type_a_nfcid_oob_test), + {} +}; + +static struct kunit_suite pn533_test_suite = { + .name = "pn533", + .test_cases = pn533_test_cases, +}; +kunit_test_suite(pn533_test_suite); +#endif /* CONFIG_NFC_PN533_KUNIT_TEST */ -- 2.53.0