AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/09/14 18:17 flow patch-triage
4m Args:
null

Results:
{
  "EnableConfigs": [
    "VIRTIO",
    "VIRTIO_BLK",
    "BLK_INLINE_ENCRYPTION",
    "VIRTIO_BLK_INLINE_ENCRYPTION"
  ],
  "FocusSymbols": [
    "virtblk_crypto_keyslot_program",
    "virtblk_crypto_keyslot_evict",
    "virtblk_crypto_derive_sw_secret",
    "virtblk_crypto_generate_key",
    "virtblk_crypto_prepare_key",
    "virtblk_crypto_import_key",
    "virtblk_get_crypto_modes",
    "virtblk_init_crypto",
    "virtblk_ctrl_vq_request",
    "virtblk_ctrlq_callback",
    "virtblk_ctrl_vq_drain",
    "virtblk_ctrl_vq_quiesce",
    "virtblk_setup_cmd",
    "virtblk_add_req"
  ],
  "KMSANReasoning": "The patch introduces support for inline encryption in virtio-blk and modifies `struct virtio_blk` by adding `struct blk_crypto_profile profile` and `bool crypto_profile_initialized`.\n\nIn `virtblk_probe()`, `vblk` is allocated with `kmalloc_obj(*vblk)` which does not zero the memory. The new field `crypto_profile_initialized` is only initialized (`= true`) if the device supports inline encryption and control VQs and `virtblk_init_crypto()` succeeds. If the device does not advertise inline encryption (or if initialization fails or on zoned devices), `vblk-\u003ecrypto_profile_initialized` and `vblk-\u003eprofile` remain uninitialized heap memory.\n\nSubsequently, reachable code paths read these uninitialized fields in conditional branches:\n1. In `virtblk_destroy_crypto()`, called from `virtblk_free_disk()` upon disk release/teardown, `if (vblk-\u003ecrypto_profile_initialized)` performs conditional branching on the uninitialized boolean field. If non-zero, it invokes `blk_crypto_profile_destroy(\u0026vblk-\u003eprofile)` on uninitialized data.\n2. In `virtblk_restore_priv()`, `if (vblk-\u003eprofile.slots)` reads the uninitialized pointer field from the unzeroed `vblk-\u003eprofile` struct.\n\nThese issues represent conditional branching and reading of uninitialized heap memory (criteria 2 and 3). Standard bug detectors such as KASAN and LOCKDEP cannot detect reads of uninitialized bytes within validly allocated slab objects; only KMSAN shadow tracking detects use of uninitialized memory. Therefore, a dedicated KMSAN fuzzing session is justified.",
  "NeedsKMSAN": true,
  "Reasoning": "The patch implements inline encryption support and a dedicated control virtqueue for virtio-blk. Virtio devices are standard virtual hardware emulated in QEMU/GCE environments and are accessible via block I/O requests, ioctls, and filesystems using blk-crypto. The changes introduce new control virtqueue handling with synchronization and WARN_ON assertions, request formatting for crypto read/write operations, and keyslot management callbacks (program, evict, derive_sw_secret, generate_key, prepare_key, import_key). Fuzzing is warranted to discover potential concurrency issues, buffer handling bugs, or assertion failures.",
  "WorthFuzzing": true
}

1/1 2026/09/14 18:17 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit a6f70448193f9a3938cdc448e8e06cb3eecf4b9a\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Mon Sep 14 18:17:05 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/drivers/block/Kconfig b/drivers/block/Kconfig\nindex 858320b6ebb7e..58bb050d4617c 100644\n--- a/drivers/block/Kconfig\n+++ b/drivers/block/Kconfig\n@@ -372,4 +372,16 @@ config BLK_DEV_ZONED_LOOP\n \n \t  If unsure, say N.\n \n+config VIRTIO_BLK_INLINE_ENCRYPTION\n+\ttristate \"Virtio block inline encryption support\"\n+\tdepends on VIRTIO_BLK \u0026\u0026 BLK_INLINE_ENCRYPTION\n+\thelp\n+\t  Say 'Y or M' here will allow the virtio block driver to route crypto\n+\t  requests to a different operating system in a virtualized\n+\t  environment. This is useful to encrypt the data stored in the storage\n+\t  by using storage inline crypto engine. The control queue feature bit\n+\t  must be negotiated to enable this functionality.\n+\n+\t  If unsure, say N.\n+\n endif # BLK_DEV\ndiff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c\nindex 32bf3ba07a9dc..3fd318e5e1f17 100644\n--- a/drivers/block/virtio_blk.c\n+++ b/drivers/block/virtio_blk.c\n@@ -6,6 +6,7 @@\n #include \u003clinux/hdreg.h\u003e\n #include \u003clinux/module.h\u003e\n #include \u003clinux/mutex.h\u003e\n+#include \u003clinux/completion.h\u003e\n #include \u003clinux/interrupt.h\u003e\n #include \u003clinux/virtio.h\u003e\n #include \u003clinux/virtio_blk.h\u003e\n@@ -16,6 +17,7 @@\n #include \u003clinux/numa.h\u003e\n #include \u003clinux/vmalloc.h\u003e\n #include \u003cuapi/linux/virtio_ring.h\u003e\n+#include \u003clinux/blk-crypto-profile.h\u003e\n \n #define PART_BITS 4\n #define VQ_NAME_LEN 16\n@@ -52,6 +54,15 @@ struct virtio_blk_vq {\n \tchar name[VQ_NAME_LEN];\n } ____cacheline_aligned_in_smp;\n \n+struct virtio_blk_ctrl_vq {\n+\tstruct virtqueue *vq;\n+\tstruct mutex mutex;\n+\tspinlock_t lock;\n+\tunsigned int inflight;\n+\tbool dead;\n+\tstruct completion drained;\n+};\n+\n struct virtio_blk {\n \t/*\n \t * This mutex must be held by anything that may run after\n@@ -83,11 +94,25 @@ struct virtio_blk {\n \n \t/* For zoned device */\n \tunsigned int zone_sectors;\n+\n+\t/* For inline encryption support */\n+\tstruct blk_crypto_profile profile;\n+\tbool crypto_profile_initialized;\n+\n+\t/* Control virtqueue state. */\n+\tstruct virtio_blk_ctrl_vq ctrl_vq;\n };\n \n struct virtblk_req {\n \t/* Out header */\n-\tstruct virtio_blk_outhdr out_hdr;\n+\tunion {\n+\t\tstruct virtio_blk_outhdr base;\n+\t\tstruct {\n+\t\t\tstruct virtio_blk_outhdr base;\n+\t\t\t/* Crypto message (if VIRTIO_BLK_F_INLINE_ENCRYPTION) */\n+\t\t\tstruct virtio_blk_crypto_msg msg;\n+\t\t} crypto_append;\n+\t} out_hdr;\n \n \t/* In header */\n \tunion {\n@@ -110,6 +135,27 @@ struct virtblk_req {\n \tstruct scatterlist sg[];\n };\n \n+struct virtblk_ctrl_request {\n+\t/* Type byte, always its own out-sg for every command. */\n+\t__virtio32 type;\n+\t/* Out request, sent as a second, separate out-sg if any. */\n+\tunion {\n+\t\tstruct virtio_blk_crypto_key_desc key_desc;\n+\t\tstruct virtio_blk_crypto_key_blob blob;\n+\t} out_req;\n+\n+\t/* In response */\n+\tunion {\n+\t\tstruct virtio_blk_crypto_key_blob blob;\n+\t\tstruct virtio_blk_crypto_sw_secret secret;\n+\t\tstruct virtio_blk_crypto_modes modes;\n+\t} in_resp;\n+\t/* Status byte, always its own in-sg for every command. */\n+\tu8 status;\n+\n+\tstruct completion compl;\n+};\n+\n static inline blk_status_t virtblk_result(u8 status)\n {\n \tswitch (status) {\n@@ -140,12 +186,17 @@ static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr)\n {\n \tstruct scatterlist out_hdr, in_hdr, *sgs[3];\n \tunsigned int num_out = 0, num_in = 0;\n+\tsize_t out_hdr_len = sizeof(vbr-\u003eout_hdr.base);\n+\n+\tif (vbr-\u003eout_hdr.base.type == cpu_to_virtio32(vq-\u003evdev, VIRTIO_BLK_T_CRYPTO_IN) ||\n+\t    vbr-\u003eout_hdr.base.type == cpu_to_virtio32(vq-\u003evdev, VIRTIO_BLK_T_CRYPTO_OUT))\n+\t\tout_hdr_len = sizeof(vbr-\u003eout_hdr.crypto_append);\n \n-\tsg_init_one(\u0026out_hdr, \u0026vbr-\u003eout_hdr, sizeof(vbr-\u003eout_hdr));\n+\tsg_init_one(\u0026out_hdr, \u0026vbr-\u003eout_hdr, out_hdr_len);\n \tsgs[num_out++] = \u0026out_hdr;\n \n \tif (vbr-\u003esg_table.nents) {\n-\t\tif (vbr-\u003eout_hdr.type \u0026 cpu_to_virtio32(vq-\u003evdev, VIRTIO_BLK_T_OUT))\n+\t\tif (vbr-\u003eout_hdr.base.type \u0026 cpu_to_virtio32(vq-\u003evdev, VIRTIO_BLK_T_OUT))\n \t\t\tsgs[num_out++] = vbr-\u003esg_table.sgl;\n \t\telse\n \t\t\tsgs[num_out + num_in++] = vbr-\u003esg_table.sgl;\n@@ -235,6 +286,22 @@ static void virtblk_cleanup_cmd(struct request *req)\n \t\tkfree(bvec_virt(\u0026req-\u003especial_vec));\n }\n \n+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)\n+static bool is_crypto_request(struct request *req)\n+{\n+\tstruct request_queue *q = req-\u003eq;\n+\n+\treturn q-\u003ecrypto_profile \u0026\u0026\n+\t       req-\u003ecrypt_ctx \u0026\u0026\n+\t       req-\u003ecrypt_keyslot;\n+}\n+#else\n+static inline bool is_crypto_request(struct request *req)\n+{\n+\treturn false;\n+}\n+#endif\n+\n static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,\n \t\t\t\t      struct request *req,\n \t\t\t\t      struct virtblk_req *vbr)\n@@ -243,20 +310,27 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,\n \tbool unmap = false;\n \tu32 type;\n \tu64 sector = 0;\n+\tint i;\n \n \tif (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) \u0026\u0026 op_is_zone_mgmt(req_op(req)))\n \t\treturn BLK_STS_NOTSUPP;\n \n \t/* Set fields for all request types */\n-\tvbr-\u003eout_hdr.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));\n+\tvbr-\u003eout_hdr.base.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));\n \n \tswitch (req_op(req)) {\n \tcase REQ_OP_READ:\n-\t\ttype = VIRTIO_BLK_T_IN;\n+\t\tif (is_crypto_request(req))\n+\t\t\ttype = VIRTIO_BLK_T_CRYPTO_IN;\n+\t\telse\n+\t\t\ttype = VIRTIO_BLK_T_IN;\n \t\tsector = blk_rq_pos(req);\n \t\tbreak;\n \tcase REQ_OP_WRITE:\n-\t\ttype = VIRTIO_BLK_T_OUT;\n+\t\tif (is_crypto_request(req))\n+\t\t\ttype = VIRTIO_BLK_T_CRYPTO_OUT;\n+\t\telse\n+\t\t\ttype = VIRTIO_BLK_T_OUT;\n \t\tsector = blk_rq_pos(req);\n \t\tbreak;\n \tcase REQ_OP_FLUSH:\n@@ -309,8 +383,8 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,\n \n \t/* Set fields for non-REQ_OP_DRV_IN request types */\n \tvbr-\u003ein_hdr_len = in_hdr_len;\n-\tvbr-\u003eout_hdr.type = cpu_to_virtio32(vdev, type);\n-\tvbr-\u003eout_hdr.sector = cpu_to_virtio64(vdev, sector);\n+\tvbr-\u003eout_hdr.base.type = cpu_to_virtio32(vdev, type);\n+\tvbr-\u003eout_hdr.base.sector = cpu_to_virtio64(vdev, sector);\n \n \tif (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES ||\n \t    type == VIRTIO_BLK_T_SECURE_ERASE) {\n@@ -318,6 +392,18 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,\n \t\t\treturn BLK_STS_RESOURCE;\n \t}\n \n+\tif (type == VIRTIO_BLK_T_CRYPTO_IN || type == VIRTIO_BLK_T_CRYPTO_OUT) {\n+\t\tmemset(\u0026vbr-\u003eout_hdr.crypto_append.msg, 0,\n+\t\t\tsizeof(vbr-\u003eout_hdr.crypto_append.msg));\n+\t\tvbr-\u003eout_hdr.crypto_append.msg.slot =\n+\t\t\tcpu_to_virtio32(vdev,\n+\t\t\t\tblk_crypto_keyslot_index(req-\u003ecrypt_keyslot));\n+\t\tfor (i = 0; i \u003c ARRAY_SIZE(vbr-\u003eout_hdr.crypto_append.msg.dun); i++) {\n+\t\t\tvbr-\u003eout_hdr.crypto_append.msg.dun[i] =\n+\t\t\t\tcpu_to_virtio64(vdev, req-\u003ecrypt_ctx-\u003ebc_dun[i]);\n+\t\t}\n+\t}\n+\n \treturn 0;\n }\n \n@@ -568,8 +654,8 @@ static int virtblk_submit_zone_report(struct virtio_blk *vblk,\n \n \tvbr = blk_mq_rq_to_pdu(req);\n \tvbr-\u003ein_hdr_len = sizeof(vbr-\u003ein_hdr.status);\n-\tvbr-\u003eout_hdr.type = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_ZONE_REPORT);\n-\tvbr-\u003eout_hdr.sector = cpu_to_virtio64(vblk-\u003evdev, sector);\n+\tvbr-\u003eout_hdr.base.type = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_ZONE_REPORT);\n+\tvbr-\u003eout_hdr.base.sector = cpu_to_virtio64(vblk-\u003evdev, sector);\n \n \terr = blk_rq_map_kern(req, report_buf, report_len, GFP_KERNEL);\n \tif (err)\n@@ -817,8 +903,8 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)\n \n \tvbr = blk_mq_rq_to_pdu(req);\n \tvbr-\u003ein_hdr_len = sizeof(vbr-\u003ein_hdr.status);\n-\tvbr-\u003eout_hdr.type = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_GET_ID);\n-\tvbr-\u003eout_hdr.sector = 0;\n+\tvbr-\u003eout_hdr.base.type = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_GET_ID);\n+\tvbr-\u003eout_hdr.base.sector = 0;\n \n \terr = blk_rq_map_kern(req, id_str, VIRTIO_BLK_ID_BYTES, GFP_KERNEL);\n \tif (err)\n@@ -863,11 +949,598 @@ static int virtblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)\n \treturn ret;\n }\n \n+#define VIRTBLK_CTRL_VQ_DRAIN_TIMEOUT (10 * HZ)\n+\n+/* Prevent new submissions and wait for in-flight requests to complete. */\n+static void virtblk_ctrl_vq_quiesce(struct virtio_blk *vblk)\n+{\n+\tunsigned long flags;\n+\tbool need_wait;\n+\n+\tif (!vblk-\u003ectrl_vq.vq)\n+\t\treturn;\n+\n+\tinit_completion(\u0026vblk-\u003ectrl_vq.drained);\n+\n+\tspin_lock_irqsave(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\tvblk-\u003ectrl_vq.dead = true;\n+\tneed_wait = vblk-\u003ectrl_vq.inflight != 0;\n+\tspin_unlock_irqrestore(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\n+\tif (need_wait \u0026\u0026\n+\t    !wait_for_completion_timeout(\u0026vblk-\u003ectrl_vq.drained,\n+\t\t\t\t\t  VIRTBLK_CTRL_VQ_DRAIN_TIMEOUT))\n+\t\tdev_warn(\u0026vblk-\u003evdev-\u003edev,\n+\t\t\t \"timed out waiting for control queue requests to complete\\n\");\n+}\n+\n+/* Fail requests left in the control queue after reset. */\n+static void virtblk_ctrl_vq_drain(struct virtio_blk *vblk)\n+{\n+\tstruct virtblk_ctrl_request *creq;\n+\tunsigned long flags;\n+\n+\tif (!vblk-\u003ectrl_vq.vq)\n+\t\treturn;\n+\n+\tspin_lock_irqsave(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\twhile ((creq = virtqueue_detach_unused_buf(vblk-\u003ectrl_vq.vq)) != NULL) {\n+\t\tif (WARN_ON_ONCE(!vblk-\u003ectrl_vq.inflight))\n+\t\t\t;\n+\t\telse\n+\t\t\tvblk-\u003ectrl_vq.inflight--;\n+\t\tcreq-\u003estatus = VIRTIO_BLK_S_IOERR;\n+\t\tspin_unlock_irqrestore(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\t\tcomplete(\u0026creq-\u003ecompl);\n+\t\tspin_lock_irqsave(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\t}\n+\tspin_unlock_irqrestore(\u0026vblk-\u003ectrl_vq.lock, flags);\n+}\n+\n+static void virtblk_ctrlq_callback(struct virtqueue *vq)\n+{\n+\tstruct virtio_blk *vblk = vq-\u003evdev-\u003epriv;\n+\tstruct virtblk_ctrl_request *creq;\n+\tunsigned long flags;\n+\tunsigned int len;\n+\n+\tspin_lock_irqsave(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\tdo {\n+\t\tvirtqueue_disable_cb(vq);\n+\t\twhile ((creq = virtqueue_get_buf(vq, \u0026len)) != NULL) {\n+\t\t\tbool drained = false;\n+\n+\t\t\tif (WARN_ON_ONCE(!vblk-\u003ectrl_vq.inflight)) {\n+\t\t\t\t/*\n+\t\t\t\t * Still complete the request.  Never leave a\n+\t\t\t\t * synchronous caller blocked because the accounting\n+\t\t\t\t * state was already inconsistent.\n+\t\t\t\t */\n+\t\t\t\tspin_unlock_irqrestore(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\t\t\t\tcomplete(\u0026creq-\u003ecompl);\n+\t\t\t\tspin_lock_irqsave(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (--vblk-\u003ectrl_vq.inflight == 0 \u0026\u0026 vblk-\u003ectrl_vq.dead)\n+\t\t\t\tdrained = true;\n+\t\t\tspin_unlock_irqrestore(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\t\t\tif (drained)\n+\t\t\t\tcomplete(\u0026vblk-\u003ectrl_vq.drained);\n+\t\t\tcomplete(\u0026creq-\u003ecompl);\n+\t\t\tspin_lock_irqsave(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\t\t}\n+\t} while (!virtqueue_enable_cb(vq));\n+\tspin_unlock_irqrestore(\u0026vblk-\u003ectrl_vq.lock, flags);\n+}\n+\n+/* Submit a control-queue request and wait for completion. */\n+static int virtblk_ctrl_vq_request(struct virtio_blk *vblk,\n+\t\t\t\t    struct virtblk_ctrl_request *creq,\n+\t\t\t\t    struct scatterlist *sgs[],\n+\t\t\t\t    unsigned int out_sgs, unsigned int in_sgs)\n+{\n+\tunsigned long flags;\n+\tint err;\n+\n+\tmutex_lock(\u0026vblk-\u003ectrl_vq.mutex);\n+\tinit_completion(\u0026creq-\u003ecompl);\n+\n+\tspin_lock_irqsave(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\tif (vblk-\u003ectrl_vq.dead) {\n+\t\tspin_unlock_irqrestore(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\t\tmutex_unlock(\u0026vblk-\u003ectrl_vq.mutex);\n+\t\treturn -ENODEV;\n+\t}\n+\terr = virtqueue_add_sgs(vblk-\u003ectrl_vq.vq, sgs, out_sgs, in_sgs, creq, GFP_ATOMIC);\n+\tif (!err) {\n+\t\tvblk-\u003ectrl_vq.inflight++;\n+\t\tvirtqueue_kick(vblk-\u003ectrl_vq.vq);\n+\t}\n+\tspin_unlock_irqrestore(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\tif (err) {\n+\t\tmutex_unlock(\u0026vblk-\u003ectrl_vq.mutex);\n+\t\treturn err;\n+\t}\n+\n+\twait_for_completion(\u0026creq-\u003ecompl);\n+\tmutex_unlock(\u0026vblk-\u003ectrl_vq.mutex);\n+\treturn 0;\n+}\n+\n+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)\n+// Maps VIRTIO_BLK_CRYPTO_MODE_* values to the kernel's internal enum.\n+static const enum blk_crypto_mode_num\n+\tvirtio_blk_crypto_mode_map[VIRTIO_BLK_CRYPTO_MODE_MAX + 1] = {\n+\t[VIRTIO_BLK_CRYPTO_MODE_INVALID]\t= BLK_ENCRYPTION_MODE_INVALID,\n+\t[VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS]\t= BLK_ENCRYPTION_MODE_AES_256_XTS,\n+};\n+\n+static int virtblk_get_crypto_modes(struct virtio_blk *vblk,\n+\t\t\t\t    unsigned int *crypto_modes_supported)\n+{\n+\tunsigned int nr_modes = VIRTIO_BLK_CRYPTO_MODE_MAX + 1;\n+\tstruct scatterlist type_sg, resp_sg, status_sg, *sgs[3];\n+\tstruct virtblk_ctrl_request *creq;\n+\tunsigned int i;\n+\tint err;\n+\n+\tcreq = kzalloc_obj(*creq, GFP_KERNEL);\n+\tif (!creq)\n+\t\treturn -ENOMEM;\n+\n+\tcreq-\u003etype = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_GET_CRYPTO_MODES);\n+\n+\tsg_init_one(\u0026type_sg, \u0026creq-\u003etype, sizeof(creq-\u003etype));\n+\tsg_init_one(\u0026resp_sg, \u0026creq-\u003ein_resp.modes, sizeof(creq-\u003ein_resp.modes));\n+\tsg_init_one(\u0026status_sg, \u0026creq-\u003estatus, sizeof(creq-\u003estatus));\n+\tsgs[0] = \u0026type_sg;\n+\tsgs[1] = \u0026resp_sg;\n+\tsgs[2] = \u0026status_sg;\n+\n+\terr = virtblk_ctrl_vq_request(vblk, creq, sgs, 1, 2);\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\terr = blk_status_to_errno(virtblk_result(creq-\u003estatus));\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\tfor (i = 1; i \u003c nr_modes; i++) {\n+\t\tu32 mode_mask = virtio32_to_cpu(vblk-\u003evdev,\n+\t\t\t\t\t\t creq-\u003ein_resp.modes.modes[i]);\n+\t\tenum blk_crypto_mode_num mode = virtio_blk_crypto_mode_map[i];\n+\n+\t\tif (!mode_mask)\n+\t\t\tcontinue;\n+\t\tif (!mode) {\n+\t\t\tdev_warn(\u0026vblk-\u003evdev-\u003edev,\n+\t\t\t\t \"ignoring unknown crypto mode %u\\n\", i);\n+\t\t\tcontinue;\n+\t\t}\n+\t\tcrypto_modes_supported[mode] = mode_mask;\n+\t}\n+\n+out_free:\n+\tkfree(creq);\n+\treturn err;\n+}\n+\n+static int set_virtblk_crypto_key_desc(struct virtio_device *vdev,\n+\t\t\t\t       struct virtblk_ctrl_request *creq,\n+\t\t\t\t       const struct blk_crypto_key *key,\n+\t\t\t\t       unsigned int slot)\n+{\n+\tstruct virtio_blk_crypto_key_desc *desc = \u0026creq-\u003eout_req.key_desc;\n+\tenum blk_crypto_key_type key_type = key-\u003ecrypto_cfg.key_type;\n+\n+\tif (sizeof(desc-\u003ebytes) \u003c key-\u003esize)\n+\t\treturn -EOVERFLOW;\n+\n+\tmemset(desc, 0, sizeof(*desc));\n+\tdesc-\u003eslot = cpu_to_virtio32(vdev, slot);\n+\tmemcpy(desc-\u003ebytes, key-\u003ebytes, key-\u003esize);\n+\tdesc-\u003ekey_size = cpu_to_virtio32(vdev, key-\u003esize);\n+\tdesc-\u003ecrypto_mode = cpu_to_virtio32(vdev, key-\u003ecrypto_cfg.crypto_mode);\n+\tswitch (key-\u003ecrypto_cfg.key_type) {\n+\tcase BLK_CRYPTO_KEY_TYPE_RAW:\n+\t\tdesc-\u003ekey_type = cpu_to_virtio32(vdev,\n+\t\t\tVIRTIO_BLK_CRYPTO_KEY_TYPE_RAW);\n+\t\tbreak;\n+\tcase BLK_CRYPTO_KEY_TYPE_HW_WRAPPED:\n+\t\tdesc-\u003ekey_type = cpu_to_virtio32(vdev,\n+\t\t\tVIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED);\n+\t\tbreak;\n+\tdefault:\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\tdesc-\u003edata_unit_size_bits = cpu_to_virtio32(vdev, key-\u003edata_unit_size_bits);\n+\tdesc-\u003edun_bytes = cpu_to_virtio32(vdev, key-\u003ecrypto_cfg.dun_bytes);\n+\n+\treturn 0;\n+}\n+\n+static inline struct virtio_blk *virtblk_from_profile(struct blk_crypto_profile *profile)\n+{\n+\treturn container_of(profile, struct virtio_blk, profile);\n+}\n+\n+static int virtblk_crypto_keyslot_program(struct blk_crypto_profile *profile,\n+\t\t\t\t\t   const struct blk_crypto_key *key,\n+\t\t\t\t\t   unsigned int slot)\n+{\n+\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\n+\tstruct scatterlist type_sg, out_req_sg, status_sg, *sgs[3];\n+\tstruct virtblk_ctrl_request *creq;\n+\tint err;\n+\n+\tcreq = kzalloc_obj(*creq, GFP_KERNEL);\n+\tif (!creq)\n+\t\treturn -ENOMEM;\n+\n+\tcreq-\u003etype = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM);\n+\n+\terr = set_virtblk_crypto_key_desc(vblk-\u003evdev, creq, key, slot);\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\tsg_init_one(\u0026type_sg, \u0026creq-\u003etype, sizeof(creq-\u003etype));\n+\tsg_init_one(\u0026out_req_sg, \u0026creq-\u003eout_req.key_desc, sizeof(creq-\u003eout_req.key_desc));\n+\tsg_init_one(\u0026status_sg, \u0026creq-\u003estatus, sizeof(creq-\u003estatus));\n+\tsgs[0] = \u0026type_sg;\n+\tsgs[1] = \u0026out_req_sg;\n+\tsgs[2] = \u0026status_sg;\n+\n+\terr = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 1);\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\terr = blk_status_to_errno(virtblk_result(creq-\u003estatus));\n+out_free:\n+\tkfree(creq);\n+\treturn err;\n+}\n+\n+static int virtblk_crypto_keyslot_evict(struct blk_crypto_profile *profile,\n+\t\t\t\t\t const struct blk_crypto_key *key,\n+\t\t\t\t\t unsigned int slot)\n+{\n+\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\n+\tstruct scatterlist type_sg, out_req_sg, status_sg, *sgs[3];\n+\tstruct virtblk_ctrl_request *creq;\n+\tint err;\n+\n+\tcreq = kzalloc_obj(*creq, GFP_KERNEL);\n+\tif (!creq)\n+\t\treturn -ENOMEM;\n+\n+\tcreq-\u003etype = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_CRYPTO_KEYSLOT_EVICT);\n+\n+\terr = set_virtblk_crypto_key_desc(vblk-\u003evdev, creq, key, slot);\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\tsg_init_one(\u0026type_sg, \u0026creq-\u003etype, sizeof(creq-\u003etype));\n+\tsg_init_one(\u0026out_req_sg, \u0026creq-\u003eout_req.key_desc, sizeof(creq-\u003eout_req.key_desc));\n+\tsg_init_one(\u0026status_sg, \u0026creq-\u003estatus, sizeof(creq-\u003estatus));\n+\tsgs[0] = \u0026type_sg;\n+\tsgs[1] = \u0026out_req_sg;\n+\tsgs[2] = \u0026status_sg;\n+\n+\terr = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 1);\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\terr = blk_status_to_errno(virtblk_result(creq-\u003estatus));\n+out_free:\n+\tkfree(creq);\n+\treturn err;\n+}\n+\n+static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *profile,\n+\t\t\t\t\t    const u8 *eph_key, size_t eph_key_size,\n+\t\t\t\t\t    u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])\n+{\n+\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\n+\tstruct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];\n+\tstruct virtblk_ctrl_request *creq;\n+\tint err;\n+\n+\tif (eph_key_size \u003e VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE\n+\t    || sizeof(creq-\u003ein_resp.secret.secret) \u003c BLK_CRYPTO_SW_SECRET_SIZE)\n+\t\treturn -EOVERFLOW;\n+\n+\tcreq = kzalloc_obj(*creq, GFP_KERNEL);\n+\tif (!creq)\n+\t\treturn -ENOMEM;\n+\n+\tcreq-\u003etype = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET);\n+\tmemcpy(creq-\u003eout_req.blob.key, eph_key, eph_key_size);\n+\tcreq-\u003eout_req.blob.key_size = cpu_to_virtio32(vblk-\u003evdev, eph_key_size);\n+\n+\tsg_init_one(\u0026type_sg, \u0026creq-\u003etype, sizeof(creq-\u003etype));\n+\tsg_init_one(\u0026out_req_sg, \u0026creq-\u003eout_req.blob, sizeof(creq-\u003eout_req.blob));\n+\tsg_init_one(\u0026resp_sg, \u0026creq-\u003ein_resp.secret, sizeof(creq-\u003ein_resp.secret));\n+\tsg_init_one(\u0026status_sg, \u0026creq-\u003estatus, sizeof(creq-\u003estatus));\n+\tsgs[0] = \u0026type_sg;\n+\tsgs[1] = \u0026out_req_sg;\n+\tsgs[2] = \u0026resp_sg;\n+\tsgs[3] = \u0026status_sg;\n+\n+\terr = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2);\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\terr = blk_status_to_errno(virtblk_result(creq-\u003estatus));\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\tmemcpy(sw_secret, creq-\u003ein_resp.secret.secret, BLK_CRYPTO_SW_SECRET_SIZE);\n+out_free:\n+\tkfree(creq);\n+\treturn err;\n+}\n+\n+static int virtblk_crypto_generate_key(struct blk_crypto_profile *profile,\n+\t\t\t\t\tu8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])\n+{\n+\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\n+\tstruct scatterlist type_sg, resp_sg, status_sg, *sgs[3];\n+\tstruct virtblk_ctrl_request *creq;\n+\tunsigned int key_size;\n+\tint err;\n+\n+\tcreq = kzalloc_obj(*creq, GFP_KERNEL);\n+\tif (!creq)\n+\t\treturn -ENOMEM;\n+\n+\tcreq-\u003etype = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_CRYPTO_GENERATE_KEY);\n+\n+\tsg_init_one(\u0026type_sg, \u0026creq-\u003etype, sizeof(creq-\u003etype));\n+\tsg_init_one(\u0026resp_sg, \u0026creq-\u003ein_resp.blob, sizeof(creq-\u003ein_resp.blob));\n+\tsg_init_one(\u0026status_sg, \u0026creq-\u003estatus, sizeof(creq-\u003estatus));\n+\tsgs[0] = \u0026type_sg;\n+\tsgs[1] = \u0026resp_sg;\n+\tsgs[2] = \u0026status_sg;\n+\n+\terr = virtblk_ctrl_vq_request(vblk, creq, sgs, 1, 2);\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\terr = blk_status_to_errno(virtblk_result(creq-\u003estatus));\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\tkey_size = virtio32_to_cpu(vblk-\u003evdev, creq-\u003ein_resp.blob.key_size);\n+\tif (!key_size ||\n+\t\tkey_size \u003e BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) {\n+\t\tdev_err(\u0026vblk-\u003evdev-\u003edev,\n+\t\t\t\"backend returned oversized generated key: %u\\n\", key_size);\n+\t\terr = -EOVERFLOW;\n+\t\tgoto out_free;\n+\t}\n+\tmemcpy(lt_key, creq-\u003ein_resp.blob.key, key_size);\n+\terr = key_size;\n+out_free:\n+\tkfree(creq);\n+\treturn err;\n+}\n+\n+static int virtblk_crypto_prepare_key(struct blk_crypto_profile *profile,\n+\t\t\t\t       const u8 *lt_key, size_t lt_key_size,\n+\t\t\t\t       u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])\n+{\n+\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\n+\tstruct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];\n+\tstruct virtblk_ctrl_request *creq;\n+\tunsigned int key_size;\n+\tint err;\n+\n+\tif (lt_key_size \u003e VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE)\n+\t\treturn -EOVERFLOW;\n+\n+\tcreq = kzalloc_obj(*creq, GFP_KERNEL);\n+\tif (!creq)\n+\t\treturn -ENOMEM;\n+\n+\tcreq-\u003etype = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_CRYPTO_PREPARE_KEY);\n+\tmemcpy(creq-\u003eout_req.blob.key, lt_key, lt_key_size);\n+\tcreq-\u003eout_req.blob.key_size = cpu_to_virtio32(vblk-\u003evdev, lt_key_size);\n+\n+\tsg_init_one(\u0026type_sg, \u0026creq-\u003etype, sizeof(creq-\u003etype));\n+\tsg_init_one(\u0026out_req_sg, \u0026creq-\u003eout_req.blob, sizeof(creq-\u003eout_req.blob));\n+\tsg_init_one(\u0026resp_sg, \u0026creq-\u003ein_resp.blob, sizeof(creq-\u003ein_resp.blob));\n+\tsg_init_one(\u0026status_sg, \u0026creq-\u003estatus, sizeof(creq-\u003estatus));\n+\tsgs[0] = \u0026type_sg;\n+\tsgs[1] = \u0026out_req_sg;\n+\tsgs[2] = \u0026resp_sg;\n+\tsgs[3] = \u0026status_sg;\n+\n+\terr = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2);\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\terr = blk_status_to_errno(virtblk_result(creq-\u003estatus));\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\tkey_size = virtio32_to_cpu(vblk-\u003evdev, creq-\u003ein_resp.blob.key_size);\n+\tif (!key_size ||\n+\t\tkey_size \u003e BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) {\n+\t\tdev_err(\u0026vblk-\u003evdev-\u003edev,\n+\t\t\t\"backend returned oversized prepared key: %u\\n\", key_size);\n+\t\terr = -EOVERFLOW;\n+\t\tgoto out_free;\n+\t}\n+\tmemcpy(eph_key, creq-\u003ein_resp.blob.key, key_size);\n+\terr = key_size;\n+out_free:\n+\tkfree(creq);\n+\treturn err;\n+}\n+\n+static int virtblk_crypto_import_key(struct blk_crypto_profile *profile,\n+\t\t\t\t      const u8 *raw_key, size_t raw_key_size,\n+\t\t\t\t      u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])\n+{\n+\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\n+\tstruct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];\n+\tstruct virtblk_ctrl_request *creq;\n+\tunsigned int key_size;\n+\tint err;\n+\n+\tif (raw_key_size \u003e VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE)\n+\t\treturn -EOVERFLOW;\n+\n+\tcreq = kzalloc_obj(*creq, GFP_KERNEL);\n+\tif (!creq)\n+\t\treturn -ENOMEM;\n+\n+\tcreq-\u003etype = cpu_to_virtio32(vblk-\u003evdev, VIRTIO_BLK_T_CRYPTO_IMPORT_KEY);\n+\tmemcpy(creq-\u003eout_req.blob.key, raw_key, raw_key_size);\n+\tcreq-\u003eout_req.blob.key_size = cpu_to_virtio32(vblk-\u003evdev, raw_key_size);\n+\n+\tsg_init_one(\u0026type_sg, \u0026creq-\u003etype, sizeof(creq-\u003etype));\n+\tsg_init_one(\u0026out_req_sg, \u0026creq-\u003eout_req.blob, sizeof(creq-\u003eout_req.blob));\n+\tsg_init_one(\u0026resp_sg, \u0026creq-\u003ein_resp.blob, sizeof(creq-\u003ein_resp.blob));\n+\tsg_init_one(\u0026status_sg, \u0026creq-\u003estatus, sizeof(creq-\u003estatus));\n+\tsgs[0] = \u0026type_sg;\n+\tsgs[1] = \u0026out_req_sg;\n+\tsgs[2] = \u0026resp_sg;\n+\tsgs[3] = \u0026status_sg;\n+\n+\terr = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2);\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\terr = blk_status_to_errno(virtblk_result(creq-\u003estatus));\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\tkey_size = virtio32_to_cpu(vblk-\u003evdev, creq-\u003ein_resp.blob.key_size);\n+\tif (!key_size ||\n+\t\tkey_size \u003e BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) {\n+\t\tdev_err(\u0026vblk-\u003evdev-\u003edev,\n+\t\t\t\"backend returned oversized imported key: %u\\n\", key_size);\n+\t\terr = -EOVERFLOW;\n+\t\tgoto out_free;\n+\t}\n+\tmemcpy(lt_key, creq-\u003ein_resp.blob.key, key_size);\n+\terr = key_size;\n+out_free:\n+\tkfree(creq);\n+\treturn err;\n+}\n+\n+static const struct blk_crypto_ll_ops virtblk_crypto_ops = {\n+\t.keyslot_program\t= virtblk_crypto_keyslot_program,\n+\t.keyslot_evict\t\t= virtblk_crypto_keyslot_evict,\n+\t.derive_sw_secret\t= virtblk_crypto_derive_sw_secret,\n+\t.generate_key\t\t= virtblk_crypto_generate_key,\n+\t.prepare_key\t\t= virtblk_crypto_prepare_key,\n+\t.import_key\t\t= virtblk_crypto_import_key,\n+};\n+\n+static int virtblk_init_crypto(struct virtio_blk *vblk)\n+{\n+\tstruct virtio_device *vdev = vblk-\u003evdev;\n+\tunsigned int crypto_modes_supported[BLK_ENCRYPTION_MODE_MAX] = { 0 };\n+\tunsigned int key_type_supported = 0;\n+\tu16 max_slots = 0;\n+\t/* virtio_cread() requires the variable size to match the config field exactly */\n+\tu8 max_dun_bytes = 0, key_types = 0;\n+\tint err;\n+\n+\tvirtio_cread(vdev, struct virtio_blk_config,\n+\t\t     enc_characteristics.max_slots, \u0026max_slots);\n+\tvirtio_cread(vdev, struct virtio_blk_config,\n+\t\t     enc_characteristics.max_dun_bytes, \u0026max_dun_bytes);\n+\tvirtio_cread(vdev, struct virtio_blk_config,\n+\t\t     enc_characteristics.key_types, \u0026key_types);\n+\n+\tdev_info_once(\u0026vdev-\u003edev,\n+\t\t \"max_slots = %u, max_dun_bytes = %u, key_types = 0x%x\\n\",\n+\t\t max_slots, max_dun_bytes, key_types);\n+\n+\tif (!max_slots)\n+\t\treturn -EINVAL;\n+\n+\t/*\n+\t * struct virtio_blk_crypto_msg.dun is a fixed array of four __virtio64\n+\t * values (32 bytes total), matching the size of\n+\t * blk_crypto_ctx::bc_dun[4].  Refuse to advertise more than that as\n+\t * supported, or blk-crypto could negotiate a larger dun_bytes with the\n+\t * filesystem and have the high-order bytes of req-\u003ecrypt_ctx-\u003ebc_dun\n+\t * silently dropped in virtblk_setup_cmd().\n+\t */\n+\tif (max_dun_bytes \u003e sizeof_field(struct virtio_blk_crypto_msg, dun))\n+\t\treturn -EINVAL;\n+\n+\tif (key_types \u0026 VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW)\n+\t\tkey_type_supported |= BLK_CRYPTO_KEY_TYPE_RAW;\n+\tif (key_types \u0026 VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED)\n+\t\tkey_type_supported |= BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;\n+\tif (!key_type_supported)\n+\t\treturn -EINVAL;\n+\n+\terr = virtblk_get_crypto_modes(vblk, crypto_modes_supported);\n+\tif (err) {\n+\t\tdev_err(\u0026vdev-\u003edev, \"get crypto modes failed: %d\\n\", err);\n+\t\treturn err;\n+\t}\n+\n+\t/*\n+\t * Use the plain (non-devm) initializer: vblk-\u003eprofile is embedded in\n+\t * struct virtio_blk, whose lifetime is tied to the gendisk, not to\n+\t * \u0026vdev-\u003edev. Tying destruction to the vdev via devm would run the\n+\t * destroy callback after virtblk_remove() has already freed vblk.\n+\t * virtblk_free_disk() calls blk_crypto_profile_destroy() explicitly\n+\t * instead, guarded by crypto_profile_initialized below.\n+\t */\n+\terr = blk_crypto_profile_init(\u0026vblk-\u003eprofile, max_slots);\n+\tif (err) {\n+\t\tdev_err(\u0026vdev-\u003edev, \"crypto profile initialization failed: %d\\n\", err);\n+\t\treturn err;\n+\t}\n+\n+\tvblk-\u003eprofile.ll_ops = virtblk_crypto_ops;\n+\tvblk-\u003eprofile.max_dun_bytes_supported = max_dun_bytes;\n+\tvblk-\u003eprofile.key_types_supported = key_type_supported;\n+\tvblk-\u003eprofile.dev = \u0026vdev-\u003edev;\n+\tmemcpy(vblk-\u003eprofile.modes_supported, crypto_modes_supported,\n+\t       BLK_ENCRYPTION_MODE_MAX * sizeof(unsigned int));\n+\n+\tvblk-\u003ecrypto_profile_initialized = true;\n+\n+\tdev_info(\u0026vdev-\u003edev, \"inline crypto profile initialized\\n\");\n+\n+\treturn 0;\n+}\n+\n+static void virtblk_destroy_crypto(struct virtio_blk *vblk)\n+{\n+\tif (vblk-\u003ecrypto_profile_initialized)\n+\t\tblk_crypto_profile_destroy(\u0026vblk-\u003eprofile);\n+}\n+#else\n+\n+static inline int virtblk_init_crypto(struct virtio_blk *vblk)\n+{\n+\treturn -EOPNOTSUPP;\n+}\n+\n+static inline void virtblk_destroy_crypto(struct virtio_blk *vblk)\n+{\n+}\n+#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */\n+\n static void virtblk_free_disk(struct gendisk *disk)\n {\n \tstruct virtio_blk *vblk = disk-\u003eprivate_data;\n \n \tida_free(\u0026vd_index_ida, vblk-\u003eindex);\n+\tvirtblk_destroy_crypto(vblk);\n+\tmutex_destroy(\u0026vblk-\u003ectrl_vq.mutex);\n \tmutex_destroy(\u0026vblk-\u003evdev_mutex);\n \tkfree(vblk);\n }\n@@ -965,6 +1638,8 @@ static int init_vq(struct virtio_blk *vblk)\n \tstruct virtqueue **vqs;\n \tunsigned short num_vqs;\n \tunsigned short num_poll_vqs;\n+\tunsigned short total_vqs;\n+\tbool has_ctrl_vq;\n \tstruct virtio_device *vdev = vblk-\u003evdev;\n \tstruct irq_affinity desc = { 0, };\n \n@@ -993,12 +1668,19 @@ static int init_vq(struct virtio_blk *vblk)\n \t\t\t\tvblk-\u003eio_queues[HCTX_TYPE_READ],\n \t\t\t\tvblk-\u003eio_queues[HCTX_TYPE_POLL]);\n \n+\t/*\n+\t * The control vq is appended after the data vqs whenever\n+\t * F_CTRL_VQ is negotiated.\n+\t */\n+\thas_ctrl_vq = virtio_has_feature(vdev, VIRTIO_BLK_F_CTRL_VQ);\n+\ttotal_vqs = num_vqs + (has_ctrl_vq ? 1 : 0);\n+\n \tvblk-\u003evqs = kmalloc_objs(*vblk-\u003evqs, num_vqs);\n \tif (!vblk-\u003evqs)\n \t\treturn -ENOMEM;\n \n-\tvqs_info = kzalloc_objs(*vqs_info, num_vqs);\n-\tvqs = kmalloc_objs(*vqs, num_vqs);\n+\tvqs_info = kzalloc_objs(*vqs_info, total_vqs);\n+\tvqs = kmalloc_objs(*vqs, total_vqs);\n \tif (!vqs_info || !vqs) {\n \t\terr = -ENOMEM;\n \t\tgoto out;\n@@ -1015,8 +1697,13 @@ static int init_vq(struct virtio_blk *vblk)\n \t\tvqs_info[i].name = vblk-\u003evqs[i].name;\n \t}\n \n+\tif (has_ctrl_vq) {\n+\t\tvqs_info[num_vqs].callback = virtblk_ctrlq_callback;\n+\t\tvqs_info[num_vqs].name = \"control\";\n+\t}\n+\n \t/* Discover virtqueues and write information to configuration.  */\n-\terr = virtio_find_vqs(vdev, num_vqs, vqs, vqs_info, \u0026desc);\n+\terr = virtio_find_vqs(vdev, total_vqs, vqs, vqs_info, \u0026desc);\n \tif (err)\n \t\tgoto out;\n \n@@ -1025,6 +1712,9 @@ static int init_vq(struct virtio_blk *vblk)\n \t\tvblk-\u003evqs[i].vq = vqs[i];\n \t}\n \tvblk-\u003enum_vqs = num_vqs;\n+\tvblk-\u003ectrl_vq.vq = has_ctrl_vq ? vqs[num_vqs] : NULL;\n+\tvblk-\u003ectrl_vq.dead = false;\n+\tvblk-\u003ectrl_vq.inflight = 0;\n \n out:\n \tkfree(vqs);\n@@ -1464,14 +2154,18 @@ static int virtblk_probe(struct virtio_device *vdev)\n \t}\n \n \tmutex_init(\u0026vblk-\u003evdev_mutex);\n+\tmutex_init(\u0026vblk-\u003ectrl_vq.mutex);\n+\tspin_lock_init(\u0026vblk-\u003ectrl_vq.lock);\n \n \tvblk-\u003evdev = vdev;\n \n \tINIT_WORK(\u0026vblk-\u003econfig_work, virtblk_config_changed_work);\n \n \terr = init_vq(vblk);\n-\tif (err)\n+\tif (err) {\n+\t\tdev_err(\u0026vdev-\u003edev, \"init virt queue failed: err = %d\\n\", err);\n \t\tgoto out_free_vblk;\n+\t}\n \n \t/* Default queue sizing is to fill the ring. */\n \tif (!virtblk_queue_depth) {\n@@ -1538,6 +2232,19 @@ static int virtblk_probe(struct virtio_device *vdev)\n \t\terr = blk_revalidate_disk_zones(vblk-\u003edisk);\n \t\tif (err)\n \t\t\tgoto out_cleanup_disk;\n+\t} else if (IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) \u0026\u0026\n+\t\t   virtio_has_feature(vdev, VIRTIO_BLK_F_INLINE_ENCRYPTION) \u0026\u0026\n+\t\t   virtio_has_feature(vdev, VIRTIO_BLK_F_CTRL_VQ)) {\n+\t\terr = virtblk_init_crypto(vblk);\n+\t\tif (!err) {\n+\t\t\tif (!blk_crypto_register(\u0026vblk-\u003eprofile, vblk-\u003edisk-\u003equeue))\n+\t\t\t\tdev_warn(\u0026vdev-\u003edev,\n+\t\t\t\t\t \"failed to register inline crypto profile\\n\");\n+\t\t} else {\n+\t\t\tdev_warn(\u0026vdev-\u003edev,\n+\t\t\t\t \"inline crypto init failed: %d, continuing without inline crypto support\\n\",\n+\t\t\t\t err);\n+\t\t}\n \t}\n \n \terr = device_add_disk(\u0026vdev-\u003edev, vblk-\u003edisk, virtblk_attr_groups);\n@@ -1553,6 +2260,7 @@ static int virtblk_probe(struct virtio_device *vdev)\n out_free_vq:\n \tvdev-\u003econfig-\u003edel_vqs(vdev);\n \tkfree(vblk-\u003evqs);\n+\tvblk-\u003ectrl_vq.vq = NULL;\n out_free_vblk:\n \tkfree(vblk);\n out_free_index:\n@@ -1571,16 +2279,21 @@ static void virtblk_remove(struct virtio_device *vdev)\n \tdel_gendisk(vblk-\u003edisk);\n \tblk_mq_free_tag_set(\u0026vblk-\u003etag_set);\n \n+\tvirtblk_ctrl_vq_quiesce(vblk);\n+\n \tmutex_lock(\u0026vblk-\u003evdev_mutex);\n \n \t/* Stop all the virtqueues. */\n \tvirtio_reset_device(vdev);\n+\tvirtblk_ctrl_vq_drain(vblk);\n \n \t/* Virtqueues are stopped, nothing can use vblk-\u003evdev anymore. */\n \tvblk-\u003evdev = NULL;\n \n \tvdev-\u003econfig-\u003edel_vqs(vdev);\n \tkfree(vblk-\u003evqs);\n+\tvblk-\u003evqs = NULL;\n+\tvblk-\u003ectrl_vq.vq = NULL;\n \n \tmutex_unlock(\u0026vblk-\u003evdev_mutex);\n \n@@ -1593,6 +2306,8 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)\n \tstruct request_queue *q = vblk-\u003edisk-\u003equeue;\n \tunsigned int memflags;\n \n+\tvirtblk_ctrl_vq_quiesce(vblk);\n+\n \t/* Ensure no requests in virtqueues before deleting vqs. */\n \tmemflags = blk_mq_freeze_queue(q);\n \tblk_mq_quiesce_queue_nowait(q);\n@@ -1600,6 +2315,7 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)\n \n \t/* Ensure we don't receive any more interrupts */\n \tvirtio_reset_device(vdev);\n+\tvirtblk_ctrl_vq_drain(vblk);\n \n \t/* Make sure no work handler is accessing the device. */\n \tflush_work(\u0026vblk-\u003econfig_work);\n@@ -1612,6 +2328,7 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)\n \t * pointers safely.\n \t */\n \tvblk-\u003evqs = NULL;\n+\tvblk-\u003ectrl_vq.vq = NULL;\n \n \treturn 0;\n }\n@@ -1628,6 +2345,9 @@ static int virtblk_restore_priv(struct virtio_device *vdev)\n \tvirtio_device_ready(vdev);\n \tblk_mq_unquiesce_queue(vblk-\u003edisk-\u003equeue);\n \n+\tif (vblk-\u003eprofile.slots)\n+\t\tblk_crypto_reprogram_all_keys(\u0026vblk-\u003eprofile);\n+\n \treturn 0;\n }\n \n@@ -1672,6 +2392,7 @@ static unsigned int features[] = {\n \tVIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE,\n \tVIRTIO_BLK_F_MQ, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_WRITE_ZEROES,\n \tVIRTIO_BLK_F_SECURE_ERASE, VIRTIO_BLK_F_ZONED,\n+\tVIRTIO_BLK_F_CTRL_VQ, VIRTIO_BLK_F_INLINE_ENCRYPTION,\n };\n \n static struct virtio_driver virtio_blk = {\ndiff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_blk.h\nindex 3744e4da1b2a7..952534e2b489d 100644\n--- a/include/uapi/linux/virtio_blk.h\n+++ b/include/uapi/linux/virtio_blk.h\n@@ -42,6 +42,8 @@\n #define VIRTIO_BLK_F_WRITE_ZEROES\t14\t/* WRITE ZEROES is supported */\n #define VIRTIO_BLK_F_SECURE_ERASE\t16 /* Secure Erase is supported */\n #define VIRTIO_BLK_F_ZONED\t\t17\t/* Zoned block device */\n+#define VIRTIO_BLK_F_CTRL_VQ\t\t\t22\t/* Control queue */\n+#define VIRTIO_BLK_F_INLINE_ENCRYPTION\t\t23\t/* Inline encryption */\n \n /* Legacy feature bits */\n #ifndef VIRTIO_BLK_NO_LEGACY\n@@ -57,6 +59,10 @@\n \n #define VIRTIO_BLK_ID_BYTES\t20\t/* ID string length */\n \n+/* Key type bitmask for VIRTIO_BLK_F_INLINE_ENCRYPTION */\n+#define VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW\t(1 \u003c\u003c 0)\n+#define VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED\t(1 \u003c\u003c 1)\n+\n struct virtio_blk_config {\n \t/* The capacity (in 512-byte sectors). */\n \t__virtio64 capacity;\n@@ -148,6 +154,15 @@ struct virtio_blk_config {\n \t\t__u8 model;\n \t\t__u8 unused2[3];\n \t} zoned;\n+\n+\t/* Inline Encryption device characteristics (if VIRTIO_BLK_F_INLINE_ENCRYPTION) */\n+\tstruct virtio_blk_enc_characteristics {\n+\t\t__virtio16 max_slots;\n+\t\t__u8 max_dun_bytes;\n+\t\t/* Bitmask of supported key types: VIRTIO_BLK_CRYPTO_KEY_TYPE_* */\n+\t\t__u8 key_types;\n+\t\t__virtio32 unused3;\n+\t} enc_characteristics;\n } __attribute__((packed));\n \n /*\n@@ -206,6 +221,33 @@ struct virtio_blk_config {\n /* Reset All zones command */\n #define VIRTIO_BLK_T_ZONE_RESET_ALL 26\n \n+/* Inline-encrypted write: crypto_msg set in outhdr */\n+#define VIRTIO_BLK_T_CRYPTO_OUT\t\t27\n+\n+/* Inline-encrypted read: crypto_msg set in outhdr */\n+#define VIRTIO_BLK_T_CRYPTO_IN\t\t28\n+\n+/* Get inline crypto modes */\n+#define VIRTIO_BLK_T_GET_CRYPTO_MODES\t29\n+\n+/* Program a key into the keyslot */\n+#define VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM\t30\n+\n+/* Evict a key */\n+#define VIRTIO_BLK_T_CRYPTO_KEYSLOT_EVICT\t31\n+\n+/* Derive the software secret from a hardware-wrapped key */\n+#define VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET\t32\n+\n+/* Generate a new hardware-wrapped key */\n+#define VIRTIO_BLK_T_CRYPTO_GENERATE_KEY\t33\n+\n+/* Import a raw key as a hardware-wrapped key */\n+#define VIRTIO_BLK_T_CRYPTO_IMPORT_KEY\t\t34\n+\n+/* Convert a long-term wrapped key to its ephemerally-wrapped form */\n+#define VIRTIO_BLK_T_CRYPTO_PREPARE_KEY\t35\n+\n #ifndef VIRTIO_BLK_NO_LEGACY\n /* Barrier before this op. */\n #define VIRTIO_BLK_T_BARRIER\t0x80000000\n@@ -225,6 +267,80 @@ struct virtio_blk_outhdr {\n \t__virtio64 sector;\n };\n \n+/*\n+ * Crypto message descriptor, appended to the outhdr of a\n+ * VIRTIO_BLK_T_CRYPTO_OUT or VIRTIO_BLK_T_CRYPTO_IN request.\n+ */\n+struct virtio_blk_crypto_msg {\n+\t/* virtual key slot index */\n+\t__virtio32 slot;\n+\t__u8 unused[4];\n+\t/* data unit number (DUN / IV) for this request */\n+\t__virtio64 dun[4];\n+};\n+\n+/*\n+ * Inline crypto key descriptor. Request part for\n+ * VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM/KEYSLOT_EVICT.\n+ */\n+/* Must be \u003e= BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE in include/linux/blk-crypto.h */\n+#define VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE\t\t128\n+\n+struct virtio_blk_crypto_key_desc {\n+\t__virtio32  slot;\n+\t__u8        bytes[VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE];\n+\t__virtio32  key_size;\n+\t__virtio32  crypto_mode;\n+\t__virtio32  key_type;\n+\t__virtio32  data_unit_size_bits;\n+\t__virtio32  dun_bytes;\n+};\n+\n+/*\n+ * A raw or hardware-wrapped key blob, used as the request and/or reply part\n+ * of the VIRTIO_BLK_T_CRYPTO_GENERATE_KEY/IMPORT_KEY/PREPARE_KEY/\n+ * DERIVE_SW_SECRET commands.\n+ */\n+struct virtio_blk_crypto_key_blob {\n+\t__virtio32 key_size;\n+\t__u8 key[VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE];\n+};\n+\n+/* Must match BLK_CRYPTO_SW_SECRET_SIZE in include/linux/blk-crypto.h */\n+#define VIRTIO_BLK_CRYPTO_SW_SECRET_SIZE\t32\n+\n+/* Reply to a VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET request. */\n+struct virtio_blk_crypto_sw_secret {\n+\t__u8 secret[VIRTIO_BLK_CRYPTO_SW_SECRET_SIZE];\n+};\n+\n+/*\n+ * Crypto mode numbers used in VIRTIO_BLK_T_GET_CRYPTO_MODES replies and in\n+ * indexing struct virtio_blk_crypto_modes.modes[] below. These numbers are\n+ * assigned by the virtio spec and are stable: a number is never reused for\n+ * a different crypto mode, and additional crypto modes are assigned new,\n+ * higher numbers.\n+ */\n+enum {\n+\tVIRTIO_BLK_CRYPTO_MODE_INVALID,\n+\tVIRTIO_BLK_CRYPTO_MODE_AES_256_XTS,\n+\t__VIRTIO_BLK_CRYPTO_MODE_MAX,\t/* sentinel: always one past the last real mode */\n+};\n+\n+/* Highest crypto mode number defined by this version of the header. */\n+#define VIRTIO_BLK_CRYPTO_MODE_MAX (__VIRTIO_BLK_CRYPTO_MODE_MAX - 1)\n+\n+/* Reply to a VIRTIO_BLK_T_GET_CRYPTO_MODES request. */\n+struct virtio_blk_crypto_modes {\n+\t/*\n+\t * modes[N], for crypto mode number N \u003c= VIRTIO_BLK_CRYPTO_MODE_MAX, is\n+\t * a bitmask of the data unit sizes with which crypto mode N can be\n+\t * used: bit i is set if a data unit size of (1 \u003c\u003c i) bytes is\n+\t * supported. modes[0] is reserved and always 0.\n+\t */\n+\t__virtio32 modes[__VIRTIO_BLK_CRYPTO_MODE_MAX];\n+};\n+\n /*\n  * Supported zoned device models.\n  */\n"
}

2/1 2026/09/14 18:17 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "VIRTIO",
    "VIRTIO_BLK",
    "BLK_INLINE_ENCRYPTION",
    "VIRTIO_BLK_INLINE_ENCRYPTION"
  ],
  "FocusSymbols": [
    "virtblk_crypto_keyslot_program",
    "virtblk_crypto_keyslot_evict",
    "virtblk_crypto_derive_sw_secret",
    "virtblk_crypto_generate_key",
    "virtblk_crypto_prepare_key",
    "virtblk_crypto_import_key",
    "virtblk_get_crypto_modes",
    "virtblk_init_crypto",
    "virtblk_ctrl_vq_request",
    "virtblk_ctrlq_callback",
    "virtblk_ctrl_vq_drain",
    "virtblk_ctrl_vq_quiesce",
    "virtblk_setup_cmd",
    "virtblk_add_req"
  ],
  "Reasoning": "The patch implements inline encryption support and a dedicated control virtqueue for virtio-blk. Virtio devices are standard virtual hardware emulated in QEMU/GCE environments and are accessible via block I/O requests, ioctls, and filesystems using blk-crypto. The changes introduce new control virtqueue handling with synchronization and WARN_ON assertions, request formatting for crypto read/write operations, and keyslot management callbacks (program, evict, derive_sw_secret, generate_key, prepare_key, import_key). Fuzzing is warranted to discover potential concurrency issues, buffer handling bugs, or assertion failures.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit a6f70448193f9a3938cdc448e8e06cb3eecf4b9a
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Sep 14 18:17:05 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 858320b6ebb7e..58bb050d4617c 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -372,4 +372,16 @@ config BLK_DEV_ZONED_LOOP
 
 	  If unsure, say N.
 
+config VIRTIO_BLK_INLINE_ENCRYPTION
+	tristate "Virtio block inline encryption support"
+	depends on VIRTIO_BLK && BLK_INLINE_ENCRYPTION
+	help
+	  Say 'Y or M' here will allow the virtio block driver to route crypto
+	  requests to a different operating system in a virtualized
+	  environment. This is useful to encrypt the data stored in the storage
+	  by using storage inline crypto engine. The control queue feature bit
+	  must be negotiated to enable this functionality.
+
+	  If unsure, say N.
+
 endif # BLK_DEV
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 32bf3ba07a9dc..3fd318e5e1f17 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -6,6 +6,7 @@
 #include <linux/hdreg.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/completion.h>
 #include <linux/interrupt.h>
 #include <linux/virtio.h>
 #include <linux/virtio_blk.h>
@@ -16,6 +17,7 @@
 #include <linux/numa.h>
 #include <linux/vmalloc.h>
 #include <uapi/linux/virtio_ring.h>
+#include <linux/blk-crypto-profile.h>
 
 #define PART_BITS 4
 #define VQ_NAME_LEN 16
@@ -52,6 +54,15 @@ struct virtio_blk_vq {
 	char name[VQ_NAME_LEN];
 } ____cacheline_aligned_in_smp;
 
+struct virtio_blk_ctrl_vq {
+	struct virtqueue *vq;
+	struct mutex mutex;
+	spinlock_t lock;
+	unsigned int inflight;
+	bool dead;
+	struct completion drained;
+};
+
 struct virtio_blk {
 	/*
 	 * This mutex must be held by anything that may run after
@@ -83,11 +94,25 @@ struct virtio_blk {
 
 	/* For zoned device */
 	unsigned int zone_sectors;
+
+	/* For inline encryption support */
+	struct blk_crypto_profile profile;
+	bool crypto_profile_initialized;
+
+	/* Control virtqueue state. */
+	struct virtio_blk_ctrl_vq ctrl_vq;
 };
 
 struct virtblk_req {
 	/* Out header */
-	struct virtio_blk_outhdr out_hdr;
+	union {
+		struct virtio_blk_outhdr base;
+		struct {
+			struct virtio_blk_outhdr base;
+			/* Crypto message (if VIRTIO_BLK_F_INLINE_ENCRYPTION) */
+			struct virtio_blk_crypto_msg msg;
+		} crypto_append;
+	} out_hdr;
 
 	/* In header */
 	union {
@@ -110,6 +135,27 @@ struct virtblk_req {
 	struct scatterlist sg[];
 };
 
+struct virtblk_ctrl_request {
+	/* Type byte, always its own out-sg for every command. */
+	__virtio32 type;
+	/* Out request, sent as a second, separate out-sg if any. */
+	union {
+		struct virtio_blk_crypto_key_desc key_desc;
+		struct virtio_blk_crypto_key_blob blob;
+	} out_req;
+
+	/* In response */
+	union {
+		struct virtio_blk_crypto_key_blob blob;
+		struct virtio_blk_crypto_sw_secret secret;
+		struct virtio_blk_crypto_modes modes;
+	} in_resp;
+	/* Status byte, always its own in-sg for every command. */
+	u8 status;
+
+	struct completion compl;
+};
+
 static inline blk_status_t virtblk_result(u8 status)
 {
 	switch (status) {
@@ -140,12 +186,17 @@ static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr)
 {
 	struct scatterlist out_hdr, in_hdr, *sgs[3];
 	unsigned int num_out = 0, num_in = 0;
+	size_t out_hdr_len = sizeof(vbr->out_hdr.base);
+
+	if (vbr->out_hdr.base.type == cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_CRYPTO_IN) ||
+	    vbr->out_hdr.base.type == cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_CRYPTO_OUT))
+		out_hdr_len = sizeof(vbr->out_hdr.crypto_append);
 
-	sg_init_one(&out_hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
+	sg_init_one(&out_hdr, &vbr->out_hdr, out_hdr_len);
 	sgs[num_out++] = &out_hdr;
 
 	if (vbr->sg_table.nents) {
-		if (vbr->out_hdr.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT))
+		if (vbr->out_hdr.base.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT))
 			sgs[num_out++] = vbr->sg_table.sgl;
 		else
 			sgs[num_out + num_in++] = vbr->sg_table.sgl;
@@ -235,6 +286,22 @@ static void virtblk_cleanup_cmd(struct request *req)
 		kfree(bvec_virt(&req->special_vec));
 }
 
+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)
+static bool is_crypto_request(struct request *req)
+{
+	struct request_queue *q = req->q;
+
+	return q->crypto_profile &&
+	       req->crypt_ctx &&
+	       req->crypt_keyslot;
+}
+#else
+static inline bool is_crypto_request(struct request *req)
+{
+	return false;
+}
+#endif
+
 static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
 				      struct request *req,
 				      struct virtblk_req *vbr)
@@ -243,20 +310,27 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
 	bool unmap = false;
 	u32 type;
 	u64 sector = 0;
+	int i;
 
 	if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) && op_is_zone_mgmt(req_op(req)))
 		return BLK_STS_NOTSUPP;
 
 	/* Set fields for all request types */
-	vbr->out_hdr.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));
+	vbr->out_hdr.base.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));
 
 	switch (req_op(req)) {
 	case REQ_OP_READ:
-		type = VIRTIO_BLK_T_IN;
+		if (is_crypto_request(req))
+			type = VIRTIO_BLK_T_CRYPTO_IN;
+		else
+			type = VIRTIO_BLK_T_IN;
 		sector = blk_rq_pos(req);
 		break;
 	case REQ_OP_WRITE:
-		type = VIRTIO_BLK_T_OUT;
+		if (is_crypto_request(req))
+			type = VIRTIO_BLK_T_CRYPTO_OUT;
+		else
+			type = VIRTIO_BLK_T_OUT;
 		sector = blk_rq_pos(req);
 		break;
 	case REQ_OP_FLUSH:
@@ -309,8 +383,8 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
 
 	/* Set fields for non-REQ_OP_DRV_IN request types */
 	vbr->in_hdr_len = in_hdr_len;
-	vbr->out_hdr.type = cpu_to_virtio32(vdev, type);
-	vbr->out_hdr.sector = cpu_to_virtio64(vdev, sector);
+	vbr->out_hdr.base.type = cpu_to_virtio32(vdev, type);
+	vbr->out_hdr.base.sector = cpu_to_virtio64(vdev, sector);
 
 	if (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES ||
 	    type == VIRTIO_BLK_T_SECURE_ERASE) {
@@ -318,6 +392,18 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
 			return BLK_STS_RESOURCE;
 	}
 
+	if (type == VIRTIO_BLK_T_CRYPTO_IN || type == VIRTIO_BLK_T_CRYPTO_OUT) {
+		memset(&vbr->out_hdr.crypto_append.msg, 0,
+			sizeof(vbr->out_hdr.crypto_append.msg));
+		vbr->out_hdr.crypto_append.msg.slot =
+			cpu_to_virtio32(vdev,
+				blk_crypto_keyslot_index(req->crypt_keyslot));
+		for (i = 0; i < ARRAY_SIZE(vbr->out_hdr.crypto_append.msg.dun); i++) {
+			vbr->out_hdr.crypto_append.msg.dun[i] =
+				cpu_to_virtio64(vdev, req->crypt_ctx->bc_dun[i]);
+		}
+	}
+
 	return 0;
 }
 
@@ -568,8 +654,8 @@ static int virtblk_submit_zone_report(struct virtio_blk *vblk,
 
 	vbr = blk_mq_rq_to_pdu(req);
 	vbr->in_hdr_len = sizeof(vbr->in_hdr.status);
-	vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_ZONE_REPORT);
-	vbr->out_hdr.sector = cpu_to_virtio64(vblk->vdev, sector);
+	vbr->out_hdr.base.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_ZONE_REPORT);
+	vbr->out_hdr.base.sector = cpu_to_virtio64(vblk->vdev, sector);
 
 	err = blk_rq_map_kern(req, report_buf, report_len, GFP_KERNEL);
 	if (err)
@@ -817,8 +903,8 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)
 
 	vbr = blk_mq_rq_to_pdu(req);
 	vbr->in_hdr_len = sizeof(vbr->in_hdr.status);
-	vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_ID);
-	vbr->out_hdr.sector = 0;
+	vbr->out_hdr.base.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_ID);
+	vbr->out_hdr.base.sector = 0;
 
 	err = blk_rq_map_kern(req, id_str, VIRTIO_BLK_ID_BYTES, GFP_KERNEL);
 	if (err)
@@ -863,11 +949,598 @@ static int virtblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)
 	return ret;
 }
 
+#define VIRTBLK_CTRL_VQ_DRAIN_TIMEOUT (10 * HZ)
+
+/* Prevent new submissions and wait for in-flight requests to complete. */
+static void virtblk_ctrl_vq_quiesce(struct virtio_blk *vblk)
+{
+	unsigned long flags;
+	bool need_wait;
+
+	if (!vblk->ctrl_vq.vq)
+		return;
+
+	init_completion(&vblk->ctrl_vq.drained);
+
+	spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	vblk->ctrl_vq.dead = true;
+	need_wait = vblk->ctrl_vq.inflight != 0;
+	spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+
+	if (need_wait &&
+	    !wait_for_completion_timeout(&vblk->ctrl_vq.drained,
+					  VIRTBLK_CTRL_VQ_DRAIN_TIMEOUT))
+		dev_warn(&vblk->vdev->dev,
+			 "timed out waiting for control queue requests to complete\n");
+}
+
+/* Fail requests left in the control queue after reset. */
+static void virtblk_ctrl_vq_drain(struct virtio_blk *vblk)
+{
+	struct virtblk_ctrl_request *creq;
+	unsigned long flags;
+
+	if (!vblk->ctrl_vq.vq)
+		return;
+
+	spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	while ((creq = virtqueue_detach_unused_buf(vblk->ctrl_vq.vq)) != NULL) {
+		if (WARN_ON_ONCE(!vblk->ctrl_vq.inflight))
+			;
+		else
+			vblk->ctrl_vq.inflight--;
+		creq->status = VIRTIO_BLK_S_IOERR;
+		spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+		complete(&creq->compl);
+		spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	}
+	spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+}
+
+static void virtblk_ctrlq_callback(struct virtqueue *vq)
+{
+	struct virtio_blk *vblk = vq->vdev->priv;
+	struct virtblk_ctrl_request *creq;
+	unsigned long flags;
+	unsigned int len;
+
+	spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	do {
+		virtqueue_disable_cb(vq);
+		while ((creq = virtqueue_get_buf(vq, &len)) != NULL) {
+			bool drained = false;
+
+			if (WARN_ON_ONCE(!vblk->ctrl_vq.inflight)) {
+				/*
+				 * Still complete the request.  Never leave a
+				 * synchronous caller blocked because the accounting
+				 * state was already inconsistent.
+				 */
+				spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+				complete(&creq->compl);
+				spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+				continue;
+			}
+
+			if (--vblk->ctrl_vq.inflight == 0 && vblk->ctrl_vq.dead)
+				drained = true;
+			spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+			if (drained)
+				complete(&vblk->ctrl_vq.drained);
+			complete(&creq->compl);
+			spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+		}
+	} while (!virtqueue_enable_cb(vq));
+	spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+}
+
+/* Submit a control-queue request and wait for completion. */
+static int virtblk_ctrl_vq_request(struct virtio_blk *vblk,
+				    struct virtblk_ctrl_request *creq,
+				    struct scatterlist *sgs[],
+				    unsigned int out_sgs, unsigned int in_sgs)
+{
+	unsigned long flags;
+	int err;
+
+	mutex_lock(&vblk->ctrl_vq.mutex);
+	init_completion(&creq->compl);
+
+	spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	if (vblk->ctrl_vq.dead) {
+		spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+		mutex_unlock(&vblk->ctrl_vq.mutex);
+		return -ENODEV;
+	}
+	err = virtqueue_add_sgs(vblk->ctrl_vq.vq, sgs, out_sgs, in_sgs, creq, GFP_ATOMIC);
+	if (!err) {
+		vblk->ctrl_vq.inflight++;
+		virtqueue_kick(vblk->ctrl_vq.vq);
+	}
+	spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+	if (err) {
+		mutex_unlock(&vblk->ctrl_vq.mutex);
+		return err;
+	}
+
+	wait_for_completion(&creq->compl);
+	mutex_unlock(&vblk->ctrl_vq.mutex);
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)
+// Maps VIRTIO_BLK_CRYPTO_MODE_* values to the kernel's internal enum.
+static const enum blk_crypto_mode_num
+	virtio_blk_crypto_mode_map[VIRTIO_BLK_CRYPTO_MODE_MAX + 1] = {
+	[VIRTIO_BLK_CRYPTO_MODE_INVALID]	= BLK_ENCRYPTION_MODE_INVALID,
+	[VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS]	= BLK_ENCRYPTION_MODE_AES_256_XTS,
+};
+
+static int virtblk_get_crypto_modes(struct virtio_blk *vblk,
+				    unsigned int *crypto_modes_supported)
+{
+	unsigned int nr_modes = VIRTIO_BLK_CRYPTO_MODE_MAX + 1;
+	struct scatterlist type_sg, resp_sg, status_sg, *sgs[3];
+	struct virtblk_ctrl_request *creq;
+	unsigned int i;
+	int err;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_CRYPTO_MODES);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&resp_sg, &creq->in_resp.modes, sizeof(creq->in_resp.modes));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &resp_sg;
+	sgs[2] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 1, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	for (i = 1; i < nr_modes; i++) {
+		u32 mode_mask = virtio32_to_cpu(vblk->vdev,
+						 creq->in_resp.modes.modes[i]);
+		enum blk_crypto_mode_num mode = virtio_blk_crypto_mode_map[i];
+
+		if (!mode_mask)
+			continue;
+		if (!mode) {
+			dev_warn(&vblk->vdev->dev,
+				 "ignoring unknown crypto mode %u\n", i);
+			continue;
+		}
+		crypto_modes_supported[mode] = mode_mask;
+	}
+
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int set_virtblk_crypto_key_desc(struct virtio_device *vdev,
+				       struct virtblk_ctrl_request *creq,
+				       const struct blk_crypto_key *key,
+				       unsigned int slot)
+{
+	struct virtio_blk_crypto_key_desc *desc = &creq->out_req.key_desc;
+	enum blk_crypto_key_type key_type = key->crypto_cfg.key_type;
+
+	if (sizeof(desc->bytes) < key->size)
+		return -EOVERFLOW;
+
+	memset(desc, 0, sizeof(*desc));
+	desc->slot = cpu_to_virtio32(vdev, slot);
+	memcpy(desc->bytes, key->bytes, key->size);
+	desc->key_size = cpu_to_virtio32(vdev, key->size);
+	desc->crypto_mode = cpu_to_virtio32(vdev, key->crypto_cfg.crypto_mode);
+	switch (key->crypto_cfg.key_type) {
+	case BLK_CRYPTO_KEY_TYPE_RAW:
+		desc->key_type = cpu_to_virtio32(vdev,
+			VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW);
+		break;
+	case BLK_CRYPTO_KEY_TYPE_HW_WRAPPED:
+		desc->key_type = cpu_to_virtio32(vdev,
+			VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+	desc->data_unit_size_bits = cpu_to_virtio32(vdev, key->data_unit_size_bits);
+	desc->dun_bytes = cpu_to_virtio32(vdev, key->crypto_cfg.dun_bytes);
+
+	return 0;
+}
+
+static inline struct virtio_blk *virtblk_from_profile(struct blk_crypto_profile *profile)
+{
+	return container_of(profile, struct virtio_blk, profile);
+}
+
+static int virtblk_crypto_keyslot_program(struct blk_crypto_profile *profile,
+					   const struct blk_crypto_key *key,
+					   unsigned int slot)
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, status_sg, *sgs[3];
+	struct virtblk_ctrl_request *creq;
+	int err;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM);
+
+	err = set_virtblk_crypto_key_desc(vblk->vdev, creq, key, slot);
+	if (err)
+		goto out_free;
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.key_desc, sizeof(creq->out_req.key_desc));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 1);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_keyslot_evict(struct blk_crypto_profile *profile,
+					 const struct blk_crypto_key *key,
+					 unsigned int slot)
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, status_sg, *sgs[3];
+	struct virtblk_ctrl_request *creq;
+	int err;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_KEYSLOT_EVICT);
+
+	err = set_virtblk_crypto_key_desc(vblk->vdev, creq, key, slot);
+	if (err)
+		goto out_free;
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.key_desc, sizeof(creq->out_req.key_desc));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 1);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *profile,
+					    const u8 *eph_key, size_t eph_key_size,
+					    u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];
+	struct virtblk_ctrl_request *creq;
+	int err;
+
+	if (eph_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE
+	    || sizeof(creq->in_resp.secret.secret) < BLK_CRYPTO_SW_SECRET_SIZE)
+		return -EOVERFLOW;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET);
+	memcpy(creq->out_req.blob.key, eph_key, eph_key_size);
+	creq->out_req.blob.key_size = cpu_to_virtio32(vblk->vdev, eph_key_size);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.blob, sizeof(creq->out_req.blob));
+	sg_init_one(&resp_sg, &creq->in_resp.secret, sizeof(creq->in_resp.secret));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &resp_sg;
+	sgs[3] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	memcpy(sw_secret, creq->in_resp.secret.secret, BLK_CRYPTO_SW_SECRET_SIZE);
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_generate_key(struct blk_crypto_profile *profile,
+					u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, resp_sg, status_sg, *sgs[3];
+	struct virtblk_ctrl_request *creq;
+	unsigned int key_size;
+	int err;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_GENERATE_KEY);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&resp_sg, &creq->in_resp.blob, sizeof(creq->in_resp.blob));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &resp_sg;
+	sgs[2] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 1, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	key_size = virtio32_to_cpu(vblk->vdev, creq->in_resp.blob.key_size);
+	if (!key_size ||
+		key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) {
+		dev_err(&vblk->vdev->dev,
+			"backend returned oversized generated key: %u\n", key_size);
+		err = -EOVERFLOW;
+		goto out_free;
+	}
+	memcpy(lt_key, creq->in_resp.blob.key, key_size);
+	err = key_size;
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_prepare_key(struct blk_crypto_profile *profile,
+				       const u8 *lt_key, size_t lt_key_size,
+				       u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];
+	struct virtblk_ctrl_request *creq;
+	unsigned int key_size;
+	int err;
+
+	if (lt_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE)
+		return -EOVERFLOW;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_PREPARE_KEY);
+	memcpy(creq->out_req.blob.key, lt_key, lt_key_size);
+	creq->out_req.blob.key_size = cpu_to_virtio32(vblk->vdev, lt_key_size);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.blob, sizeof(creq->out_req.blob));
+	sg_init_one(&resp_sg, &creq->in_resp.blob, sizeof(creq->in_resp.blob));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &resp_sg;
+	sgs[3] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	key_size = virtio32_to_cpu(vblk->vdev, creq->in_resp.blob.key_size);
+	if (!key_size ||
+		key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) {
+		dev_err(&vblk->vdev->dev,
+			"backend returned oversized prepared key: %u\n", key_size);
+		err = -EOVERFLOW;
+		goto out_free;
+	}
+	memcpy(eph_key, creq->in_resp.blob.key, key_size);
+	err = key_size;
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_import_key(struct blk_crypto_profile *profile,
+				      const u8 *raw_key, size_t raw_key_size,
+				      u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];
+	struct virtblk_ctrl_request *creq;
+	unsigned int key_size;
+	int err;
+
+	if (raw_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE)
+		return -EOVERFLOW;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_IMPORT_KEY);
+	memcpy(creq->out_req.blob.key, raw_key, raw_key_size);
+	creq->out_req.blob.key_size = cpu_to_virtio32(vblk->vdev, raw_key_size);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.blob, sizeof(creq->out_req.blob));
+	sg_init_one(&resp_sg, &creq->in_resp.blob, sizeof(creq->in_resp.blob));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &resp_sg;
+	sgs[3] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	key_size = virtio32_to_cpu(vblk->vdev, creq->in_resp.blob.key_size);
+	if (!key_size ||
+		key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) {
+		dev_err(&vblk->vdev->dev,
+			"backend returned oversized imported key: %u\n", key_size);
+		err = -EOVERFLOW;
+		goto out_free;
+	}
+	memcpy(lt_key, creq->in_resp.blob.key, key_size);
+	err = key_size;
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static const struct blk_crypto_ll_ops virtblk_crypto_ops = {
+	.keyslot_program	= virtblk_crypto_keyslot_program,
+	.keyslot_evict		= virtblk_crypto_keyslot_evict,
+	.derive_sw_secret	= virtblk_crypto_derive_sw_secret,
+	.generate_key		= virtblk_crypto_generate_key,
+	.prepare_key		= virtblk_crypto_prepare_key,
+	.import_key		= virtblk_crypto_import_key,
+};
+
+static int virtblk_init_crypto(struct virtio_blk *vblk)
+{
+	struct virtio_device *vdev = vblk->vdev;
+	unsigned int crypto_modes_supported[BLK_ENCRYPTION_MODE_MAX] = { 0 };
+	unsigned int key_type_supported = 0;
+	u16 max_slots = 0;
+	/* virtio_cread() requires the variable size to match the config field exactly */
+	u8 max_dun_bytes = 0, key_types = 0;
+	int err;
+
+	virtio_cread(vdev, struct virtio_blk_config,
+		     enc_characteristics.max_slots, &max_slots);
+	virtio_cread(vdev, struct virtio_blk_config,
+		     enc_characteristics.max_dun_bytes, &max_dun_bytes);
+	virtio_cread(vdev, struct virtio_blk_config,
+		     enc_characteristics.key_types, &key_types);
+
+	dev_info_once(&vdev->dev,
+		 "max_slots = %u, max_dun_bytes = %u, key_types = 0x%x\n",
+		 max_slots, max_dun_bytes, key_types);
+
+	if (!max_slots)
+		return -EINVAL;
+
+	/*
+	 * struct virtio_blk_crypto_msg.dun is a fixed array of four __virtio64
+	 * values (32 bytes total), matching the size of
+	 * blk_crypto_ctx::bc_dun[4].  Refuse to advertise more than that as
+	 * supported, or blk-crypto could negotiate a larger dun_bytes with the
+	 * filesystem and have the high-order bytes of req->crypt_ctx->bc_dun
+	 * silently dropped in virtblk_setup_cmd().
+	 */
+	if (max_dun_bytes > sizeof_field(struct virtio_blk_crypto_msg, dun))
+		return -EINVAL;
+
+	if (key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW)
+		key_type_supported |= BLK_CRYPTO_KEY_TYPE_RAW;
+	if (key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED)
+		key_type_supported |= BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
+	if (!key_type_supported)
+		return -EINVAL;
+
+	err = virtblk_get_crypto_modes(vblk, crypto_modes_supported);
+	if (err) {
+		dev_err(&vdev->dev, "get crypto modes failed: %d\n", err);
+		return err;
+	}
+
+	/*
+	 * Use the plain (non-devm) initializer: vblk->profile is embedded in
+	 * struct virtio_blk, whose lifetime is tied to the gendisk, not to
+	 * &vdev->dev. Tying destruction to the vdev via devm would run the
+	 * destroy callback after virtblk_remove() has already freed vblk.
+	 * virtblk_free_disk() calls blk_crypto_profile_destroy() explicitly
+	 * instead, guarded by crypto_profile_initialized below.
+	 */
+	err = blk_crypto_profile_init(&vblk->profile, max_slots);
+	if (err) {
+		dev_err(&vdev->dev, "crypto profile initialization failed: %d\n", err);
+		return err;
+	}
+
+	vblk->profile.ll_ops = virtblk_crypto_ops;
+	vblk->profile.max_dun_bytes_supported = max_dun_bytes;
+	vblk->profile.key_types_supported = key_type_supported;
+	vblk->profile.dev = &vdev->dev;
+	memcpy(vblk->profile.modes_supported, crypto_modes_supported,
+	       BLK_ENCRYPTION_MODE_MAX * sizeof(unsigned int));
+
+	vblk->crypto_profile_initialized = true;
+
+	dev_info(&vdev->dev, "inline crypto profile initialized\n");
+
+	return 0;
+}
+
+static void virtblk_destroy_crypto(struct virtio_blk *vblk)
+{
+	if (vblk->crypto_profile_initialized)
+		blk_crypto_profile_destroy(&vblk->profile);
+}
+#else
+
+static inline int virtblk_init_crypto(struct virtio_blk *vblk)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void virtblk_destroy_crypto(struct virtio_blk *vblk)
+{
+}
+#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */
+
 static void virtblk_free_disk(struct gendisk *disk)
 {
 	struct virtio_blk *vblk = disk->private_data;
 
 	ida_free(&vd_index_ida, vblk->index);
+	virtblk_destroy_crypto(vblk);
+	mutex_destroy(&vblk->ctrl_vq.mutex);
 	mutex_destroy(&vblk->vdev_mutex);
 	kfree(vblk);
 }
@@ -965,6 +1638,8 @@ static int init_vq(struct virtio_blk *vblk)
 	struct virtqueue **vqs;
 	unsigned short num_vqs;
 	unsigned short num_poll_vqs;
+	unsigned short total_vqs;
+	bool has_ctrl_vq;
 	struct virtio_device *vdev = vblk->vdev;
 	struct irq_affinity desc = { 0, };
 
@@ -993,12 +1668,19 @@ static int init_vq(struct virtio_blk *vblk)
 				vblk->io_queues[HCTX_TYPE_READ],
 				vblk->io_queues[HCTX_TYPE_POLL]);
 
+	/*
+	 * The control vq is appended after the data vqs whenever
+	 * F_CTRL_VQ is negotiated.
+	 */
+	has_ctrl_vq = virtio_has_feature(vdev, VIRTIO_BLK_F_CTRL_VQ);
+	total_vqs = num_vqs + (has_ctrl_vq ? 1 : 0);
+
 	vblk->vqs = kmalloc_objs(*vblk->vqs, num_vqs);
 	if (!vblk->vqs)
 		return -ENOMEM;
 
-	vqs_info = kzalloc_objs(*vqs_info, num_vqs);
-	vqs = kmalloc_objs(*vqs, num_vqs);
+	vqs_info = kzalloc_objs(*vqs_info, total_vqs);
+	vqs = kmalloc_objs(*vqs, total_vqs);
 	if (!vqs_info || !vqs) {
 		err = -ENOMEM;
 		goto out;
@@ -1015,8 +1697,13 @@ static int init_vq(struct virtio_blk *vblk)
 		vqs_info[i].name = vblk->vqs[i].name;
 	}
 
+	if (has_ctrl_vq) {
+		vqs_info[num_vqs].callback = virtblk_ctrlq_callback;
+		vqs_info[num_vqs].name = "control";
+	}
+
 	/* Discover virtqueues and write information to configuration.  */
-	err = virtio_find_vqs(vdev, num_vqs, vqs, vqs_info, &desc);
+	err = virtio_find_vqs(vdev, total_vqs, vqs, vqs_info, &desc);
 	if (err)
 		goto out;
 
@@ -1025,6 +1712,9 @@ static int init_vq(struct virtio_blk *vblk)
 		vblk->vqs[i].vq = vqs[i];
 	}
 	vblk->num_vqs = num_vqs;
+	vblk->ctrl_vq.vq = has_ctrl_vq ? vqs[num_vqs] : NULL;
+	vblk->ctrl_vq.dead = false;
+	vblk->ctrl_vq.inflight = 0;
 
 out:
 	kfree(vqs);
@@ -1464,14 +2154,18 @@ static int virtblk_probe(struct virtio_device *vdev)
 	}
 
 	mutex_init(&vblk->vdev_mutex);
+	mutex_init(&vblk->ctrl_vq.mutex);
+	spin_lock_init(&vblk->ctrl_vq.lock);
 
 	vblk->vdev = vdev;
 
 	INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
 
 	err = init_vq(vblk);
-	if (err)
+	if (err) {
+		dev_err(&vdev->dev, "init virt queue failed: err = %d\n", err);
 		goto out_free_vblk;
+	}
 
 	/* Default queue sizing is to fill the ring. */
 	if (!virtblk_queue_depth) {
@@ -1538,6 +2232,19 @@ static int virtblk_probe(struct virtio_device *vdev)
 		err = blk_revalidate_disk_zones(vblk->disk);
 		if (err)
 			goto out_cleanup_disk;
+	} else if (IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) &&
+		   virtio_has_feature(vdev, VIRTIO_BLK_F_INLINE_ENCRYPTION) &&
+		   virtio_has_feature(vdev, VIRTIO_BLK_F_CTRL_VQ)) {
+		err = virtblk_init_crypto(vblk);
+		if (!err) {
+			if (!blk_crypto_register(&vblk->profile, vblk->disk->queue))
+				dev_warn(&vdev->dev,
+					 "failed to register inline crypto profile\n");
+		} else {
+			dev_warn(&vdev->dev,
+				 "inline crypto init failed: %d, continuing without inline crypto support\n",
+				 err);
+		}
 	}
 
 	err = device_add_disk(&vdev->dev, vblk->disk, virtblk_attr_groups);
@@ -1553,6 +2260,7 @@ static int virtblk_probe(struct virtio_device *vdev)
 out_free_vq:
 	vdev->config->del_vqs(vdev);
 	kfree(vblk->vqs);
+	vblk->ctrl_vq.vq = NULL;
 out_free_vblk:
 	kfree(vblk);
 out_free_index:
@@ -1571,16 +2279,21 @@ static void virtblk_remove(struct virtio_device *vdev)
 	del_gendisk(vblk->disk);
 	blk_mq_free_tag_set(&vblk->tag_set);
 
+	virtblk_ctrl_vq_quiesce(vblk);
+
 	mutex_lock(&vblk->vdev_mutex);
 
 	/* Stop all the virtqueues. */
 	virtio_reset_device(vdev);
+	virtblk_ctrl_vq_drain(vblk);
 
 	/* Virtqueues are stopped, nothing can use vblk->vdev anymore. */
 	vblk->vdev = NULL;
 
 	vdev->config->del_vqs(vdev);
 	kfree(vblk->vqs);
+	vblk->vqs = NULL;
+	vblk->ctrl_vq.vq = NULL;
 
 	mutex_unlock(&vblk->vdev_mutex);
 
@@ -1593,6 +2306,8 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
 	struct request_queue *q = vblk->disk->queue;
 	unsigned int memflags;
 
+	virtblk_ctrl_vq_quiesce(vblk);
+
 	/* Ensure no requests in virtqueues before deleting vqs. */
 	memflags = blk_mq_freeze_queue(q);
 	blk_mq_quiesce_queue_nowait(q);
@@ -1600,6 +2315,7 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
 
 	/* Ensure we don't receive any more interrupts */
 	virtio_reset_device(vdev);
+	virtblk_ctrl_vq_drain(vblk);
 
 	/* Make sure no work handler is accessing the device. */
 	flush_work(&vblk->config_work);
@@ -1612,6 +2328,7 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
 	 * pointers safely.
 	 */
 	vblk->vqs = NULL;
+	vblk->ctrl_vq.vq = NULL;
 
 	return 0;
 }
@@ -1628,6 +2345,9 @@ static int virtblk_restore_priv(struct virtio_device *vdev)
 	virtio_device_ready(vdev);
 	blk_mq_unquiesce_queue(vblk->disk->queue);
 
+	if (vblk->profile.slots)
+		blk_crypto_reprogram_all_keys(&vblk->profile);
+
 	return 0;
 }
 
@@ -1672,6 +2392,7 @@ static unsigned int features[] = {
 	VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE,
 	VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_WRITE_ZEROES,
 	VIRTIO_BLK_F_SECURE_ERASE, VIRTIO_BLK_F_ZONED,
+	VIRTIO_BLK_F_CTRL_VQ, VIRTIO_BLK_F_INLINE_ENCRYPTION,
 };
 
 static struct virtio_driver virtio_blk = {
diff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_blk.h
index 3744e4da1b2a7..952534e2b489d 100644
--- a/include/uapi/linux/virtio_blk.h
+++ b/include/uapi/linux/virtio_blk.h
@@ -42,6 +42,8 @@
 #define VIRTIO_BLK_F_WRITE_ZEROES	14	/* WRITE ZEROES is supported */
 #define VIRTIO_BLK_F_SECURE_ERASE	16 /* Secure Erase is supported */
 #define VIRTIO_BLK_F_ZONED		17	/* Zoned block device */
+#define VIRTIO_BLK_F_CTRL_VQ			22	/* Control queue */
+#define VIRTIO_BLK_F_INLINE_ENCRYPTION		23	/* Inline encryption */
 
 /* Legacy feature bits */
 #ifndef VIRTIO_BLK_NO_LEGACY
@@ -57,6 +59,10 @@
 
 #define VIRTIO_BLK_ID_BYTES	20	/* ID string length */
 
+/* Key type bitmask for VIRTIO_BLK_F_INLINE_ENCRYPTION */
+#define VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW	(1 << 0)
+#define VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED	(1 << 1)
+
 struct virtio_blk_config {
 	/* The capacity (in 512-byte sectors). */
 	__virtio64 capacity;
@@ -148,6 +154,15 @@ struct virtio_blk_config {
 		__u8 model;
 		__u8 unused2[3];
 	} zoned;
+
+	/* Inline Encryption device characteristics (if VIRTIO_BLK_F_INLINE_ENCRYPTION) */
+	struct virtio_blk_enc_characteristics {
+		__virtio16 max_slots;
+		__u8 max_dun_bytes;
+		/* Bitmask of supported key types: VIRTIO_BLK_CRYPTO_KEY_TYPE_* */
+		__u8 key_types;
+		__virtio32 unused3;
+	} enc_characteristics;
 } __attribute__((packed));
 
 /*
@@ -206,6 +221,33 @@ struct virtio_blk_config {
 /* Reset All zones command */
 #define VIRTIO_BLK_T_ZONE_RESET_ALL 26
 
+/* Inline-encrypted write: crypto_msg set in outhdr */
+#define VIRTIO_BLK_T_CRYPTO_OUT		27
+
+/* Inline-encrypted read: crypto_msg set in outhdr */
+#define VIRTIO_BLK_T_CRYPTO_IN		28
+
+/* Get inline crypto modes */
+#define VIRTIO_BLK_T_GET_CRYPTO_MODES	29
+
+/* Program a key into the keyslot */
+#define VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM	30
+
+/* Evict a key */
+#define VIRTIO_BLK_T_CRYPTO_KEYSLOT_EVICT	31
+
+/* Derive the software secret from a hardware-wrapped key */
+#define VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET	32
+
+/* Generate a new hardware-wrapped key */
+#define VIRTIO_BLK_T_CRYPTO_GENERATE_KEY	33
+
+/* Import a raw key as a hardware-wrapped key */
+#define VIRTIO_BLK_T_CRYPTO_IMPORT_KEY		34
+
+/* Convert a long-term wrapped key to its ephemerally-wrapped form */
+#define VIRTIO_BLK_T_CRYPTO_PREPARE_KEY	35
+
 #ifndef VIRTIO_BLK_NO_LEGACY
 /* Barrier before this op. */
 #define VIRTIO_BLK_T_BARRIER	0x80000000
@@ -225,6 +267,80 @@ struct virtio_blk_outhdr {
 	__virtio64 sector;
 };
 
+/*
+ * Crypto message descriptor, appended to the outhdr of a
+ * VIRTIO_BLK_T_CRYPTO_OUT or VIRTIO_BLK_T_CRYPTO_IN request.
+ */
+struct virtio_blk_crypto_msg {
+	/* virtual key slot index */
+	__virtio32 slot;
+	__u8 unused[4];
+	/* data unit number (DUN / IV) for this request */
+	__virtio64 dun[4];
+};
+
+/*
+ * Inline crypto key descriptor. Request part for
+ * VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM/KEYSLOT_EVICT.
+ */
+/* Must be >= BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE in include/linux/blk-crypto.h */
+#define VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE		128
+
+struct virtio_blk_crypto_key_desc {
+	__virtio32  slot;
+	__u8        bytes[VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE];
+	__virtio32  key_size;
+	__virtio32  crypto_mode;
+	__virtio32  key_type;
+	__virtio32  data_unit_size_bits;
+	__virtio32  dun_bytes;
+};
+
+/*
+ * A raw or hardware-wrapped key blob, used as the request and/or reply part
+ * of the VIRTIO_BLK_T_CRYPTO_GENERATE_KEY/IMPORT_KEY/PREPARE_KEY/
+ * DERIVE_SW_SECRET commands.
+ */
+struct virtio_blk_crypto_key_blob {
+	__virtio32 key_size;
+	__u8 key[VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE];
+};
+
+/* Must match BLK_CRYPTO_SW_SECRET_SIZE in include/linux/blk-crypto.h */
+#define VIRTIO_BLK_CRYPTO_SW_SECRET_SIZE	32
+
+/* Reply to a VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET request. */
+struct virtio_blk_crypto_sw_secret {
+	__u8 secret[VIRTIO_BLK_CRYPTO_SW_SECRET_SIZE];
+};
+
+/*
+ * Crypto mode numbers used in VIRTIO_BLK_T_GET_CRYPTO_MODES replies and in
+ * indexing struct virtio_blk_crypto_modes.modes[] below. These numbers are
+ * assigned by the virtio spec and are stable: a number is never reused for
+ * a different crypto mode, and additional crypto modes are assigned new,
+ * higher numbers.
+ */
+enum {
+	VIRTIO_BLK_CRYPTO_MODE_INVALID,
+	VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS,
+	__VIRTIO_BLK_CRYPTO_MODE_MAX,	/* sentinel: always one past the last real mode */
+};
+
+/* Highest crypto mode number defined by this version of the header. */
+#define VIRTIO_BLK_CRYPTO_MODE_MAX (__VIRTIO_BLK_CRYPTO_MODE_MAX - 1)
+
+/* Reply to a VIRTIO_BLK_T_GET_CRYPTO_MODES request. */
+struct virtio_blk_crypto_modes {
+	/*
+	 * modes[N], for crypto mode number N <= VIRTIO_BLK_CRYPTO_MODE_MAX, is
+	 * a bitmask of the data unit sizes with which crypto mode N can be
+	 * used: bit i is set if a data unit size of (1 << i) bytes is
+	 * supported. modes[0] is reserved and always 0.
+	 */
+	__virtio32 modes[__VIRTIO_BLK_CRYPTO_MODE_MAX];
+};
+
 /*
  * Supported zoned device models.
  */

3/2 2026/09/14 18:17 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 18324
							output: 18
							thoughts: 783
						

Thoughts:
**Analyzing the Virtio Block Inline Encryption Patch**

Okay, let's dive into this patch series. My initial thought is to figure out if this warrants a syzkaller fuzzing campaign. The first step is to ascertain if the new functionality is reachable and if there are good target symbols to focus on. We need to determine if it's worth the effort of enabling any specific kernel configurations.

Reading through the patch, it seems pretty straightforward. The primary change is the addition of `VIRTIO_BLK_INLINE_ENCRYPTION` in `drivers/block/Kconfig`. It's a tristate option that depends on `VIRTIO_BLK` and `BLK_INLINE_ENCRYPTION`, so that's the base requirement.

The real meat is in the `drivers/block/virtio_blk.c` file. This adds control virtqueue support, with new structures and functions for quiescing, draining, and handling control requests (`struct virtio_blk_ctrl_vq`, `virtblk_ctrl_vq_quiesce`, etc.).

More significantly, this patch seems to be adding inline encryption support via `blk_crypto_profile`. I see several new functions related to crypto key management and processing: `virtblk_get_crypto_modes`, `virtblk_crypto_keyslot_program`, `virtblk_crypto_keyslot_evict`, deriving and generating keys, import and preparation with `virtblk_crypto_derive_sw_secret`, `virtblk_crypto_generate_key`, `virtblk_crypto_prepare_key`, `virtblk_crypto_import_key`, as well as `virtblk_init_crypto` and `virtblk_destroy_crypto`. There are also some helper functions: `is_crypto_request` and changes in the core request handling (`virtblk_add_req`, `virtblk_setup_cmd`). The patch also modifies `init_vq` to handle the control virtqueue, and `virtblk_probe` to initialize the crypto and register the `blk_crypto_profile`. `virtblk_remove`, `virtblk_freeze_priv`, and `virtblk_restore_priv` also receive updates. The feature list is updated with `VIRTIO_BLK_F_CTRL_VQ` and `VIRTIO_BLK_F_INLINE_ENCRYPTION`.

The key question is reachability. Virtio devices are common in virtualized environments like QEMU and GCE. But can syzkaller interact with `virtio-blk` or `blk-crypto` directly? Wait, does syzkaller even fuzz `virtio_blk`? Let's check. Yes! QEMU does emulate `virtio-blk-pci`, so that's promising.

The next question is how crypto requests are passed to virtio_blk. Do they come from userspace? How does `BLK_INLINE_ENCRYPTION` work? How do userspace and the kernel interact? I need to check how inline encryption is actually *used*. For example, `FS_IOC_SET_ENCRYPTION_POLICY` in `ext4` or `f2fs`, perhaps `dm-crypt`, or `ioctl(BLKCRYPTO_...)`? What triggers keyslot programming, is it an ioctl, sysfs, keyring, or is it via fs-crypt? I'll probably need to use `grepper` to see how `blk_crypto_profile` and keyslots are used throughout the kernel to understand the trigger.



4/2 2026/09/14 18:17 tool grepper
0m Args:
{
  "Expression": "keyslot_program"
}

Results:
{
  "Output": "Documentation/block/inline-encryption.rst=238=hardware, e.g. how to program and evict keyslots.  Most drivers will need to\nDocumentation/block/inline-encryption.rst:239:implement ``keyslot_program`` and ``keyslot_evict``.  For details, see the\nDocumentation/block/inline-encryption.rst-240-comments for ``struct blk_crypto_ll_ops``.\n--\nDocumentation/block/inline-encryption.rst=467=when hardware support is available.  This works in the following way:\n--\nDocumentation/block/inline-encryption.rst-483-- The programming and eviction of hardware-wrapped keys happens via\nDocumentation/block/inline-encryption.rst:484:  ``blk_crypto_ll_ops::keyslot_program`` and\nDocumentation/block/inline-encryption.rst-485-  ``blk_crypto_ll_ops::keyslot_evict``, just like it does for raw keys.  If a\n--\nblock/blk-crypto-fallback.c=107=static int\nblock/blk-crypto-fallback.c:108:blk_crypto_fallback_keyslot_program(struct blk_crypto_profile *profile,\nblock/blk-crypto-fallback.c-109-\t\t\t\t    const struct blk_crypto_key *key,\n--\nblock/blk-crypto-fallback.c=139=static const struct blk_crypto_ll_ops blk_crypto_fallback_ll_ops = {\nblock/blk-crypto-fallback.c:140:\t.keyslot_program        = blk_crypto_fallback_keyslot_program,\nblock/blk-crypto-fallback.c-141-\t.keyslot_evict          = blk_crypto_fallback_keyslot_evict,\n--\nblock/blk-crypto-profile.c=248=blk_status_t blk_crypto_get_keyslot(struct blk_crypto_profile *profile,\n--\nblock/blk-crypto-profile.c-294-\nblock/blk-crypto-profile.c:295:\terr = profile-\u003ell_ops.keyslot_program(profile, key, slot_idx);\nblock/blk-crypto-profile.c-296-\tif (err) {\n--\nblock/blk-crypto-profile.c=398=void blk_crypto_reprogram_all_keys(struct blk_crypto_profile *profile)\n--\nblock/blk-crypto-profile.c-413-\nblock/blk-crypto-profile.c:414:\t\terr = profile-\u003ell_ops.keyslot_program(profile, key, slot);\nblock/blk-crypto-profile.c-415-\t\tWARN_ON(err);\n--\ndrivers/block/virtio_blk.c=1163=static inline struct virtio_blk *virtblk_from_profile(struct blk_crypto_profile *profile)\n--\ndrivers/block/virtio_blk.c-1167-\ndrivers/block/virtio_blk.c:1168:static int virtblk_crypto_keyslot_program(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c-1169-\t\t\t\t\t   const struct blk_crypto_key *key,\n--\ndrivers/block/virtio_blk.c=1435=static const struct blk_crypto_ll_ops virtblk_crypto_ops = {\ndrivers/block/virtio_blk.c:1436:\t.keyslot_program\t= virtblk_crypto_keyslot_program,\ndrivers/block/virtio_blk.c-1437-\t.keyslot_evict\t\t= virtblk_crypto_keyslot_evict,\n--\ndrivers/mmc/host/cqhci-crypto.c=31=static void cqhci_crypto_program_key(struct cqhci_host *cq_host,\n--\ndrivers/mmc/host/cqhci-crypto.c-53-\ndrivers/mmc/host/cqhci-crypto.c:54:static int cqhci_crypto_keyslot_program(struct blk_crypto_profile *profile,\ndrivers/mmc/host/cqhci-crypto.c-55-\t\t\t\t\tconst struct blk_crypto_key *key,\n--\ndrivers/mmc/host/cqhci-crypto.c=129=static const struct blk_crypto_ll_ops cqhci_crypto_ops = {\ndrivers/mmc/host/cqhci-crypto.c:130:\t.keyslot_program\t= cqhci_crypto_keyslot_program,\ndrivers/mmc/host/cqhci-crypto.c-131-\t.keyslot_evict\t\t= cqhci_crypto_keyslot_evict,\n--\ndrivers/mmc/host/sdhci-msm.c=1988=sdhci_msm_host_from_crypto_profile(struct blk_crypto_profile *profile)\n--\ndrivers/mmc/host/sdhci-msm.c-2001- */\ndrivers/mmc/host/sdhci-msm.c:2002:static int sdhci_msm_ice_keyslot_program(struct blk_crypto_profile *profile,\ndrivers/mmc/host/sdhci-msm.c-2003-\t\t\t\t\t const struct blk_crypto_key *key,\n--\ndrivers/mmc/host/sdhci-msm.c=2120=static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = {\ndrivers/mmc/host/sdhci-msm.c:2121:\t.keyslot_program\t= sdhci_msm_ice_keyslot_program,\ndrivers/mmc/host/sdhci-msm.c-2122-\t.keyslot_evict\t\t= sdhci_msm_ice_keyslot_evict,\n--\ndrivers/ufs/core/ufshcd-crypto.c=20=static void ufshcd_program_key(struct ufs_hba *hba,\n--\ndrivers/ufs/core/ufshcd-crypto.c-42-\ndrivers/ufs/core/ufshcd-crypto.c:43:static int ufshcd_crypto_keyslot_program(struct blk_crypto_profile *profile,\ndrivers/ufs/core/ufshcd-crypto.c-44-\t\t\t\t\t const struct blk_crypto_key *key,\n--\ndrivers/ufs/core/ufshcd-crypto.c=121=static const struct blk_crypto_ll_ops ufshcd_crypto_ops = {\ndrivers/ufs/core/ufshcd-crypto.c:122:\t.keyslot_program\t= ufshcd_crypto_keyslot_program,\ndrivers/ufs/core/ufshcd-crypto.c-123-\t.keyslot_evict\t\t= ufshcd_crypto_keyslot_evict,\n--\ndrivers/ufs/host/ufs-qcom.c=233=static inline int ufs_qcom_ice_suspend(struct ufs_qcom_host *host)\n--\ndrivers/ufs/host/ufs-qcom.c-240-\ndrivers/ufs/host/ufs-qcom.c:241:static int ufs_qcom_ice_keyslot_program(struct blk_crypto_profile *profile,\ndrivers/ufs/host/ufs-qcom.c-242-\t\t\t\t\tconst struct blk_crypto_key *key,\n--\ndrivers/ufs/host/ufs-qcom.c=309=static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops = {\ndrivers/ufs/host/ufs-qcom.c:310:\t.keyslot_program\t= ufs_qcom_ice_keyslot_program,\ndrivers/ufs/host/ufs-qcom.c-311-\t.keyslot_evict\t\t= ufs_qcom_ice_keyslot_evict,\n--\ninclude/linux/blk-crypto-profile.h=22=struct blk_crypto_ll_ops {\n--\ninclude/linux/blk-crypto-profile.h-24-\t/**\ninclude/linux/blk-crypto-profile.h:25:\t * @keyslot_program: Program a key into the inline encryption hardware.\ninclude/linux/blk-crypto-profile.h-26-\t *\n--\ninclude/linux/blk-crypto-profile.h-36-\t */\ninclude/linux/blk-crypto-profile.h:37:\tint (*keyslot_program)(struct blk_crypto_profile *profile,\ninclude/linux/blk-crypto-profile.h-38-\t\t\t       const struct blk_crypto_key *key,\n"
}

5/2 2026/09/14 18:17 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 22013
							output: 25
							thoughts: 64
						

Thoughts:
Okay, let's dive into this. Here's what I'm thinking, from my perspective as an expert:

**Investigating the Low-Level Blk-Crypto and Fscrypt Interface**

Alright, I need to get a clear picture of how blk-crypto and fscrypt are working together, particularly when it comes to the lower-level key management. I know there's a delicate interplay here, and I need to understand it in detail. My immediate focus is on how hardware-wrapped keys and keyslot programming are orchestrated. I need a clear understanding of the calling flow.

Wait a second, before I get too deep in the weeds with the specifics of those operations, I should take a step back and examine the wider system. I should check exactly what code is *calling* those critical functions. I need to trace the calling chain for routines like `profile->ll_ops.keyslot_program`, or `derive_sw_secret`, and `generate_key`, and the like. Understanding the context of the calls will be absolutely crucial for my analysis.



6/2 2026/09/14 18:17 tool grepper
0m Args:
{
  "Expression": "generate_key",
  "PathPrefix": "block/"
}

Results:
{
  "Output": "block/blk-crypto-profile.c=502=EXPORT_SYMBOL_GPL(blk_crypto_import_key);\nblock/blk-crypto-profile.c-503-\nblock/blk-crypto-profile.c:504:int blk_crypto_generate_key(struct blk_crypto_profile *profile,\nblock/blk-crypto-profile.c-505-\t\t\t    u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])\n--\nblock/blk-crypto-profile.c-512-\t\treturn -EOPNOTSUPP;\nblock/blk-crypto-profile.c:513:\tif (!profile-\u003ell_ops.generate_key)\nblock/blk-crypto-profile.c-514-\t\treturn -EOPNOTSUPP;\nblock/blk-crypto-profile.c-515-\tblk_crypto_hw_enter(profile);\nblock/blk-crypto-profile.c:516:\tret = profile-\u003ell_ops.generate_key(profile, lt_key);\nblock/blk-crypto-profile.c-517-\tblk_crypto_hw_exit(profile);\n--\nblock/blk-crypto-profile.c-519-}\nblock/blk-crypto-profile.c:520:EXPORT_SYMBOL_GPL(blk_crypto_generate_key);\nblock/blk-crypto-profile.c-521-\n--\nblock/blk-crypto.c=457=static int blk_crypto_ioctl_import_key(struct blk_crypto_profile *profile,\n--\nblock/blk-crypto.c-500-\nblock/blk-crypto.c:501:static int blk_crypto_ioctl_generate_key(struct blk_crypto_profile *profile,\nblock/blk-crypto.c-502-\t\t\t\t\t void __user *argp)\nblock/blk-crypto.c-503-{\nblock/blk-crypto.c:504:\tstruct blk_crypto_generate_key_arg arg;\nblock/blk-crypto.c-505-\tu8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];\n--\nblock/blk-crypto.c-513-\nblock/blk-crypto.c:514:\tret = blk_crypto_generate_key(profile, lt_key);\nblock/blk-crypto.c-515-\tif (ret \u003c 0)\n--\nblock/blk-crypto.c=579=int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd,\n--\nblock/blk-crypto.c-591-\tcase BLKCRYPTOGENERATEKEY:\nblock/blk-crypto.c:592:\t\treturn blk_crypto_ioctl_generate_key(profile, argp);\nblock/blk-crypto.c-593-\tcase BLKCRYPTOPREPAREKEY:\n"
}

7/2 2026/09/14 18:17 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 23052
							output: 19
							thoughts: 49
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Diving into Block Device Crypto ioctls**

*   `blk_crypto_ioctl`! Right, I'm thinking about the block device crypto interface. That brings up a whole slew of ioctls: `BLKCRYPTOPREPAREKEY`, `BLKCRYPTOGENERATEKEY`, the usual suspects. But hold on a second... Let's not get ahead of ourselves. Before I go too deep, I should probably take a look at *who* is actually calling `blk_crypto_ioctl`. Understanding the call graph will give a much better sense of the various uses cases, and any potential security or performance implications, before diving deeper into any specific functionality.



8/2 2026/09/14 18:17 tool grepper
0m Args:
{
  "Expression": "blk_crypto_ioctl"
}

Results:
{
  "Output": "block/blk-crypto-internal.h=80=int __blk_crypto_evict_key(struct blk_crypto_profile *profile,\n--\nblock/blk-crypto-internal.h-82-\nblock/blk-crypto-internal.h:83:int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd,\nblock/blk-crypto-internal.h-84-\t\t     void __user *argp);\n--\nblock/blk-crypto-internal.h=134=static inline bool blk_crypto_rq_has_keyslot(struct request *rq)\n--\nblock/blk-crypto-internal.h-138-\nblock/blk-crypto-internal.h:139:static inline int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd,\nblock/blk-crypto-internal.h-140-\t\t\t\t   void __user *argp)\n--\nblock/blk-crypto.c=455=EXPORT_SYMBOL_GPL(blk_crypto_evict_key);\nblock/blk-crypto.c-456-\nblock/blk-crypto.c:457:static int blk_crypto_ioctl_import_key(struct blk_crypto_profile *profile,\nblock/blk-crypto.c-458-\t\t\t\t       void __user *argp)\n--\nblock/blk-crypto.c-500-\nblock/blk-crypto.c:501:static int blk_crypto_ioctl_generate_key(struct blk_crypto_profile *profile,\nblock/blk-crypto.c-502-\t\t\t\t\t void __user *argp)\n--\nblock/blk-crypto.c-534-\nblock/blk-crypto.c:535:static int blk_crypto_ioctl_prepare_key(struct blk_crypto_profile *profile,\nblock/blk-crypto.c-536-\t\t\t\t\tvoid __user *argp)\n--\nblock/blk-crypto.c-578-\nblock/blk-crypto.c:579:int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd,\nblock/blk-crypto.c-580-\t\t     void __user *argp)\n--\nblock/blk-crypto.c-589-\tcase BLKCRYPTOIMPORTKEY:\nblock/blk-crypto.c:590:\t\treturn blk_crypto_ioctl_import_key(profile, argp);\nblock/blk-crypto.c-591-\tcase BLKCRYPTOGENERATEKEY:\nblock/blk-crypto.c:592:\t\treturn blk_crypto_ioctl_generate_key(profile, argp);\nblock/blk-crypto.c-593-\tcase BLKCRYPTOPREPAREKEY:\nblock/blk-crypto.c:594:\t\treturn blk_crypto_ioctl_prepare_key(profile, argp);\nblock/blk-crypto.c-595-\tdefault:\n--\nblock/ioctl.c=651=static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode,\n--\nblock/ioctl.c-721-\tcase BLKCRYPTOPREPAREKEY:\nblock/ioctl.c:722:\t\treturn blk_crypto_ioctl(bdev, cmd, argp);\nblock/ioctl.c-723-\tcase IOC_PR_REGISTER:\n"
}

9/2 2026/09/14 18:17 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 24118
							output: 24
							thoughts: 2888
						

Thoughts:
**Fuzzing Analysis of Virtio Block Device Encryption**

Okay, so I'm diving into this patch series and my initial thought process is to determine if it's worth fuzzing. I'm focusing on whether the changes are reachable and if they introduce new assertions.  First off, I'm thinking about the standard user-space interaction with virtio block devices, specifically `/dev/vda` and the potential for direct ioctl usage like `blkdev_common_ioctl`, `blk_crypto_ioctl`, `import_key` and how this integrates with something like fscrypt on an ext4/f2fs filesystem mounted on that device. My gut tells me this could be interesting since it involves direct key programming, encrypted I/O, and potential new features like `VIRTIO_BLK_F_INLINE_ENCRYPTION` or `VIRTIO_BLK_F_CTRL_VQ`.

Now, the question is, *can* this actually be tested, and *how*? Does QEMU or some other virtualization layer even *emulate* these more advanced virtio features that this patch might be utilizing? I need to know.  I start by checking syzkaller's capabilities. Does it have virtio drivers fuzzed, or vhost, or does syzkaller run on QEMU where virtio-blk is used as root disk or secondary disk? If QEMU does *probe* virtio-blk, does standard QEMU support those more advanced features?

My main concern now is *reachability*. I'm thinking about the criteria laid out in Section 1 and Section 2 of the patch fuzzing guidelines I've been following. Reachability is the primary gate: can the code actually *execute* in a standard virtualized environment?  This means I need to quickly review the negative and positive criteria.  I know I should *not* fuzz code if it's non-executable, or if it's only reachable in specific vendor-specific hardware setups that aren't emulated.  On the other hand, if it's reachable executable code, I should fuzz. Especially if it involves core subsystems, refactorings, assertions, or drivers accessible via virtual buses such as *virtio*.

Section 3 is explicitly mentions `virtio` as an example of a reachable driver, so that's a *good* sign.

Now, I'm digging into the code.  I need to determine if the modified code is even touched during standard virtio-blk execution, even if the `VIRTIO_BLK_F_INLINE_ENCRYPTION` feature bit isn't negotiated.  I can see that *all* virtio_blk requests go through `virtblk_setup_cmd`, `virtblk_add_req`, `init_vq`, `virtblk_probe`, and `virtblk_free_disk`. This is a *major* point; every single read/write operation is going through the changed functions. Whether encryption is enabled or not, those paths are always taken. Virtio devices can be created via virtio-pci, vhost-user, root disk. And those *are* the standard environments, right? QEMU, GCE VMs, those environments use virtio-blk as their primary block device! And syzkaller *does* fuzz the block layer, and filesystems *on* virtio-blk! *Bingo*! Virtio-blk is *reachable*!

Now for one final step: Are there any new or modified assertions? If there are, I need to know about them, because that is a very strong trigger for fuzzing. I'm seeing `WARN_ON_ONCE` assertions in `virtblk_ctrl_vq_drain` and `virtblk_ctrlq_callback`, as well as checks related to `is_crypto_request`, `virtblk_setup_cmd`, and several other functions. These are definitely worth noting.

Finally, I'm looking at `VIRTIO_BLK_INLINE_ENCRYPTION`'s Kconfig dependencies. It requires both `VIRTIO_BLK` and `BLK_INLINE_ENCRYPTION`. That's important to keep in mind, because those need to be enabled for this part to be fully exercised.

In summary, this patch *is* worth fuzzing. It touches reachable code in core virtio-blk paths that are *always* executed. It introduces assertions, and it involves features that should be tested in common virtualized environments.



10/2 2026/09/14 18:17 tool grepper
0m Args:
{
  "Expression": "config BLK_INLINE_ENCRYPTION"
}

Results:
{
  "Output": "block/Kconfig=185=config BLK_SED_OPAL\n--\nblock/Kconfig-194-\nblock/Kconfig:195:config BLK_INLINE_ENCRYPTION\nblock/Kconfig-196-\tbool \"Enable inline encryption support in block layer\"\n--\nblock/Kconfig-201-\nblock/Kconfig:202:config BLK_INLINE_ENCRYPTION_FALLBACK\nblock/Kconfig-203-\tbool \"Enable crypto API fallback for blk-crypto\"\n"
}

11/2 2026/09/14 18:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28131
							output: 32
							thoughts: 17
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Diving into the Kernel Configuration Landscape**

Okay, so I need to track down something related to a specific configuration option. Based on past experience, `block/Kconfig` is usually the right place to start for anything block device related. Specifically, I vaguely recall something around line 195 might be relevant. It's a bit of a hunch, and I'll need to confirm, but that's where my initial attention will be focused. Let's see what that area of the file reveals. It could be anything from a simple definition to a more complex conditional dependency. Time to get digging!



12/2 2026/09/14 18:18 tool read-file
0m Args:
{
  "File": "block/Kconfig",
  "FirstLine": 190,
  "LineCount": 25
}

Results:
{
  "Contents": " 190:\t\thelp\n 191:\t\tBuilds Logic for interfacing with Opal enabled controllers.\n 192:\t\tEnabling this option enables users to setup/unlock/lock\n 193:\t\tLocking ranges for SED devices using the Opal protocol.\n 194:\t\n 195:\tconfig BLK_INLINE_ENCRYPTION\n 196:\t\tbool \"Enable inline encryption support in block layer\"\n 197:\t\thelp\n 198:\t\t  Build the blk-crypto subsystem. Enabling this lets the\n 199:\t\t  block layer handle encryption, so users can take\n 200:\t\t  advantage of inline encryption hardware if present.\n 201:\t\n 202:\tconfig BLK_INLINE_ENCRYPTION_FALLBACK\n 203:\t\tbool \"Enable crypto API fallback for blk-crypto\"\n 204:\t\tdepends on BLK_INLINE_ENCRYPTION\n 205:\t\tselect CRYPTO\n 206:\t\tselect CRYPTO_SKCIPHER\n 207:\t\thelp\n 208:\t\t  Enabling this lets the block layer handle inline encryption\n 209:\t\t  by falling back to the kernel crypto API when inline\n 210:\t\t  encryption hardware is not present.\n 211:\t\n 212:\tsource \"block/partitions/Kconfig\"\n 213:\t\n 214:\tconfig BLK_PM\n"
}

13/2 2026/09/14 18:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 28690
							output: 22
							thoughts: 39
						

Thoughts:
Okay, here's my interpretation of those thoughts, summarized from my perspective as someone well-versed in this domain:

**My Line of Reasoning**

Alright, let's break this down. First, `BLK_INLINE_ENCRYPTION` – that's a straightforward boolean, no other configs hanging off it, which simplifies things. Good. We know that's its own independent flag.

Next, `VIRTIO_BLK`... Ah, right, that's the VirtIO block device driver. Now, I need to check its configuration. I'll need to double check the dependencies. So, `config VIRTIO_BLK` is next on my mental checklist. That's the key to understanding if any of this is connected, if there's any important configuration to consider. Time to go deeper.



14/2 2026/09/14 18:18 tool grepper
0m Args:
{
  "Expression": "config VIRTIO_BLK\\b"
}

Results:
{
  "Output": "drivers/block/Kconfig=286=config XEN_BLKDEV_BACKEND\n--\ndrivers/block/Kconfig-306-\ndrivers/block/Kconfig:307:config VIRTIO_BLK\ndrivers/block/Kconfig-308-\ttristate \"Virtio block driver\"\n"
}

15/2 2026/09/14 18:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 29013
							output: 34
							thoughts: 20
						

Thoughts:
Here's my take on those lines from `drivers/block/Kconfig`:

**Kconfig Dive: Block Device Options**

Okay, let's zero in on lines 306 through 320 in the `drivers/block/Kconfig` file. I'm expecting this section to deal with some specific block device configuration options. Given the context of the larger block device subsystem, I'm anticipating we'll be dealing with specific device drivers or features related to block storage. This could be things like specific disk types, RAID configurations, I/O schedulers, or maybe even features like data encryption or compression within the block layer itself. I'll need to see if it’s more device-specific, or broadly affecting how block devices are used.

I'll be looking closely for the usual Kconfig conventions: `config` blocks defining the option, `bool` or `tristate` indicating the type, `depends on` clauses which are crucial for conditional compilation, `default` values that the user might not even notice until an edge case, and of course, the `help` text – the most important part because it documents what's really going on. I'll also check to see if there's any commentary that provides context or clarifies any unusual dependencies. I'm hoping to see well-written descriptions to avoid having to trace every dependency upstream.

Essentially, I'm expecting this to be a granular look at some block-related configuration parameters. Let's see what gems are hidden in there.



16/2 2026/09/14 18:18 tool read-file
0m Args:
{
  "File": "drivers/block/Kconfig",
  "FirstLine": 306,
  "LineCount": 20
}

Results:
{
  "Contents": " 306:\t\n 307:\tconfig VIRTIO_BLK\n 308:\t\ttristate \"Virtio block driver\"\n 309:\t\tdepends on VIRTIO\n 310:\t\tselect SG_POOL\n 311:\t\thelp\n 312:\t\t  This is the virtual block driver for virtio.  It can be used with\n 313:\t          QEMU based VMMs (like KVM or Xen).  Say Y or M.\n 314:\t\n 315:\tconfig BLK_DEV_RBD\n 316:\t\ttristate \"Rados block device (RBD)\"\n 317:\t\tdepends on INET \u0026\u0026 BLOCK\n 318:\t\tselect CEPH_LIB\n 319:\t\thelp\n 320:\t\t  Say Y here if you want include the Rados block device, which stripes\n 321:\t\t  a block device over objects stored in the Ceph distributed object\n 322:\t\t  store.\n 323:\t\n 324:\t\t  More information at http://ceph.newdream.net/.\n 325:\t\n"
}

17/2 2026/09/14 18:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 29683
							output: 38
							thoughts: 236
						

Thoughts:
**My Configuration Checklist**

Okay, so I'm configuring this new system and need to enable inline encryption for the virtio block device. Let's break down the dependencies. It's clear that `VIRTIO_BLK` is a foundational requirement because `VIRTIO_BLK_INLINE_ENCRYPTION` directly depends on it, and also on `BLK_INLINE_ENCRYPTION`, of course. Therefore, I need to make sure I've enabled `VIRTIO_BLK` and `BLK_INLINE_ENCRYPTION`. And, of course `VIRTIO_BLK_INLINE_ENCRYPTION`.

But hang on… doesn't `VIRTIO_BLK_INLINE_ENCRYPTION` also implicitly require `VIRTIO`? Well, if `VIRTIO_BLK` is enabled, then `VIRTIO` *is* a dependency through the configuration system. So, I don't need to enable `VIRTIO` explicitly. That's a relief.

Before I commit to those settings, let's just make sure there aren't any other gotchas. I'll need to double check the code around where the encryption feature is checked in `virtio_blk.c`. Let's see... looking at that file, around lines 286, 950, and other related spots. Specifically, I see this: `#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)`. Excellent, this confirms my understanding. Now, I should make sure there is no other code. I'll need to double check that `CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION` is properly defined.



18/2 2026/09/14 18:18 tool grepper
0m Args:
{
  "Expression": "CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION",
  "PathPrefix": "drivers/block/"
}

Results:
{
  "Output": "drivers/block/virtio_blk.c=283=static void virtblk_cleanup_cmd(struct request *req)\n--\ndrivers/block/virtio_blk.c-288-\ndrivers/block/virtio_blk.c:289:#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)\ndrivers/block/virtio_blk.c-290-static bool is_crypto_request(struct request *req)\n--\ndrivers/block/virtio_blk.c=1038=static int virtblk_ctrl_vq_request(struct virtio_blk *vblk,\n--\ndrivers/block/virtio_blk.c-1070-\ndrivers/block/virtio_blk.c:1071:#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)\ndrivers/block/virtio_blk.c-1072-// Maps VIRTIO_BLK_CRYPTO_MODE_* values to the kernel's internal enum.\n--\ndrivers/block/virtio_blk.c=1532=static inline void virtblk_destroy_crypto(struct virtio_blk *vblk)\n--\ndrivers/block/virtio_blk.c-1534-}\ndrivers/block/virtio_blk.c:1535:#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */\ndrivers/block/virtio_blk.c-1536-\n--\ndrivers/block/virtio_blk.c=2128=static int virtblk_probe(struct virtio_device *vdev)\n--\ndrivers/block/virtio_blk.c-2234-\t\t\tgoto out_cleanup_disk;\ndrivers/block/virtio_blk.c:2235:\t} else if (IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) \u0026\u0026\ndrivers/block/virtio_blk.c-2236-\t\t   virtio_has_feature(vdev, VIRTIO_BLK_F_INLINE_ENCRYPTION) \u0026\u0026\n"
}

19/2 2026/09/14 18:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 30841
							output: 25
							thoughts: 63
						

Thoughts:
**Examining Relevant Kernel Configurations**

Okay, let's start by digging into the kernel configurations. My primary focus is inline encryption for block devices, so I should definitely check `CONFIG_BLK_INLINE_ENCRYPTION`. But, hold on a second... are there other configurations that might be related, perhaps indirectly? What about `FS_ENCRYPTION`? And wouldn't `FS_ENCRYPTION_INLINE_CRYPT` be worth a look as well? Yes, given the focus on inline crypto and filesystems, that sounds like the most relevant. Let's start with `FS_ENCRYPTION_INLINE_CRYPT` and see what that config is all about.



20/2 2026/09/14 18:18 tool grepper
0m Args:
{
  "Expression": "config FS_ENCRYPTION_INLINE_CRYPT"
}

Results:
{
  "Output": "fs/crypto/Kconfig=31=config FS_ENCRYPTION_ALGS\n--\nfs/crypto/Kconfig-37-\nfs/crypto/Kconfig:38:config FS_ENCRYPTION_INLINE_CRYPT\nfs/crypto/Kconfig-39-\tbool\n"
}

21/2 2026/09/14 18:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31155
							output: 33
							thoughts: 12
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience:

**Examining the Crypto Configuration**

Okay, so I need to get a handle on the current state of the crypto configuration within the kernel. Let's start by looking at `fs/crypto/Kconfig`. This is the logical place to begin, as it should define all the relevant configuration options specifically related to filesystem encryption. I'm expecting to find the standard `Kconfig` syntax – `config` blocks, `bool`, `tristate`, `select`, `depends on`, and so on. I'll be looking for the main encryption framework options, any algorithms available for selection, and perhaps some fine-grained controls for performance or security considerations, if they're present. My initial goal is a high-level understanding of what's *enabled* by default and what options are available to further customize the configuration. I want to build a mental map of the configuration space; what's exposed, how it's structured, and what dependencies are in play. The details will follow, but for now, it's about the bigger picture of what's configurable.



22/2 2026/09/14 18:18 tool read-file
0m Args:
{
  "File": "fs/crypto/Kconfig",
  "FirstLine": 30,
  "LineCount": 25
}

Results:
{
  "Contents": "  30:\t# strongly recommended to enable optimized implementations too.\n  31:\tconfig FS_ENCRYPTION_ALGS\n  32:\t\ttristate\n  33:\t\tselect CRYPTO_AES\n  34:\t\tselect CRYPTO_CBC\n  35:\t\tselect CRYPTO_CTS\n  36:\t\tselect CRYPTO_XTS\n  37:\t\n  38:\tconfig FS_ENCRYPTION_INLINE_CRYPT\n  39:\t\tbool\n  40:\t\tdefault y if FS_ENCRYPTION \u0026\u0026 BLOCK\n"
}

23/2 2026/09/14 18:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 31585
							output: 28
							thoughts: 73
						

Thoughts:
**Investigating the Configuration Landscape for Modified Block Code**

Okay, so I'm trying to figure out which kernel configurations are enabling the changes I've just introduced to the block device driver. Let's see... based on the code's modifications, I'm pretty sure I need to focus on `VIRTIO_BLK` for sure. And, since it's inline encryption, `BLK_INLINE_ENCRYPTION` and specifically `VIRTIO_BLK_INLINE_ENCRYPTION` are probably critical. Those seem obvious, but I want to be *absolutely* certain.

Now, hold on a second. Could there be something in the `drivers/block/Makefile` that's subtly involved? It's always good to cover all the bases. I'll need to double-check that Makefile for any dependencies or build rules that might pertain to `virtio_blk`. Best to be thorough, especially in this area, you know? Let me just pull that up and confirm.



24/2 2026/09/14 18:18 tool grepper
0m Args:
{
  "Expression": "virtio_blk",
  "PathPrefix": "drivers/block/"
}

Results:
{
  "Output": "drivers/block/Makefile=25=obj-$(CONFIG_BLK_DEV_NBD)\t+= nbd.o\ndrivers/block/Makefile:26:obj-$(CONFIG_VIRTIO_BLK)\t+= virtio_blk.o\ndrivers/block/Makefile-27-\n--\ndrivers/block/virtio_blk.c-11-#include \u003clinux/virtio.h\u003e\ndrivers/block/virtio_blk.c:12:#include \u003clinux/virtio_blk.h\u003e\ndrivers/block/virtio_blk.c-13-#include \u003clinux/scatterlist.h\u003e\n--\ndrivers/block/virtio_blk.c=49=static struct workqueue_struct *virtblk_wq;\ndrivers/block/virtio_blk.c-50-\ndrivers/block/virtio_blk.c:51:struct virtio_blk_vq {\ndrivers/block/virtio_blk.c-52-\tstruct virtqueue *vq;\n--\ndrivers/block/virtio_blk.c-56-\ndrivers/block/virtio_blk.c:57:struct virtio_blk_ctrl_vq {\ndrivers/block/virtio_blk.c-58-\tstruct virtqueue *vq;\n--\ndrivers/block/virtio_blk.c-65-\ndrivers/block/virtio_blk.c:66:struct virtio_blk {\ndrivers/block/virtio_blk.c-67-\t/*\n--\ndrivers/block/virtio_blk.c-92-\tint io_queues[HCTX_MAX_TYPES];\ndrivers/block/virtio_blk.c:93:\tstruct virtio_blk_vq *vqs;\ndrivers/block/virtio_blk.c-94-\n--\ndrivers/block/virtio_blk.c-102-\t/* Control virtqueue state. */\ndrivers/block/virtio_blk.c:103:\tstruct virtio_blk_ctrl_vq ctrl_vq;\ndrivers/block/virtio_blk.c-104-};\n--\ndrivers/block/virtio_blk.c=106=struct virtblk_req {\n--\ndrivers/block/virtio_blk.c-108-\tunion {\ndrivers/block/virtio_blk.c:109:\t\tstruct virtio_blk_outhdr base;\ndrivers/block/virtio_blk.c-110-\t\tstruct {\ndrivers/block/virtio_blk.c:111:\t\t\tstruct virtio_blk_outhdr base;\ndrivers/block/virtio_blk.c-112-\t\t\t/* Crypto message (if VIRTIO_BLK_F_INLINE_ENCRYPTION) */\ndrivers/block/virtio_blk.c:113:\t\t\tstruct virtio_blk_crypto_msg msg;\ndrivers/block/virtio_blk.c-114-\t\t} crypto_append;\n--\ndrivers/block/virtio_blk.c=138=struct virtblk_ctrl_request {\n--\ndrivers/block/virtio_blk.c-142-\tunion {\ndrivers/block/virtio_blk.c:143:\t\tstruct virtio_blk_crypto_key_desc key_desc;\ndrivers/block/virtio_blk.c:144:\t\tstruct virtio_blk_crypto_key_blob blob;\ndrivers/block/virtio_blk.c-145-\t} out_req;\n--\ndrivers/block/virtio_blk.c-148-\tunion {\ndrivers/block/virtio_blk.c:149:\t\tstruct virtio_blk_crypto_key_blob blob;\ndrivers/block/virtio_blk.c:150:\t\tstruct virtio_blk_crypto_sw_secret secret;\ndrivers/block/virtio_blk.c:151:\t\tstruct virtio_blk_crypto_modes modes;\ndrivers/block/virtio_blk.c-152-\t} in_resp;\n--\ndrivers/block/virtio_blk.c=159=static inline blk_status_t virtblk_result(u8 status)\n--\ndrivers/block/virtio_blk.c-176-\ndrivers/block/virtio_blk.c:177:static inline struct virtio_blk_vq *get_virtio_blk_vq(struct blk_mq_hw_ctx *hctx)\ndrivers/block/virtio_blk.c-178-{\ndrivers/block/virtio_blk.c:179:\tstruct virtio_blk *vblk = hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c:180:\tstruct virtio_blk_vq *vq = \u0026vblk-\u003evqs[hctx-\u003equeue_num];\ndrivers/block/virtio_blk.c-181-\n--\ndrivers/block/virtio_blk.c=211=static int virtblk_setup_discard_write_zeroes_erase(struct request *req, bool unmap)\n--\ndrivers/block/virtio_blk.c-214-\tunsigned short n = 0;\ndrivers/block/virtio_blk.c:215:\tstruct virtio_blk_discard_write_zeroes *range;\ndrivers/block/virtio_blk.c-216-\tstruct bio *bio;\n--\ndrivers/block/virtio_blk.c=420=static inline void virtblk_request_done(struct request *req)\n--\ndrivers/block/virtio_blk.c-423-\tblk_status_t status = virtblk_result(virtblk_vbr_status(vbr));\ndrivers/block/virtio_blk.c:424:\tstruct virtio_blk *vblk = req-\u003emq_hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c-425-\n--\ndrivers/block/virtio_blk.c=436=static void virtblk_done(struct virtqueue *vq)\ndrivers/block/virtio_blk.c-437-{\ndrivers/block/virtio_blk.c:438:\tstruct virtio_blk *vblk = vq-\u003evdev-\u003epriv;\ndrivers/block/virtio_blk.c-439-\tbool req_done = false;\n--\ndrivers/block/virtio_blk.c=463=static void virtio_commit_rqs(struct blk_mq_hw_ctx *hctx)\ndrivers/block/virtio_blk.c-464-{\ndrivers/block/virtio_blk.c:465:\tstruct virtio_blk *vblk = hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c:466:\tstruct virtio_blk_vq *vq = \u0026vblk-\u003evqs[hctx-\u003equeue_num];\ndrivers/block/virtio_blk.c-467-\tbool kick;\n--\ndrivers/block/virtio_blk.c=490=static blk_status_t virtblk_prep_rq(struct blk_mq_hw_ctx *hctx,\ndrivers/block/virtio_blk.c:491:\t\t\t\t\tstruct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-492-\t\t\t\t\tstruct request *req,\n--\ndrivers/block/virtio_blk.c=512=static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,\n--\ndrivers/block/virtio_blk.c-514-{\ndrivers/block/virtio_blk.c:515:\tstruct virtio_blk *vblk = hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c-516-\tstruct request *req = bd-\u003erq;\n--\ndrivers/block/virtio_blk.c=551=static bool virtblk_prep_rq_batch(struct request *req)\ndrivers/block/virtio_blk.c-552-{\ndrivers/block/virtio_blk.c:553:\tstruct virtio_blk *vblk = req-\u003emq_hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c-554-\tstruct virtblk_req *vbr = blk_mq_rq_to_pdu(req);\n--\ndrivers/block/virtio_blk.c-558-\ndrivers/block/virtio_blk.c:559:static void virtblk_add_req_batch(struct virtio_blk_vq *vq,\ndrivers/block/virtio_blk.c-560-\t\tstruct rq_list *rqlist)\n--\ndrivers/block/virtio_blk.c=587=static void virtio_queue_rqs(struct rq_list *rqlist)\n--\ndrivers/block/virtio_blk.c-590-\tstruct rq_list requeue_list = { };\ndrivers/block/virtio_blk.c:591:\tstruct virtio_blk_vq *vq = NULL;\ndrivers/block/virtio_blk.c-592-\tstruct request *req;\n--\ndrivers/block/virtio_blk.c-594-\twhile ((req = rq_list_pop(rqlist))) {\ndrivers/block/virtio_blk.c:595:\t\tstruct virtio_blk_vq *this_vq = get_virtio_blk_vq(req-\u003emq_hctx);\ndrivers/block/virtio_blk.c-596-\n--\ndrivers/block/virtio_blk.c-612-#ifdef CONFIG_BLK_DEV_ZONED\ndrivers/block/virtio_blk.c:613:static void *virtblk_alloc_report_buffer(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-614-\t\t\t\t\t  unsigned int nr_zones,\n--\ndrivers/block/virtio_blk.c-623-\ndrivers/block/virtio_blk.c:624:\tbufsize = sizeof(struct virtio_blk_zone_report) +\ndrivers/block/virtio_blk.c:625:\t\tnr_zones * sizeof(struct virtio_blk_zone_descriptor);\ndrivers/block/virtio_blk.c-626-\tbufsize = min_t(size_t, bufsize,\n--\ndrivers/block/virtio_blk.c-629-\ndrivers/block/virtio_blk.c:630:\twhile (bufsize \u003e= sizeof(struct virtio_blk_zone_report)) {\ndrivers/block/virtio_blk.c-631-\t\tbuf = __vmalloc(bufsize, GFP_KERNEL | __GFP_NORETRY);\n--\ndrivers/block/virtio_blk.c-641-\ndrivers/block/virtio_blk.c:642:static int virtblk_submit_zone_report(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-643-\t\t\t\t       char *report_buf, size_t report_len,\n--\ndrivers/block/virtio_blk.c-670-\ndrivers/block/virtio_blk.c:671:static int virtblk_parse_zone(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c:672:\t\t\t       struct virtio_blk_zone_descriptor *entry,\ndrivers/block/virtio_blk.c-673-\t\t\t       unsigned int idx,\n--\ndrivers/block/virtio_blk.c=743=static int virtblk_report_zones(struct gendisk *disk, sector_t sector,\n--\ndrivers/block/virtio_blk.c-746-{\ndrivers/block/virtio_blk.c:747:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c:748:\tstruct virtio_blk_zone_report *report;\ndrivers/block/virtio_blk.c-749-\tunsigned long long nz, i;\n--\ndrivers/block/virtio_blk.c-805-\ndrivers/block/virtio_blk.c:806:static int virtblk_read_zoned_limits(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-807-\t\tstruct queue_limits *lim)\n--\ndrivers/block/virtio_blk.c-815-\ndrivers/block/virtio_blk.c:816:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-817-\t\t     zoned.max_open_zones, \u0026v);\n--\ndrivers/block/virtio_blk.c-820-\ndrivers/block/virtio_blk.c:821:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-822-\t\t     zoned.max_active_zones, \u0026v);\n--\ndrivers/block/virtio_blk.c-825-\ndrivers/block/virtio_blk.c:826:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-827-\t\t     zoned.write_granularity, \u0026wg);\n--\ndrivers/block/virtio_blk.c-840-\t */\ndrivers/block/virtio_blk.c:841:\tvirtio_cread(vdev, struct virtio_blk_config, zoned.zone_sectors,\ndrivers/block/virtio_blk.c-842-\t\t     \u0026vblk-\u003ezone_sectors);\n--\ndrivers/block/virtio_blk.c-857-\ndrivers/block/virtio_blk.c:858:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-859-\t\t     zoned.max_append_sectors, \u0026v);\n--\ndrivers/block/virtio_blk.c-880-#define virtblk_report_zones       NULL\ndrivers/block/virtio_blk.c:881:static inline int virtblk_read_zoned_limits(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-882-\t\tstruct queue_limits *lim)\n--\ndrivers/block/virtio_blk.c-884-\tdev_err(\u0026vblk-\u003evdev-\u003edev,\ndrivers/block/virtio_blk.c:885:\t\t\"virtio_blk: zoned devices are not supported\");\ndrivers/block/virtio_blk.c-886-\treturn -EOPNOTSUPP;\n--\ndrivers/block/virtio_blk.c=892=static int virtblk_get_id(struct gendisk *disk, char *id_str)\ndrivers/block/virtio_blk.c-893-{\ndrivers/block/virtio_blk.c:894:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c-895-\tstruct request_queue *q = vblk-\u003edisk-\u003equeue;\n--\ndrivers/block/virtio_blk.c=921=static int virtblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)\ndrivers/block/virtio_blk.c-922-{\ndrivers/block/virtio_blk.c:923:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c-924-\tint ret = 0;\n--\ndrivers/block/virtio_blk.c-934-\tif (virtio_has_feature(vblk-\u003evdev, VIRTIO_BLK_F_GEOMETRY)) {\ndrivers/block/virtio_blk.c:935:\t\tvirtio_cread(vblk-\u003evdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-936-\t\t\t     geometry.cylinders, \u0026geo-\u003ecylinders);\ndrivers/block/virtio_blk.c:937:\t\tvirtio_cread(vblk-\u003evdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-938-\t\t\t     geometry.heads, \u0026geo-\u003eheads);\ndrivers/block/virtio_blk.c:939:\t\tvirtio_cread(vblk-\u003evdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-940-\t\t\t     geometry.sectors, \u0026geo-\u003esectors);\n--\ndrivers/block/virtio_blk.c-954-/* Prevent new submissions and wait for in-flight requests to complete. */\ndrivers/block/virtio_blk.c:955:static void virtblk_ctrl_vq_quiesce(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-956-{\n--\ndrivers/block/virtio_blk.c-977-/* Fail requests left in the control queue after reset. */\ndrivers/block/virtio_blk.c:978:static void virtblk_ctrl_vq_drain(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-979-{\n--\ndrivers/block/virtio_blk.c=1000=static void virtblk_ctrlq_callback(struct virtqueue *vq)\ndrivers/block/virtio_blk.c-1001-{\ndrivers/block/virtio_blk.c:1002:\tstruct virtio_blk *vblk = vq-\u003evdev-\u003epriv;\ndrivers/block/virtio_blk.c-1003-\tstruct virtblk_ctrl_request *creq;\n--\ndrivers/block/virtio_blk.c-1037-/* Submit a control-queue request and wait for completion. */\ndrivers/block/virtio_blk.c:1038:static int virtblk_ctrl_vq_request(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-1039-\t\t\t\t    struct virtblk_ctrl_request *creq,\n--\ndrivers/block/virtio_blk.c=1073=static const enum blk_crypto_mode_num\ndrivers/block/virtio_blk.c:1074:\tvirtio_blk_crypto_mode_map[VIRTIO_BLK_CRYPTO_MODE_MAX + 1] = {\ndrivers/block/virtio_blk.c-1075-\t[VIRTIO_BLK_CRYPTO_MODE_INVALID]\t= BLK_ENCRYPTION_MODE_INVALID,\n--\ndrivers/block/virtio_blk.c-1078-\ndrivers/block/virtio_blk.c:1079:static int virtblk_get_crypto_modes(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-1080-\t\t\t\t    unsigned int *crypto_modes_supported)\n--\ndrivers/block/virtio_blk.c-1111-\t\t\t\t\t\t creq-\u003ein_resp.modes.modes[i]);\ndrivers/block/virtio_blk.c:1112:\t\tenum blk_crypto_mode_num mode = virtio_blk_crypto_mode_map[i];\ndrivers/block/virtio_blk.c-1113-\n--\ndrivers/block/virtio_blk.c=1129=static int set_virtblk_crypto_key_desc(struct virtio_device *vdev,\n--\ndrivers/block/virtio_blk.c-1133-{\ndrivers/block/virtio_blk.c:1134:\tstruct virtio_blk_crypto_key_desc *desc = \u0026creq-\u003eout_req.key_desc;\ndrivers/block/virtio_blk.c-1135-\tenum blk_crypto_key_type key_type = key-\u003ecrypto_cfg.key_type;\n--\ndrivers/block/virtio_blk.c-1162-\ndrivers/block/virtio_blk.c:1163:static inline struct virtio_blk *virtblk_from_profile(struct blk_crypto_profile *profile)\ndrivers/block/virtio_blk.c-1164-{\ndrivers/block/virtio_blk.c:1165:\treturn container_of(profile, struct virtio_blk, profile);\ndrivers/block/virtio_blk.c-1166-}\n--\ndrivers/block/virtio_blk.c=1168=static int virtblk_crypto_keyslot_program(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1171-{\ndrivers/block/virtio_blk.c:1172:\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\ndrivers/block/virtio_blk.c-1173-\tstruct scatterlist type_sg, out_req_sg, status_sg, *sgs[3];\n--\ndrivers/block/virtio_blk.c=1204=static int virtblk_crypto_keyslot_evict(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1207-{\ndrivers/block/virtio_blk.c:1208:\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\ndrivers/block/virtio_blk.c-1209-\tstruct scatterlist type_sg, out_req_sg, status_sg, *sgs[3];\n--\ndrivers/block/virtio_blk.c=1240=static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1243-{\ndrivers/block/virtio_blk.c:1244:\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\ndrivers/block/virtio_blk.c-1245-\tstruct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];\n--\ndrivers/block/virtio_blk.c=1284=static int virtblk_crypto_generate_key(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1286-{\ndrivers/block/virtio_blk.c:1287:\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\ndrivers/block/virtio_blk.c-1288-\tstruct scatterlist type_sg, resp_sg, status_sg, *sgs[3];\n--\ndrivers/block/virtio_blk.c=1329=static int virtblk_crypto_prepare_key(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1332-{\ndrivers/block/virtio_blk.c:1333:\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\ndrivers/block/virtio_blk.c-1334-\tstruct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];\n--\ndrivers/block/virtio_blk.c=1382=static int virtblk_crypto_import_key(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1385-{\ndrivers/block/virtio_blk.c:1386:\tstruct virtio_blk *vblk = virtblk_from_profile(profile);\ndrivers/block/virtio_blk.c-1387-\tstruct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];\n--\ndrivers/block/virtio_blk.c=1435=static const struct blk_crypto_ll_ops virtblk_crypto_ops = {\n--\ndrivers/block/virtio_blk.c-1443-\ndrivers/block/virtio_blk.c:1444:static int virtblk_init_crypto(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-1445-{\n--\ndrivers/block/virtio_blk.c-1453-\ndrivers/block/virtio_blk.c:1454:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-1455-\t\t     enc_characteristics.max_slots, \u0026max_slots);\ndrivers/block/virtio_blk.c:1456:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-1457-\t\t     enc_characteristics.max_dun_bytes, \u0026max_dun_bytes);\ndrivers/block/virtio_blk.c:1458:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-1459-\t\t     enc_characteristics.key_types, \u0026key_types);\n--\ndrivers/block/virtio_blk.c-1468-\t/*\ndrivers/block/virtio_blk.c:1469:\t * struct virtio_blk_crypto_msg.dun is a fixed array of four __virtio64\ndrivers/block/virtio_blk.c-1470-\t * values (32 bytes total), matching the size of\n--\ndrivers/block/virtio_blk.c-1475-\t */\ndrivers/block/virtio_blk.c:1476:\tif (max_dun_bytes \u003e sizeof_field(struct virtio_blk_crypto_msg, dun))\ndrivers/block/virtio_blk.c-1477-\t\treturn -EINVAL;\n--\ndrivers/block/virtio_blk.c-1493-\t * Use the plain (non-devm) initializer: vblk-\u003eprofile is embedded in\ndrivers/block/virtio_blk.c:1494:\t * struct virtio_blk, whose lifetime is tied to the gendisk, not to\ndrivers/block/virtio_blk.c-1495-\t * \u0026vdev-\u003edev. Tying destruction to the vdev via devm would run the\n--\ndrivers/block/virtio_blk.c-1519-\ndrivers/block/virtio_blk.c:1520:static void virtblk_destroy_crypto(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-1521-{\n--\ndrivers/block/virtio_blk.c-1526-\ndrivers/block/virtio_blk.c:1527:static inline int virtblk_init_crypto(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-1528-{\n--\ndrivers/block/virtio_blk.c-1531-\ndrivers/block/virtio_blk.c:1532:static inline void virtblk_destroy_crypto(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-1533-{\n--\ndrivers/block/virtio_blk.c=1537=static void virtblk_free_disk(struct gendisk *disk)\ndrivers/block/virtio_blk.c-1538-{\ndrivers/block/virtio_blk.c:1539:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c-1540-\n--\ndrivers/block/virtio_blk.c=1585=static DEVICE_ATTR_RO(serial);\n--\ndrivers/block/virtio_blk.c-1587-/* The queue's logical block size must be set before calling this */\ndrivers/block/virtio_blk.c:1588:static void virtblk_update_capacity(struct virtio_blk *vblk, bool resize)\ndrivers/block/virtio_blk.c-1589-{\n--\ndrivers/block/virtio_blk.c-1596-\t/* Host must always specify the capacity. */\ndrivers/block/virtio_blk.c:1597:\tvirtio_cread(vdev, struct virtio_blk_config, capacity, \u0026capacity);\ndrivers/block/virtio_blk.c-1598-\n--\ndrivers/block/virtio_blk.c=1618=static void virtblk_config_changed_work(struct work_struct *work)\ndrivers/block/virtio_blk.c-1619-{\ndrivers/block/virtio_blk.c:1620:\tstruct virtio_blk *vblk =\ndrivers/block/virtio_blk.c:1621:\t\tcontainer_of(work, struct virtio_blk, config_work);\ndrivers/block/virtio_blk.c-1622-\n--\ndrivers/block/virtio_blk.c=1626=static void virtblk_config_changed(struct virtio_device *vdev)\ndrivers/block/virtio_blk.c-1627-{\ndrivers/block/virtio_blk.c:1628:\tstruct virtio_blk *vblk = vdev-\u003epriv;\ndrivers/block/virtio_blk.c-1629-\n--\ndrivers/block/virtio_blk.c-1632-\ndrivers/block/virtio_blk.c:1633:static int init_vq(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-1634-{\n--\ndrivers/block/virtio_blk.c-1646-\terr = virtio_cread_feature(vdev, VIRTIO_BLK_F_MQ,\ndrivers/block/virtio_blk.c:1647:\t\t\t\t   struct virtio_blk_config, num_queues,\ndrivers/block/virtio_blk.c-1648-\t\t\t\t   \u0026num_vqs);\n--\ndrivers/block/virtio_blk.c=1760=static int virtblk_get_cache_mode(struct virtio_device *vdev)\n--\ndrivers/block/virtio_blk.c-1765-\terr = virtio_cread_feature(vdev, VIRTIO_BLK_F_CONFIG_WCE,\ndrivers/block/virtio_blk.c:1766:\t\t\t\t   struct virtio_blk_config, wce,\ndrivers/block/virtio_blk.c-1767-\t\t\t\t   \u0026writeback);\n--\ndrivers/block/virtio_blk.c=1784=cache_type_store(struct device *dev, struct device_attribute *attr,\n--\ndrivers/block/virtio_blk.c-1787-\tstruct gendisk *disk = dev_to_disk(dev);\ndrivers/block/virtio_blk.c:1788:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c-1789-\tstruct virtio_device *vdev = vblk-\u003evdev;\n--\ndrivers/block/virtio_blk.c-1797-\ndrivers/block/virtio_blk.c:1798:\tvirtio_cwrite8(vdev, offsetof(struct virtio_blk_config, wce), i);\ndrivers/block/virtio_blk.c-1799-\n--\ndrivers/block/virtio_blk.c=1812=cache_type_show(struct device *dev, struct device_attribute *attr, char *buf)\n--\ndrivers/block/virtio_blk.c-1814-\tstruct gendisk *disk = dev_to_disk(dev);\ndrivers/block/virtio_blk.c:1815:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c-1816-\tu8 writeback = virtblk_get_cache_mode(vblk-\u003evdev);\n--\ndrivers/block/virtio_blk.c=1830=static umode_t virtblk_attrs_are_visible(struct kobject *kobj,\n--\ndrivers/block/virtio_blk.c-1834-\tstruct gendisk *disk = dev_to_disk(dev);\ndrivers/block/virtio_blk.c:1835:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c-1836-\tstruct virtio_device *vdev = vblk-\u003evdev;\n--\ndrivers/block/virtio_blk.c=1855=static void virtblk_map_queues(struct blk_mq_tag_set *set)\ndrivers/block/virtio_blk.c-1856-{\ndrivers/block/virtio_blk.c:1857:\tstruct virtio_blk *vblk = set-\u003edriver_data;\ndrivers/block/virtio_blk.c-1858-\tint i, qoff;\n--\ndrivers/block/virtio_blk.c=1894=static int virtblk_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob)\ndrivers/block/virtio_blk.c-1895-{\ndrivers/block/virtio_blk.c:1896:\tstruct virtio_blk *vblk = hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c:1897:\tstruct virtio_blk_vq *vq = get_virtio_blk_vq(hctx);\ndrivers/block/virtio_blk.c-1898-\tstruct virtblk_req *vbr;\n--\ndrivers/block/virtio_blk.c=1934=module_param_named(queue_depth, virtblk_queue_depth, uint, 0444);\ndrivers/block/virtio_blk.c-1935-\ndrivers/block/virtio_blk.c:1936:static int virtblk_read_limits(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-1937-\t\tstruct queue_limits *lim)\n--\ndrivers/block/virtio_blk.c-1949-\terr = virtio_cread_feature(vdev, VIRTIO_BLK_F_SEG_MAX,\ndrivers/block/virtio_blk.c:1950:\t\t\t\t   struct virtio_blk_config, seg_max,\ndrivers/block/virtio_blk.c-1951-\t\t\t\t   \u0026sg_elems);\n--\ndrivers/block/virtio_blk.c-1971-\terr = virtio_cread_feature(vdev, VIRTIO_BLK_F_SIZE_MAX,\ndrivers/block/virtio_blk.c:1972:\t\t\t\t   struct virtio_blk_config, size_max, \u0026v);\ndrivers/block/virtio_blk.c-1973-\tif (!err)\n--\ndrivers/block/virtio_blk.c-1979-\tvirtio_cread_feature(vdev, VIRTIO_BLK_F_BLK_SIZE,\ndrivers/block/virtio_blk.c:1980:\t\t\t\t   struct virtio_blk_config, blk_size,\ndrivers/block/virtio_blk.c-1981-\t\t\t\t   \u0026lim-\u003elogical_block_size);\n--\ndrivers/block/virtio_blk.c-1984-\terr = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,\ndrivers/block/virtio_blk.c:1985:\t\t\t\t   struct virtio_blk_config, physical_block_exp,\ndrivers/block/virtio_blk.c-1986-\t\t\t\t   \u0026physical_block_exp);\n--\ndrivers/block/virtio_blk.c-1991-\terr = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,\ndrivers/block/virtio_blk.c:1992:\t\t\t\t   struct virtio_blk_config, alignment_offset,\ndrivers/block/virtio_blk.c-1993-\t\t\t\t   \u0026alignment_offset);\n--\ndrivers/block/virtio_blk.c-1998-\terr = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,\ndrivers/block/virtio_blk.c:1999:\t\t\t\t   struct virtio_blk_config, min_io_size,\ndrivers/block/virtio_blk.c-2000-\t\t\t\t   \u0026min_io_size);\n--\ndrivers/block/virtio_blk.c-2004-\terr = virtio_cread_feature(vdev, VIRTIO_BLK_F_TOPOLOGY,\ndrivers/block/virtio_blk.c:2005:\t\t\t\t   struct virtio_blk_config, opt_io_size,\ndrivers/block/virtio_blk.c-2006-\t\t\t\t   \u0026opt_io_size);\n--\ndrivers/block/virtio_blk.c-2010-\tif (virtio_has_feature(vdev, VIRTIO_BLK_F_DISCARD)) {\ndrivers/block/virtio_blk.c:2011:\t\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-2012-\t\t\t     discard_sector_alignment, \u0026discard_granularity);\ndrivers/block/virtio_blk.c-2013-\ndrivers/block/virtio_blk.c:2014:\t\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-2015-\t\t\t     max_discard_sectors, \u0026v);\n--\ndrivers/block/virtio_blk.c-2017-\ndrivers/block/virtio_blk.c:2018:\t\tvirtio_cread(vdev, struct virtio_blk_config, max_discard_seg,\ndrivers/block/virtio_blk.c-2019-\t\t\t     \u0026max_discard_segs);\n--\ndrivers/block/virtio_blk.c-2022-\tif (virtio_has_feature(vdev, VIRTIO_BLK_F_WRITE_ZEROES)) {\ndrivers/block/virtio_blk.c:2023:\t\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-2024-\t\t\t     max_write_zeroes_sectors, \u0026v);\n--\ndrivers/block/virtio_blk.c-2041-\ndrivers/block/virtio_blk.c:2042:\t\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-2043-\t\t\t     secure_erase_sector_alignment, \u0026v);\n--\ndrivers/block/virtio_blk.c-2049-\t\t\tdev_err(\u0026vdev-\u003edev,\ndrivers/block/virtio_blk.c:2050:\t\t\t\t\"virtio_blk: secure_erase_sector_alignment can't be 0\\n\");\ndrivers/block/virtio_blk.c-2051-\t\t\treturn -EINVAL;\n--\ndrivers/block/virtio_blk.c-2055-\ndrivers/block/virtio_blk.c:2056:\t\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-2057-\t\t\t     max_secure_erase_sectors, \u0026v);\n--\ndrivers/block/virtio_blk.c-2063-\t\t\tdev_err(\u0026vdev-\u003edev,\ndrivers/block/virtio_blk.c:2064:\t\t\t\t\"virtio_blk: max_secure_erase_sectors can't be 0\\n\");\ndrivers/block/virtio_blk.c-2065-\t\t\treturn -EINVAL;\n--\ndrivers/block/virtio_blk.c-2069-\ndrivers/block/virtio_blk.c:2070:\t\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-2071-\t\t\t     max_secure_erase_seg, \u0026v);\n--\ndrivers/block/virtio_blk.c-2077-\t\t\tdev_err(\u0026vdev-\u003edev,\ndrivers/block/virtio_blk.c:2078:\t\t\t\t\"virtio_blk: max_secure_erase_seg can't be 0\\n\");\ndrivers/block/virtio_blk.c-2079-\t\t\treturn -EINVAL;\n--\ndrivers/block/virtio_blk.c-2107-\ndrivers/block/virtio_blk.c:2108:\t\tvirtio_cread(vdev, struct virtio_blk_config, zoned.model, \u0026model);\ndrivers/block/virtio_blk.c-2109-\t\tswitch (model) {\n--\ndrivers/block/virtio_blk.c=2128=static int virtblk_probe(struct virtio_device *vdev)\ndrivers/block/virtio_blk.c-2129-{\ndrivers/block/virtio_blk.c:2130:\tstruct virtio_blk *vblk;\ndrivers/block/virtio_blk.c-2131-\tstruct queue_limits lim = {\n--\ndrivers/block/virtio_blk.c=2272=static void virtblk_remove(struct virtio_device *vdev)\ndrivers/block/virtio_blk.c-2273-{\ndrivers/block/virtio_blk.c:2274:\tstruct virtio_blk *vblk = vdev-\u003epriv;\ndrivers/block/virtio_blk.c-2275-\n--\ndrivers/block/virtio_blk.c=2303=static int virtblk_freeze_priv(struct virtio_device *vdev)\ndrivers/block/virtio_blk.c-2304-{\ndrivers/block/virtio_blk.c:2305:\tstruct virtio_blk *vblk = vdev-\u003epriv;\ndrivers/block/virtio_blk.c-2306-\tstruct request_queue *q = vblk-\u003edisk-\u003equeue;\n--\ndrivers/block/virtio_blk.c=2336=static int virtblk_restore_priv(struct virtio_device *vdev)\ndrivers/block/virtio_blk.c-2337-{\ndrivers/block/virtio_blk.c:2338:\tstruct virtio_blk *vblk = vdev-\u003epriv;\ndrivers/block/virtio_blk.c-2339-\tint ret;\n--\ndrivers/block/virtio_blk.c=2389=static unsigned int features[] = {\n--\ndrivers/block/virtio_blk.c-2397-\ndrivers/block/virtio_blk.c:2398:static struct virtio_driver virtio_blk = {\ndrivers/block/virtio_blk.c-2399-\t.feature_table\t\t\t= features,\n--\ndrivers/block/virtio_blk.c-2415-\ndrivers/block/virtio_blk.c:2416:static int __init virtio_blk_init(void)\ndrivers/block/virtio_blk.c-2417-{\n--\ndrivers/block/virtio_blk.c-2429-\ndrivers/block/virtio_blk.c:2430:\terror = register_virtio_driver(\u0026virtio_blk);\ndrivers/block/virtio_blk.c-2431-\tif (error)\n--\ndrivers/block/virtio_blk.c-2441-\ndrivers/block/virtio_blk.c:2442:static void __exit virtio_blk_fini(void)\ndrivers/block/virtio_blk.c-2443-{\ndrivers/block/virtio_blk.c:2444:\tunregister_virtio_driver(\u0026virtio_blk);\ndrivers/block/virtio_blk.c-2445-\tunregister_blkdev(major, \"virtblk\");\n--\ndrivers/block/virtio_blk.c-2447-}\ndrivers/block/virtio_blk.c:2448:module_init(virtio_blk_init);\ndrivers/block/virtio_blk.c:2449:module_exit(virtio_blk_fini);\ndrivers/block/virtio_blk.c-2450-\n"
}

25/2 2026/09/14 18:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 42956
							output: 22
							thoughts: 2525
						

Thoughts:
**WorthFuzzing Assessment: virtio-blk Inline Encryption Patch**

Okay, let's break this down systematically. First, we have a patch introducing inline encryption and control queue support to the `virtio_blk.c` driver, along with a new Kconfig option, `VIRTIO_BLK_INLINE_ENCRYPTION`. Is this worth fuzzing? Based on the provided criteria, here's the thought process:

*   **Reachability:** The core question is reachability. This patch modifies `virtio_blk`, a crucial virtio driver. Virtio, as a virtual bus, is *explicitly* listed as a reachable driver via virtual buses in the instructions. Furthermore, virtio-blk is foundational to QEMU and GCE environments where we perform syzkaller fuzzing. Therefore, the modified code is executable in our testing environment. **Reachability = TRUE.**

*   **Positive/Negative Criteria:** The instructions heavily emphasize core subsystems and reachable drivers. This clearly falls under the umbrella of a reachable driver modifying core logic. We are not dealing with structurally unreachable hardware or unexecuted code paths. The criteria state that we should "Return WorthFuzzing=true whenever the patch touches reachable executable code," which includes reachable drivers.

*   **Assertions:** The patch introduces new crypto related functions, thus we're highly likely to expose new assertions.

*   **Focus Symbols (Target Specific Logic):** We need to identify specific functions that are added or altered by the patch, avoiding ubiquitous lifecycle functions that might dilute our fuzzing efforts. The patch introduces a number of functions, specifically related to the encryption and control queue mechanisms:

    -   `virtblk_setup_cmd`
    -   `virtblk_add_req`
    -   `virtblk_ctrl_vq_request`
    -   `virtblk_ctrlq_callback`
    -   `virtblk_ctrl_vq_drain`
    -   `virtblk_ctrl_vq_quiesce`
    -   `virtblk_get_crypto_modes`
    -   `virtblk_crypto_keyslot_program`
    -   `virtblk_crypto_keyslot_evict`
    -   `virtblk_crypto_derive_sw_secret`
    -   `virtblk_crypto_generate_key`
    -   `virtblk_crypto_prepare_key`
    -   `virtblk_crypto_import_key`
    -   `virtblk_init_crypto`

    The `virtblk_crypto_*` functions are particularly interesting. They are static, meaning they're not inlined, and we *can* target these with fuzzing (since they will have symbols in the vmlinux). Since we are dealing with a control queue to deal with keys, it is likely that these have novel behaviors compared to generic requests. This is what we want to focus on to avoid dilution. The "ctrl" functions are good, since they are core new logic.

*   **Config Dependencies:** We've got a new config `VIRTIO_BLK_INLINE_ENCRYPTION`. We need to determine the minimal configs required to enable the feature:
    1.  `VIRTIO_BLK`
    2.  `BLK_INLINE_ENCRYPTION`
    3.  `VIRTIO_BLK_INLINE_ENCRYPTION`
    4.  `VIRTIO`

*   **WorthFuzzing Verdict:** Given the reachability, the introduction of new encryption logic within a core driver, the availability of specific, non-ubiquitous functions for fuzzing, and the ability to define specific configs, this patch is *highly* worth fuzzing.

**WorthFuzzing = TRUE**

**Focus Symbols:**

*   `virtblk_crypto_keyslot_program`
*   `virtblk_crypto_keyslot_evict`
*   `virtblk_crypto_derive_sw_secret`
*   `virtblk_crypto_generate_key`
*   `virtblk_crypto_prepare_key`
*   `virtblk_crypto_import_key`
*   `virtblk_get_crypto_modes`
*   `virtblk_init_crypto`
*   `virtblk_ctrl_vq_request`
*   `virtblk_ctrlq_callback`
*   `virtblk_setup_cmd`
*   `virtblk_add_req`

**Configurations:**

*   `VIRTIO_BLK=y`
*   `BLK_INLINE_ENCRYPTION=y`
*   `VIRTIO_BLK_INLINE_ENCRYPTION=y`
*   `VIRTIO


26/2 2026/09/14 18:18 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "config BLK_CRYPTO\\b"
}

Results:
{
  "error": "no matches"
}

27/2 2026/09/14 18:18 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 46542
							output: 355
							thoughts: 490
						

Thoughts:
**My Fuzzing Assessment**

Okay, so I've been analyzing this `virtio_blk` patch. The key here is `CONFIG_BLK_INLINE_ENCRYPTION`, right? That's the core feature we're enabling. I quickly confirmed that `CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK` is *optional* – it's not a dependency. Now, does `FS_ENCRYPTION` come into play? Yes, that enables filesystem encryption which, in turn, *uses* the `BLK_INLINE_ENCRYPTION` feature. `BLK_DEV_IO_TRACE` isn't critical for this, so we can ignore it.

The *direct* configuration dependencies for compiling this stuff into `virtio_blk` are: `VIRTIO`, `VIRTIO_BLK`, `BLK_INLINE_ENCRYPTION`, and `VIRTIO_BLK_INLINE_ENCRYPTION`. Got it.

Now, let's run through the "WorthFuzzing" checklist. It's important to make sure we aren't chasing a rabbit down a hole.

*   **Non-kernel/non-executable changes:** Nope, we're touching `drivers/block/virtio_blk.c`, so it's kernel code.
*   **Structurally unreachable hardware:** No, this uses virtio, which is virtualized and emulated, so it's definitely reachable in a fuzzing setup like QEMU or GCE.
*   **Unreachable execution paths:** Definitely not. This patch deals with block I/O, a control queue, the handling of crypto keyslots, and runtime I/O/ioctls. It's all runtime stuff, so it's reachable.

Based on that, **WorthFuzzing is definitely TRUE**!

Alright, let's sanity-check the *reasoning* here:

*   The patch *adds* inline encryption support and a control virtqueue to the `virtio_blk` driver. That makes sense.
*   *Reachability*: virtio devices are *standard* and emulated in virtualization, so that's sound.
*   *Functionality*: We're adding control queue handling (functions like `virtblk_ctrl_vq_request`, `virtblk_ctrlq_callback`), some draining/quiescing with WARN assertions, keyslot programming/eviction, wrapped-key operations via the `blk_crypto_profile`, and changes to request preparation (`virtblk_setup_cmd`, `virtblk_add_req`). This is where the magic happens.
*   *Focus*: The fuzzing should absolutely target the new crypto profile operations, control virtqueue stuff, and the modified request setup paths. That's our attack surface.
*   *EnableConfigs*: Already nailed the necessary configs: `VIRTIO`, `VIRTIO_BLK`, `BLK_INLINE_ENCRYPTION`, `VIRTIO_BLK_INLINE_ENCRYPTION`.

I'm confident in this analysis. Time to call `set-results`.



28/2 2026/09/14 18:18 tool set-results
0m Args:
{
  "EnableConfigs": [
    "VIRTIO",
    "VIRTIO_BLK",
    "BLK_INLINE_ENCRYPTION",
    "VIRTIO_BLK_INLINE_ENCRYPTION"
  ],
  "FocusSymbols": [
    "virtblk_crypto_keyslot_program",
    "virtblk_crypto_keyslot_evict",
    "virtblk_crypto_derive_sw_secret",
    "virtblk_crypto_generate_key",
    "virtblk_crypto_prepare_key",
    "virtblk_crypto_import_key",
    "virtblk_get_crypto_modes",
    "virtblk_init_crypto",
    "virtblk_ctrl_vq_request",
    "virtblk_ctrlq_callback",
    "virtblk_ctrl_vq_drain",
    "virtblk_ctrl_vq_quiesce",
    "virtblk_setup_cmd",
    "virtblk_add_req"
  ],
  "Reasoning": "The patch implements inline encryption support and a dedicated control virtqueue for virtio-blk. Virtio devices are standard virtual hardware emulated in QEMU/GCE environments and are accessible via block I/O requests, ioctls, and filesystems using blk-crypto. The changes introduce new control virtqueue handling with synchronization and WARN_ON assertions, request formatting for crypto read/write operations, and keyslot management callbacks (program, evict, derive_sw_secret, generate_key, prepare_key, import_key). Fuzzing is warranted to discover potential concurrency issues, buffer handling bugs, or assertion failures.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "VIRTIO",
    "VIRTIO_BLK",
    "BLK_INLINE_ENCRYPTION",
    "VIRTIO_BLK_INLINE_ENCRYPTION"
  ],
  "FocusSymbols": [
    "virtblk_crypto_keyslot_program",
    "virtblk_crypto_keyslot_evict",
    "virtblk_crypto_derive_sw_secret",
    "virtblk_crypto_generate_key",
    "virtblk_crypto_prepare_key",
    "virtblk_crypto_import_key",
    "virtblk_get_crypto_modes",
    "virtblk_init_crypto",
    "virtblk_ctrl_vq_request",
    "virtblk_ctrlq_callback",
    "virtblk_ctrl_vq_drain",
    "virtblk_ctrl_vq_quiesce",
    "virtblk_setup_cmd",
    "virtblk_add_req"
  ],
  "Reasoning": "The patch implements inline encryption support and a dedicated control virtqueue for virtio-blk. Virtio devices are standard virtual hardware emulated in QEMU/GCE environments and are accessible via block I/O requests, ioctls, and filesystems using blk-crypto. The changes introduce new control virtqueue handling with synchronization and WARN_ON assertions, request formatting for crypto read/write operations, and keyslot management callbacks (program, evict, derive_sw_secret, generate_key, prepare_key, import_key). Fuzzing is warranted to discover potential concurrency issues, buffer handling bugs, or assertion failures.",
  "WorthFuzzing": true
}

29/1 2026/09/14 18:18 action If
2m Args:
{
  "WorthFuzzing": true
}

Results:
null

30/2 2026/09/14 18:18 agent kmsan-evaluator
2m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch introduces support for inline encryption in virtio-blk and modifies `struct virtio_blk` by adding `struct blk_crypto_profile profile` and `bool crypto_profile_initialized`.\n\nIn `virtblk_probe()`, `vblk` is allocated with `kmalloc_obj(*vblk)` which does not zero the memory. The new field `crypto_profile_initialized` is only initialized (`= true`) if the device supports inline encryption and control VQs and `virtblk_init_crypto()` succeeds. If the device does not advertise inline encryption (or if initialization fails or on zoned devices), `vblk-\u003ecrypto_profile_initialized` and `vblk-\u003eprofile` remain uninitialized heap memory.\n\nSubsequently, reachable code paths read these uninitialized fields in conditional branches:\n1. In `virtblk_destroy_crypto()`, called from `virtblk_free_disk()` upon disk release/teardown, `if (vblk-\u003ecrypto_profile_initialized)` performs conditional branching on the uninitialized boolean field. If non-zero, it invokes `blk_crypto_profile_destroy(\u0026vblk-\u003eprofile)` on uninitialized data.\n2. In `virtblk_restore_priv()`, `if (vblk-\u003eprofile.slots)` reads the uninitialized pointer field from the unzeroed `vblk-\u003eprofile` struct.\n\nThese issues represent conditional branching and reading of uninitialized heap memory (criteria 2 and 3). Standard bug detectors such as KASAN and LOCKDEP cannot detect reads of uninitialized bytes within validly allocated slab objects; only KMSAN shadow tracking detects use of uninitialized memory. Therefore, a dedicated KMSAN fuzzing session is justified.",
  "NeedsKMSAN": true
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit a6f70448193f9a3938cdc448e8e06cb3eecf4b9a
Author: syz-cluster <triage@syzkaller.com>
Date:   Mon Sep 14 18:17:05 2026 +0000

    syz-cluster: applied patch under review

diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig
index 858320b6ebb7e..58bb050d4617c 100644
--- a/drivers/block/Kconfig
+++ b/drivers/block/Kconfig
@@ -372,4 +372,16 @@ config BLK_DEV_ZONED_LOOP
 
 	  If unsure, say N.
 
+config VIRTIO_BLK_INLINE_ENCRYPTION
+	tristate "Virtio block inline encryption support"
+	depends on VIRTIO_BLK && BLK_INLINE_ENCRYPTION
+	help
+	  Say 'Y or M' here will allow the virtio block driver to route crypto
+	  requests to a different operating system in a virtualized
+	  environment. This is useful to encrypt the data stored in the storage
+	  by using storage inline crypto engine. The control queue feature bit
+	  must be negotiated to enable this functionality.
+
+	  If unsure, say N.
+
 endif # BLK_DEV
diff --git a/drivers/block/virtio_blk.c b/drivers/block/virtio_blk.c
index 32bf3ba07a9dc..3fd318e5e1f17 100644
--- a/drivers/block/virtio_blk.c
+++ b/drivers/block/virtio_blk.c
@@ -6,6 +6,7 @@
 #include <linux/hdreg.h>
 #include <linux/module.h>
 #include <linux/mutex.h>
+#include <linux/completion.h>
 #include <linux/interrupt.h>
 #include <linux/virtio.h>
 #include <linux/virtio_blk.h>
@@ -16,6 +17,7 @@
 #include <linux/numa.h>
 #include <linux/vmalloc.h>
 #include <uapi/linux/virtio_ring.h>
+#include <linux/blk-crypto-profile.h>
 
 #define PART_BITS 4
 #define VQ_NAME_LEN 16
@@ -52,6 +54,15 @@ struct virtio_blk_vq {
 	char name[VQ_NAME_LEN];
 } ____cacheline_aligned_in_smp;
 
+struct virtio_blk_ctrl_vq {
+	struct virtqueue *vq;
+	struct mutex mutex;
+	spinlock_t lock;
+	unsigned int inflight;
+	bool dead;
+	struct completion drained;
+};
+
 struct virtio_blk {
 	/*
 	 * This mutex must be held by anything that may run after
@@ -83,11 +94,25 @@ struct virtio_blk {
 
 	/* For zoned device */
 	unsigned int zone_sectors;
+
+	/* For inline encryption support */
+	struct blk_crypto_profile profile;
+	bool crypto_profile_initialized;
+
+	/* Control virtqueue state. */
+	struct virtio_blk_ctrl_vq ctrl_vq;
 };
 
 struct virtblk_req {
 	/* Out header */
-	struct virtio_blk_outhdr out_hdr;
+	union {
+		struct virtio_blk_outhdr base;
+		struct {
+			struct virtio_blk_outhdr base;
+			/* Crypto message (if VIRTIO_BLK_F_INLINE_ENCRYPTION) */
+			struct virtio_blk_crypto_msg msg;
+		} crypto_append;
+	} out_hdr;
 
 	/* In header */
 	union {
@@ -110,6 +135,27 @@ struct virtblk_req {
 	struct scatterlist sg[];
 };
 
+struct virtblk_ctrl_request {
+	/* Type byte, always its own out-sg for every command. */
+	__virtio32 type;
+	/* Out request, sent as a second, separate out-sg if any. */
+	union {
+		struct virtio_blk_crypto_key_desc key_desc;
+		struct virtio_blk_crypto_key_blob blob;
+	} out_req;
+
+	/* In response */
+	union {
+		struct virtio_blk_crypto_key_blob blob;
+		struct virtio_blk_crypto_sw_secret secret;
+		struct virtio_blk_crypto_modes modes;
+	} in_resp;
+	/* Status byte, always its own in-sg for every command. */
+	u8 status;
+
+	struct completion compl;
+};
+
 static inline blk_status_t virtblk_result(u8 status)
 {
 	switch (status) {
@@ -140,12 +186,17 @@ static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr)
 {
 	struct scatterlist out_hdr, in_hdr, *sgs[3];
 	unsigned int num_out = 0, num_in = 0;
+	size_t out_hdr_len = sizeof(vbr->out_hdr.base);
+
+	if (vbr->out_hdr.base.type == cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_CRYPTO_IN) ||
+	    vbr->out_hdr.base.type == cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_CRYPTO_OUT))
+		out_hdr_len = sizeof(vbr->out_hdr.crypto_append);
 
-	sg_init_one(&out_hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
+	sg_init_one(&out_hdr, &vbr->out_hdr, out_hdr_len);
 	sgs[num_out++] = &out_hdr;
 
 	if (vbr->sg_table.nents) {
-		if (vbr->out_hdr.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT))
+		if (vbr->out_hdr.base.type & cpu_to_virtio32(vq->vdev, VIRTIO_BLK_T_OUT))
 			sgs[num_out++] = vbr->sg_table.sgl;
 		else
 			sgs[num_out + num_in++] = vbr->sg_table.sgl;
@@ -235,6 +286,22 @@ static void virtblk_cleanup_cmd(struct request *req)
 		kfree(bvec_virt(&req->special_vec));
 }
 
+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)
+static bool is_crypto_request(struct request *req)
+{
+	struct request_queue *q = req->q;
+
+	return q->crypto_profile &&
+	       req->crypt_ctx &&
+	       req->crypt_keyslot;
+}
+#else
+static inline bool is_crypto_request(struct request *req)
+{
+	return false;
+}
+#endif
+
 static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
 				      struct request *req,
 				      struct virtblk_req *vbr)
@@ -243,20 +310,27 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
 	bool unmap = false;
 	u32 type;
 	u64 sector = 0;
+	int i;
 
 	if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) && op_is_zone_mgmt(req_op(req)))
 		return BLK_STS_NOTSUPP;
 
 	/* Set fields for all request types */
-	vbr->out_hdr.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));
+	vbr->out_hdr.base.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));
 
 	switch (req_op(req)) {
 	case REQ_OP_READ:
-		type = VIRTIO_BLK_T_IN;
+		if (is_crypto_request(req))
+			type = VIRTIO_BLK_T_CRYPTO_IN;
+		else
+			type = VIRTIO_BLK_T_IN;
 		sector = blk_rq_pos(req);
 		break;
 	case REQ_OP_WRITE:
-		type = VIRTIO_BLK_T_OUT;
+		if (is_crypto_request(req))
+			type = VIRTIO_BLK_T_CRYPTO_OUT;
+		else
+			type = VIRTIO_BLK_T_OUT;
 		sector = blk_rq_pos(req);
 		break;
 	case REQ_OP_FLUSH:
@@ -309,8 +383,8 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
 
 	/* Set fields for non-REQ_OP_DRV_IN request types */
 	vbr->in_hdr_len = in_hdr_len;
-	vbr->out_hdr.type = cpu_to_virtio32(vdev, type);
-	vbr->out_hdr.sector = cpu_to_virtio64(vdev, sector);
+	vbr->out_hdr.base.type = cpu_to_virtio32(vdev, type);
+	vbr->out_hdr.base.sector = cpu_to_virtio64(vdev, sector);
 
 	if (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES ||
 	    type == VIRTIO_BLK_T_SECURE_ERASE) {
@@ -318,6 +392,18 @@ static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,
 			return BLK_STS_RESOURCE;
 	}
 
+	if (type == VIRTIO_BLK_T_CRYPTO_IN || type == VIRTIO_BLK_T_CRYPTO_OUT) {
+		memset(&vbr->out_hdr.crypto_append.msg, 0,
+			sizeof(vbr->out_hdr.crypto_append.msg));
+		vbr->out_hdr.crypto_append.msg.slot =
+			cpu_to_virtio32(vdev,
+				blk_crypto_keyslot_index(req->crypt_keyslot));
+		for (i = 0; i < ARRAY_SIZE(vbr->out_hdr.crypto_append.msg.dun); i++) {
+			vbr->out_hdr.crypto_append.msg.dun[i] =
+				cpu_to_virtio64(vdev, req->crypt_ctx->bc_dun[i]);
+		}
+	}
+
 	return 0;
 }
 
@@ -568,8 +654,8 @@ static int virtblk_submit_zone_report(struct virtio_blk *vblk,
 
 	vbr = blk_mq_rq_to_pdu(req);
 	vbr->in_hdr_len = sizeof(vbr->in_hdr.status);
-	vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_ZONE_REPORT);
-	vbr->out_hdr.sector = cpu_to_virtio64(vblk->vdev, sector);
+	vbr->out_hdr.base.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_ZONE_REPORT);
+	vbr->out_hdr.base.sector = cpu_to_virtio64(vblk->vdev, sector);
 
 	err = blk_rq_map_kern(req, report_buf, report_len, GFP_KERNEL);
 	if (err)
@@ -817,8 +903,8 @@ static int virtblk_get_id(struct gendisk *disk, char *id_str)
 
 	vbr = blk_mq_rq_to_pdu(req);
 	vbr->in_hdr_len = sizeof(vbr->in_hdr.status);
-	vbr->out_hdr.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_ID);
-	vbr->out_hdr.sector = 0;
+	vbr->out_hdr.base.type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_ID);
+	vbr->out_hdr.base.sector = 0;
 
 	err = blk_rq_map_kern(req, id_str, VIRTIO_BLK_ID_BYTES, GFP_KERNEL);
 	if (err)
@@ -863,11 +949,598 @@ static int virtblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)
 	return ret;
 }
 
+#define VIRTBLK_CTRL_VQ_DRAIN_TIMEOUT (10 * HZ)
+
+/* Prevent new submissions and wait for in-flight requests to complete. */
+static void virtblk_ctrl_vq_quiesce(struct virtio_blk *vblk)
+{
+	unsigned long flags;
+	bool need_wait;
+
+	if (!vblk->ctrl_vq.vq)
+		return;
+
+	init_completion(&vblk->ctrl_vq.drained);
+
+	spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	vblk->ctrl_vq.dead = true;
+	need_wait = vblk->ctrl_vq.inflight != 0;
+	spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+
+	if (need_wait &&
+	    !wait_for_completion_timeout(&vblk->ctrl_vq.drained,
+					  VIRTBLK_CTRL_VQ_DRAIN_TIMEOUT))
+		dev_warn(&vblk->vdev->dev,
+			 "timed out waiting for control queue requests to complete\n");
+}
+
+/* Fail requests left in the control queue after reset. */
+static void virtblk_ctrl_vq_drain(struct virtio_blk *vblk)
+{
+	struct virtblk_ctrl_request *creq;
+	unsigned long flags;
+
+	if (!vblk->ctrl_vq.vq)
+		return;
+
+	spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	while ((creq = virtqueue_detach_unused_buf(vblk->ctrl_vq.vq)) != NULL) {
+		if (WARN_ON_ONCE(!vblk->ctrl_vq.inflight))
+			;
+		else
+			vblk->ctrl_vq.inflight--;
+		creq->status = VIRTIO_BLK_S_IOERR;
+		spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+		complete(&creq->compl);
+		spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	}
+	spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+}
+
+static void virtblk_ctrlq_callback(struct virtqueue *vq)
+{
+	struct virtio_blk *vblk = vq->vdev->priv;
+	struct virtblk_ctrl_request *creq;
+	unsigned long flags;
+	unsigned int len;
+
+	spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	do {
+		virtqueue_disable_cb(vq);
+		while ((creq = virtqueue_get_buf(vq, &len)) != NULL) {
+			bool drained = false;
+
+			if (WARN_ON_ONCE(!vblk->ctrl_vq.inflight)) {
+				/*
+				 * Still complete the request.  Never leave a
+				 * synchronous caller blocked because the accounting
+				 * state was already inconsistent.
+				 */
+				spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+				complete(&creq->compl);
+				spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+				continue;
+			}
+
+			if (--vblk->ctrl_vq.inflight == 0 && vblk->ctrl_vq.dead)
+				drained = true;
+			spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+			if (drained)
+				complete(&vblk->ctrl_vq.drained);
+			complete(&creq->compl);
+			spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+		}
+	} while (!virtqueue_enable_cb(vq));
+	spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+}
+
+/* Submit a control-queue request and wait for completion. */
+static int virtblk_ctrl_vq_request(struct virtio_blk *vblk,
+				    struct virtblk_ctrl_request *creq,
+				    struct scatterlist *sgs[],
+				    unsigned int out_sgs, unsigned int in_sgs)
+{
+	unsigned long flags;
+	int err;
+
+	mutex_lock(&vblk->ctrl_vq.mutex);
+	init_completion(&creq->compl);
+
+	spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+	if (vblk->ctrl_vq.dead) {
+		spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+		mutex_unlock(&vblk->ctrl_vq.mutex);
+		return -ENODEV;
+	}
+	err = virtqueue_add_sgs(vblk->ctrl_vq.vq, sgs, out_sgs, in_sgs, creq, GFP_ATOMIC);
+	if (!err) {
+		vblk->ctrl_vq.inflight++;
+		virtqueue_kick(vblk->ctrl_vq.vq);
+	}
+	spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+	if (err) {
+		mutex_unlock(&vblk->ctrl_vq.mutex);
+		return err;
+	}
+
+	wait_for_completion(&creq->compl);
+	mutex_unlock(&vblk->ctrl_vq.mutex);
+	return 0;
+}
+
+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)
+// Maps VIRTIO_BLK_CRYPTO_MODE_* values to the kernel's internal enum.
+static const enum blk_crypto_mode_num
+	virtio_blk_crypto_mode_map[VIRTIO_BLK_CRYPTO_MODE_MAX + 1] = {
+	[VIRTIO_BLK_CRYPTO_MODE_INVALID]	= BLK_ENCRYPTION_MODE_INVALID,
+	[VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS]	= BLK_ENCRYPTION_MODE_AES_256_XTS,
+};
+
+static int virtblk_get_crypto_modes(struct virtio_blk *vblk,
+				    unsigned int *crypto_modes_supported)
+{
+	unsigned int nr_modes = VIRTIO_BLK_CRYPTO_MODE_MAX + 1;
+	struct scatterlist type_sg, resp_sg, status_sg, *sgs[3];
+	struct virtblk_ctrl_request *creq;
+	unsigned int i;
+	int err;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_GET_CRYPTO_MODES);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&resp_sg, &creq->in_resp.modes, sizeof(creq->in_resp.modes));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &resp_sg;
+	sgs[2] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 1, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	for (i = 1; i < nr_modes; i++) {
+		u32 mode_mask = virtio32_to_cpu(vblk->vdev,
+						 creq->in_resp.modes.modes[i]);
+		enum blk_crypto_mode_num mode = virtio_blk_crypto_mode_map[i];
+
+		if (!mode_mask)
+			continue;
+		if (!mode) {
+			dev_warn(&vblk->vdev->dev,
+				 "ignoring unknown crypto mode %u\n", i);
+			continue;
+		}
+		crypto_modes_supported[mode] = mode_mask;
+	}
+
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int set_virtblk_crypto_key_desc(struct virtio_device *vdev,
+				       struct virtblk_ctrl_request *creq,
+				       const struct blk_crypto_key *key,
+				       unsigned int slot)
+{
+	struct virtio_blk_crypto_key_desc *desc = &creq->out_req.key_desc;
+	enum blk_crypto_key_type key_type = key->crypto_cfg.key_type;
+
+	if (sizeof(desc->bytes) < key->size)
+		return -EOVERFLOW;
+
+	memset(desc, 0, sizeof(*desc));
+	desc->slot = cpu_to_virtio32(vdev, slot);
+	memcpy(desc->bytes, key->bytes, key->size);
+	desc->key_size = cpu_to_virtio32(vdev, key->size);
+	desc->crypto_mode = cpu_to_virtio32(vdev, key->crypto_cfg.crypto_mode);
+	switch (key->crypto_cfg.key_type) {
+	case BLK_CRYPTO_KEY_TYPE_RAW:
+		desc->key_type = cpu_to_virtio32(vdev,
+			VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW);
+		break;
+	case BLK_CRYPTO_KEY_TYPE_HW_WRAPPED:
+		desc->key_type = cpu_to_virtio32(vdev,
+			VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED);
+		break;
+	default:
+		return -EOPNOTSUPP;
+	}
+	desc->data_unit_size_bits = cpu_to_virtio32(vdev, key->data_unit_size_bits);
+	desc->dun_bytes = cpu_to_virtio32(vdev, key->crypto_cfg.dun_bytes);
+
+	return 0;
+}
+
+static inline struct virtio_blk *virtblk_from_profile(struct blk_crypto_profile *profile)
+{
+	return container_of(profile, struct virtio_blk, profile);
+}
+
+static int virtblk_crypto_keyslot_program(struct blk_crypto_profile *profile,
+					   const struct blk_crypto_key *key,
+					   unsigned int slot)
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, status_sg, *sgs[3];
+	struct virtblk_ctrl_request *creq;
+	int err;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM);
+
+	err = set_virtblk_crypto_key_desc(vblk->vdev, creq, key, slot);
+	if (err)
+		goto out_free;
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.key_desc, sizeof(creq->out_req.key_desc));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 1);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_keyslot_evict(struct blk_crypto_profile *profile,
+					 const struct blk_crypto_key *key,
+					 unsigned int slot)
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, status_sg, *sgs[3];
+	struct virtblk_ctrl_request *creq;
+	int err;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_KEYSLOT_EVICT);
+
+	err = set_virtblk_crypto_key_desc(vblk->vdev, creq, key, slot);
+	if (err)
+		goto out_free;
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.key_desc, sizeof(creq->out_req.key_desc));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 1);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *profile,
+					    const u8 *eph_key, size_t eph_key_size,
+					    u8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE])
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];
+	struct virtblk_ctrl_request *creq;
+	int err;
+
+	if (eph_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE
+	    || sizeof(creq->in_resp.secret.secret) < BLK_CRYPTO_SW_SECRET_SIZE)
+		return -EOVERFLOW;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET);
+	memcpy(creq->out_req.blob.key, eph_key, eph_key_size);
+	creq->out_req.blob.key_size = cpu_to_virtio32(vblk->vdev, eph_key_size);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.blob, sizeof(creq->out_req.blob));
+	sg_init_one(&resp_sg, &creq->in_resp.secret, sizeof(creq->in_resp.secret));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &resp_sg;
+	sgs[3] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	memcpy(sw_secret, creq->in_resp.secret.secret, BLK_CRYPTO_SW_SECRET_SIZE);
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_generate_key(struct blk_crypto_profile *profile,
+					u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, resp_sg, status_sg, *sgs[3];
+	struct virtblk_ctrl_request *creq;
+	unsigned int key_size;
+	int err;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_GENERATE_KEY);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&resp_sg, &creq->in_resp.blob, sizeof(creq->in_resp.blob));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &resp_sg;
+	sgs[2] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 1, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	key_size = virtio32_to_cpu(vblk->vdev, creq->in_resp.blob.key_size);
+	if (!key_size ||
+		key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) {
+		dev_err(&vblk->vdev->dev,
+			"backend returned oversized generated key: %u\n", key_size);
+		err = -EOVERFLOW;
+		goto out_free;
+	}
+	memcpy(lt_key, creq->in_resp.blob.key, key_size);
+	err = key_size;
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_prepare_key(struct blk_crypto_profile *profile,
+				       const u8 *lt_key, size_t lt_key_size,
+				       u8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];
+	struct virtblk_ctrl_request *creq;
+	unsigned int key_size;
+	int err;
+
+	if (lt_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE)
+		return -EOVERFLOW;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_PREPARE_KEY);
+	memcpy(creq->out_req.blob.key, lt_key, lt_key_size);
+	creq->out_req.blob.key_size = cpu_to_virtio32(vblk->vdev, lt_key_size);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.blob, sizeof(creq->out_req.blob));
+	sg_init_one(&resp_sg, &creq->in_resp.blob, sizeof(creq->in_resp.blob));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &resp_sg;
+	sgs[3] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	key_size = virtio32_to_cpu(vblk->vdev, creq->in_resp.blob.key_size);
+	if (!key_size ||
+		key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) {
+		dev_err(&vblk->vdev->dev,
+			"backend returned oversized prepared key: %u\n", key_size);
+		err = -EOVERFLOW;
+		goto out_free;
+	}
+	memcpy(eph_key, creq->in_resp.blob.key, key_size);
+	err = key_size;
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static int virtblk_crypto_import_key(struct blk_crypto_profile *profile,
+				      const u8 *raw_key, size_t raw_key_size,
+				      u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])
+{
+	struct virtio_blk *vblk = virtblk_from_profile(profile);
+	struct scatterlist type_sg, out_req_sg, resp_sg, status_sg, *sgs[4];
+	struct virtblk_ctrl_request *creq;
+	unsigned int key_size;
+	int err;
+
+	if (raw_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE)
+		return -EOVERFLOW;
+
+	creq = kzalloc_obj(*creq, GFP_KERNEL);
+	if (!creq)
+		return -ENOMEM;
+
+	creq->type = cpu_to_virtio32(vblk->vdev, VIRTIO_BLK_T_CRYPTO_IMPORT_KEY);
+	memcpy(creq->out_req.blob.key, raw_key, raw_key_size);
+	creq->out_req.blob.key_size = cpu_to_virtio32(vblk->vdev, raw_key_size);
+
+	sg_init_one(&type_sg, &creq->type, sizeof(creq->type));
+	sg_init_one(&out_req_sg, &creq->out_req.blob, sizeof(creq->out_req.blob));
+	sg_init_one(&resp_sg, &creq->in_resp.blob, sizeof(creq->in_resp.blob));
+	sg_init_one(&status_sg, &creq->status, sizeof(creq->status));
+	sgs[0] = &type_sg;
+	sgs[1] = &out_req_sg;
+	sgs[2] = &resp_sg;
+	sgs[3] = &status_sg;
+
+	err = virtblk_ctrl_vq_request(vblk, creq, sgs, 2, 2);
+	if (err)
+		goto out_free;
+
+	err = blk_status_to_errno(virtblk_result(creq->status));
+	if (err)
+		goto out_free;
+
+	key_size = virtio32_to_cpu(vblk->vdev, creq->in_resp.blob.key_size);
+	if (!key_size ||
+		key_size > BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE) {
+		dev_err(&vblk->vdev->dev,
+			"backend returned oversized imported key: %u\n", key_size);
+		err = -EOVERFLOW;
+		goto out_free;
+	}
+	memcpy(lt_key, creq->in_resp.blob.key, key_size);
+	err = key_size;
+out_free:
+	kfree(creq);
+	return err;
+}
+
+static const struct blk_crypto_ll_ops virtblk_crypto_ops = {
+	.keyslot_program	= virtblk_crypto_keyslot_program,
+	.keyslot_evict		= virtblk_crypto_keyslot_evict,
+	.derive_sw_secret	= virtblk_crypto_derive_sw_secret,
+	.generate_key		= virtblk_crypto_generate_key,
+	.prepare_key		= virtblk_crypto_prepare_key,
+	.import_key		= virtblk_crypto_import_key,
+};
+
+static int virtblk_init_crypto(struct virtio_blk *vblk)
+{
+	struct virtio_device *vdev = vblk->vdev;
+	unsigned int crypto_modes_supported[BLK_ENCRYPTION_MODE_MAX] = { 0 };
+	unsigned int key_type_supported = 0;
+	u16 max_slots = 0;
+	/* virtio_cread() requires the variable size to match the config field exactly */
+	u8 max_dun_bytes = 0, key_types = 0;
+	int err;
+
+	virtio_cread(vdev, struct virtio_blk_config,
+		     enc_characteristics.max_slots, &max_slots);
+	virtio_cread(vdev, struct virtio_blk_config,
+		     enc_characteristics.max_dun_bytes, &max_dun_bytes);
+	virtio_cread(vdev, struct virtio_blk_config,
+		     enc_characteristics.key_types, &key_types);
+
+	dev_info_once(&vdev->dev,
+		 "max_slots = %u, max_dun_bytes = %u, key_types = 0x%x\n",
+		 max_slots, max_dun_bytes, key_types);
+
+	if (!max_slots)
+		return -EINVAL;
+
+	/*
+	 * struct virtio_blk_crypto_msg.dun is a fixed array of four __virtio64
+	 * values (32 bytes total), matching the size of
+	 * blk_crypto_ctx::bc_dun[4].  Refuse to advertise more than that as
+	 * supported, or blk-crypto could negotiate a larger dun_bytes with the
+	 * filesystem and have the high-order bytes of req->crypt_ctx->bc_dun
+	 * silently dropped in virtblk_setup_cmd().
+	 */
+	if (max_dun_bytes > sizeof_field(struct virtio_blk_crypto_msg, dun))
+		return -EINVAL;
+
+	if (key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW)
+		key_type_supported |= BLK_CRYPTO_KEY_TYPE_RAW;
+	if (key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED)
+		key_type_supported |= BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
+	if (!key_type_supported)
+		return -EINVAL;
+
+	err = virtblk_get_crypto_modes(vblk, crypto_modes_supported);
+	if (err) {
+		dev_err(&vdev->dev, "get crypto modes failed: %d\n", err);
+		return err;
+	}
+
+	/*
+	 * Use the plain (non-devm) initializer: vblk->profile is embedded in
+	 * struct virtio_blk, whose lifetime is tied to the gendisk, not to
+	 * &vdev->dev. Tying destruction to the vdev via devm would run the
+	 * destroy callback after virtblk_remove() has already freed vblk.
+	 * virtblk_free_disk() calls blk_crypto_profile_destroy() explicitly
+	 * instead, guarded by crypto_profile_initialized below.
+	 */
+	err = blk_crypto_profile_init(&vblk->profile, max_slots);
+	if (err) {
+		dev_err(&vdev->dev, "crypto profile initialization failed: %d\n", err);
+		return err;
+	}
+
+	vblk->profile.ll_ops = virtblk_crypto_ops;
+	vblk->profile.max_dun_bytes_supported = max_dun_bytes;
+	vblk->profile.key_types_supported = key_type_supported;
+	vblk->profile.dev = &vdev->dev;
+	memcpy(vblk->profile.modes_supported, crypto_modes_supported,
+	       BLK_ENCRYPTION_MODE_MAX * sizeof(unsigned int));
+
+	vblk->crypto_profile_initialized = true;
+
+	dev_info(&vdev->dev, "inline crypto profile initialized\n");
+
+	return 0;
+}
+
+static void virtblk_destroy_crypto(struct virtio_blk *vblk)
+{
+	if (vblk->crypto_profile_initialized)
+		blk_crypto_profile_destroy(&vblk->profile);
+}
+#else
+
+static inline int virtblk_init_crypto(struct virtio_blk *vblk)
+{
+	return -EOPNOTSUPP;
+}
+
+static inline void virtblk_destroy_crypto(struct virtio_blk *vblk)
+{
+}
+#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */
+
 static void virtblk_free_disk(struct gendisk *disk)
 {
 	struct virtio_blk *vblk = disk->private_data;
 
 	ida_free(&vd_index_ida, vblk->index);
+	virtblk_destroy_crypto(vblk);
+	mutex_destroy(&vblk->ctrl_vq.mutex);
 	mutex_destroy(&vblk->vdev_mutex);
 	kfree(vblk);
 }
@@ -965,6 +1638,8 @@ static int init_vq(struct virtio_blk *vblk)
 	struct virtqueue **vqs;
 	unsigned short num_vqs;
 	unsigned short num_poll_vqs;
+	unsigned short total_vqs;
+	bool has_ctrl_vq;
 	struct virtio_device *vdev = vblk->vdev;
 	struct irq_affinity desc = { 0, };
 
@@ -993,12 +1668,19 @@ static int init_vq(struct virtio_blk *vblk)
 				vblk->io_queues[HCTX_TYPE_READ],
 				vblk->io_queues[HCTX_TYPE_POLL]);
 
+	/*
+	 * The control vq is appended after the data vqs whenever
+	 * F_CTRL_VQ is negotiated.
+	 */
+	has_ctrl_vq = virtio_has_feature(vdev, VIRTIO_BLK_F_CTRL_VQ);
+	total_vqs = num_vqs + (has_ctrl_vq ? 1 : 0);
+
 	vblk->vqs = kmalloc_objs(*vblk->vqs, num_vqs);
 	if (!vblk->vqs)
 		return -ENOMEM;
 
-	vqs_info = kzalloc_objs(*vqs_info, num_vqs);
-	vqs = kmalloc_objs(*vqs, num_vqs);
+	vqs_info = kzalloc_objs(*vqs_info, total_vqs);
+	vqs = kmalloc_objs(*vqs, total_vqs);
 	if (!vqs_info || !vqs) {
 		err = -ENOMEM;
 		goto out;
@@ -1015,8 +1697,13 @@ static int init_vq(struct virtio_blk *vblk)
 		vqs_info[i].name = vblk->vqs[i].name;
 	}
 
+	if (has_ctrl_vq) {
+		vqs_info[num_vqs].callback = virtblk_ctrlq_callback;
+		vqs_info[num_vqs].name = "control";
+	}
+
 	/* Discover virtqueues and write information to configuration.  */
-	err = virtio_find_vqs(vdev, num_vqs, vqs, vqs_info, &desc);
+	err = virtio_find_vqs(vdev, total_vqs, vqs, vqs_info, &desc);
 	if (err)
 		goto out;
 
@@ -1025,6 +1712,9 @@ static int init_vq(struct virtio_blk *vblk)
 		vblk->vqs[i].vq = vqs[i];
 	}
 	vblk->num_vqs = num_vqs;
+	vblk->ctrl_vq.vq = has_ctrl_vq ? vqs[num_vqs] : NULL;
+	vblk->ctrl_vq.dead = false;
+	vblk->ctrl_vq.inflight = 0;
 
 out:
 	kfree(vqs);
@@ -1464,14 +2154,18 @@ static int virtblk_probe(struct virtio_device *vdev)
 	}
 
 	mutex_init(&vblk->vdev_mutex);
+	mutex_init(&vblk->ctrl_vq.mutex);
+	spin_lock_init(&vblk->ctrl_vq.lock);
 
 	vblk->vdev = vdev;
 
 	INIT_WORK(&vblk->config_work, virtblk_config_changed_work);
 
 	err = init_vq(vblk);
-	if (err)
+	if (err) {
+		dev_err(&vdev->dev, "init virt queue failed: err = %d\n", err);
 		goto out_free_vblk;
+	}
 
 	/* Default queue sizing is to fill the ring. */
 	if (!virtblk_queue_depth) {
@@ -1538,6 +2232,19 @@ static int virtblk_probe(struct virtio_device *vdev)
 		err = blk_revalidate_disk_zones(vblk->disk);
 		if (err)
 			goto out_cleanup_disk;
+	} else if (IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) &&
+		   virtio_has_feature(vdev, VIRTIO_BLK_F_INLINE_ENCRYPTION) &&
+		   virtio_has_feature(vdev, VIRTIO_BLK_F_CTRL_VQ)) {
+		err = virtblk_init_crypto(vblk);
+		if (!err) {
+			if (!blk_crypto_register(&vblk->profile, vblk->disk->queue))
+				dev_warn(&vdev->dev,
+					 "failed to register inline crypto profile\n");
+		} else {
+			dev_warn(&vdev->dev,
+				 "inline crypto init failed: %d, continuing without inline crypto support\n",
+				 err);
+		}
 	}
 
 	err = device_add_disk(&vdev->dev, vblk->disk, virtblk_attr_groups);
@@ -1553,6 +2260,7 @@ static int virtblk_probe(struct virtio_device *vdev)
 out_free_vq:
 	vdev->config->del_vqs(vdev);
 	kfree(vblk->vqs);
+	vblk->ctrl_vq.vq = NULL;
 out_free_vblk:
 	kfree(vblk);
 out_free_index:
@@ -1571,16 +2279,21 @@ static void virtblk_remove(struct virtio_device *vdev)
 	del_gendisk(vblk->disk);
 	blk_mq_free_tag_set(&vblk->tag_set);
 
+	virtblk_ctrl_vq_quiesce(vblk);
+
 	mutex_lock(&vblk->vdev_mutex);
 
 	/* Stop all the virtqueues. */
 	virtio_reset_device(vdev);
+	virtblk_ctrl_vq_drain(vblk);
 
 	/* Virtqueues are stopped, nothing can use vblk->vdev anymore. */
 	vblk->vdev = NULL;
 
 	vdev->config->del_vqs(vdev);
 	kfree(vblk->vqs);
+	vblk->vqs = NULL;
+	vblk->ctrl_vq.vq = NULL;
 
 	mutex_unlock(&vblk->vdev_mutex);
 
@@ -1593,6 +2306,8 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
 	struct request_queue *q = vblk->disk->queue;
 	unsigned int memflags;
 
+	virtblk_ctrl_vq_quiesce(vblk);
+
 	/* Ensure no requests in virtqueues before deleting vqs. */
 	memflags = blk_mq_freeze_queue(q);
 	blk_mq_quiesce_queue_nowait(q);
@@ -1600,6 +2315,7 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
 
 	/* Ensure we don't receive any more interrupts */
 	virtio_reset_device(vdev);
+	virtblk_ctrl_vq_drain(vblk);
 
 	/* Make sure no work handler is accessing the device. */
 	flush_work(&vblk->config_work);
@@ -1612,6 +2328,7 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
 	 * pointers safely.
 	 */
 	vblk->vqs = NULL;
+	vblk->ctrl_vq.vq = NULL;
 
 	return 0;
 }
@@ -1628,6 +2345,9 @@ static int virtblk_restore_priv(struct virtio_device *vdev)
 	virtio_device_ready(vdev);
 	blk_mq_unquiesce_queue(vblk->disk->queue);
 
+	if (vblk->profile.slots)
+		blk_crypto_reprogram_all_keys(&vblk->profile);
+
 	return 0;
 }
 
@@ -1672,6 +2392,7 @@ static unsigned int features[] = {
 	VIRTIO_BLK_F_FLUSH, VIRTIO_BLK_F_TOPOLOGY, VIRTIO_BLK_F_CONFIG_WCE,
 	VIRTIO_BLK_F_MQ, VIRTIO_BLK_F_DISCARD, VIRTIO_BLK_F_WRITE_ZEROES,
 	VIRTIO_BLK_F_SECURE_ERASE, VIRTIO_BLK_F_ZONED,
+	VIRTIO_BLK_F_CTRL_VQ, VIRTIO_BLK_F_INLINE_ENCRYPTION,
 };
 
 static struct virtio_driver virtio_blk = {
diff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_blk.h
index 3744e4da1b2a7..952534e2b489d 100644
--- a/include/uapi/linux/virtio_blk.h
+++ b/include/uapi/linux/virtio_blk.h
@@ -42,6 +42,8 @@
 #define VIRTIO_BLK_F_WRITE_ZEROES	14	/* WRITE ZEROES is supported */
 #define VIRTIO_BLK_F_SECURE_ERASE	16 /* Secure Erase is supported */
 #define VIRTIO_BLK_F_ZONED		17	/* Zoned block device */
+#define VIRTIO_BLK_F_CTRL_VQ			22	/* Control queue */
+#define VIRTIO_BLK_F_INLINE_ENCRYPTION		23	/* Inline encryption */
 
 /* Legacy feature bits */
 #ifndef VIRTIO_BLK_NO_LEGACY
@@ -57,6 +59,10 @@
 
 #define VIRTIO_BLK_ID_BYTES	20	/* ID string length */
 
+/* Key type bitmask for VIRTIO_BLK_F_INLINE_ENCRYPTION */
+#define VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW	(1 << 0)
+#define VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED	(1 << 1)
+
 struct virtio_blk_config {
 	/* The capacity (in 512-byte sectors). */
 	__virtio64 capacity;
@@ -148,6 +154,15 @@ struct virtio_blk_config {
 		__u8 model;
 		__u8 unused2[3];
 	} zoned;
+
+	/* Inline Encryption device characteristics (if VIRTIO_BLK_F_INLINE_ENCRYPTION) */
+	struct virtio_blk_enc_characteristics {
+		__virtio16 max_slots;
+		__u8 max_dun_bytes;
+		/* Bitmask of supported key types: VIRTIO_BLK_CRYPTO_KEY_TYPE_* */
+		__u8 key_types;
+		__virtio32 unused3;
+	} enc_characteristics;
 } __attribute__((packed));
 
 /*
@@ -206,6 +221,33 @@ struct virtio_blk_config {
 /* Reset All zones command */
 #define VIRTIO_BLK_T_ZONE_RESET_ALL 26
 
+/* Inline-encrypted write: crypto_msg set in outhdr */
+#define VIRTIO_BLK_T_CRYPTO_OUT		27
+
+/* Inline-encrypted read: crypto_msg set in outhdr */
+#define VIRTIO_BLK_T_CRYPTO_IN		28
+
+/* Get inline crypto modes */
+#define VIRTIO_BLK_T_GET_CRYPTO_MODES	29
+
+/* Program a key into the keyslot */
+#define VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM	30
+
+/* Evict a key */
+#define VIRTIO_BLK_T_CRYPTO_KEYSLOT_EVICT	31
+
+/* Derive the software secret from a hardware-wrapped key */
+#define VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET	32
+
+/* Generate a new hardware-wrapped key */
+#define VIRTIO_BLK_T_CRYPTO_GENERATE_KEY	33
+
+/* Import a raw key as a hardware-wrapped key */
+#define VIRTIO_BLK_T_CRYPTO_IMPORT_KEY		34
+
+/* Convert a long-term wrapped key to its ephemerally-wrapped form */
+#define VIRTIO_BLK_T_CRYPTO_PREPARE_KEY	35
+
 #ifndef VIRTIO_BLK_NO_LEGACY
 /* Barrier before this op. */
 #define VIRTIO_BLK_T_BARRIER	0x80000000
@@ -225,6 +267,80 @@ struct virtio_blk_outhdr {
 	__virtio64 sector;
 };
 
+/*
+ * Crypto message descriptor, appended to the outhdr of a
+ * VIRTIO_BLK_T_CRYPTO_OUT or VIRTIO_BLK_T_CRYPTO_IN request.
+ */
+struct virtio_blk_crypto_msg {
+	/* virtual key slot index */
+	__virtio32 slot;
+	__u8 unused[4];
+	/* data unit number (DUN / IV) for this request */
+	__virtio64 dun[4];
+};
+
+/*
+ * Inline crypto key descriptor. Request part for
+ * VIRTIO_BLK_T_CRYPTO_KEYSLOT_PROGRAM/KEYSLOT_EVICT.
+ */
+/* Must be >= BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE in include/linux/blk-crypto.h */
+#define VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE		128
+
+struct virtio_blk_crypto_key_desc {
+	__virtio32  slot;
+	__u8        bytes[VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE];
+	__virtio32  key_size;
+	__virtio32  crypto_mode;
+	__virtio32  key_type;
+	__virtio32  data_unit_size_bits;
+	__virtio32  dun_bytes;
+};
+
+/*
+ * A raw or hardware-wrapped key blob, used as the request and/or reply part
+ * of the VIRTIO_BLK_T_CRYPTO_GENERATE_KEY/IMPORT_KEY/PREPARE_KEY/
+ * DERIVE_SW_SECRET commands.
+ */
+struct virtio_blk_crypto_key_blob {
+	__virtio32 key_size;
+	__u8 key[VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE];
+};
+
+/* Must match BLK_CRYPTO_SW_SECRET_SIZE in include/linux/blk-crypto.h */
+#define VIRTIO_BLK_CRYPTO_SW_SECRET_SIZE	32
+
+/* Reply to a VIRTIO_BLK_T_CRYPTO_DERIVE_SW_SECRET request. */
+struct virtio_blk_crypto_sw_secret {
+	__u8 secret[VIRTIO_BLK_CRYPTO_SW_SECRET_SIZE];
+};
+
+/*
+ * Crypto mode numbers used in VIRTIO_BLK_T_GET_CRYPTO_MODES replies and in
+ * indexing struct virtio_blk_crypto_modes.modes[] below. These numbers are
+ * assigned by the virtio spec and are stable: a number is never reused for
+ * a different crypto mode, and additional crypto modes are assigned new,
+ * higher numbers.
+ */
+enum {
+	VIRTIO_BLK_CRYPTO_MODE_INVALID,
+	VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS,
+	__VIRTIO_BLK_CRYPTO_MODE_MAX,	/* sentinel: always one past the last real mode */
+};
+
+/* Highest crypto mode number defined by this version of the header. */
+#define VIRTIO_BLK_CRYPTO_MODE_MAX (__VIRTIO_BLK_CRYPTO_MODE_MAX - 1)
+
+/* Reply to a VIRTIO_BLK_T_GET_CRYPTO_MODES request. */
+struct virtio_blk_crypto_modes {
+	/*
+	 * modes[N], for crypto mode number N <= VIRTIO_BLK_CRYPTO_MODE_MAX, is
+	 * a bitmask of the data unit sizes with which crypto mode N can be
+	 * used: bit i is set if a data unit size of (1 << i) bytes is
+	 * supported. modes[0] is reserved and always 0.
+	 */
+	__virtio32 modes[__VIRTIO_BLK_CRYPTO_MODE_MAX];
+};
+
 /*
  * Supported zoned device models.
  */

31/3 2026/09/14 18:18 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 17536
							output: 34
							thoughts: 2003
						

Thoughts:
**KMSAN Evaluation for Virtio-blk Inline Encryption Patch**

Okay, let's break this down. My goal is to determine if this patch series necessitates a KMSAN fuzzing session, in addition to our standard KASAN efforts. The instructions are clear: if KASAN, LOCKDEP, or other standard tools would catch a bug, we *don't* need KMSAN. We only use KMSAN if the patch introduces risks of uninitialized memory *that are uniquely detectable by KMSAN*.

This patch adds `CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION` and modifies the virtio-blk driver. It introduces new structures for handling an inline encryption feature, specifically `struct virtio_blk_ctrl_vq`, `struct virtio_blk_ctrl_request`, and alters the existing `struct virtblk_req` by introducing a union for the output header. The core of my concern revolves around how these new and modified structures are initialized and whether uninitialized data could leak to userspace.

My initial examination focused on identifying any instances where kernel structures are passed to userspace and could potentially contain uninitialized fields. The patch modifies `virtblk_add_req` and `virtblk_setup_cmd` where `struct virtblk_req`'s `out_hdr` is handled.  The key is the `out_hdr` union. The code calculates `out_hdr_len` based on whether the `crypto_append` part of the union is used.

I drilled down into the initialization of the `out_hdr`. The code explicitly sets some fields of `out_hdr.base` (`ioprio`, `type`, and `sector`).  Crucially, it is then `memset` to zero. After looking at the `union` definition of `out_hdr`, it is clear that `base` and `crypto_append` both occupy the *same* memory, meaning the initial writes to `base` effectively initialize the shared memory.  Subsequent writes to fields of `crypto_append.msg` are also guaranteed to be operating on initialized memory.  I checked both `struct virtio_blk_crypto_msg` and `struct virtio_blk_outhdr` and saw no padding holes in them.

Importantly, based on how the memory is laid out, the key thing is that the `crypto_append.msg` is `memset`'d. Since the relevant structure is `memset` before use, the risk of uninitialized memory leading to a KMSAN problem seems *low*. Let's review the criteria for `NeedsKMSAN=false` -- all stack/heap structures are fully zeroed or initialized before being read or copied. Check. The patch does not appear to introduce any risk of uninitialized memory usage or info-leaks. Check.  I still need to confirm that `vbr` is zeroed on allocation, but if so, I'm confident that KASAN coverage is sufficient.



32/3 2026/09/14 18:19 tool grepper
0m Args:
{
  "Expression": "virtblk_req",
  "PathPrefix": "drivers/block/virtio_blk.c"
}

Results:
{
  "Output": "drivers/block/virtio_blk.c=66=struct virtio_blk {\n--\ndrivers/block/virtio_blk.c-105-\ndrivers/block/virtio_blk.c:106:struct virtblk_req {\ndrivers/block/virtio_blk.c-107-\t/* Out header */\n--\ndrivers/block/virtio_blk.c=177=static inline struct virtio_blk_vq *get_virtio_blk_vq(struct blk_mq_hw_ctx *hctx)\n--\ndrivers/block/virtio_blk.c-184-\ndrivers/block/virtio_blk.c:185:static int virtblk_add_req(struct virtqueue *vq, struct virtblk_req *vbr)\ndrivers/block/virtio_blk.c-186-{\n--\ndrivers/block/virtio_blk.c=211=static int virtblk_setup_discard_write_zeroes_erase(struct request *req, bool unmap)\n--\ndrivers/block/virtio_blk.c-256-\ndrivers/block/virtio_blk.c:257:static void virtblk_unmap_data(struct request *req, struct virtblk_req *vbr)\ndrivers/block/virtio_blk.c-258-{\n--\ndrivers/block/virtio_blk.c=264=static int virtblk_map_data(struct blk_mq_hw_ctx *hctx, struct request *req,\ndrivers/block/virtio_blk.c:265:\t\tstruct virtblk_req *vbr)\ndrivers/block/virtio_blk.c-266-{\n--\ndrivers/block/virtio_blk.c=305=static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,\ndrivers/block/virtio_blk.c-306-\t\t\t\t      struct request *req,\ndrivers/block/virtio_blk.c:307:\t\t\t\t      struct virtblk_req *vbr)\ndrivers/block/virtio_blk.c-308-{\n--\ndrivers/block/virtio_blk.c-414- */\ndrivers/block/virtio_blk.c:415:static inline u8 virtblk_vbr_status(struct virtblk_req *vbr)\ndrivers/block/virtio_blk.c-416-{\n--\ndrivers/block/virtio_blk.c-419-\ndrivers/block/virtio_blk.c:420:static inline void virtblk_request_done(struct request *req)\ndrivers/block/virtio_blk.c-421-{\ndrivers/block/virtio_blk.c:422:\tstruct virtblk_req *vbr = blk_mq_rq_to_pdu(req);\ndrivers/block/virtio_blk.c-423-\tblk_status_t status = virtblk_result(virtblk_vbr_status(vbr));\n--\ndrivers/block/virtio_blk.c=436=static void virtblk_done(struct virtqueue *vq)\n--\ndrivers/block/virtio_blk.c-440-\tint qid = vq-\u003eindex;\ndrivers/block/virtio_blk.c:441:\tstruct virtblk_req *vbr;\ndrivers/block/virtio_blk.c-442-\tunsigned long flags;\n--\ndrivers/block/virtio_blk.c=490=static blk_status_t virtblk_prep_rq(struct blk_mq_hw_ctx *hctx,\n--\ndrivers/block/virtio_blk.c-492-\t\t\t\t\tstruct request *req,\ndrivers/block/virtio_blk.c:493:\t\t\t\t\tstruct virtblk_req *vbr)\ndrivers/block/virtio_blk.c-494-{\n--\ndrivers/block/virtio_blk.c=512=static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,\n--\ndrivers/block/virtio_blk.c-516-\tstruct request *req = bd-\u003erq;\ndrivers/block/virtio_blk.c:517:\tstruct virtblk_req *vbr = blk_mq_rq_to_pdu(req);\ndrivers/block/virtio_blk.c-518-\tunsigned long flags;\n--\ndrivers/block/virtio_blk.c=551=static bool virtblk_prep_rq_batch(struct request *req)\n--\ndrivers/block/virtio_blk.c-553-\tstruct virtio_blk *vblk = req-\u003emq_hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c:554:\tstruct virtblk_req *vbr = blk_mq_rq_to_pdu(req);\ndrivers/block/virtio_blk.c-555-\n--\ndrivers/block/virtio_blk.c=559=static void virtblk_add_req_batch(struct virtio_blk_vq *vq,\n--\ndrivers/block/virtio_blk.c-568-\twhile ((req = rq_list_pop(rqlist))) {\ndrivers/block/virtio_blk.c:569:\t\tstruct virtblk_req *vbr = blk_mq_rq_to_pdu(req);\ndrivers/block/virtio_blk.c-570-\t\tint err;\n--\ndrivers/block/virtio_blk.c=642=static int virtblk_submit_zone_report(struct virtio_blk *vblk,\n--\ndrivers/block/virtio_blk.c-647-\tstruct request *req;\ndrivers/block/virtio_blk.c:648:\tstruct virtblk_req *vbr;\ndrivers/block/virtio_blk.c-649-\tint err;\n--\ndrivers/block/virtio_blk.c=892=static int virtblk_get_id(struct gendisk *disk, char *id_str)\n--\ndrivers/block/virtio_blk.c-896-\tstruct request *req;\ndrivers/block/virtio_blk.c:897:\tstruct virtblk_req *vbr;\ndrivers/block/virtio_blk.c-898-\tint err;\n--\ndrivers/block/virtio_blk.c=1894=static int virtblk_poll(struct blk_mq_hw_ctx *hctx, struct io_comp_batch *iob)\n--\ndrivers/block/virtio_blk.c-1897-\tstruct virtio_blk_vq *vq = get_virtio_blk_vq(hctx);\ndrivers/block/virtio_blk.c:1898:\tstruct virtblk_req *vbr;\ndrivers/block/virtio_blk.c-1899-\tunsigned long flags;\n--\ndrivers/block/virtio_blk.c-1912-\t\t\t\t\t virtblk_complete_batch))\ndrivers/block/virtio_blk.c:1913:\t\t\tvirtblk_request_done(req);\ndrivers/block/virtio_blk.c-1914-\t}\n--\ndrivers/block/virtio_blk.c=1924=static const struct blk_mq_ops virtio_mq_ops = {\n--\ndrivers/block/virtio_blk.c-1927-\t.commit_rqs\t= virtio_commit_rqs,\ndrivers/block/virtio_blk.c:1928:\t.complete\t= virtblk_request_done,\ndrivers/block/virtio_blk.c-1929-\t.map_queues\t= virtblk_map_queues,\n--\ndrivers/block/virtio_blk.c=2128=static int virtblk_probe(struct virtio_device *vdev)\n--\ndrivers/block/virtio_blk.c-2184-\tvblk-\u003etag_set.cmd_size =\ndrivers/block/virtio_blk.c:2185:\t\tsizeof(struct virtblk_req) +\ndrivers/block/virtio_blk.c-2186-\t\tsizeof(struct scatterlist) * VIRTIO_BLK_INLINE_SG_CNT;\n"
}

33/3 2026/09/14 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 22250
							output: 38
							thoughts: 31
						

Thoughts:
Here's my summary, as if I were the one thinking it:

**Diving into the Virtio Block Request Structure**

Alright, time to dig into this. I need to understand how the virtio block requests are being handled, so let's start by zeroing in on the `struct virtblk_req` definition. My initial thought is to navigate to `virtio_blk.c` and specifically examine the code in the vicinity of lines 100 to 140. That region should give me a good overview of the core data structure that governs the block I/O requests within the virtio framework. I expect to find critical information regarding command fields, status reporting, data buffers, and the overall request lifecycle encoded there. Understanding the components of `virtblk_req` is the foundational step for properly debugging and analyzing performance.



34/3 2026/09/14 18:19 tool read-file
0m Args:
{
  "File": "drivers/block/virtio_blk.c",
  "FirstLine": 100,
  "LineCount": 45
}

Results:
{
  "Contents": " 100:\t\tbool crypto_profile_initialized;\n 101:\t\n 102:\t\t/* Control virtqueue state. */\n 103:\t\tstruct virtio_blk_ctrl_vq ctrl_vq;\n 104:\t};\n 105:\t\n 106:\tstruct virtblk_req {\n 107:\t\t/* Out header */\n 108:\t\tunion {\n 109:\t\t\tstruct virtio_blk_outhdr base;\n 110:\t\t\tstruct {\n 111:\t\t\t\tstruct virtio_blk_outhdr base;\n 112:\t\t\t\t/* Crypto message (if VIRTIO_BLK_F_INLINE_ENCRYPTION) */\n 113:\t\t\t\tstruct virtio_blk_crypto_msg msg;\n 114:\t\t\t} crypto_append;\n 115:\t\t} out_hdr;\n 116:\t\n 117:\t\t/* In header */\n 118:\t\tunion {\n 119:\t\t\tu8 status;\n 120:\t\n 121:\t\t\t/*\n 122:\t\t\t * The zone append command has an extended in header.\n 123:\t\t\t * The status field in zone_append_in_hdr must always\n 124:\t\t\t * be the last byte.\n 125:\t\t\t */\n 126:\t\t\tstruct {\n 127:\t\t\t\t__virtio64 sector;\n 128:\t\t\t\tu8 status;\n 129:\t\t\t} zone_append;\n 130:\t\t} in_hdr;\n 131:\t\n 132:\t\tsize_t in_hdr_len;\n 133:\t\n 134:\t\tstruct sg_table sg_table;\n 135:\t\tstruct scatterlist sg[];\n 136:\t};\n 137:\t\n 138:\tstruct virtblk_ctrl_request {\n 139:\t\t/* Type byte, always its own out-sg for every command. */\n 140:\t\t__virtio32 type;\n 141:\t\t/* Out request, sent as a second, separate out-sg if any. */\n 142:\t\tunion {\n 143:\t\t\tstruct virtio_blk_crypto_key_desc key_desc;\n 144:\t\t\tstruct virtio_blk_crypto_key_blob blob;\n"
}

35/3 2026/09/14 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 23095
							output: 39
							thoughts: 50
						

Thoughts:
**Diving into the Virtio Block Code**

Okay, so I need to refresh my memory on how `virtblk_setup_cmd` works, specifically how the `vbr` variable is used within that function. I'm going to start by taking a look at `drivers/block/virtio_blk.c`. I need to zero in on the relevant section, so let's start reading around line 300 and go up to line 415. That should be the core of where I need to focus. I'm hoping to get a clearer picture of the data flow and any potential areas of concern relating to that `vbr` instance. Let's see what we can find.



36/3 2026/09/14 18:19 tool read-file
0m Args:
{
  "File": "drivers/block/virtio_blk.c",
  "FirstLine": 300,
  "LineCount": 100
}

Results:
{
  "Contents": " 300:\t{\n 301:\t\treturn false;\n 302:\t}\n 303:\t#endif\n 304:\t\n 305:\tstatic blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,\n 306:\t\t\t\t\t      struct request *req,\n 307:\t\t\t\t\t      struct virtblk_req *vbr)\n 308:\t{\n 309:\t\tsize_t in_hdr_len = sizeof(vbr-\u003ein_hdr.status);\n 310:\t\tbool unmap = false;\n 311:\t\tu32 type;\n 312:\t\tu64 sector = 0;\n 313:\t\tint i;\n 314:\t\n 315:\t\tif (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) \u0026\u0026 op_is_zone_mgmt(req_op(req)))\n 316:\t\t\treturn BLK_STS_NOTSUPP;\n 317:\t\n 318:\t\t/* Set fields for all request types */\n 319:\t\tvbr-\u003eout_hdr.base.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));\n 320:\t\n 321:\t\tswitch (req_op(req)) {\n 322:\t\tcase REQ_OP_READ:\n 323:\t\t\tif (is_crypto_request(req))\n 324:\t\t\t\ttype = VIRTIO_BLK_T_CRYPTO_IN;\n 325:\t\t\telse\n 326:\t\t\t\ttype = VIRTIO_BLK_T_IN;\n 327:\t\t\tsector = blk_rq_pos(req);\n 328:\t\t\tbreak;\n 329:\t\tcase REQ_OP_WRITE:\n 330:\t\t\tif (is_crypto_request(req))\n 331:\t\t\t\ttype = VIRTIO_BLK_T_CRYPTO_OUT;\n 332:\t\t\telse\n 333:\t\t\t\ttype = VIRTIO_BLK_T_OUT;\n 334:\t\t\tsector = blk_rq_pos(req);\n 335:\t\t\tbreak;\n 336:\t\tcase REQ_OP_FLUSH:\n 337:\t\t\ttype = VIRTIO_BLK_T_FLUSH;\n 338:\t\t\tbreak;\n 339:\t\tcase REQ_OP_DISCARD:\n 340:\t\t\ttype = VIRTIO_BLK_T_DISCARD;\n 341:\t\t\tbreak;\n 342:\t\tcase REQ_OP_WRITE_ZEROES:\n 343:\t\t\ttype = VIRTIO_BLK_T_WRITE_ZEROES;\n 344:\t\t\tunmap = !(req-\u003ecmd_flags \u0026 REQ_NOUNMAP);\n 345:\t\t\tbreak;\n 346:\t\tcase REQ_OP_SECURE_ERASE:\n 347:\t\t\ttype = VIRTIO_BLK_T_SECURE_ERASE;\n 348:\t\t\tbreak;\n 349:\t\tcase REQ_OP_ZONE_OPEN:\n 350:\t\t\ttype = VIRTIO_BLK_T_ZONE_OPEN;\n 351:\t\t\tsector = blk_rq_pos(req);\n 352:\t\t\tbreak;\n 353:\t\tcase REQ_OP_ZONE_CLOSE:\n 354:\t\t\ttype = VIRTIO_BLK_T_ZONE_CLOSE;\n 355:\t\t\tsector = blk_rq_pos(req);\n 356:\t\t\tbreak;\n 357:\t\tcase REQ_OP_ZONE_FINISH:\n 358:\t\t\ttype = VIRTIO_BLK_T_ZONE_FINISH;\n 359:\t\t\tsector = blk_rq_pos(req);\n 360:\t\t\tbreak;\n 361:\t\tcase REQ_OP_ZONE_APPEND:\n 362:\t\t\ttype = VIRTIO_BLK_T_ZONE_APPEND;\n 363:\t\t\tsector = blk_rq_pos(req);\n 364:\t\t\tin_hdr_len = sizeof(vbr-\u003ein_hdr.zone_append);\n 365:\t\t\tbreak;\n 366:\t\tcase REQ_OP_ZONE_RESET:\n 367:\t\t\ttype = VIRTIO_BLK_T_ZONE_RESET;\n 368:\t\t\tsector = blk_rq_pos(req);\n 369:\t\t\tbreak;\n 370:\t\tcase REQ_OP_ZONE_RESET_ALL:\n 371:\t\t\ttype = VIRTIO_BLK_T_ZONE_RESET_ALL;\n 372:\t\t\tbreak;\n 373:\t\tcase REQ_OP_DRV_IN:\n 374:\t\t\t/*\n 375:\t\t\t * Out header has already been prepared by the caller (virtblk_get_id()\n 376:\t\t\t * or virtblk_submit_zone_report()), nothing to do here.\n 377:\t\t\t */\n 378:\t\t\treturn 0;\n 379:\t\tdefault:\n 380:\t\t\tWARN_ON_ONCE(1);\n 381:\t\t\treturn BLK_STS_IOERR;\n 382:\t\t}\n 383:\t\n 384:\t\t/* Set fields for non-REQ_OP_DRV_IN request types */\n 385:\t\tvbr-\u003ein_hdr_len = in_hdr_len;\n 386:\t\tvbr-\u003eout_hdr.base.type = cpu_to_virtio32(vdev, type);\n 387:\t\tvbr-\u003eout_hdr.base.sector = cpu_to_virtio64(vdev, sector);\n 388:\t\n 389:\t\tif (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES ||\n 390:\t\t    type == VIRTIO_BLK_T_SECURE_ERASE) {\n 391:\t\t\tif (virtblk_setup_discard_write_zeroes_erase(req, unmap))\n 392:\t\t\t\treturn BLK_STS_RESOURCE;\n 393:\t\t}\n 394:\t\n 395:\t\tif (type == VIRTIO_BLK_T_CRYPTO_IN || type == VIRTIO_BLK_T_CRYPTO_OUT) {\n 396:\t\t\tmemset(\u0026vbr-\u003eout_hdr.crypto_append.msg, 0,\n 397:\t\t\t\tsizeof(vbr-\u003eout_hdr.crypto_append.msg));\n 398:\t\t\tvbr-\u003eout_hdr.crypto_append.msg.slot =\n 399:\t\t\t\tcpu_to_virtio32(vdev,\n"
}

37/3 2026/09/14 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 25029
							output: 38
							thoughts: 16
						

Thoughts:
Okay, here's the summary, rendered as if I'm the one thinking it:

**Focusing In for Precision**

Alright, I've been reviewing this section and making some preliminary observations. Now, just to be absolutely certain we haven't missed anything subtle, I want to take a closer look at lines 400 through 420. My initial read-through was comprehensive, but this specific range often contains crucial edge cases or subtle dependencies that can easily be overlooked. Given my expertise, I know where the "gotchas" tend to hide, and a focused examination here will ensure we've considered all angles and haven't skipped anything that could later cause a problem. Let's make sure our bases are covered.



38/3 2026/09/14 18:19 tool read-file
0m Args:
{
  "File": "drivers/block/virtio_blk.c",
  "FirstLine": 398,
  "LineCount": 30
}

Results:
{
  "Contents": " 398:\t\t\tvbr-\u003eout_hdr.crypto_append.msg.slot =\n 399:\t\t\t\tcpu_to_virtio32(vdev,\n 400:\t\t\t\t\tblk_crypto_keyslot_index(req-\u003ecrypt_keyslot));\n 401:\t\t\tfor (i = 0; i \u003c ARRAY_SIZE(vbr-\u003eout_hdr.crypto_append.msg.dun); i++) {\n 402:\t\t\t\tvbr-\u003eout_hdr.crypto_append.msg.dun[i] =\n 403:\t\t\t\t\tcpu_to_virtio64(vdev, req-\u003ecrypt_ctx-\u003ebc_dun[i]);\n 404:\t\t\t}\n 405:\t\t}\n 406:\t\n 407:\t\treturn 0;\n 408:\t}\n 409:\t\n 410:\t/*\n 411:\t * The status byte is always the last byte of the virtblk request\n 412:\t * in-header. This helper fetches its value for all in-header formats\n 413:\t * that are currently defined.\n 414:\t */\n 415:\tstatic inline u8 virtblk_vbr_status(struct virtblk_req *vbr)\n 416:\t{\n 417:\t\treturn *((u8 *)\u0026vbr-\u003ein_hdr + vbr-\u003ein_hdr_len - 1);\n 418:\t}\n 419:\t\n 420:\tstatic inline void virtblk_request_done(struct request *req)\n 421:\t{\n 422:\t\tstruct virtblk_req *vbr = blk_mq_rq_to_pdu(req);\n 423:\t\tblk_status_t status = virtblk_result(virtblk_vbr_status(vbr));\n 424:\t\tstruct virtio_blk *vblk = req-\u003emq_hctx-\u003equeue-\u003equeuedata;\n 425:\t\n 426:\t\tvirtblk_unmap_data(req, vbr);\n 427:\t\tvirtblk_cleanup_cmd(req);\n"
}

39/3 2026/09/14 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 25775
							output: 38
							thoughts: 22
						

Thoughts:
Here's my attempt at summarizing the thought process, tailored for an expert audience and written in the first person:

**Diving into `virtblk_prep_rq` for `vbr` Preparation**

Okay, so I need to understand the data flow for `virtblk`, specifically how the `vbr` structure is built within a request. I'm going to jump right into the `virtblk_prep_rq` function. I want to see precisely where and how the `vbr` fields are initialized and populated before the request hits the driver. The nuances of this initialization have implications for how efficient the whole process is, so I'll be looking for optimizations or potential bottlenecks. I'll need to pay close attention to the interaction between the request flags and how these translate into `vbr` parameters. Anything that involves copy-on-write or caching is critical to examine. My focus is on data consistency and minimizing overhead. Let's see how they handle that.



40/3 2026/09/14 18:19 tool read-file
0m Args:
{
  "File": "drivers/block/virtio_blk.c",
  "FirstLine": 485,
  "LineCount": 40
}

Results:
{
  "Contents": " 485:\t\tdefault:\n 486:\t\t\treturn BLK_STS_IOERR;\n 487:\t\t}\n 488:\t}\n 489:\t\n 490:\tstatic blk_status_t virtblk_prep_rq(struct blk_mq_hw_ctx *hctx,\n 491:\t\t\t\t\t\tstruct virtio_blk *vblk,\n 492:\t\t\t\t\t\tstruct request *req,\n 493:\t\t\t\t\t\tstruct virtblk_req *vbr)\n 494:\t{\n 495:\t\tblk_status_t status;\n 496:\t\tint num;\n 497:\t\n 498:\t\tstatus = virtblk_setup_cmd(vblk-\u003evdev, req, vbr);\n 499:\t\tif (unlikely(status))\n 500:\t\t\treturn status;\n 501:\t\n 502:\t\tnum = virtblk_map_data(hctx, req, vbr);\n 503:\t\tif (unlikely(num \u003c 0))\n 504:\t\t\treturn virtblk_fail_to_queue(req, -ENOMEM);\n 505:\t\tvbr-\u003esg_table.nents = num;\n 506:\t\n 507:\t\tblk_mq_start_request(req);\n 508:\t\n 509:\t\treturn BLK_STS_OK;\n 510:\t}\n 511:\t\n 512:\tstatic blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,\n 513:\t\t\t\t   const struct blk_mq_queue_data *bd)\n 514:\t{\n 515:\t\tstruct virtio_blk *vblk = hctx-\u003equeue-\u003equeuedata;\n 516:\t\tstruct request *req = bd-\u003erq;\n 517:\t\tstruct virtblk_req *vbr = blk_mq_rq_to_pdu(req);\n 518:\t\tunsigned long flags;\n 519:\t\tint qid = hctx-\u003equeue_num;\n 520:\t\tbool notify = false;\n 521:\t\tblk_status_t status;\n 522:\t\tint err;\n 523:\t\n 524:\t\tstatus = virtblk_prep_rq(hctx, vblk, req, vbr);\n"
}

41/3 2026/09/14 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 26653
							output: 27
							thoughts: 3136
						

Thoughts:
**Analysis of Virtio-blk Crypto Control Requests and Initialization**

Alright, let's break down this code. My primary concern is data initialization and potential uninitialized reads, especially given the interactions with a virtualized environment where the hypervisor (QEMU, etc.) acts as the virtual device. I see a pattern here: the primary mechanism for allocating the `creq` structure is using `kzalloc_obj`, which is fantastic, as it guarantees that all the memory allocated for the control request structures is zeroed out before use. This seems like a consistent and reliable approach to avoid uninitialized memory problems.

My initial question about the virtio device writing into `in_resp` is somewhat moot, at least with the existing `kzalloc_obj` calls. KMSAN might flag issues with the virtio DMA or virtqueue handling, but the memory is already clean.

I've checked the control request functions one by one: `virtblk_get_crypto_modes`, `set_virtblk_crypto_key_desc`, `virtblk_crypto_keyslot_program`, `virtblk_crypto_keyslot_evict`, `virtblk_crypto_derive_sw_secret`, `virtblk_crypto_generate_key`, `virtblk_crypto_prepare_key`, and `virtblk_crypto_import_key`. Each one relies on `kzalloc_obj` to initialize the `creq` struct, and the subsequent data copies are all from known initialized sources or to explicitly-set fields within the struct. This confirms my expectation that memory is properly initialized before any read operations.

`virtblk_init_crypto` shows stack variables initialized. The `vblk->profile` structure also initialized. I'm focusing on control requests and interaction with the hypervisor, so this is critical.

The initialization of the virtqueues in `init_vq` reveals a potential sizing discrepancy with the allocation of `vblk->vqs` (using `kmalloc_objs` with `num_vqs`) versus the temporary arrays `vqs_info` and `vqs` (sized with `total_vqs`, which includes a control queue if present). However, it looks like `vblk->vqs` is sized correctly, and the extra control queue is stored in `vblk->ctrl_vq.vq`, and all relevant control queue fields are initialized (`dead` and `inflight`).

My final check involves searching how the `blk_crypto_ll_ops` functions are called. I need to be absolutely certain that there's no data leakage or unexpected user-space interaction where uninitialized memory could become a problem. I'll search the kernel's block layer for the relevant `profile->ll_ops` function calls to find all of the code paths where these functions are called, and look for how data is passed to those functions. I'll look for `derive_sw_secret`, `generate_key` calls in the `block/` directory to locate any interactions with user space.



42/3 2026/09/14 18:19 tool grepper
0m Args:
{
  "Expression": "derive_sw_secret",
  "PathPrefix": "block/"
}

Results:
{
  "Output": "block/blk-crypto-profile.c=443=EXPORT_SYMBOL_GPL(blk_crypto_register);\n--\nblock/blk-crypto-profile.c-445-/**\nblock/blk-crypto-profile.c:446: * blk_crypto_derive_sw_secret() - Derive software secret from wrapped key\nblock/blk-crypto-profile.c-447- * @bdev: a block device that supports hardware-wrapped keys\n--\nblock/blk-crypto-profile.c-461- */\nblock/blk-crypto-profile.c:462:int blk_crypto_derive_sw_secret(struct block_device *bdev,\nblock/blk-crypto-profile.c-463-\t\t\t\tconst u8 *eph_key, size_t eph_key_size,\n--\nblock/blk-crypto-profile.c-473-\t\treturn -EOPNOTSUPP;\nblock/blk-crypto-profile.c:474:\tif (!profile-\u003ell_ops.derive_sw_secret)\nblock/blk-crypto-profile.c-475-\t\treturn -EOPNOTSUPP;\nblock/blk-crypto-profile.c-476-\tblk_crypto_hw_enter(profile);\nblock/blk-crypto-profile.c:477:\terr = profile-\u003ell_ops.derive_sw_secret(profile, eph_key, eph_key_size,\nblock/blk-crypto-profile.c-478-\t\t\t\t\t       sw_secret);\n--\nblock/blk-crypto-profile.c-481-}\nblock/blk-crypto-profile.c:482:EXPORT_SYMBOL_GPL(blk_crypto_derive_sw_secret);\nblock/blk-crypto-profile.c-483-\n"
}

43/3 2026/09/14 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 30893
							output: 21
							thoughts: 27
						

Thoughts:
Okay, here's my attempt at summarizing those thoughts from an expert's perspective, using a first-person approach:

**Debugging Crypto Key Derivation & Generation**

Alright, time to get to the bottom of this. I need to understand the call flow here. Specifically, I'm interested in how `blk_crypto_derive_sw_secret` and `blk_crypto_generate_key` are being used. What modules, drivers, or higher-level functions are actually *calling* these routines?  That's where the investigation starts. I need to see the context – what's the preceding logic, what data is being passed in, what's the intended purpose of the key derivation and generation in the bigger picture. Are we talking about disk encryption, secure boot, key management, something else entirely? Understanding the calling context is absolutely critical to grasping the impact of the crypto operations. From there I'll be able to tell if anything unexpected is happening.



44/3 2026/09/14 18:19 tool grepper
0m Args:
{
  "Expression": "blk_crypto_generate_key"
}

Results:
{
  "Output": "Documentation/block/inline-encryption.rst=494=provide a generic interface to creating and preparing such keys:\n--\nDocumentation/block/inline-encryption.rst-508-  hardware generate the key instead of importing one.  It takes in a pointer to\nDocumentation/block/inline-encryption.rst:509:  a ``struct blk_crypto_generate_key_arg``.\nDocumentation/block/inline-encryption.rst-510-\n--\nblock/blk-crypto-profile.c=502=EXPORT_SYMBOL_GPL(blk_crypto_import_key);\nblock/blk-crypto-profile.c-503-\nblock/blk-crypto-profile.c:504:int blk_crypto_generate_key(struct blk_crypto_profile *profile,\nblock/blk-crypto-profile.c-505-\t\t\t    u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])\n--\nblock/blk-crypto-profile.c-519-}\nblock/blk-crypto-profile.c:520:EXPORT_SYMBOL_GPL(blk_crypto_generate_key);\nblock/blk-crypto-profile.c-521-\n--\nblock/blk-crypto.c=501=static int blk_crypto_ioctl_generate_key(struct blk_crypto_profile *profile,\n--\nblock/blk-crypto.c-503-{\nblock/blk-crypto.c:504:\tstruct blk_crypto_generate_key_arg arg;\nblock/blk-crypto.c-505-\tu8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];\n--\nblock/blk-crypto.c-513-\nblock/blk-crypto.c:514:\tret = blk_crypto_generate_key(profile, lt_key);\nblock/blk-crypto.c-515-\tif (ret \u003c 0)\n--\ndrivers/block/virtio_blk.c=1240=static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1283-\ndrivers/block/virtio_blk.c:1284:static int virtblk_crypto_generate_key(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c-1285-\t\t\t\t\tu8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])\n--\ndrivers/block/virtio_blk.c=1435=static const struct blk_crypto_ll_ops virtblk_crypto_ops = {\n--\ndrivers/block/virtio_blk.c-1438-\t.derive_sw_secret\t= virtblk_crypto_derive_sw_secret,\ndrivers/block/virtio_blk.c:1439:\t.generate_key\t\t= virtblk_crypto_generate_key,\ndrivers/block/virtio_blk.c-1440-\t.prepare_key\t\t= virtblk_crypto_prepare_key,\n--\ndrivers/md/dm-table.c=1215=static int dm_wrappedkey_op_callback(struct dm_target *ti, struct dm_dev *dev,\n--\ndrivers/md/dm-table.c-1238-\tcase GENERATE_KEY:\ndrivers/md/dm-table.c:1239:\t\terr = blk_crypto_generate_key(profile,\ndrivers/md/dm-table.c-1240-\t\t\t\t\t      args-\u003egenerate_key.lt_key);\n--\ninclude/linux/blk-crypto-profile.h=208=int blk_crypto_import_key(struct blk_crypto_profile *profile,\n--\ninclude/linux/blk-crypto-profile.h-211-\ninclude/linux/blk-crypto-profile.h:212:int blk_crypto_generate_key(struct blk_crypto_profile *profile,\ninclude/linux/blk-crypto-profile.h-213-\t\t\t    u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);\n--\ninclude/uapi/linux/blk-crypto.h=8=struct blk_crypto_import_key_arg {\n--\ninclude/uapi/linux/blk-crypto.h-17-\ninclude/uapi/linux/blk-crypto.h:18:struct blk_crypto_generate_key_arg {\ninclude/uapi/linux/blk-crypto.h-19-\t/* Long-term wrapped key blob (output) */\n--\ninclude/uapi/linux/blk-crypto.h=25=struct blk_crypto_prepare_key_arg {\n--\ninclude/uapi/linux/blk-crypto.h-40-#define BLKCRYPTOIMPORTKEY _IOWR(0x12, 137, struct blk_crypto_import_key_arg)\ninclude/uapi/linux/blk-crypto.h:41:#define BLKCRYPTOGENERATEKEY _IOWR(0x12, 138, struct blk_crypto_generate_key_arg)\ninclude/uapi/linux/blk-crypto.h-42-#define BLKCRYPTOPREPAREKEY _IOWR(0x12, 139, struct blk_crypto_prepare_key_arg)\n"
}

45/3 2026/09/14 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 32466
							output: 35
							thoughts: 26
						

Thoughts:
Here's my take on the thought process, as requested:

**Diving into `blk-crypto.c` and `blk_crypto_ioctl_generate_key`**

Okay, time to dig into the guts of the block layer crypto implementation. I want to understand how the key generation mechanism works, so I'm focusing in on `block/blk-crypto.c`. Specifically, I'm zeroing in on the `blk_crypto_ioctl_generate_key` function. This feels like the logical place to start – it's likely where the magic happens when a user, through an `ioctl`, requests a new key for encryption or decryption within a block device context. I'm keen to see how they've handled security, key derivation, any potential weak points, and the overall interaction with the underlying crypto primitives. Let's see what's actually going on in there!



46/3 2026/09/14 18:19 tool read-file
0m Args:
{
  "File": "block/blk-crypto.c",
  "FirstLine": 500,
  "LineCount": 50
}

Results:
{
  "Contents": " 500:\t\n 501:\tstatic int blk_crypto_ioctl_generate_key(struct blk_crypto_profile *profile,\n 502:\t\t\t\t\t\t void __user *argp)\n 503:\t{\n 504:\t\tstruct blk_crypto_generate_key_arg arg;\n 505:\t\tu8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];\n 506:\t\tint ret;\n 507:\t\n 508:\t\tif (copy_from_user(\u0026arg, argp, sizeof(arg)))\n 509:\t\t\treturn -EFAULT;\n 510:\t\n 511:\t\tif (memchr_inv(arg.reserved, 0, sizeof(arg.reserved)))\n 512:\t\t\treturn -EINVAL;\n 513:\t\n 514:\t\tret = blk_crypto_generate_key(profile, lt_key);\n 515:\t\tif (ret \u003c 0)\n 516:\t\t\tgoto out;\n 517:\t\tif (ret \u003e arg.lt_key_size) {\n 518:\t\t\tret = -EOVERFLOW;\n 519:\t\t\tgoto out;\n 520:\t\t}\n 521:\t\targ.lt_key_size = ret;\n 522:\t\tif (copy_to_user(u64_to_user_ptr(arg.lt_key_ptr), lt_key,\n 523:\t\t\t\t arg.lt_key_size) ||\n 524:\t\t    copy_to_user(argp, \u0026arg, sizeof(arg))) {\n 525:\t\t\tret = -EFAULT;\n 526:\t\t\tgoto out;\n 527:\t\t}\n 528:\t\tret = 0;\n 529:\t\n 530:\tout:\n 531:\t\tmemzero_explicit(lt_key, sizeof(lt_key));\n 532:\t\treturn ret;\n 533:\t}\n 534:\t\n 535:\tstatic int blk_crypto_ioctl_prepare_key(struct blk_crypto_profile *profile,\n 536:\t\t\t\t\t\tvoid __user *argp)\n 537:\t{\n 538:\t\tstruct blk_crypto_prepare_key_arg arg;\n 539:\t\tu8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];\n 540:\t\tu8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];\n 541:\t\tint ret;\n 542:\t\n 543:\t\tif (copy_from_user(\u0026arg, argp, sizeof(arg)))\n 544:\t\t\treturn -EFAULT;\n 545:\t\n 546:\t\tif (memchr_inv(arg.reserved, 0, sizeof(arg.reserved)))\n 547:\t\t\treturn -EINVAL;\n 548:\t\n 549:\t\tif (arg.lt_key_size \u003e sizeof(lt_key))\n"
}

47/3 2026/09/14 18:19 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 33503
							output: 35
							thoughts: 873
						

Thoughts:
**Analyzing the Key Generation Process**

Okay, so I'm looking at how this key generation is handled, specifically within the `virtio_blk` driver and the `blk_crypto_ioctl_generate_key` function. The core of it seems to be in `virtblk_crypto_generate_key`. The `blk_crypto_ioctl_generate_key` function calls `blk_crypto_generate_key` to actually do the work.

Inside `virtblk_crypto_generate_key`, the code allocates a `virtblk_ctrl_request` structure, `creq`, using `kzalloc_obj`. This is important because it means everything within `creq`, including `creq->in_resp.blob`, is initialized to zero. That's a solid start against potential vulnerabilities.

The function then constructs scatter-gather lists to send a request to the virtio block device. The device presumably generates the key, and returns the key size and the key itself within the `creq->in_resp.blob` structure. After the device responds, the function retrieves the key size and checks if it's within acceptable bounds. It also performs a validity check of the key size.

The crucial part is the `memcpy(lt_key, creq->in_resp.blob.key, key_size);` line. This copies the generated key from the `creq` buffer into the `lt_key` buffer. Since `creq` was zero-initialized and `key_size` is validated, I'm confident that we are not copying potentially uninitialized data. Specifically, `creq->in_resp.blob.key` will only contain `key_size` meaningful bytes. And `key_size` is the value passed to `memcpy` and is also validated.

Now, let's consider the user-space interaction. The size of the key, which is `key_size`, is copied to user space, so that is fine. However, in `blk_crypto_ioctl_generate_key`, the key size is copied *from* user space into the `arg` struct via `copy_from_user` and then copied back via `copy_to_user`. So I'm curious what happens if user space specifies a bogus value. The checks in `virtblk_crypto_generate_key` will still keep us safe.

Overall, given the zero-initialization of the `creq` struct and the validation of `key_size` before the `memcpy`, I don't see any obvious issues with uninitialized memory being copied to user space in this key generation process.



48/3 2026/09/14 18:20 tool read-file
0m Args:
{
  "File": "block/blk-crypto.c",
  "FirstLine": 540,
  "LineCount": 45
}

Results:
{
  "Contents": " 540:\t\tu8 eph_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];\n 541:\t\tint ret;\n 542:\t\n 543:\t\tif (copy_from_user(\u0026arg, argp, sizeof(arg)))\n 544:\t\t\treturn -EFAULT;\n 545:\t\n 546:\t\tif (memchr_inv(arg.reserved, 0, sizeof(arg.reserved)))\n 547:\t\t\treturn -EINVAL;\n 548:\t\n 549:\t\tif (arg.lt_key_size \u003e sizeof(lt_key))\n 550:\t\t\treturn -EINVAL;\n 551:\t\n 552:\t\tif (copy_from_user(lt_key, u64_to_user_ptr(arg.lt_key_ptr),\n 553:\t\t\t\t   arg.lt_key_size)) {\n 554:\t\t\tret = -EFAULT;\n 555:\t\t\tgoto out;\n 556:\t\t}\n 557:\t\tret = blk_crypto_prepare_key(profile, lt_key, arg.lt_key_size, eph_key);\n 558:\t\tif (ret \u003c 0)\n 559:\t\t\tgoto out;\n 560:\t\tif (ret \u003e arg.eph_key_size) {\n 561:\t\t\tret = -EOVERFLOW;\n 562:\t\t\tgoto out;\n 563:\t\t}\n 564:\t\targ.eph_key_size = ret;\n 565:\t\tif (copy_to_user(u64_to_user_ptr(arg.eph_key_ptr), eph_key,\n 566:\t\t\t\t arg.eph_key_size) ||\n 567:\t\t    copy_to_user(argp, \u0026arg, sizeof(arg))) {\n 568:\t\t\tret = -EFAULT;\n 569:\t\t\tgoto out;\n 570:\t\t}\n 571:\t\tret = 0;\n 572:\t\n 573:\tout:\n 574:\t\tmemzero_explicit(lt_key, sizeof(lt_key));\n 575:\t\tmemzero_explicit(eph_key, sizeof(eph_key));\n 576:\t\treturn ret;\n 577:\t}\n 578:\t\n 579:\tint blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd,\n 580:\t\t\t     void __user *argp)\n 581:\t{\n 582:\t\tstruct blk_crypto_profile *profile =\n 583:\t\t\tbdev_get_queue(bdev)-\u003ecrypto_profile;\n 584:\t\n"
}

49/3 2026/09/14 18:20 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 35677
							output: 35
							thoughts: 18
						

Thoughts:
Here's my take on those thoughts, tailored for an expert audience:

**Diving Deeper into Key Import**

Okay, so we're looking at key management, which means we have to consider all the angles. We just looked at [context of previous information], but I'm not satisfied. We can't afford to miss anything. The security implications of this are too high. To ensure comprehensive coverage, I need to check `blk_crypto_ioctl_import_key` as well. This system call is a prime candidate for potential vulnerabilities related to key import. I need to understand exactly how it handles key data, validation, and any interactions with the underlying crypto hardware or modules. We should make sure we check all of the edge cases. Gotta make sure we cover the basics. Let's see what we can find.



50/3 2026/09/14 18:20 tool read-file
0m Args:
{
  "File": "block/blk-crypto.c",
  "FirstLine": 460,
  "LineCount": 45
}

Results:
{
  "Contents": " 460:\t\tstruct blk_crypto_import_key_arg arg;\n 461:\t\tu8 raw_key[BLK_CRYPTO_MAX_RAW_KEY_SIZE];\n 462:\t\tu8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE];\n 463:\t\tint ret;\n 464:\t\n 465:\t\tif (copy_from_user(\u0026arg, argp, sizeof(arg)))\n 466:\t\t\treturn -EFAULT;\n 467:\t\n 468:\t\tif (memchr_inv(arg.reserved, 0, sizeof(arg.reserved)))\n 469:\t\t\treturn -EINVAL;\n 470:\t\n 471:\t\tif (arg.raw_key_size \u003c 16 || arg.raw_key_size \u003e sizeof(raw_key))\n 472:\t\t\treturn -EINVAL;\n 473:\t\n 474:\t\tif (copy_from_user(raw_key, u64_to_user_ptr(arg.raw_key_ptr),\n 475:\t\t\t\t   arg.raw_key_size)) {\n 476:\t\t\tret = -EFAULT;\n 477:\t\t\tgoto out;\n 478:\t\t}\n 479:\t\tret = blk_crypto_import_key(profile, raw_key, arg.raw_key_size, lt_key);\n 480:\t\tif (ret \u003c 0)\n 481:\t\t\tgoto out;\n 482:\t\tif (ret \u003e arg.lt_key_size) {\n 483:\t\t\tret = -EOVERFLOW;\n 484:\t\t\tgoto out;\n 485:\t\t}\n 486:\t\targ.lt_key_size = ret;\n 487:\t\tif (copy_to_user(u64_to_user_ptr(arg.lt_key_ptr), lt_key,\n 488:\t\t\t\t arg.lt_key_size) ||\n 489:\t\t    copy_to_user(argp, \u0026arg, sizeof(arg))) {\n 490:\t\t\tret = -EFAULT;\n 491:\t\t\tgoto out;\n 492:\t\t}\n 493:\t\tret = 0;\n 494:\t\n 495:\tout:\n 496:\t\tmemzero_explicit(raw_key, sizeof(raw_key));\n 497:\t\tmemzero_explicit(lt_key, sizeof(lt_key));\n 498:\t\treturn ret;\n 499:\t}\n 500:\t\n 501:\tstatic int blk_crypto_ioctl_generate_key(struct blk_crypto_profile *profile,\n 502:\t\t\t\t\t\t void __user *argp)\n 503:\t{\n 504:\t\tstruct blk_crypto_generate_key_arg arg;\n"
}

51/3 2026/09/14 18:20 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 36644
							output: 23
							thoughts: 46
						

Thoughts:
Okay, let's take a look at this `derive_sw_secret` function. I should probably investigate that as well. Hmm, the question that immediately pops into my head is: who is calling `blk_crypto_derive_sw_secret`? I need to understand the call graph to fully grasp its role and implications. Time to search the codebase for every instance of that function name. Let's start the hunt for `blk_crypto_derive_sw_secret`. I'm hoping to quickly identify the upstream callers and their context within the overall cryptographic workflow.



52/3 2026/09/14 18:20 tool grepper
0m Args:
{
  "Expression": "blk_crypto_derive_sw_secret"
}

Results:
{
  "Output": "Documentation/block/inline-encryption.rst=467=when hardware support is available.  This works in the following way:\n--\nDocumentation/block/inline-encryption.rst-480-  that support hardware-wrapped keys must implement this method.  Users of\nDocumentation/block/inline-encryption.rst:481:  blk-crypto can call ``blk_crypto_derive_sw_secret()`` to access this method.\nDocumentation/block/inline-encryption.rst-482-\n--\nblock/blk-crypto-profile.c=443=EXPORT_SYMBOL_GPL(blk_crypto_register);\n--\nblock/blk-crypto-profile.c-445-/**\nblock/blk-crypto-profile.c:446: * blk_crypto_derive_sw_secret() - Derive software secret from wrapped key\nblock/blk-crypto-profile.c-447- * @bdev: a block device that supports hardware-wrapped keys\n--\nblock/blk-crypto-profile.c-461- */\nblock/blk-crypto-profile.c:462:int blk_crypto_derive_sw_secret(struct block_device *bdev,\nblock/blk-crypto-profile.c-463-\t\t\t\tconst u8 *eph_key, size_t eph_key_size,\n--\nblock/blk-crypto-profile.c-481-}\nblock/blk-crypto-profile.c:482:EXPORT_SYMBOL_GPL(blk_crypto_derive_sw_secret);\nblock/blk-crypto-profile.c-483-\n--\ndrivers/block/virtio_blk.c=1204=static int virtblk_crypto_keyslot_evict(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1239-\ndrivers/block/virtio_blk.c:1240:static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c-1241-\t\t\t\t\t    const u8 *eph_key, size_t eph_key_size,\n--\ndrivers/block/virtio_blk.c=1435=static const struct blk_crypto_ll_ops virtblk_crypto_ops = {\n--\ndrivers/block/virtio_blk.c-1437-\t.keyslot_evict\t\t= virtblk_crypto_keyslot_evict,\ndrivers/block/virtio_blk.c:1438:\t.derive_sw_secret\t= virtblk_crypto_derive_sw_secret,\ndrivers/block/virtio_blk.c-1439-\t.generate_key\t\t= virtblk_crypto_generate_key,\n--\ndrivers/md/dm-table.c=1215=static int dm_wrappedkey_op_callback(struct dm_target *ti, struct dm_dev *dev,\n--\ndrivers/md/dm-table.c-1225-\tcase DERIVE_SW_SECRET:\ndrivers/md/dm-table.c:1226:\t\terr = blk_crypto_derive_sw_secret(\ndrivers/md/dm-table.c-1227-\t\t\t\t\tbdev,\n--\nfs/crypto/block.c=174=int fscrypt_derive_sw_secret(struct super_block *sb,\n--\nfs/crypto/block.c-187-\nfs/crypto/block.c:188:\terr = blk_crypto_derive_sw_secret(sb-\u003es_bdev, wrapped_key,\nfs/crypto/block.c-189-\t\t\t\t\t  wrapped_key_size, sw_secret);\n--\ninclude/linux/blk-crypto.h=172=bool blk_crypto_config_supported_natively(struct block_device *bdev,\n--\ninclude/linux/blk-crypto.h-174-\ninclude/linux/blk-crypto.h:175:int blk_crypto_derive_sw_secret(struct block_device *bdev,\ninclude/linux/blk-crypto.h-176-\t\t\t\tconst u8 *eph_key, size_t eph_key_size,\n"
}

53/3 2026/09/14 18:20 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 37861
							output: 17
							thoughts: 2122
						

Thoughts:
**Uninitialized Memory Analysis of fscrypt Patch**

Okay, so I'm diving into this `fscrypt` patch, specifically focusing on potential uninitialized memory issues. The core function here is `fscrypt_derive_sw_secret`, which is used internally by `fscrypt`. My main concern is ensuring that the patch doesn't introduce any vulnerabilities related to reading uninitialized data.

First, I'm looking at the `struct virtblk_req` and its `out_hdr` union. This is where the magic happens, specifically when dealing with crypto requests (`VIRTIO_BLK_F_INLINE_ENCRYPTION`).  I see this union:
```c
union {
    struct virtio_blk_outhdr base;
    struct {
        struct virtio_blk_outhdr base;
        struct virtio_blk_crypto_msg msg;
    } crypto_append;
} out_hdr;
```
It looks like we have a union here, `out_hdr`. If we *don't* have a crypto request, we use `base`, which is a standard `virtio_blk_outhdr`. If it *is* a crypto request, the `crypto_append` member is used and we will need to zero the `msg` part. This makes sense.

In `virtblk_setup_cmd`, when dealing with `VIRTIO_BLK_T_CRYPTO_IN` or `VIRTIO_BLK_T_CRYPTO_OUT`, I noticed a `memset` on `vbr->out_hdr.crypto_append.msg`. This is good, ensuring that `msg` is zeroed out before we write to it.  Then we populate the `slot` and `dun` fields of `msg`. The question is, are all members of `vbr->out_hdr.crypto_append` properly initialized?

Given that `out_hdr` is a union, both `vbr->out_hdr.base` and `vbr->out_hdr.crypto_append.base` point to the same memory location. The offsets of `ioprio`, `type`, and `sector` in `vbr->out_hdr.base` are 4, 0, and 8 bytes respectively. The `msg` part of `crypto_append` starts at offset 16 (the size of `struct virtio_blk_outhdr`). After reviewing the code that initializes the out header, I am satisfied that all 56 bytes are fully initialized when crypto is enabled.

In `virtblk_add_req`, the `out_hdr_len` is determined by whether it's a crypto request or not. If crypto, the length is set to `sizeof(vbr->out_hdr.crypto_append)` which is 56 bytes. If not crypto, the length is `sizeof(vbr->out_hdr.base)` which is 16 bytes.  Then `sg_init_one` is called. It looks like the correct number of bytes are being sent to the virtqueue.

Alright, but what about non-crypto requests? I need to check how the out header is populated for those. Looking at `virtblk_setup_cmd`, I see the code first sets `ioprio` and then sets `type` and `sector`. The crucial thing here is that even before the patch, the code set `ioprio`, `type`, and `sector`. So, no change here, which is good.

Next, I need to investigate the special case, `REQ_OP_DRV_IN`. The comment states that the out header has already been prepared by the caller in `virtblk_get_id` or `virtblk_submit_zone_report`. Let's see how those functions initialize the `out_hdr`: In both functions, `type` and `sector` are initialized. And since `virtblk_setup_cmd` is always called before this, then `ioprio` will be initialized before those calls.

So, it appears that the `ioprio` field will always be initialized before we send it to the virtqueue, no matter the request type.

Finally, I want to investigate `req->crypt_ctx->bc_dun`. This is the `dun` member that is populated in the crypto case. I need to make sure that the `bc_dun` array is properly initialized. I'll need to look at the `struct blk_crypto_ctx` definition to confirm its structure and how this field is handled upstream. It is likely initialized elsewhere, and this patch just uses the pre-existing, correctly initialized data.



54/3 2026/09/14 18:20 tool grepper
0m Args:
{
  "Expression": "bc_dun"
}

Results:
{
  "Output": "block/blk-crypto-fallback.c=242=static void __blk_crypto_fallback_encrypt_bio(struct bio *src_bio,\n--\nblock/blk-crypto-fallback.c-259-\nblock/blk-crypto-fallback.c:260:\tmemcpy(curr_dun, bc-\u003ebc_dun, sizeof(curr_dun));\nblock/blk-crypto-fallback.c-261-\tsg_init_table(\u0026src, 1);\n--\nblock/blk-crypto-fallback.c=373=static blk_status_t __blk_crypto_fallback_decrypt_bio(struct bio *bio,\n--\nblock/blk-crypto-fallback.c-388-\nblock/blk-crypto-fallback.c:389:\tmemcpy(curr_dun, bc-\u003ebc_dun, sizeof(curr_dun));\nblock/blk-crypto-fallback.c-390-\tsg_init_table(\u0026sg, 1);\n--\nblock/blk-crypto-internal.h=166=static inline void bio_crypt_do_front_merge(struct request *rq,\n--\nblock/blk-crypto-internal.h-170-\tif (bio_has_crypt_ctx(bio))\nblock/blk-crypto-internal.h:171:\t\tmemcpy(rq-\u003ecrypt_ctx-\u003ebc_dun, bio-\u003ebi_crypt_context-\u003ebc_dun,\nblock/blk-crypto-internal.h:172:\t\t       sizeof(rq-\u003ecrypt_ctx-\u003ebc_dun));\nblock/blk-crypto-internal.h-173-#endif\n--\nblock/blk-crypto.c=101=void bio_crypt_set_ctx(struct bio *bio, const struct blk_crypto_key *key,\n--\nblock/blk-crypto.c-114-\tbc-\u003ebc_key = key;\nblock/blk-crypto.c:115:\tmemcpy(bc-\u003ebc_dun, dun, sizeof(bc-\u003ebc_dun));\nblock/blk-crypto.c-116-\n--\nblock/blk-crypto.c=155=void __bio_crypt_advance(struct bio *bio, unsigned int bytes)\n--\nblock/blk-crypto.c-158-\nblock/blk-crypto.c:159:\tbio_crypt_dun_increment(bc-\u003ebc_dun,\nblock/blk-crypto.c-160-\t\t\t\tbytes \u003e\u003e bc-\u003ebc_key-\u003edata_unit_size_bits);\n--\nblock/blk-crypto.c-163-/*\nblock/blk-crypto.c:164: * Returns true if @bc-\u003ebc_dun plus @bytes converted to data units is equal to\nblock/blk-crypto.c-165- * @next_dun, treating the DUNs as multi-limb integers.\n--\nblock/blk-crypto.c=167=bool bio_crypt_dun_is_contiguous(const struct bio_crypt_ctx *bc,\n--\nblock/blk-crypto.c-174-\tfor (i = 0; i \u003c BLK_CRYPTO_DUN_ARRAY_SIZE; i++) {\nblock/blk-crypto.c:175:\t\tif (bc-\u003ebc_dun[i] + carry != next_dun[i])\nblock/blk-crypto.c-176-\t\t\treturn false;\n--\nblock/blk-crypto.c-180-\t\t */\nblock/blk-crypto.c:181:\t\tif ((bc-\u003ebc_dun[i] + carry) \u003c carry)\nblock/blk-crypto.c-182-\t\t\tcarry = 1;\n--\nblock/blk-crypto.c=214=bool bio_crypt_ctx_mergeable(struct bio_crypt_ctx *bc1, unsigned int bc1_bytes,\n--\nblock/blk-crypto.c-219-\nblock/blk-crypto.c:220:\treturn !bc1 || bio_crypt_dun_is_contiguous(bc1, bc1_bytes, bc2-\u003ebc_dun);\nblock/blk-crypto.c-221-}\n--\ndrivers/block/virtio_blk.c=305=static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,\n--\ndrivers/block/virtio_blk.c-402-\t\t\tvbr-\u003eout_hdr.crypto_append.msg.dun[i] =\ndrivers/block/virtio_blk.c:403:\t\t\t\tcpu_to_virtio64(vdev, req-\u003ecrypt_ctx-\u003ebc_dun[i]);\ndrivers/block/virtio_blk.c-404-\t\t}\n--\ndrivers/block/virtio_blk.c=1444=static int virtblk_init_crypto(struct virtio_blk *vblk)\n--\ndrivers/block/virtio_blk.c-1470-\t * values (32 bytes total), matching the size of\ndrivers/block/virtio_blk.c:1471:\t * blk_crypto_ctx::bc_dun[4].  Refuse to advertise more than that as\ndrivers/block/virtio_blk.c-1472-\t * supported, or blk-crypto could negotiate a larger dun_bytes with the\ndrivers/block/virtio_blk.c:1473:\t * filesystem and have the high-order bytes of req-\u003ecrypt_ctx-\u003ebc_dun\ndrivers/block/virtio_blk.c-1474-\t * silently dropped in virtblk_setup_cmd().\n--\ndrivers/md/dm-io-rewind.c=94=static void dm_bio_crypt_rewind(struct bio *bio, unsigned int bytes)\n--\ndrivers/md/dm-io-rewind.c-97-\ndrivers/md/dm-io-rewind.c:98:\tdm_bio_crypt_dun_decrement(bc-\u003ebc_dun,\ndrivers/md/dm-io-rewind.c-99-\t\t\t\t   bytes \u003e\u003e bc-\u003ebc_key-\u003edata_unit_size_bits);\n--\ndrivers/mmc/host/cqhci-crypto.h=23=static inline u64 cqhci_crypto_prep_task_desc(struct mmc_request *mrq)\n--\ndrivers/mmc/host/cqhci-crypto.h-28-\t/* We set max_dun_bytes_supported=4, so all DUNs should be 32-bit. */\ndrivers/mmc/host/cqhci-crypto.h:29:\tWARN_ON_ONCE(mrq-\u003ecrypto_ctx-\u003ebc_dun[0] \u003e U32_MAX);\ndrivers/mmc/host/cqhci-crypto.h-30-\n--\ndrivers/mmc/host/cqhci-crypto.h-32-\t       CQHCI_CRYPTO_KEYSLOT(mrq-\u003ecrypto_key_slot) |\ndrivers/mmc/host/cqhci-crypto.h:33:\t       mrq-\u003ecrypto_ctx-\u003ebc_dun[0];\ndrivers/mmc/host/cqhci-crypto.h-34-}\n--\ndrivers/mmc/host/sdhci-msm.c=2074=static void sdhci_msm_ice_cfg(struct sdhci_host *host, struct mmc_request *mrq)\n--\ndrivers/mmc/host/sdhci-msm.c-2093-\t\tcqhci_writel(cq_host, crypto_params, NONCQ_CRYPTO_PARM);\ndrivers/mmc/host/sdhci-msm.c:2094:\t\tcqhci_writel(cq_host, lower_32_bits(mrq-\u003ecrypto_ctx-\u003ebc_dun[0]),\ndrivers/mmc/host/sdhci-msm.c-2095-\t\t\t     NONCQ_CRYPTO_DUN);\n--\ndrivers/ufs/core/ufshcd-crypto.h=15=static inline void ufshcd_prepare_lrbp_crypto(struct request *rq,\n--\ndrivers/ufs/core/ufshcd-crypto.h-23-\tlrbp-\u003ecrypto_key_slot = blk_crypto_keyslot_index(rq-\u003ecrypt_keyslot);\ndrivers/ufs/core/ufshcd-crypto.h:24:\tlrbp-\u003edata_unit_num = rq-\u003ecrypt_ctx-\u003ebc_dun[0];\ndrivers/ufs/core/ufshcd-crypto.h-25-}\n--\ndrivers/ufs/host/ufs-exynos.c=1470=static int exynos_ufs_fmp_fill_prdt(struct ufs_hba *hba,\n--\ndrivers/ufs/host/ufs-exynos.c-1476-\tconst u8 *twkey = enckey + AES_KEYSIZE_256;\ndrivers/ufs/host/ufs-exynos.c:1477:\tu64 dun_lo = crypt_ctx-\u003ebc_dun[0];\ndrivers/ufs/host/ufs-exynos.c:1478:\tu64 dun_hi = crypt_ctx-\u003ebc_dun[1];\ndrivers/ufs/host/ufs-exynos.c-1479-\tunsigned int i;\n--\ninclude/linux/blk-crypto.h=112=struct blk_crypto_key {\n--\ninclude/linux/blk-crypto.h-124- * @bc_key: the key, algorithm, and data unit size to use\ninclude/linux/blk-crypto.h:125: * @bc_dun: the data unit number (starting IV) to use\ninclude/linux/blk-crypto.h-126- *\n--\ninclude/linux/blk-crypto.h=131=struct bio_crypt_ctx {\ninclude/linux/blk-crypto.h-132-\tconst struct blk_crypto_key\t*bc_key;\ninclude/linux/blk-crypto.h:133:\tu64\t\t\t\tbc_dun[BLK_CRYPTO_DUN_ARRAY_SIZE];\ninclude/linux/blk-crypto.h-134-};\n"
}

55/3 2026/09/14 18:20 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 43438
							output: 21
							thoughts: 4818
						

Thoughts:
**Analysis of Virtio Block Inline Encryption Patch for Potential Uninitialized Memory Bugs**

Okay, here's the breakdown of my thought process. I'm focusing on potential uninitialized memory vulnerabilities in this virtio-blk inline encryption patch. My primary concern is if the patch could expose kernel memory via the device, especially through any ioctl interaction it might introduce or existing ones.

First, I examined the `vbr->out_hdr.crypto_append.msg.dun` and `req->crypt_ctx->bc_dun[i]` copying, confirming sizes and the data transfer. Then I considered the synchronization and control queue mechanisms to ensure concurrency is handled correctly. I also checked for the new Kconfig options and relevant functions introduced.

A key moment was when I realized this patch *didn't* introduce new ioctls but instead hooked into existing `blk-crypto` ioctls. This immediately raised a red flag. Could linking new encryption logic to existing ioctls lead to unintended memory exposure? I drilled down into `blk_crypto_ioctl_generate_key` and its call to `virtblk_crypto_generate_key`. My concern was the generated key size and whether the device's response could lead to a read of uninitialized memory. I meticulously analyzed the `kzalloc_obj` allocation of `creq`, the device-side response, and the `memcpy` to `lt_key`. I concluded that the memory in `creq` is zero-initialized and any data copied is device provided, hence not a source of uninitialized memory.

I then followed this same approach to `virtblk_crypto_derive_sw_secret`, `virtblk_crypto_prepare_key`, and `virtblk_crypto_import_key`, carefully examining memory allocation and data flow. In each case, I confirmed that `creq` is allocated with `kzalloc_obj`, and that any data used in an `memcpy` is initialized.

Next, I focused on the `struct virtblk_ctrl_request` and how it's used. I checked EVERY single instance and found that it is consistently allocated using `kzalloc_obj`, which guarantees zero initialization. This eliminated any concern about uninitialized padding bytes or unassigned fields.

I dug further into `set_virtblk_crypto_key_desc`, confirming that the `desc` structure is zeroed out before use, protecting against any memory leakage.

Then, I looked closely at how `vbr` (the virtblk request structure) is initialized, tracing it back to `virtio_queue_rq`. I checked the state of `vbr` before and after this patch, noting the explicit `memset` call when a crypto operation is performed. This confirmed that `crypto_append.msg` is zeroed out. Then, I looked at how `vbr->out_hdr` is initialized and used.

Finally, I checked `virtblk_init_crypto` and found the variables are initialized. And then, I reviewed `virtblk_get_crypto_modes` confirming the `crypto_modes_supported` array is zero-initialized.

In conclusion, after a very careful review, I am confident that this patch does not introduce any new uninitialized memory vulnerabilities by connecting to existing ioctls, because, in every case, memory is either zeroed, or allocated with `kzalloc_obj`, or filled by the hypervisor and the patch ensures everything is zeroed prior to any use by the kernel.



56/3 2026/09/14 18:20 tool grepper
0m Args:
{
  "Expression": "blk_crypto_profile_init"
}

Results:
{
  "Output": "Documentation/block/inline-encryption.rst=229=blk_crypto_profile in the request_queue of its device.  To do this, it first\nDocumentation/block/inline-encryption.rst:230:must call ``blk_crypto_profile_init()`` (or its resource-managed variant\nDocumentation/block/inline-encryption.rst:231:``devm_blk_crypto_profile_init()``), providing the number of keyslots.\nDocumentation/block/inline-encryption.rst-232-\n--\nDocumentation/block/inline-encryption.rst=257=keyslots.  To do this, the driver may call ``blk_crypto_reprogram_all_keys()``.\nDocumentation/block/inline-encryption.rst-258-\nDocumentation/block/inline-encryption.rst:259:Finally, if the driver used ``blk_crypto_profile_init()`` instead of\nDocumentation/block/inline-encryption.rst:260:``devm_blk_crypto_profile_init()``, then it is responsible for calling\nDocumentation/block/inline-encryption.rst-261-``blk_crypto_profile_destroy()`` when the crypto profile is no longer needed.\n--\nblock/blk-crypto-fallback.c=531=static int blk_crypto_fallback_init(void)\n--\nblock/blk-crypto-fallback.c-551-\nblock/blk-crypto-fallback.c:552:\terr = blk_crypto_profile_init(blk_crypto_fallback_profile,\nblock/blk-crypto-fallback.c-553-\t\t\t\t      blk_crypto_num_keyslots);\n--\nblock/blk-crypto-profile.c=58=static inline void blk_crypto_hw_exit(struct blk_crypto_profile *profile)\n--\nblock/blk-crypto-profile.c-66-/**\nblock/blk-crypto-profile.c:67: * blk_crypto_profile_init() - Initialize a blk_crypto_profile\nblock/blk-crypto-profile.c-68- * @profile: the blk_crypto_profile to initialize\n--\nblock/blk-crypto-profile.c-75- */\nblock/blk-crypto-profile.c:76:int blk_crypto_profile_init(struct blk_crypto_profile *profile,\nblock/blk-crypto-profile.c-77-\t\t\t    unsigned int num_slots)\n--\nblock/blk-crypto-profile.c-136-}\nblock/blk-crypto-profile.c:137:EXPORT_SYMBOL_GPL(blk_crypto_profile_init);\nblock/blk-crypto-profile.c-138-\nblock/blk-crypto-profile.c=139=static void blk_crypto_profile_destroy_callback(void *profile)\n--\nblock/blk-crypto-profile.c-144-/**\nblock/blk-crypto-profile.c:145: * devm_blk_crypto_profile_init() - Resource-managed blk_crypto_profile_init()\nblock/blk-crypto-profile.c-146- * @dev: the device which owns the blk_crypto_profile\n--\nblock/blk-crypto-profile.c-149- *\nblock/blk-crypto-profile.c:150: * Like blk_crypto_profile_init(), but causes blk_crypto_profile_destroy() to be\nblock/blk-crypto-profile.c-151- * called automatically on driver detach.\n--\nblock/blk-crypto-profile.c-154- */\nblock/blk-crypto-profile.c:155:int devm_blk_crypto_profile_init(struct device *dev,\nblock/blk-crypto-profile.c-156-\t\t\t\t struct blk_crypto_profile *profile,\n--\nblock/blk-crypto-profile.c-158-{\nblock/blk-crypto-profile.c:159:\tint err = blk_crypto_profile_init(profile, num_slots);\nblock/blk-crypto-profile.c-160-\n--\nblock/blk-crypto-profile.c-167-}\nblock/blk-crypto-profile.c:168:EXPORT_SYMBOL_GPL(devm_blk_crypto_profile_init);\nblock/blk-crypto-profile.c-169-\n--\ndrivers/block/virtio_blk.c=1444=static int virtblk_init_crypto(struct virtio_blk *vblk)\n--\ndrivers/block/virtio_blk.c-1499-\t */\ndrivers/block/virtio_blk.c:1500:\terr = blk_crypto_profile_init(\u0026vblk-\u003eprofile, max_slots);\ndrivers/block/virtio_blk.c-1501-\tif (err) {\n--\ndrivers/md/dm-table.c=1387=static int dm_table_construct_crypto_profile(struct dm_table *t)\n--\ndrivers/md/dm-table.c-1399-\tprofile = \u0026dmcp-\u003eprofile;\ndrivers/md/dm-table.c:1400:\tblk_crypto_profile_init(profile, 0);\ndrivers/md/dm-table.c-1401-\tprofile-\u003ell_ops.keyslot_evict = dm_keyslot_evict;\n--\ndrivers/mmc/host/cqhci-crypto.c=161=int cqhci_crypto_init(struct cqhci_host *cq_host)\n--\ndrivers/mmc/host/cqhci-crypto.c-195-\t */\ndrivers/mmc/host/cqhci-crypto.c:196:\terr = devm_blk_crypto_profile_init(\ndrivers/mmc/host/cqhci-crypto.c-197-\t\tdev, profile, cq_host-\u003ecrypto_capabilities.config_count + 1);\n--\ndrivers/mmc/host/sdhci-msm.c=1905=static int sdhci_msm_ice_init(struct sdhci_msm_host *msm_host,\n--\ndrivers/mmc/host/sdhci-msm.c-1935-\t/* The number of keyslots supported is (CFGC+1) */\ndrivers/mmc/host/sdhci-msm.c:1936:\terr = devm_blk_crypto_profile_init(dev, profile, caps.config_count + 1);\ndrivers/mmc/host/sdhci-msm.c-1937-\tif (err)\n--\ndrivers/ufs/core/ufshcd-crypto.c=148=int ufshcd_hba_init_crypto_capabilities(struct ufs_hba *hba)\n--\ndrivers/ufs/core/ufshcd-crypto.c-178-\t/* The actual number of configurations supported is (CFGC+1) */\ndrivers/ufs/core/ufshcd-crypto.c:179:\terr = devm_blk_crypto_profile_init(\ndrivers/ufs/core/ufshcd-crypto.c-180-\t\t\thba-\u003edev, \u0026hba-\u003ecrypto_profile,\n--\ndrivers/ufs/host/ufs-exynos.c=1362=static void exynos_ufs_fmp_init(struct ufs_hba *hba, struct exynos_ufs *ufs)\n--\ndrivers/ufs/host/ufs-exynos.c-1419-\t/* Advertise crypto capabilities to the block layer. */\ndrivers/ufs/host/ufs-exynos.c:1420:\terr = devm_blk_crypto_profile_init(hba-\u003edev, profile, 0);\ndrivers/ufs/host/ufs-exynos.c-1421-\tif (err) {\n--\ndrivers/ufs/host/ufs-qcom.c=168=static int ufs_qcom_ice_init(struct ufs_qcom_host *host)\n--\ndrivers/ufs/host/ufs-qcom.c-194-\t/* The number of keyslots supported is (CFGC+1) */\ndrivers/ufs/host/ufs-qcom.c:195:\terr = devm_blk_crypto_profile_init(dev, profile, caps.config_count + 1);\ndrivers/ufs/host/ufs-qcom.c-196-\tif (err)\n--\ninclude/linux/blk-crypto-profile.h=126=struct blk_crypto_profile {\n--\ninclude/linux/blk-crypto-profile.h-194-\ninclude/linux/blk-crypto-profile.h:195:int blk_crypto_profile_init(struct blk_crypto_profile *profile,\ninclude/linux/blk-crypto-profile.h-196-\t\t\t    unsigned int num_slots);\ninclude/linux/blk-crypto-profile.h-197-\ninclude/linux/blk-crypto-profile.h:198:int devm_blk_crypto_profile_init(struct device *dev,\ninclude/linux/blk-crypto-profile.h-199-\t\t\t\t struct blk_crypto_profile *profile,\n"
}

57/3 2026/09/14 18:20 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 51244
							output: 36
							thoughts: 25
						

Thoughts:
Here's my summary, as though I'm thinking through this process:

**Diving into `blk_crypto_profile_init`**

Okay, time to dig into the `blk_crypto_profile_init` function. I need to refresh my memory, so let's navigate to `block/blk-crypto-profile.c`. I expect this initialization function is responsible for setting up the cryptographic profiles used for block device encryption. It's likely involved in registering ciphers, key management schemes, or setting default configurations. I'll be looking for things like memory allocations, function calls to crypto API primitives, potential locking, and error handling. My gut tells me this is a critical entry point for any block-level encryption implementation, so I need to understand it thoroughly. This will give me an understanding of the overall approach to how the crypto profiles get set up within the block layer.



58/3 2026/09/14 18:20 tool read-file
0m Args:
{
  "File": "block/blk-crypto-profile.c",
  "FirstLine": 75,
  "LineCount": 40
}

Results:
{
  "Contents": "  75:\t */\n  76:\tint blk_crypto_profile_init(struct blk_crypto_profile *profile,\n  77:\t\t\t\t    unsigned int num_slots)\n  78:\t{\n  79:\t\tunsigned int slot;\n  80:\t\tunsigned int i;\n  81:\t\tunsigned int slot_hashtable_size;\n  82:\t\n  83:\t\tmemset(profile, 0, sizeof(*profile));\n  84:\t\n  85:\t\t/*\n  86:\t\t * profile-\u003elock of an underlying device can nest inside profile-\u003elock\n  87:\t\t * of a device-mapper device, so use a dynamic lock class to avoid\n  88:\t\t * false-positive lockdep reports.\n  89:\t\t */\n  90:\t\tlockdep_register_key(\u0026profile-\u003elockdep_key);\n  91:\t\t__init_rwsem(\u0026profile-\u003elock, \"\u0026profile-\u003elock\", \u0026profile-\u003elockdep_key);\n  92:\t\n  93:\t\tif (num_slots == 0)\n  94:\t\t\treturn 0;\n  95:\t\n  96:\t\t/* Initialize keyslot management data. */\n  97:\t\n  98:\t\tprofile-\u003eslots = kvzalloc_objs(profile-\u003eslots[0], num_slots);\n  99:\t\tif (!profile-\u003eslots)\n 100:\t\t\tgoto err_destroy;\n 101:\t\n 102:\t\tprofile-\u003enum_slots = num_slots;\n 103:\t\n 104:\t\tinit_waitqueue_head(\u0026profile-\u003eidle_slots_wait_queue);\n 105:\t\tINIT_LIST_HEAD(\u0026profile-\u003eidle_slots);\n 106:\t\n 107:\t\tfor (slot = 0; slot \u003c num_slots; slot++) {\n 108:\t\t\tprofile-\u003eslots[slot].profile = profile;\n 109:\t\t\tlist_add_tail(\u0026profile-\u003eslots[slot].idle_slot_node,\n 110:\t\t\t\t      \u0026profile-\u003eidle_slots);\n 111:\t\t}\n 112:\t\n 113:\t\tspin_lock_init(\u0026profile-\u003eidle_slots_lock);\n 114:\t\n"
}

59/3 2026/09/14 18:20 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 52053
							output: 38
							thoughts: 67
						

Thoughts:
Here's my take on those thoughts:

**Addressing a Potential Initialization Oversight in Virtio-blk**

Okay, so I see `memset(profile, 0, sizeof(*profile));` being called within `blk_crypto_profile_init`. That's good, it's a standard practice for zeroing out the profile structure, ensuring a known initial state. But, it's got me thinking, it feels like we might be overlooking a crucial detail about data structures in the bigger picture!

My mind immediately jumps to `struct virtio_blk`. The `profile` is part of it, but where is the parent structure `struct virtio_blk *vblk` even allocated?  Is the initialization of *that* structure handled correctly? Is the entire `vblk` structure cleared, or only the `profile` element?

I need to confirm this. I'm going to look at `virtblk_probe`, as that's usually where the device setup happens. I'll need to check the allocation and initialization steps there to make sure there aren't any potential uninitialized memory issues lurking around. My focus will be on when the pointer `vblk` is first initialized and if there are any `memset()` calls for the whole `vblk` structure. Need to make sure no uninitialized memory could lead to unintended behaviors in a secure context.



60/3 2026/09/14 18:20 tool grepper
0m Error:
no matches

Args:
{
  "Expression": "kzalloc.*struct virtio_blk",
  "PathPrefix": "drivers/block/virtio_blk.c"
}

Results:
{
  "error": "no matches"
}

61/3 2026/09/14 18:20 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 52459
							output: 39
							thoughts: 18
						

Thoughts:
Here's a first-person summary, tailored for an expert audience, based on the provided thought:

**Probing the Virtual Block Device**

Okay, so I'm diving into `virtblk_probe`. My primary concern right now is understanding how the virtual block device, represented by `vblk`, gets its memory allocation. Is it a static allocation within the driver itself, or is it dynamically allocated? And if dynamic, what's the mechanism? I need to trace the code to see precisely where and how this `vblk` structure is initialized and subsequently populated. This is crucial for understanding the device's lifecycle and potential resource management issues, especially with regard to memory fragmentation or leaks. I’m thinking I'll start with a search for the `vblk` variable within the `virtblk_probe` function itself, then maybe trace back to function calls that might allocate or initialize it if I can't find it directly within that function. My objective is a clear mental map of `vblk`'s instantiation.



62/3 2026/09/14 18:20 tool read-file
0m Args:
{
  "File": "drivers/block/virtio_blk.c",
  "FirstLine": 2130,
  "LineCount": 40
}

Results:
{
  "Contents": "2130:\t\tstruct virtio_blk *vblk;\n2131:\t\tstruct queue_limits lim = {\n2132:\t\t\t.features\t\t= BLK_FEAT_ROTATIONAL,\n2133:\t\t\t.logical_block_size\t= SECTOR_SIZE,\n2134:\t\t};\n2135:\t\tint err, index;\n2136:\t\tunsigned int queue_depth;\n2137:\t\n2138:\t\tif (!vdev-\u003econfig-\u003eget) {\n2139:\t\t\tdev_err(\u0026vdev-\u003edev, \"%s failure: config access disabled\\n\",\n2140:\t\t\t\t__func__);\n2141:\t\t\treturn -EINVAL;\n2142:\t\t}\n2143:\t\n2144:\t\terr = ida_alloc_range(\u0026vd_index_ida, 0,\n2145:\t\t\t\t      minor_to_index(1 \u003c\u003c MINORBITS) - 1, GFP_KERNEL);\n2146:\t\tif (err \u003c 0)\n2147:\t\t\tgoto out;\n2148:\t\tindex = err;\n2149:\t\n2150:\t\tvdev-\u003epriv = vblk = kmalloc_obj(*vblk);\n2151:\t\tif (!vblk) {\n2152:\t\t\terr = -ENOMEM;\n2153:\t\t\tgoto out_free_index;\n2154:\t\t}\n2155:\t\n2156:\t\tmutex_init(\u0026vblk-\u003evdev_mutex);\n2157:\t\tmutex_init(\u0026vblk-\u003ectrl_vq.mutex);\n2158:\t\tspin_lock_init(\u0026vblk-\u003ectrl_vq.lock);\n2159:\t\n2160:\t\tvblk-\u003evdev = vdev;\n2161:\t\n2162:\t\tINIT_WORK(\u0026vblk-\u003econfig_work, virtblk_config_changed_work);\n2163:\t\n2164:\t\terr = init_vq(vblk);\n2165:\t\tif (err) {\n2166:\t\t\tdev_err(\u0026vdev-\u003edev, \"init virt queue failed: err = %d\\n\", err);\n2167:\t\t\tgoto out_free_vblk;\n2168:\t\t}\n2169:\t\n"
}

63/3 2026/09/14 18:20 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 53321
							output: 19
							thoughts: 1166
						

Thoughts:
**Potential Uninitialized Memory Vulnerability in virtio-blk Driver**

Okay, let's break this down. Line 2150 immediately caught my eye: `vdev->priv = vblk = kmalloc_obj(*vblk);` Right away, I see that `vblk` is allocated using `kmalloc_obj`, which is not the same as `kzalloc`. This means that `vblk` might not be zero-initialized. Now, I need to understand what new fields have been added to the `struct virtio_blk`. I see that `struct virtio_blk` includes: `zone_sectors`, `struct blk_crypto_profile profile`, `bool crypto_profile_initialized`, and `struct virtio_blk_ctrl_vq ctrl_vq`. 

My primary concern now is whether or not these added members are properly initialized. I see initialization of the following members of `vblk->ctrl_vq`: `vq`, `mutex`, `lock`, `inflight`, `dead`, and `drained`. But what about `vblk->crypto_profile_initialized`? I need to track down where this is set.

I find that `vblk->crypto_profile_initialized` is only set in `virtblk_init_crypto`. I need to understand the control flow here: what happens if the device doesn't have `VIRTIO_BLK_F_INLINE_ENCRYPTION` or `VIRTIO_BLK_F_CTRL_VQ`? What if `virtblk_init_crypto` fails? Importantly, is `vblk->crypto_profile_initialized` initialized to anything if `VIRTIO_BLK_F_INLINE_ENCRYPTION` isn't present, or `virtblk_init_crypto` fails? 

Here's the problem. Since `vblk` is allocated with `kmalloc`, the memory is uninitialized. And, in the typical case, the `crypto_profile_initialized` flag is only set to `true` if inline encryption is enabled *and* `virtblk_init_crypto` succeeds. Critically, there's *no* initialization to `false` in `virtblk_probe`, and the allocation is done via `kmalloc`, so it will have uninitialized values!

Now, let's look at `virtblk_free_disk`. I see that `virtblk_destroy_crypto` is called, and *within* that function, there's a check for `vblk->crypto_profile_initialized`. If `crypto_profile_initialized` is true, then `blk_crypto_profile_destroy` is called. So, the code might attempt to deallocate something that was never initialized.

This is a serious issue. If the device does not support inline encryption, or if `virtblk_init_crypto` fails for some other reason, `vblk->crypto_profile_initialized` will contain an uninitialized value, which could be *anything*. And the `virtblk_destroy_crypto` will attempt to use it! This is a classic uninitialized memory read, potentially leading to all sorts of undefined behavior or security vulnerabilities.

I'm going to verify this with a grep across the codebase to ensure this is the only location where `crypto_profile_initialized` is set. This looks like a bug to me. We need a fix!



64/3 2026/09/14 18:21 tool grepper
0m Args:
{
  "Expression": "crypto_profile_initialized"
}

Results:
{
  "Output": "drivers/block/virtio_blk.c=66=struct virtio_blk {\n--\ndrivers/block/virtio_blk.c-99-\tstruct blk_crypto_profile profile;\ndrivers/block/virtio_blk.c:100:\tbool crypto_profile_initialized;\ndrivers/block/virtio_blk.c-101-\n--\ndrivers/block/virtio_blk.c=1444=static int virtblk_init_crypto(struct virtio_blk *vblk)\n--\ndrivers/block/virtio_blk.c-1497-\t * virtblk_free_disk() calls blk_crypto_profile_destroy() explicitly\ndrivers/block/virtio_blk.c:1498:\t * instead, guarded by crypto_profile_initialized below.\ndrivers/block/virtio_blk.c-1499-\t */\n--\ndrivers/block/virtio_blk.c-1512-\ndrivers/block/virtio_blk.c:1513:\tvblk-\u003ecrypto_profile_initialized = true;\ndrivers/block/virtio_blk.c-1514-\n--\ndrivers/block/virtio_blk.c=1520=static void virtblk_destroy_crypto(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-1521-{\ndrivers/block/virtio_blk.c:1522:\tif (vblk-\u003ecrypto_profile_initialized)\ndrivers/block/virtio_blk.c-1523-\t\tblk_crypto_profile_destroy(\u0026vblk-\u003eprofile);\n"
}

65/3 2026/09/14 18:21 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 55627
							output: 40
							thoughts: 96
						

Thoughts:
**Debugging a Cryptographic Initialization**

Okay, so `crypto_profile_initialized` appears to be restricted to just those four locations, which is helpful. Now, the next step is to examine the allocation of `vblk` in `virtio_blk.c`, specifically around line 2150. I see that `vdev->priv = vblk = kmalloc_obj(*vblk);` is where it's happening, which seems standard enough. The critical question now is whether `vblk` is explicitly zeroed out anywhere within `virtblk_probe`. Memory allocation like this is often a source of subtle security bugs, so it's essential to understand its initial state. Let's shift our focus and read through the `virtblk_probe` function, covering roughly lines 2150 to 2260, to see what happens before and after that allocation. I need to understand if there's any initialization that would affect my initial suspicion, or any operations that make use of the `vblk` data structure and how.



66/3 2026/09/14 18:21 tool read-file
0m Args:
{
  "File": "drivers/block/virtio_blk.c",
  "FirstLine": 2150,
  "LineCount": 100
}

Results:
{
  "Contents": "2150:\t\tvdev-\u003epriv = vblk = kmalloc_obj(*vblk);\n2151:\t\tif (!vblk) {\n2152:\t\t\terr = -ENOMEM;\n2153:\t\t\tgoto out_free_index;\n2154:\t\t}\n2155:\t\n2156:\t\tmutex_init(\u0026vblk-\u003evdev_mutex);\n2157:\t\tmutex_init(\u0026vblk-\u003ectrl_vq.mutex);\n2158:\t\tspin_lock_init(\u0026vblk-\u003ectrl_vq.lock);\n2159:\t\n2160:\t\tvblk-\u003evdev = vdev;\n2161:\t\n2162:\t\tINIT_WORK(\u0026vblk-\u003econfig_work, virtblk_config_changed_work);\n2163:\t\n2164:\t\terr = init_vq(vblk);\n2165:\t\tif (err) {\n2166:\t\t\tdev_err(\u0026vdev-\u003edev, \"init virt queue failed: err = %d\\n\", err);\n2167:\t\t\tgoto out_free_vblk;\n2168:\t\t}\n2169:\t\n2170:\t\t/* Default queue sizing is to fill the ring. */\n2171:\t\tif (!virtblk_queue_depth) {\n2172:\t\t\tqueue_depth = vblk-\u003evqs[0].vq-\u003enum_free;\n2173:\t\t\t/* ... but without indirect descs, we use 2 descs per req */\n2174:\t\t\tif (!virtio_has_feature(vdev, VIRTIO_RING_F_INDIRECT_DESC))\n2175:\t\t\t\tqueue_depth /= 2;\n2176:\t\t} else {\n2177:\t\t\tqueue_depth = virtblk_queue_depth;\n2178:\t\t}\n2179:\t\n2180:\t\tmemset(\u0026vblk-\u003etag_set, 0, sizeof(vblk-\u003etag_set));\n2181:\t\tvblk-\u003etag_set.ops = \u0026virtio_mq_ops;\n2182:\t\tvblk-\u003etag_set.queue_depth = queue_depth;\n2183:\t\tvblk-\u003etag_set.numa_node = NUMA_NO_NODE;\n2184:\t\tvblk-\u003etag_set.cmd_size =\n2185:\t\t\tsizeof(struct virtblk_req) +\n2186:\t\t\tsizeof(struct scatterlist) * VIRTIO_BLK_INLINE_SG_CNT;\n2187:\t\tvblk-\u003etag_set.driver_data = vblk;\n2188:\t\tvblk-\u003etag_set.nr_hw_queues = vblk-\u003enum_vqs;\n2189:\t\tvblk-\u003etag_set.nr_maps = 1;\n2190:\t\tif (vblk-\u003eio_queues[HCTX_TYPE_POLL])\n2191:\t\t\tvblk-\u003etag_set.nr_maps = 3;\n2192:\t\n2193:\t\terr = blk_mq_alloc_tag_set(\u0026vblk-\u003etag_set);\n2194:\t\tif (err)\n2195:\t\t\tgoto out_free_vq;\n2196:\t\n2197:\t\terr = virtblk_read_limits(vblk, \u0026lim);\n2198:\t\tif (err)\n2199:\t\t\tgoto out_free_tags;\n2200:\t\n2201:\t\tif (virtblk_get_cache_mode(vdev))\n2202:\t\t\tlim.features |= BLK_FEAT_WRITE_CACHE;\n2203:\t\n2204:\t\tvblk-\u003edisk = blk_mq_alloc_disk(\u0026vblk-\u003etag_set, \u0026lim, vblk);\n2205:\t\tif (IS_ERR(vblk-\u003edisk)) {\n2206:\t\t\terr = PTR_ERR(vblk-\u003edisk);\n2207:\t\t\tgoto out_free_tags;\n2208:\t\t}\n2209:\t\n2210:\t\tvirtblk_name_format(\"vd\", index, vblk-\u003edisk-\u003edisk_name, DISK_NAME_LEN);\n2211:\t\n2212:\t\tvblk-\u003edisk-\u003emajor = major;\n2213:\t\tvblk-\u003edisk-\u003efirst_minor = index_to_minor(index);\n2214:\t\tvblk-\u003edisk-\u003eminors = 1 \u003c\u003c PART_BITS;\n2215:\t\tvblk-\u003edisk-\u003eprivate_data = vblk;\n2216:\t\tvblk-\u003edisk-\u003efops = \u0026virtblk_fops;\n2217:\t\tvblk-\u003eindex = index;\n2218:\t\n2219:\t\t/* If disk is read-only in the host, the guest should obey */\n2220:\t\tif (virtio_has_feature(vdev, VIRTIO_BLK_F_RO))\n2221:\t\t\tset_disk_ro(vblk-\u003edisk, 1);\n2222:\t\n2223:\t\tvirtblk_update_capacity(vblk, false);\n2224:\t\tvirtio_device_ready(vdev);\n2225:\t\n2226:\t\t/*\n2227:\t\t * All steps that follow use the VQs therefore they need to be\n2228:\t\t * placed after the virtio_device_ready() call above.\n2229:\t\t */\n2230:\t\tif (IS_ENABLED(CONFIG_BLK_DEV_ZONED) \u0026\u0026\n2231:\t\t    (lim.features \u0026 BLK_FEAT_ZONED)) {\n2232:\t\t\terr = blk_revalidate_disk_zones(vblk-\u003edisk);\n2233:\t\t\tif (err)\n2234:\t\t\t\tgoto out_cleanup_disk;\n2235:\t\t} else if (IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) \u0026\u0026\n2236:\t\t\t   virtio_has_feature(vdev, VIRTIO_BLK_F_INLINE_ENCRYPTION) \u0026\u0026\n2237:\t\t\t   virtio_has_feature(vdev, VIRTIO_BLK_F_CTRL_VQ)) {\n2238:\t\t\terr = virtblk_init_crypto(vblk);\n2239:\t\t\tif (!err) {\n2240:\t\t\t\tif (!blk_crypto_register(\u0026vblk-\u003eprofile, vblk-\u003edisk-\u003equeue))\n2241:\t\t\t\t\tdev_warn(\u0026vdev-\u003edev,\n2242:\t\t\t\t\t\t \"failed to register inline crypto profile\\n\");\n2243:\t\t\t} else {\n2244:\t\t\t\tdev_warn(\u0026vdev-\u003edev,\n2245:\t\t\t\t\t \"inline crypto init failed: %d, continuing without inline crypto support\\n\",\n2246:\t\t\t\t\t err);\n2247:\t\t\t}\n2248:\t\t}\n2249:\t\n"
}

67/3 2026/09/14 18:21 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 57724
							output: 21
							thoughts: 752
						

Thoughts:
**A Potential Heap-Use-After-Free Vulnerability in virtio_blk**

Okay, here's what I'm seeing. In `virtblk_probe`, `kmalloc_obj` is used to allocate `vblk`, which is a `struct virtio_blk`. Now, the important point is that `kmalloc_obj` *doesn't* zero the memory it allocates. That's immediately a red flag. We initialize some mutexes and spinlocks, and assign `vdev`, but crucially, `vblk->crypto_profile_initialized`, a boolean, is *not* initialized here. It's left to chance.

The code then checks for `VIRTIO_BLK_F_INLINE_ENCRYPTION` and `VIRTIO_BLK_F_CTRL_VQ`. If the device doesn't have these features, or if `virtblk_init_crypto` fails within the `else if` block, `vblk->crypto_profile_initialized` never gets set to `true`. So, if those conditions are met and there is an error during initialization, that boolean remains uninitialized.

Now, where's the problem? The `virtblk_free_disk` function is called when the disk is released, which is what `disk->fops` does. Inside `virtblk_free_disk`, we see `virtblk_destroy_crypto` being called. And within `virtblk_destroy_crypto`, there's this `if (vblk->crypto_profile_initialized)` check. It's reading an uninitialized value! If that uninitialized byte, which comes directly from the heap and could easily contain a non-zero value due to heap recycling, happens to be non-zero, the code then branches into `blk_crypto_profile_destroy(&vblk->profile);`.

So, what does `blk_crypto_profile_destroy` do with a completely uninitialized `vblk->profile`? That's the critical question now. This looks like a potential heap-use-after-free vulnerability, depending on the implementation details of `blk_crypto_profile_destroy`. We could potentially be freeing or accessing memory that was never properly initialized, or possibly worse, freed elsewhere.



68/3 2026/09/14 18:21 tool grepper
0m Args:
{
  "Expression": "blk_crypto_profile_destroy"
}

Results:
{
  "Output": "Documentation/block/inline-encryption.rst=259=Finally, if the driver used ``blk_crypto_profile_init()`` instead of\nDocumentation/block/inline-encryption.rst-260-``devm_blk_crypto_profile_init()``, then it is responsible for calling\nDocumentation/block/inline-encryption.rst:261:``blk_crypto_profile_destroy()`` when the crypto profile is no longer needed.\nDocumentation/block/inline-encryption.rst-262-\n--\nblock/blk-crypto-fallback.c=531=static int blk_crypto_fallback_init(void)\n--\nblock/blk-crypto-fallback.c-604-fail_destroy_profile:\nblock/blk-crypto-fallback.c:605:\tblk_crypto_profile_destroy(blk_crypto_fallback_profile);\nblock/blk-crypto-fallback.c-606-fail_free_profile:\n--\nblock/blk-crypto-profile.c=76=int blk_crypto_profile_init(struct blk_crypto_profile *profile,\n--\nblock/blk-crypto-profile.c-133-err_destroy:\nblock/blk-crypto-profile.c:134:\tblk_crypto_profile_destroy(profile);\nblock/blk-crypto-profile.c-135-\treturn -ENOMEM;\n--\nblock/blk-crypto-profile.c=137=EXPORT_SYMBOL_GPL(blk_crypto_profile_init);\nblock/blk-crypto-profile.c-138-\nblock/blk-crypto-profile.c:139:static void blk_crypto_profile_destroy_callback(void *profile)\nblock/blk-crypto-profile.c-140-{\nblock/blk-crypto-profile.c:141:\tblk_crypto_profile_destroy(profile);\nblock/blk-crypto-profile.c-142-}\n--\nblock/blk-crypto-profile.c-149- *\nblock/blk-crypto-profile.c:150: * Like blk_crypto_profile_init(), but causes blk_crypto_profile_destroy() to be\nblock/blk-crypto-profile.c-151- * called automatically on driver detach.\n--\nblock/blk-crypto-profile.c=155=int devm_blk_crypto_profile_init(struct device *dev,\n--\nblock/blk-crypto-profile.c-164-\treturn devm_add_action_or_reset(dev,\nblock/blk-crypto-profile.c:165:\t\t\t\t\tblk_crypto_profile_destroy_callback,\nblock/blk-crypto-profile.c-166-\t\t\t\t\tprofile);\n--\nblock/blk-crypto-profile.c=419=EXPORT_SYMBOL_GPL(blk_crypto_reprogram_all_keys);\nblock/blk-crypto-profile.c-420-\nblock/blk-crypto-profile.c:421:void blk_crypto_profile_destroy(struct blk_crypto_profile *profile)\nblock/blk-crypto-profile.c-422-{\n--\nblock/blk-crypto-profile.c-430-}\nblock/blk-crypto-profile.c:431:EXPORT_SYMBOL_GPL(blk_crypto_profile_destroy);\nblock/blk-crypto-profile.c-432-\n--\ndrivers/block/virtio_blk.c=1444=static int virtblk_init_crypto(struct virtio_blk *vblk)\n--\ndrivers/block/virtio_blk.c-1496-\t * destroy callback after virtblk_remove() has already freed vblk.\ndrivers/block/virtio_blk.c:1497:\t * virtblk_free_disk() calls blk_crypto_profile_destroy() explicitly\ndrivers/block/virtio_blk.c-1498-\t * instead, guarded by crypto_profile_initialized below.\n--\ndrivers/block/virtio_blk.c=1520=static void virtblk_destroy_crypto(struct virtio_blk *vblk)\n--\ndrivers/block/virtio_blk.c-1522-\tif (vblk-\u003ecrypto_profile_initialized)\ndrivers/block/virtio_blk.c:1523:\t\tblk_crypto_profile_destroy(\u0026vblk-\u003eprofile);\ndrivers/block/virtio_blk.c-1524-}\n--\ndrivers/md/dm-table.c=1359=void dm_destroy_crypto_profile(struct blk_crypto_profile *profile)\n--\ndrivers/md/dm-table.c-1367-\ndrivers/md/dm-table.c:1368:\tblk_crypto_profile_destroy(profile);\ndrivers/md/dm-table.c-1369-\tkfree(dmcp);\n--\ninclude/linux/blk-crypto-profile.h=204=void blk_crypto_reprogram_all_keys(struct blk_crypto_profile *profile);\ninclude/linux/blk-crypto-profile.h-205-\ninclude/linux/blk-crypto-profile.h:206:void blk_crypto_profile_destroy(struct blk_crypto_profile *profile);\ninclude/linux/blk-crypto-profile.h-207-\n"
}

69/3 2026/09/14 18:21 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 60273
							output: 37
							thoughts: 26
						

Thoughts:
Okay, here we go...

**Analyzing `blk_crypto_profile_destroy`**

Alright, let's dive into this `blk_crypto_profile_destroy` function within `block/blk-crypto-profile.c`. I need to understand how these crypto profiles are cleaned up when they're no longer needed. My immediate thought is, given its name, this function is responsible for deallocating resources associated with a crypto profile, right? I'm anticipating it'll handle freeing allocated memory, potentially releasing locks, and possibly disabling any associated hardware acceleration. I'm keen to see if it properly handles error conditions and ensures that all resources are cleaned up safely, avoiding any memory leaks or dangling pointers. I'm expecting to see calls to `kfree` or similar memory management functions, along with checks to verify all the relevant structures are initialized and valid before any deallocation takes place. It'll be interesting to see the interaction between this function and any related data structures within the block layer's crypto framework. Let's see if it's thread-safe.



70/3 2026/09/14 18:21 tool read-file
0m Args:
{
  "File": "block/blk-crypto-profile.c",
  "FirstLine": 415,
  "LineCount": 25
}

Results:
{
  "Contents": " 415:\t\t\tWARN_ON(err);\n 416:\t\t}\n 417:\t\tup_write(\u0026profile-\u003elock);\n 418:\t}\n 419:\tEXPORT_SYMBOL_GPL(blk_crypto_reprogram_all_keys);\n 420:\t\n 421:\tvoid blk_crypto_profile_destroy(struct blk_crypto_profile *profile)\n 422:\t{\n 423:\t\tif (!profile)\n 424:\t\t\treturn;\n 425:\t\tlockdep_unregister_key(\u0026profile-\u003elockdep_key);\n 426:\t\tkvfree(profile-\u003eslot_hashtable);\n 427:\t\tkvfree_sensitive(profile-\u003eslots,\n 428:\t\t\t\t sizeof(profile-\u003eslots[0]) * profile-\u003enum_slots);\n 429:\t\tmemzero_explicit(profile, sizeof(*profile));\n 430:\t}\n 431:\tEXPORT_SYMBOL_GPL(blk_crypto_profile_destroy);\n 432:\t\n 433:\tbool blk_crypto_register(struct blk_crypto_profile *profile,\n 434:\t\t\t\t struct request_queue *q)\n 435:\t{\n 436:\t\tif (blk_integrity_queue_supports_integrity(q)) {\n 437:\t\t\tpr_warn(\"Integrity and hardware inline encryption are not supported together. Disabling hardware inline encryption.\\n\");\n 438:\t\t\treturn false;\n 439:\t\t}\n"
}

71/3 2026/09/14 18:21 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 60954
							output: 20
							thoughts: 1862
						

Thoughts:
**KMSAN Alert: Uninitialized Memory Read Vulnerability Detected**

Okay, here's what's running through my head. I'm looking at `blk_crypto_profile_destroy` and immediately see the potential for issues if `vblk->crypto_profile_initialized` isn't properly handled. The destruction function itself looks sound, but its safe execution depends on correct initialization.

My mind immediately jumps to `virtblk_destroy_crypto`. The code checks `vblk->crypto_profile_initialized` before calling `blk_crypto_profile_destroy`. This is where it gets interesting, and it looks like it's a condition for enabling NeedsKMSAN.  We've got conditional logic depending on a field that may be uninitialized, which triggers the check. The allocation and use of a complex structure with uninitialized fields is also something that needs to be considered.

Now I have to check `virtblk_restore_priv`. Boom!  Line 2345: `if (vblk->profile.slots)`. If encryption wasn't initialized (maybe the feature wasn't negotiated, or it's a zoned device, or initialization just failed), then `vblk->profile` was never even touched. The `vblk` struct, where `vblk->profile` is a member, was allocated with `kmalloc_obj`. That allocation may be from slab, but isn't zeroed unless explicitly requested! Therefore, `vblk->profile.slots` *contains garbage* uninitialized pointer data from `kmalloc_obj`. `virtblk_restore_priv` reads the value of uninitialized `vblk->profile.slots`, and then proceeds to call `blk_crypto_reprogram_all_keys(&vblk->profile)`! This could lead to a crash or corruption because of reading garbage pointer data.

Alright, what about `virtblk_ctrl_vq_quiesce`? I'm checking it, but it seems that `vblk->ctrl_vq.vq` is initialized in `init_vq`.  So, that seems safe.

But then I start thinking about error paths in `virtblk_probe`. If `init_vq` fails, `vblk` is freed. But then what if init_vq succeeds, but something *later* in the probe fails, like `blk_mq_alloc_tag_set`? It goes to `out_free_vq` and frees `vblk` *without* calling `virtblk_destroy_crypto`! That's concerning, but it seems to be addressed.  I need to check where `virtblk_destroy_crypto` *is* called. That's `virtblk_free_disk`!

If we are in the normal operation case (which is the vast majority of cases), where the device does *not* support inline encryption, this is what I see:

1. `virtblk_probe` is called.
2. `vblk = kmalloc_obj(*vblk);` allocates `vblk` from slab (kmalloc-N).
3. `vblk->crypto_profile_initialized` is *uninitialized*!  It's just whatever garbage was in that slab object.
4. `vblk->profile.slots` is also *uninitialized*!
5. The disk is added and used normally.
6. Later, either the device is suspended and restored (`virtblk_restore_priv` and reads `vblk->profile.slots`) OR the device is unbound/removed/cleaned up (`virtblk_free_disk` calls `virtblk_destroy_crypto(vblk)`).
7. `virtblk_destroy_crypto` reads `if (vblk->crypto_profile_initialized)`.

This is the key: `crypto_profile_initialized` is potentially uninitialized *heap* memory, and the code is making a conditional branch based on it! That can be very bad!

If `crypto_profile_initialized` is non-zero, it will call `blk_crypto_profile_destroy` with garbage values in the `profile` struct, probably leading to a crash when it tries to use garbage pointers!

And even if it doesn't crash immediately, reading uninitialized heap memory for a conditional branch is a classic KMSAN detection!

Now, let's confirm, can KASAN catch this? NO! It only detects out-of-bounds access and use-after-free. LOCKDEP? NO! UB-sanitizer? NO! Only KMSAN would catch branching on uninitialized memory, `kmsan_check_memory`.

Okay, I'm going to search the kernel definition of `kmalloc_obj` to confirm that it might *not* be zeroing, and that'll confirm that this looks like a KMS


72/3 2026/09/14 18:21 tool grepper
0m Args:
{
  "Expression": "#define kmalloc_obj"
}

Results:
{
  "Output": "include/linux/slab.h=1073=void *kmalloc_nolock(size_t size, gfp_t gfp_flags, int node);\n--\ninclude/linux/slab.h-1123- */\ninclude/linux/slab.h:1124:#define kmalloc_obj(VAR_OR_TYPE, ...) \\\ninclude/linux/slab.h-1125-\t__alloc_objs(kmalloc, default_gfp(__VA_ARGS__), typeof(VAR_OR_TYPE), 1)\n--\ninclude/linux/slab.h-1135- */\ninclude/linux/slab.h:1136:#define kmalloc_objs(VAR_OR_TYPE, COUNT, ...) \\\ninclude/linux/slab.h-1137-\t__alloc_objs(kmalloc, default_gfp(__VA_ARGS__), typeof(VAR_OR_TYPE), COUNT)\n--\nlib/decompress_unxz.c-163-#undef vfree\nlib/decompress_unxz.c:164:#define kmalloc_obj(type) malloc(sizeof(type))\nlib/decompress_unxz.c-165-#define kfree(ptr) free(ptr)\n--\ntools/virtio/linux/kernel.h=63=static inline void *kmalloc_array(unsigned n, size_t s, gfp_t gfp)\n--\ntools/virtio/linux/kernel.h-67-\ntools/virtio/linux/kernel.h:68:#define kmalloc_obj(VAR_OR_TYPE, ...) \\\ntools/virtio/linux/kernel.h-69-\t((typeof(VAR_OR_TYPE) *)kmalloc(sizeof(typeof(VAR_OR_TYPE)), 0))\ntools/virtio/linux/kernel.h-70-\ntools/virtio/linux/kernel.h:71:#define kmalloc_objs(VAR_OR_TYPE, COUNT, ...) \\\ntools/virtio/linux/kernel.h-72-\t((typeof(VAR_OR_TYPE) *)kmalloc(sizeof(typeof(VAR_OR_TYPE)) * (COUNT), 0))\n"
}

73/3 2026/09/14 18:21 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 64386
							output: 20
							thoughts: 56
						

Thoughts:
Here's my summary, as though I'm thinking through this myself:

**Investigating Memory Allocation Strategies in the Kernel**

Okay, so I'm looking at `kmalloc_obj`. Interesting, it's using `kmalloc` without the `__GFP_ZERO` flag. That immediately sparks a comparison. What's the rationale behind *that* decision? It's right next to `kzalloc_obj`, which I assume is defined near it. Time to dig into the header files. Let me go check `kzalloc_obj`'s definition in `include/linux/slab.h`. See if there's any immediate insight there to understand the choice. I'm guessing there's a good reason for this difference in initialization strategies, but I need the details to be certain.



74/3 2026/09/14 18:21 tool grepper
0m Args:
{
  "Expression": "#define kzalloc_obj"
}

Results:
{
  "Output": "include/linux/slab.h=1073=void *kmalloc_nolock(size_t size, gfp_t gfp_flags, int node);\n--\ninclude/linux/slab.h-1154-/* All kzalloc aliases for kmalloc_(obj|objs|flex). */\ninclude/linux/slab.h:1155:#define kzalloc_obj(P, ...) \\\ninclude/linux/slab.h-1156-\t__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), 1)\ninclude/linux/slab.h:1157:#define kzalloc_objs(P, COUNT, ...) \\\ninclude/linux/slab.h-1158-\t__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), COUNT)\n--\ntools/include/linux/slab.h=200=static inline unsigned int kmem_cache_sheaf_size(struct slab_sheaf *sheaf)\n--\ntools/include/linux/slab.h-210-\ntools/include/linux/slab.h:211:#define kzalloc_obj(P, ...) \\\ntools/include/linux/slab.h-212-\t__alloc_objs(kzalloc, default_gfp(__VA_ARGS__), typeof(P), 1)\n"
}

75/3 2026/09/14 18:21 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 64968
							output: 385
							thoughts: 1277
						

Thoughts:
**Analysis of Uninitialized Memory Read Vulnerability in virtio_blk Driver**

Okay, so I've been digging into this patch for the virtio_blk driver, and I've found a critical issue related to uninitialized memory reads. Here's the breakdown as I see it:

The core of the problem lies in how `kmalloc_obj` is used to allocate the `vblk` structure. Unlike `kzalloc_obj`, `kmalloc_obj` doesn't initialize the allocated memory. In this patch, a `struct virtio_blk` is allocated using `kmalloc_obj(*vblk)`. This means any newly added fields, such as `crypto_profile_initialized` and `profile`, will contain garbage data unless explicitly initialized.

Now, let's follow the code flow: During the `virtblk_probe` function, we see that some fields are initialized (mutexes, spinlocks, workqueue). However, `vblk->crypto_profile_initialized` is only set to `true` if inline encryption is supported *and* initialization is successful via the `virtblk_init_crypto` function. Crucially, if inline encryption is *not* supported, or if initialization fails, this flag remains uninitialized.

Fast forward to `virtblk_free_disk` and `virtblk_restore_priv`. In `virtblk_free_disk`, the code calls `virtblk_destroy_crypto`. Inside this function, there's a conditional check: `if (vblk->crypto_profile_initialized)`. This is a direct read of an uninitialized variable, leading to undefined behavior and a potential information leak. Similarly, in `virtblk_restore_priv`, there's a check `if (vblk->profile.slots)` which reads the potentially uninitialized `profile.slots` field.

This satisfies criteria 2 and 3: We have *direct* conditional logic branching on uninitialized fields, and we are dealing with a complex data structure allocation where uninitialized fields are later read in reachable code paths.

KASAN will not catch this because it only tracks allocation and deallocation, but not initialization. The memory is validly allocated, therefore KASAN will not trigger. KMSAN, which tracks memory initialization state, *will* detect this.

Given this analysis, I'm confident in setting the `NeedsKMSAN=true` flag. This patch introduces a vulnerability due to uninitialized memory reads in a conditional branch. The logic is now very clear, so I am setting the flag.



76/3 2026/09/14 18:21 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch introduces support for inline encryption in virtio-blk and modifies `struct virtio_blk` by adding `struct blk_crypto_profile profile` and `bool crypto_profile_initialized`.\n\nIn `virtblk_probe()`, `vblk` is allocated with `kmalloc_obj(*vblk)` which does not zero the memory. The new field `crypto_profile_initialized` is only initialized (`= true`) if the device supports inline encryption and control VQs and `virtblk_init_crypto()` succeeds. If the device does not advertise inline encryption (or if initialization fails or on zoned devices), `vblk-\u003ecrypto_profile_initialized` and `vblk-\u003eprofile` remain uninitialized heap memory.\n\nSubsequently, reachable code paths read these uninitialized fields in conditional branches:\n1. In `virtblk_destroy_crypto()`, called from `virtblk_free_disk()` upon disk release/teardown, `if (vblk-\u003ecrypto_profile_initialized)` performs conditional branching on the uninitialized boolean field. If non-zero, it invokes `blk_crypto_profile_destroy(\u0026vblk-\u003eprofile)` on uninitialized data.\n2. In `virtblk_restore_priv()`, `if (vblk-\u003eprofile.slots)` reads the uninitialized pointer field from the unzeroed `vblk-\u003eprofile` struct.\n\nThese issues represent conditional branching and reading of uninitialized heap memory (criteria 2 and 3). Standard bug detectors such as KASAN and LOCKDEP cannot detect reads of uninitialized bytes within validly allocated slab objects; only KMSAN shadow tracking detects use of uninitialized memory. Therefore, a dedicated KMSAN fuzzing session is justified.",
  "NeedsKMSAN": true
}

Results:
{
  "KMSANReasoning": "The patch introduces support for inline encryption in virtio-blk and modifies `struct virtio_blk` by adding `struct blk_crypto_profile profile` and `bool crypto_profile_initialized`.\n\nIn `virtblk_probe()`, `vblk` is allocated with `kmalloc_obj(*vblk)` which does not zero the memory. The new field `crypto_profile_initialized` is only initialized (`= true`) if the device supports inline encryption and control VQs and `virtblk_init_crypto()` succeeds. If the device does not advertise inline encryption (or if initialization fails or on zoned devices), `vblk-\u003ecrypto_profile_initialized` and `vblk-\u003eprofile` remain uninitialized heap memory.\n\nSubsequently, reachable code paths read these uninitialized fields in conditional branches:\n1. In `virtblk_destroy_crypto()`, called from `virtblk_free_disk()` upon disk release/teardown, `if (vblk-\u003ecrypto_profile_initialized)` performs conditional branching on the uninitialized boolean field. If non-zero, it invokes `blk_crypto_profile_destroy(\u0026vblk-\u003eprofile)` on uninitialized data.\n2. In `virtblk_restore_priv()`, `if (vblk-\u003eprofile.slots)` reads the uninitialized pointer field from the unzeroed `vblk-\u003eprofile` struct.\n\nThese issues represent conditional branching and reading of uninitialized heap memory (criteria 2 and 3). Standard bug detectors such as KASAN and LOCKDEP cannot detect reads of uninitialized bytes within validly allocated slab objects; only KMSAN shadow tracking detects use of uninitialized memory. Therefore, a dedicated KMSAN fuzzing session is justified.",
  "NeedsKMSAN": true
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)