[PATCH 2/2] crypto: qat - add KUnit coverage for the migration import parser

From: Michael Bommarito

Date: Sun Jun 14 2026 - 09:07:46 EST


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@xxxxxxxxx>
---
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