{"id":2230818,"url":"http://patchwork.ozlabs.org/api/1.1/patches/2230818/?format=json","web_url":"http://patchwork.ozlabs.org/project/qemu-devel/patch/20260430050103.3172190-1-physicalmtea@gmail.com/","project":{"id":14,"url":"http://patchwork.ozlabs.org/api/1.1/projects/14/?format=json","name":"QEMU Development","link_name":"qemu-devel","list_id":"qemu-devel.nongnu.org","list_email":"qemu-devel@nongnu.org","web_url":"","scm_url":"","webscm_url":""},"msgid":"<20260430050103.3172190-1-physicalmtea@gmail.com>","date":"2026-04-30T05:01:03","name":"hw/cxl: bound Set Feature cleanup to written extent","commit_ref":null,"pull_url":null,"state":"new","archived":false,"hash":"8d08c214a2447cccb9b3994a8bbc3ad01c09b19f","submitter":{"id":93269,"url":"http://patchwork.ozlabs.org/api/1.1/people/93269/?format=json","name":"Jia Jia","email":"physicalmtea@gmail.com"},"delegate":null,"mbox":"http://patchwork.ozlabs.org/project/qemu-devel/patch/20260430050103.3172190-1-physicalmtea@gmail.com/mbox/","series":[{"id":502195,"url":"http://patchwork.ozlabs.org/api/1.1/series/502195/?format=json","web_url":"http://patchwork.ozlabs.org/project/qemu-devel/list/?series=502195","date":"2026-04-30T05:01:03","name":"hw/cxl: bound Set Feature cleanup to written extent","version":1,"mbox":"http://patchwork.ozlabs.org/series/502195/mbox/"}],"comments":"http://patchwork.ozlabs.org/api/patches/2230818/comments/","check":"pending","checks":"http://patchwork.ozlabs.org/api/patches/2230818/checks/","tags":{},"headers":{"Return-Path":"<qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org>","X-Original-To":"incoming@patchwork.ozlabs.org","Delivered-To":"patchwork-incoming@legolas.ozlabs.org","Authentication-Results":["legolas.ozlabs.org;\n\tdkim=pass (2048-bit key;\n unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256\n header.s=20251104 header.b=prk+x0cr;\n\tdkim-atps=neutral","legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=nongnu.org\n (client-ip=209.51.188.17; helo=lists1p.gnu.org;\n envelope-from=qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org;\n receiver=patchwork.ozlabs.org)"],"Received":["from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17])\n\t(using TLSv1.2 with cipher ECDHE-ECDSA-AES256-GCM-SHA384 (256/256 bits))\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4g5hr65LyZz1yHv\n\tfor <incoming@patchwork.ozlabs.org>; Thu, 30 Apr 2026 15:02:45 +1000 (AEST)","from localhost ([::1] helo=lists1p.gnu.org)\n\tby lists1p.gnu.org with esmtp (Exim 4.90_1)\n\t(envelope-from <qemu-devel-bounces@nongnu.org>)\n\tid 1wIJXk-0000ak-4f; Thu, 30 Apr 2026 01:02:32 -0400","from eggs.gnu.org ([2001:470:142:3::10])\n by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)\n (Exim 4.90_1) (envelope-from <physicalmtea@gmail.com>)\n id 1wIJX7-0000FC-Br\n for qemu-devel@nongnu.org; Thu, 30 Apr 2026 01:01:57 -0400","from mail-pl1-x632.google.com ([2607:f8b0:4864:20::632])\n by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128)\n (Exim 4.90_1) (envelope-from <physicalmtea@gmail.com>)\n id 1wIJWR-00057x-UA\n for qemu-devel@nongnu.org; Thu, 30 Apr 2026 01:01:50 -0400","by mail-pl1-x632.google.com with SMTP id\n d9443c01a7336-2b24fede2acso3002405ad.3\n for <qemu-devel@nongnu.org>; Wed, 29 Apr 2026 22:01:11 -0700 (PDT)","from localhost.localdomain ([114.249.134.218])\n by smtp.gmail.com with ESMTPSA id\n d9443c01a7336-2b988963ed0sm39527405ad.65.2026.04.29.22.01.06\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Wed, 29 Apr 2026 22:01:09 -0700 (PDT)"],"DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=gmail.com; s=20251104; t=1777525270; x=1778130070; darn=nongnu.org;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:from:to:cc:subject:date:message-id:reply-to;\n bh=yQ10o/nhNMp9WiPLO3oSpqcfumGQlVQMd3hRta3eci0=;\n b=prk+x0crdHLN1fKm8sw9lNOCLlurerEG/Kv+XiJy/mPxH53gLKqCTQ69aC/1lC5WRw\n v3bgGr4JzQKn2Q63jhzGYaARJegu+7mIOfJwL5evAeOKB7g8L6hDe5Ry7S6UVmP/tB26\n 9dk05Nxk1b5+/I39bBwaB0BQeRcmYvnD8CMWGUTDxKST1cFdHiQp8bP34UB5v/iSDua3\n okBckwqtMPXCafjzzh5ONI0P8bxXeemxNvP+tbhYxeDBrinY/xy8it+juojv8prD7UF7\n 7cJSenDo9xLQhnA+z7VZeX/KBABH1oqgmmcPVZas49PaPWkOuYdT9nTIitlNLOiDraJd\n x3qg==","X-Google-DKIM-Signature":"v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20251104; t=1777525270; x=1778130070;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n :message-id:reply-to;\n bh=yQ10o/nhNMp9WiPLO3oSpqcfumGQlVQMd3hRta3eci0=;\n b=LtRHJiIH23TpeEt/46v3l/HzXuX9umbcCV3lPWtHi1M3ghsEyFguNDvk1CfFqB27cs\n nvmnrLAAKt9NpyqizyjdADLlYo+aHrrNdPROxWf24THWH+RIy0xLb5/Vfi9VBzZ8tZuc\n cR6FaW7JzIFLa3dmt3i2OkdxRbAK5D5Sb8FsD+u02u2IO12vMch0GztiKK56B3TO7B4a\n pPMfKQXpjE7ryK9BRbANln563+9iujKnYCzen1Vv3J/NaCMsLMhiuMKpBSfSgl+29C+q\n 6wgw+tX8eQ/L8yzREB7PXfmoNuzI17hBVtT8t4GUW6JKo5Cizp8klEPpWXaJ0GBUR1V4\n pK/A==","X-Gm-Message-State":"AOJu0YwH4lEJ2SXtKjWEp9jDQJf+cyWJ4fgbR+SqERqv9J8ud9M3McHL\n Mkkw4sjV0ZpUbtPBIfnahbfQjRvMRChxKUCLBwHrcMMH1MPnF0U2Zs0lr97Ql15bDPgKnQ==","X-Gm-Gg":"AeBDietN5x57hmtK6m8wIp6WUDtXlYq48Sgo2f2JlfYFi/YPJNRBiL/vHmsKu96+X23\n UISGS/pYpEUME0Lf6W+6rU1eX4TY7zfwSNgeVuxKrOpwNgbU4IPEhrd2l1UXG6FbjHzYLnWH2iY\n j+wNH2otiFtkwqulGFHASonWY1rqecSoXviF7EynSHADMT1xIbLCfD5r2qQZN3j/gcqlQBiqNar\n CzFWc6njBS4Ru0LhSOqURppm/kYNYzoAV8erOn6Qtcks7aKvf9dkf+ZllOX5izzePrzGY2KtA+g\n w5geJ31l1nLN1L7BiBLmbLq2abJbk1hxKbOgoMSPtvhV1p6nu+zJasmew2bXZmH3SSoBzvbwrNe\n v7Z/Ck1+5P0o2PGp7amVo5w+V1i49gmU8u3Jm/5bqdmRkKdPqtNaW5PH8ati1E5sO3ytlqX702I\n Q2oIr07oX/xk0XQ3Y7Np7nqQ008PqkDgRCOfbLn3pmcQ1EHPU1m64=","X-Received":"by 2002:a17:903:3c4f:b0:2b0:6e6a:8504 with SMTP id\n d9443c01a7336-2b9a24b954fmr12675645ad.27.1777525269442;\n Wed, 29 Apr 2026 22:01:09 -0700 (PDT)","From":"Jia Jia <physicalmtea@gmail.com>","To":"qemu-devel@nongnu.org","Cc":"jonathan.cameron@huawei.com, fan.ni@samsung.com, farosas@suse.de,\n lvivier@redhat.com, pbonzini@redhat.com","Subject":"[PATCH] hw/cxl: bound Set Feature cleanup to written extent","Date":"Thu, 30 Apr 2026 13:01:03 +0800","Message-Id":"<20260430050103.3172190-1-physicalmtea@gmail.com>","X-Mailer":"git-send-email 2.34.1","MIME-Version":"1.0","Content-Transfer-Encoding":"8bit","Received-SPF":"pass client-ip=2607:f8b0:4864:20::632;\n envelope-from=physicalmtea@gmail.com; helo=mail-pl1-x632.google.com","X-Spam_score_int":"-20","X-Spam_score":"-2.1","X-Spam_bar":"--","X-Spam_report":"(-2.1 / 5.0 requ) BAYES_00=-1.9, DKIM_SIGNED=0.1,\n DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, FREEMAIL_FROM=0.001,\n RCVD_IN_DNSWL_NONE=-0.0001, SPF_HELO_NONE=0.001,\n SPF_PASS=-0.001 autolearn=ham autolearn_force=no","X-Spam_action":"no action","X-BeenThere":"qemu-devel@nongnu.org","X-Mailman-Version":"2.1.29","Precedence":"list","List-Id":"qemu development <qemu-devel.nongnu.org>","List-Unsubscribe":"<https://lists.nongnu.org/mailman/options/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=unsubscribe>","List-Archive":"<https://lists.nongnu.org/archive/html/qemu-devel>","List-Post":"<mailto:qemu-devel@nongnu.org>","List-Help":"<mailto:qemu-devel-request@nongnu.org?subject=help>","List-Subscribe":"<https://lists.nongnu.org/mailman/listinfo/qemu-devel>,\n <mailto:qemu-devel-request@nongnu.org?subject=subscribe>","Errors-To":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org","Sender":"qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org"},"content":"cmd_features_set_feature() validates each fragment against the target\nwrite-attribute buffer, but it tracks data_size as the sum of every\nfragment copied so far.\n\nThat becomes a problem at transfer completion. Cleanup uses data_size as\nits memset() length, so a sequence of legal partial transfers can keep\nrewriting the same small range while growing data_size far beyond the\nfeature buffer that is actually being updated.\n\nOn an ASan build, repeatedly sending 2-byte rank_sparing fragments at\noffset 0 and then finishing aborts the host with:\n\n  ERROR: AddressSanitizer: heap-buffer-overflow\n  WRITE of size 1144\n      #0 __interceptor_memset\n      #1 cmd_features_set_feature ../hw/cxl/cxl-mailbox-utils.c:1961\n      #2 cxl_process_cci_message ../hw/cxl/cxl-mailbox-utils.c:4642\n      #3 mailbox_reg_write ../hw/cxl/cxl-device-utils.c:209\n\nFix this by tracking the maximum written extent instead of the cumulative\nnumber of copied bytes. That keeps cleanup bounded to the actual\nwrite-attribute window without changing the rest of the transfer flow.\n\nAdd a qtest that confirms a multipart bank_sparing transfer does not\nclobber adjacent rank_sparing state during cleanup.\n\nResolves: https://gitlab.com/qemu-project/qemu/-/issues/3461\nSigned-off-by: Jia Jia <physicalmtea@gmail.com>\n\n---\n hw/cxl/cxl-mailbox-utils.c |  11 +---\n tests/qtest/cxl-test.c     | 119 ++++++++++++++++++++++++++++++++++++-\n 2 files changed, 121 insertions(+), 9 deletions(-)","diff":"diff --git a/hw/cxl/cxl-mailbox-utils.c b/hw/cxl/cxl-mailbox-utils.c\nindex ce139e30eb..8346177b79 100644\n--- a/hw/cxl/cxl-mailbox-utils.c\n+++ b/hw/cxl/cxl-mailbox-utils.c\n@@ -1769,7 +1769,6 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,\n         memcpy((uint8_t *)&ct3d->patrol_scrub_wr_attrs + hdr->offset,\n                ps_write_attrs,\n                bytes_to_copy);\n-        set_feat_info->data_size += bytes_to_copy;\n \n         if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||\n             data_transfer_flag ==  CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {\n@@ -1796,7 +1795,6 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,\n         memcpy((uint8_t *)&ct3d->ecs_wr_attrs + hdr->offset,\n                ecs_write_attrs,\n                bytes_to_copy);\n-        set_feat_info->data_size += bytes_to_copy;\n \n         if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||\n             data_transfer_flag ==  CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {\n@@ -1820,7 +1818,6 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,\n         }\n         memcpy((uint8_t *)&ct3d->soft_ppr_wr_attrs + hdr->offset,\n                sppr_write_attrs, bytes_to_copy);\n-        set_feat_info->data_size += bytes_to_copy;\n \n         if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||\n             data_transfer_flag ==  CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {\n@@ -1842,7 +1839,6 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,\n         }\n         memcpy((uint8_t *)&ct3d->hard_ppr_wr_attrs + hdr->offset,\n                hppr_write_attrs, bytes_to_copy);\n-        set_feat_info->data_size += bytes_to_copy;\n \n         if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||\n             data_transfer_flag ==  CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {\n@@ -1864,7 +1860,6 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,\n         }\n         memcpy((uint8_t *)&ct3d->cacheline_sparing_wr_attrs + hdr->offset,\n                mem_sparing_write_attrs, bytes_to_copy);\n-        set_feat_info->data_size += bytes_to_copy;\n \n         if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||\n             data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {\n@@ -1885,7 +1880,6 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,\n         }\n         memcpy((uint8_t *)&ct3d->row_sparing_wr_attrs + hdr->offset,\n                mem_sparing_write_attrs, bytes_to_copy);\n-        set_feat_info->data_size += bytes_to_copy;\n \n         if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||\n             data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {\n@@ -1906,7 +1900,6 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,\n         }\n         memcpy((uint8_t *)&ct3d->bank_sparing_wr_attrs + hdr->offset,\n                mem_sparing_write_attrs, bytes_to_copy);\n-        set_feat_info->data_size += bytes_to_copy;\n \n         if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||\n             data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {\n@@ -1927,7 +1920,6 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,\n         }\n         memcpy((uint8_t *)&ct3d->rank_sparing_wr_attrs + hdr->offset,\n                mem_sparing_write_attrs, bytes_to_copy);\n-        set_feat_info->data_size += bytes_to_copy;\n \n         if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||\n             data_transfer_flag == CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER) {\n@@ -1938,6 +1930,9 @@ static CXLRetCode cmd_features_set_feature(const struct cxl_cmd *cmd,\n         return CXL_MBOX_UNSUPPORTED;\n     }\n \n+    set_feat_info->data_size = MAX(set_feat_info->data_size,\n+                                   (size_t)end_offset);\n+\n     if (data_transfer_flag == CXL_SET_FEATURE_FLAG_FULL_DATA_TRANSFER ||\n         data_transfer_flag ==  CXL_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER ||\n         data_transfer_flag ==  CXL_SET_FEATURE_FLAG_ABORT_DATA_TRANSFER) {\ndiff --git a/tests/qtest/cxl-test.c b/tests/qtest/cxl-test.c\nindex a9fcd98736..ed222aca4b 100644\n--- a/tests/qtest/cxl-test.c\n+++ b/tests/qtest/cxl-test.c\n@@ -99,6 +99,21 @@ typedef struct QEMU_PACKED CXLSetFeatureInHeaderTest {\n     uint8_t rsvd[9];\n } CXLSetFeatureInHeaderTest;\n \n+typedef struct QEMU_PACKED CXLGetFeatureInHeaderTest {\n+    uint8_t uuid[16];\n+    uint16_t offset;\n+    uint16_t count;\n+    uint8_t selection;\n+} CXLGetFeatureInHeaderTest;\n+\n+enum {\n+    CXL_TEST_GET_FEATURE_SEL_CURRENT_VALUE = 0,\n+    CXL_TEST_SET_FEATURE_FLAG_FULL_DATA_TRANSFER = 0,\n+    CXL_TEST_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER = 1,\n+    CXL_TEST_SET_FEATURE_FLAG_CONTINUE_DATA_TRANSFER = 2,\n+    CXL_TEST_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER = 3,\n+};\n+\n static void cxl_basic_hb(void)\n {\n     qtest_start(\"-machine q35,cxl=on\");\n@@ -177,15 +192,37 @@ static uint16_t cxl_test_t3d_mailbox_errno(void)\n \n static void cxl_test_fill_set_feature_header(CXLSetFeatureInHeaderTest *hdr,\n                                              const uint8_t uuid[16],\n+                                             uint32_t flags,\n                                              uint16_t offset,\n                                              uint8_t version)\n {\n     memset(hdr, 0, sizeof(*hdr));\n     memcpy(hdr->uuid, uuid, 16);\n+    hdr->flags = cpu_to_le32(flags);\n     hdr->offset = cpu_to_le16(offset);\n     hdr->version = version;\n }\n \n+static void cxl_test_fill_get_feature_header(CXLGetFeatureInHeaderTest *hdr,\n+                                             const uint8_t uuid[16],\n+                                             uint16_t offset,\n+                                             uint16_t count)\n+{\n+    memset(hdr, 0, sizeof(*hdr));\n+    memcpy(hdr->uuid, uuid, 16);\n+    hdr->offset = cpu_to_le16(offset);\n+    hdr->count = cpu_to_le16(count);\n+    hdr->selection = CXL_TEST_GET_FEATURE_SEL_CURRENT_VALUE;\n+}\n+\n+static void cxl_test_t3d_submit_get_feature(const void *payload, size_t len)\n+{\n+    memwrite(cxl_test_t3d_payload_base(), payload, len);\n+    writeq(cxl_test_t3d_mailbox_base() + A_CXL_DEV_MAILBOX_CMD,\n+           ((uint64_t)len << 16) | (0x05 << 8) | 0x01);\n+    writel(cxl_test_t3d_mailbox_base() + A_CXL_DEV_MAILBOX_CTRL, 1);\n+}\n+\n static void cxl_t3d_set_feature_rejects_oversized_rank_sparing(void)\n {\n     static const uint8_t rank_sparing_uuid[16] = {\n@@ -203,7 +240,7 @@ static void cxl_t3d_set_feature_rejects_oversized_rank_sparing(void)\n     qtest_start(cmdline->str);\n     cxl_test_t3d_enable_bar2();\n \n-    cxl_test_fill_set_feature_header(hdr, rank_sparing_uuid, 0,\n+    cxl_test_fill_set_feature_header(hdr, rank_sparing_uuid, 0, 0,\n                                      CXL_MEMDEV_SPARING_SET_FEATURE_VERSION);\n     memset(payload + sizeof(*hdr), 0x41,\n            sizeof(payload) - sizeof(*hdr));\n@@ -215,6 +252,84 @@ static void cxl_t3d_set_feature_rejects_oversized_rank_sparing(void)\n     rmdir(tmpfs);\n }\n \n+static void cxl_t3d_set_feature_cleanup_stays_within_feature_state(void)\n+{\n+    static const uint8_t bank_sparing_uuid[16] = {\n+        0x36, 0x96, 0xb7, 0x78, 0xac, 0x90, 0x64, 0x4b,\n+        0xa4, 0xef, 0xfa, 0xac, 0x5d, 0x18, 0xa8, 0x63,\n+    };\n+    static const uint8_t rank_sparing_uuid[16] = {\n+        0x34, 0xdb, 0xaf, 0xf5, 0x05, 0x52, 0x42, 0x81,\n+        0x8f, 0x76, 0xda, 0x0b, 0x5e, 0x7a, 0x76, 0xa7,\n+    };\n+    g_autoptr(GString) cmdline = g_string_new(NULL);\n+    g_autofree const char *tmpfs = NULL;\n+    uint8_t set_payload[sizeof(CXLSetFeatureInHeaderTest) + 2] = { 0 };\n+    uint8_t get_payload[sizeof(CXLGetFeatureInHeaderTest)] = { 0 };\n+    uint8_t out[2];\n+    CXLSetFeatureInHeaderTest *set_hdr = (void *)set_payload;\n+    CXLGetFeatureInHeaderTest *get_hdr = (void *)get_payload;\n+    int i;\n+\n+    tmpfs = g_dir_make_tmp(\"cxl-test-XXXXXX\", NULL);\n+    g_string_printf(cmdline, QEMU_T3D_DIRECT_PMEM, tmpfs, tmpfs);\n+\n+    qtest_start(cmdline->str);\n+    cxl_test_t3d_enable_bar2();\n+\n+    cxl_test_fill_set_feature_header(\n+        set_hdr, rank_sparing_uuid,\n+        CXL_TEST_SET_FEATURE_FLAG_FULL_DATA_TRANSFER, 0,\n+        CXL_MEMDEV_SPARING_SET_FEATURE_VERSION);\n+    set_payload[sizeof(*set_hdr)] = 0x34;\n+    set_payload[sizeof(*set_hdr) + 1] = 0x12;\n+    cxl_test_t3d_submit_set_feature(set_payload, sizeof(set_payload));\n+    g_assert_cmphex(cxl_test_t3d_mailbox_errno(), ==, CXL_MBOX_SUCCESS);\n+\n+    cxl_test_fill_set_feature_header(set_hdr, bank_sparing_uuid,\n+                                     CXL_TEST_SET_FEATURE_FLAG_INITIATE_DATA_TRANSFER,\n+                                     0,\n+                                     CXL_MEMDEV_SPARING_SET_FEATURE_VERSION);\n+    set_payload[sizeof(*set_hdr)] = 0x78;\n+    set_payload[sizeof(*set_hdr) + 1] = 0x56;\n+    cxl_test_t3d_submit_set_feature(set_payload, sizeof(set_payload));\n+    g_assert_cmphex(cxl_test_t3d_mailbox_errno(), ==, CXL_MBOX_SUCCESS);\n+\n+    /* Repeating the same 2-byte fragment must not clobber adjacent state. */\n+    for (i = 0; i < 2; i++) {\n+        cxl_test_fill_set_feature_header(\n+            set_hdr, bank_sparing_uuid,\n+            CXL_TEST_SET_FEATURE_FLAG_CONTINUE_DATA_TRANSFER,\n+            0, CXL_MEMDEV_SPARING_SET_FEATURE_VERSION);\n+        set_payload[sizeof(*set_hdr)] = 0xaa;\n+        set_payload[sizeof(*set_hdr) + 1] = 0xbb;\n+        cxl_test_t3d_submit_set_feature(set_payload, sizeof(set_payload));\n+        g_assert_cmphex(cxl_test_t3d_mailbox_errno(), ==, CXL_MBOX_SUCCESS);\n+    }\n+\n+    cxl_test_fill_set_feature_header(set_hdr, bank_sparing_uuid,\n+                                     CXL_TEST_SET_FEATURE_FLAG_FINISH_DATA_TRANSFER,\n+                                     0,\n+                                     CXL_MEMDEV_SPARING_SET_FEATURE_VERSION);\n+    set_payload[sizeof(*set_hdr)] = 0xcc;\n+    set_payload[sizeof(*set_hdr) + 1] = 0xdd;\n+    cxl_test_t3d_submit_set_feature(set_payload, sizeof(set_payload));\n+    g_assert_cmphex(cxl_test_t3d_mailbox_errno(), ==, CXL_MBOX_SUCCESS);\n+\n+    cxl_test_fill_get_feature_header(get_hdr, rank_sparing_uuid,\n+                                     offsetof(CXLMemSparingReadAttrs, op_mode),\n+                                     2);\n+    cxl_test_t3d_submit_get_feature(get_payload, sizeof(get_payload));\n+    g_assert_cmphex(cxl_test_t3d_mailbox_errno(), ==, CXL_MBOX_SUCCESS);\n+\n+    memread(cxl_test_t3d_payload_base(), out, sizeof(out));\n+    g_assert_cmphex(out[0], ==, 0x34);\n+    g_assert_cmphex(out[1], ==, 0x12);\n+\n+    qtest_end();\n+    rmdir(tmpfs);\n+}\n+\n static void cxl_t3d_deprecated(void)\n {\n     g_autoptr(GString) cmdline = g_string_new(NULL);\n@@ -337,6 +452,8 @@ int main(int argc, char **argv)\n         qtest_add_func(\"/pci/cxl/type3_device_vmem_lsa\", cxl_t3d_volatile_lsa);\n         qtest_add_func(\"/pci/cxl/type3_device_set_feature_rank_sparing_bounds\",\n                        cxl_t3d_set_feature_rejects_oversized_rank_sparing);\n+        qtest_add_func(\"/pci/cxl/type3_device_set_feature_cleanup_bounds\",\n+                       cxl_t3d_set_feature_cleanup_stays_within_feature_state);\n         qtest_add_func(\"/pci/cxl/rp_x2_type3_x2\", cxl_1pxb_2rp_2t3d);\n         qtest_add_func(\"/pci/cxl/pxb_x2_root_port_x4_type3_x4\",\n                        cxl_2pxb_4rp_4t3d);\n","prefixes":[]}