* [PATCH 1/2] crypto: qat - validate migration section header is in bounds
2026-06-14 13:06 [PATCH 0/2] crypto: qat - bound the live migration import parser Michael Bommarito
@ 2026-06-14 13:06 ` Michael Bommarito
2026-06-14 13:06 ` [PATCH 2/2] crypto: qat - add KUnit coverage for the migration import parser Michael Bommarito
1 sibling, 0 replies; 3+ messages in thread
From: Michael Bommarito @ 2026-06-14 13:06 UTC (permalink / raw)
To: Giovanni Cabiddu, Herbert Xu
Cc: David S . Miller, Kees Cook, qat-linux, linux-crypto,
linux-kernel
adf_mstate_mgr_init_from_remote() sets the section-walk cursor to
mgr->buf + preh_len from the remote migration preamble. The default
preamble checker only rejects preh_len > mgr->size, so preh_len ==
mgr->size (the 4096-byte QAT VF state buffer) puts mgr->state one
region past the allocation while n_sects is still honoured.
adf_mstate_sect_validate() then reads sect->size from that cursor
before proving the section header is in the buffer. The remote stream
reaches this parser from the destination-host VFIO migration path
(qat_vf_resume_write), so a malformed import reads out of bounds.
Reject section headers not fully contained in the state buffer before
dereferencing any of their fields.
Reproduced under KASAN on QEMU via the KUnit case in patch 2; the
slab-out-of-bounds read is gone after this change.
Fixes: f0bbfc391aa7 ("crypto: qat - implement interface for live migration")
Cc: stable@vger.kernel.org
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
The patch 2 KUnit case drives the real parser on a kmalloc(4096) buffer
under KASAN on QEMU x86_64. Trigger {preh_len=4096, n_sects=1}: stock
tree reports
BUG: KASAN: slab-out-of-bounds in qat_mstate_remote_run
reading sect->size 8 bytes past the allocation; patched it returns
-EINVAL and KASAN is silent. Two benign controls (empty preamble,
in-bounds section header) drive the same path with no OOB and pass on
both trees. No in-tree selftest exercises adf_mstate_mgr.c; patch 2 is
the coverage offered. KASAN build of the touched object is warning clean.
.../crypto/intel/qat/qat_common/adf_mstate_mgr.c | 14 ++++++++++++--
1 file changed, 12 insertions(+), 2 deletions(-)
diff --git a/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c b/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c
index f9017e03ec0f2..7370b87f72a2f 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c
@@ -231,8 +231,18 @@ static int adf_mstate_sect_validate(struct adf_mstate_mgr *mgr)
end = (uintptr_t)mgr->buf + mgr->size;
for (i = 0; i < mgr->n_sects; i++) {
- uintptr_t s_start = (uintptr_t)sect->state;
- uintptr_t s_end = s_start + sect->size;
+ uintptr_t s_start, s_end;
+
+ /* The section header must be in the buffer before it is read. */
+ if ((uintptr_t)sect < (uintptr_t)mgr->buf ||
+ (uintptr_t)sect > end - sizeof(*sect)) {
+ pr_debug("QAT: LM - Section header out of bounds (index=%u) in state_mgr (size=%u, secs=%u)\n",
+ i, mgr->size, mgr->n_sects);
+ return -EINVAL;
+ }
+
+ s_start = (uintptr_t)sect->state;
+ s_end = s_start + sect->size;
if (s_end < s_start || s_end > end) {
pr_debug("QAT: LM - Corrupted state section (index=%u, size=%u) in state_mgr (size=%u, secs=%u)\n",
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread* [PATCH 2/2] crypto: qat - add KUnit coverage for the migration import parser
2026-06-14 13:06 [PATCH 0/2] crypto: qat - bound the live migration import parser Michael Bommarito
2026-06-14 13:06 ` [PATCH 1/2] crypto: qat - validate migration section header is in bounds Michael Bommarito
@ 2026-06-14 13:06 ` Michael Bommarito
1 sibling, 0 replies; 3+ messages in thread
From: Michael Bommarito @ 2026-06-14 13:06 UTC (permalink / raw)
To: Giovanni Cabiddu, Herbert Xu
Cc: David S . Miller, Kees Cook, qat-linux, linux-crypto,
linux-kernel
Add KUnit coverage for the remote (migration import) path of the QAT
live migration state manager. The cases drive the real
adf_mstate_mgr_init_from_remote() -> adf_mstate_sect_validate() parser
on a buffer sized as the GEN4 VFIO migration backend allocates it, and
include the preh_len == buffer-size boundary case that is the
regression oracle for the preceding fix. The test file is included from
adf_mstate_mgr.c so it can reach the file-local preamble and section
types, gated by CONFIG_CRYPTO_DEV_QAT_KUNIT_TEST. It is offered as a
separate patch so it can be taken or dropped independently of the fix.
Reproduced under KASAN on QEMU; the boundary case reports the
slab-out-of-bounds read on an unfixed tree and passes once the fix is
in place.
Assisted-by: Claude:claude-opus-4-8
Signed-off-by: Michael Bommarito <michael.bommarito@gmail.com>
---
Three cases under KASAN on QEMU x86_64: a valid empty preamble, a valid
in-bounds section header, and the preh_len == buffer-size (4096) boundary
case. The boundary case is the regression oracle for patch 1: it reports
the slab-out-of-bounds read on an unfixed tree and returns -EINVAL with
the fix in place. The two valid cases drive the same parser path with no
out-of-bounds access and pass on both trees.
drivers/crypto/intel/qat/Kconfig | 16 ++++
.../intel/qat/qat_common/adf_mstate_mgr.c | 4 +
.../qat/qat_common/adf_mstate_mgr_test.c | 81 +++++++++++++++++++
3 files changed, 101 insertions(+)
create mode 100644 drivers/crypto/intel/qat/qat_common/adf_mstate_mgr_test.c
diff --git a/drivers/crypto/intel/qat/Kconfig b/drivers/crypto/intel/qat/Kconfig
index 9d6e6f52d2dcb..116f7f94c9a64 100644
--- a/drivers/crypto/intel/qat/Kconfig
+++ b/drivers/crypto/intel/qat/Kconfig
@@ -133,3 +133,19 @@ config CRYPTO_DEV_QAT_ERROR_INJECTION
This functionality is available via debugfs entry of the Intel(R)
QuickAssist device
+
+config CRYPTO_DEV_QAT_KUNIT_TEST
+ bool "KUnit tests for Intel(R) QAT live migration state manager" if !KUNIT_ALL_TESTS
+ depends on CRYPTO_DEV_QAT && KUNIT=y
+ default KUNIT_ALL_TESTS
+ help
+ Build KUnit tests for the Intel(R) QAT live migration state
+ manager remote-import parser (adf_mstate_mgr.c). The tests drive
+ the migration setup parser on a buffer sized as the QAT GEN4
+ VFIO migration backend allocates it and check that a malformed
+ remote preamble is rejected before any out-of-bounds access.
+
+ For more information on KUnit and unit tests in general, please
+ refer to the KUnit documentation in Documentation/dev-tools/kunit/.
+
+ If unsure, say N.
diff --git a/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c b/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c
index 7370b87f72a2f..701279409c32c 100644
--- a/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c
+++ b/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr.c
@@ -326,3 +326,7 @@ found:
return sect;
}
+
+#if IS_ENABLED(CONFIG_CRYPTO_DEV_QAT_KUNIT_TEST)
+#include "adf_mstate_mgr_test.c"
+#endif
diff --git a/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr_test.c b/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr_test.c
new file mode 100644
index 0000000000000..e067c13f4a9dd
--- /dev/null
+++ b/drivers/crypto/intel/qat/qat_common/adf_mstate_mgr_test.c
@@ -0,0 +1,81 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/* Copyright(c) 2026 Intel Corporation */
+
+/*
+ * KUnit coverage for the QAT live migration remote-import parser. The cases
+ * drive the real adf_mstate_mgr_init_from_remote() on a buffer sized as the
+ * GEN4 VFIO migration backend allocates it (4096 bytes), including the
+ * preh_len == buffer-size boundary case. Included from adf_mstate_mgr.c to
+ * reach the file-local preamble and section types.
+ */
+
+#include <kunit/test.h>
+
+#define ADF_MSTATE_TEST_BUF_SIZE 4096
+
+static void qat_mstate_remote_run(struct kunit *test, u16 preh_len,
+ u16 n_sects, u32 sect0_size, int expect)
+{
+ struct adf_mstate_mgr mgr;
+ struct adf_mstate_preh *pre;
+ u8 *buf;
+ int ret;
+
+ buf = kzalloc(ADF_MSTATE_TEST_BUF_SIZE, GFP_KERNEL);
+ KUNIT_ASSERT_NOT_NULL(test, buf);
+
+ pre = (struct adf_mstate_preh *)buf;
+ pre->magic = ADF_MSTATE_MAGIC;
+ pre->version = ADF_MSTATE_VERSION;
+ pre->preh_len = preh_len;
+ pre->n_sects = n_sects;
+ pre->size = 0;
+
+ /* Place an in-bounds section header when there is room for one. */
+ if (n_sects &&
+ (u32)preh_len + sizeof(struct adf_mstate_sect_h) <= ADF_MSTATE_TEST_BUF_SIZE) {
+ struct adf_mstate_sect_h *s =
+ (struct adf_mstate_sect_h *)(buf + preh_len);
+
+ s->size = sect0_size;
+ s->sub_sects = 0;
+ }
+
+ ret = adf_mstate_mgr_init_from_remote(&mgr, buf, ADF_MSTATE_TEST_BUF_SIZE,
+ NULL, NULL);
+ KUNIT_EXPECT_EQ(test, ret, expect);
+
+ kfree(buf);
+}
+
+/* Valid empty preamble: the validation loop never runs. */
+static void qat_mstate_remote_empty(struct kunit *test)
+{
+ qat_mstate_remote_run(test, sizeof(struct adf_mstate_preh), 0, 0, 0);
+}
+
+/* Valid in-bounds section header: same parser path, no out-of-bounds read. */
+static void qat_mstate_remote_inbounds_sect(struct kunit *test)
+{
+ qat_mstate_remote_run(test, sizeof(struct adf_mstate_preh), 1, 0, 0);
+}
+
+/* preh_len == buffer size puts the cursor past the allocation; expect -EINVAL. */
+static void qat_mstate_remote_oob_header(struct kunit *test)
+{
+ qat_mstate_remote_run(test, ADF_MSTATE_TEST_BUF_SIZE, 1, 0, -EINVAL);
+}
+
+static struct kunit_case qat_mstate_remote_cases[] = {
+ KUNIT_CASE(qat_mstate_remote_empty),
+ KUNIT_CASE(qat_mstate_remote_inbounds_sect),
+ KUNIT_CASE(qat_mstate_remote_oob_header),
+ {}
+};
+
+static struct kunit_suite qat_mstate_remote_suite = {
+ .name = "qat_mstate_remote",
+ .test_cases = qat_mstate_remote_cases,
+};
+
+kunit_test_suite(qat_mstate_remote_suite);
--
2.53.0
^ permalink raw reply related [flat|nested] 3+ messages in thread