| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/09 05:44 | flow | patch-triage |
6mArgs:null Results: {
"EnableConfigs": [
"VIRTIO_BLK_INLINE_ENCRYPTION",
"VIRTIO_BLK",
"BLK_INLINE_ENCRYPTION",
"VIRTIO"
],
"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_ctrlq_callback",
"virtblk_ctrl_vq_request",
"virtblk_init_crypto"
],
"KMSANReasoning": "The patch introduces inline encryption support and a control virtqueue to the virtio-blk driver (`drivers/block/virtio_blk.c`).\n\nAnalysis of uninitialized memory and KMSAN relevance:\n1. Heap allocations:\n - Control queue requests (`creq`) in all crypto operations (`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`) are allocated using `kzalloc_obj`, ensuring all fields and padding are zero-initialized.\n - The completion object `comp` in `virtblk_ctrl_vq_request()` is allocated via `kmalloc_obj`, but all of its fields (`done` and `abandoned`) are immediately initialized before submission.\n - Request structures in `init_vq()` use `kzalloc_objs` for `vqs_info`.\n2. Stack variables and structures:\n - All stack variables (e.g. `crypto_modes_supported`, `max_slots`, `max_dun_bytes`, `key_types`) are explicitly initialized with `= 0` or `= { 0 }`.\n - In `virtblk_setup_cmd()`, `vbr-\u003eout_hdr.crypto_append.msg` is explicitly zeroed via `memset()` before its fields are populated.\n3. Information leaks:\n - Wrapped keys generated or imported and returned to userspace via block device ioctls (`BLKCRYPTOGENERATEKEY`, `BLKCRYPTOIMPORTKEY`) copy only `key_size` bytes from the kzalloc'd response buffers.\n4. KASAN vs KMSAN:\n - The primary risks introduced by this patch involve concurrency, request lifetime handling on timeouts/drain (`abandoned` requests), locking (`vblk-\u003ectrl_vq.mutex`, `lock`), and virtqueue buffer management. These potential issues (use-after-free, double-free, leaks, or deadlocks) are strictly within the scope of KASAN, LOCKDEP, and standard kernel debugging facilities, not KMSAN.\n\nTherefore, no uninitialized memory risks are introduced, and a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false,
"Reasoning": "The patch introduces inline encryption support and a control virtqueue to the virtio-blk driver (under CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION). It adds new request handling, state tracking, and timeout/abandonment logic for control queue operations, blk-crypto callbacks, and new WARN_ON_ONCE assertions in the callback and drain paths. Because virtio devices are emulated and exercised in virtualized environments, this code is reachable and warrants fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/09 05:44 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit 6ea11839f29c8ce9d6ca1010a93b4181e5bed46f\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Fri Oct 9 05:44:02 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..df24c28d8ca59 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,41 @@ struct virtblk_req {\n \tstruct scatterlist sg[];\n };\n \n+/*\n+ * Software-only completion state for a control-queue request.\n+ */\n+struct virtblk_ctrl_completion {\n+\tstruct completion done;\n+\t/*\n+\t * Set when virtblk_ctrl_vq_request()'s waiter timed out and moved on\n+\t * without freeing this request. Whichever of virtblk_ctrlq_callback()\n+\t * or virtblk_ctrl_vq_drain() later retrieves the buffer must free\n+\t * this struct and the request instead of calling complete() on it.\n+\t */\n+\tbool abandoned;\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 virtblk_ctrl_completion *compl;\n+};\n+\n static inline blk_status_t virtblk_result(u8 status)\n {\n \tswitch (status) {\n@@ -140,12 +200,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-\tsg_init_one(\u0026out_hdr, \u0026vbr-\u003eout_hdr, sizeof(vbr-\u003eout_hdr));\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, 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 +300,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 +324,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 +397,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 +406,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 +668,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 +917,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 +963,736 @@ static int virtblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)\n \treturn ret;\n }\n \n+#define VIRTBLK_CTRL_VQ_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_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\tbool abandoned = creq-\u003ecompl-\u003eabandoned;\n+\n+\t\tif (!WARN_ON_ONCE(!vblk-\u003ectrl_vq.inflight))\n+\t\t\tvblk-\u003ectrl_vq.inflight--;\n+\n+\t\tif (abandoned) {\n+\t\t\tkfree(creq-\u003ecompl);\n+\t\t\tkfree_sensitive(creq);\n+\t\t} else {\n+\t\t\tcreq-\u003estatus = VIRTIO_BLK_S_IOERR;\n+\t\t\tcomplete(\u0026creq-\u003ecompl-\u003edone);\n+\t\t}\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+\t\t\tbool abandoned = creq-\u003ecompl-\u003eabandoned;\n+\n+\t\t\t/*\n+\t\t\t * Skip the inflight decrement/drained check when\n+\t\t\t * inflight was already 0 (a bug, hence WARN_ON_ONCE)\n+\t\t\t * — but always fall through to resolve @creq below\n+\t\t\t * regardless. Never leave a synchronous caller\n+\t\t\t * blocked because the accounting state was already\n+\t\t\t * inconsistent.\n+\t\t\t */\n+\t\t\tif (!WARN_ON_ONCE(!vblk-\u003ectrl_vq.inflight) \u0026\u0026\n+\t\t\t --vblk-\u003ectrl_vq.inflight == 0 \u0026\u0026 vblk-\u003ectrl_vq.dead)\n+\t\t\t\tdrained = true;\n+\n+\t\t\t/*\n+\t\t\t * Decide and act on @abandoned without dropping the lock\n+\t\t\t * to avoid the memory leakage of @creq and its completion\n+\t\t\t * in the race condition that virtblk_ctrl_vq_request()\n+\t\t\t * wakes up from the timeout, and the callback resumes at\n+\t\t\t * the same time.\n+\t\t\t */\n+\t\t\tif (abandoned) {\n+\t\t\t\tkfree(creq-\u003ecompl);\n+\t\t\t\tkfree_sensitive(creq);\n+\t\t\t} else {\n+\t\t\t\tcomplete(\u0026creq-\u003ecompl-\u003edone);\n+\t\t\t}\n+\t\t\tif (drained)\n+\t\t\t\tcomplete(\u0026vblk-\u003ectrl_vq.drained);\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+\tstruct virtblk_ctrl_completion *comp;\n+\tunsigned long flags;\n+\tint err;\n+\n+\t/*\n+\t * GFP_NOIO: this may be reached on the bio-submission path\n+\t * (memory reclaim writing back dirty pages to this same device),\n+\t * so GFP_KERNEL could self-deadlock.\n+\t */\n+\tcomp = kmalloc_obj(*comp, GFP_NOIO);\n+\tif (!comp)\n+\t\treturn -ENOMEM;\n+\tinit_completion(\u0026comp-\u003edone);\n+\tcomp-\u003eabandoned = false;\n+\n+\tmutex_lock(\u0026vblk-\u003ectrl_vq.mutex);\n+\tcreq-\u003ecompl = comp;\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\tkfree(comp);\n+\t\tcreq-\u003ecompl = NULL;\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\tkfree(comp);\n+\t\tcreq-\u003ecompl = NULL;\n+\t\treturn err;\n+\t}\n+\n+\tif (wait_for_completion_timeout(\u0026comp-\u003edone, VIRTBLK_CTRL_VQ_TIMEOUT)) {\n+\t\tmutex_unlock(\u0026vblk-\u003ectrl_vq.mutex);\n+\t\tkfree(comp);\n+\t\tcreq-\u003ecompl = NULL;\n+\t\treturn 0;\n+\t}\n+\n+\t/*\n+\t * The host hasn't responded within the timeout. @creq is still\n+\t * owned by the device, so don't touch its DMA-target fields or\n+\t * free it here. Mark it abandoned and hand ownership of both @creq\n+\t * and @comp to whichever of virtblk_ctrlq_callback() or\n+\t * virtblk_ctrl_vq_drain() retrieves the buffer later; unlock the\n+\t * mutex so subsequent requests aren't serialized behind an\n+\t * unresponsive host.\n+\t */\n+\tspin_lock_irqsave(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\tcomp-\u003eabandoned = true;\n+\tspin_unlock_irqrestore(\u0026vblk-\u003ectrl_vq.lock, flags);\n+\tmutex_unlock(\u0026vblk-\u003ectrl_vq.mutex);\n+\n+\tdev_warn(\u0026vblk-\u003evdev-\u003edev,\n+\t\t \"control queue request timed out, abandoning\\n\");\n+\treturn -ETIMEDOUT;\n+}\n+\n+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)\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 == -ETIMEDOUT)\n+\t\treturn err;\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_mode_to_blk(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+\tunsigned int vtype = blk_key_type_to_virtio(key-\u003ecrypto_cfg.key_type);\n+\n+\tif (sizeof(desc-\u003ebytes) \u003c key-\u003esize)\n+\t\treturn -EOVERFLOW;\n+\tif (!vtype)\n+\t\treturn -EOPNOTSUPP;\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,\n+\t\tblk_mode_to_virtio(key-\u003ecrypto_cfg.crypto_mode));\n+\tdesc-\u003ekey_type = cpu_to_virtio32(vdev, vtype);\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+\tmutex_lock(\u0026vblk-\u003evdev_mutex);\n+\tif (!vblk-\u003evdev) {\n+\t\terr = -ENXIO;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\t/*\n+\t * GFP_NOIO: this callback runs on the bio-submission path, which\n+\t * memory reclaim can reach while writing back dirty pages to this\n+\t * same device; GFP_KERNEL here could recurse into that same reclaim\n+\t * and self-deadlock.\n+\t */\n+\tcreq = kzalloc_obj(*creq, GFP_NOIO);\n+\tif (!creq) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out_unlock;\n+\t}\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 == -ETIMEDOUT)\n+\t\tgoto out_unlock;\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\terr = blk_status_to_errno(virtblk_result(creq-\u003estatus));\n+out_free:\n+\tkfree_sensitive(creq);\n+out_unlock:\n+\tmutex_unlock(\u0026vblk-\u003evdev_mutex);\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+\tmutex_lock(\u0026vblk-\u003evdev_mutex);\n+\tif (!vblk-\u003evdev) {\n+\t\terr = -ENXIO;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\tcreq = kzalloc_obj(*creq, GFP_NOIO);\n+\tif (!creq) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out_unlock;\n+\t}\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 == -ETIMEDOUT)\n+\t\tgoto out_unlock;\n+\tif (err)\n+\t\tgoto out_free;\n+\n+\terr = blk_status_to_errno(virtblk_result(creq-\u003estatus));\n+out_free:\n+\tkfree_sensitive(creq);\n+out_unlock:\n+\tmutex_unlock(\u0026vblk-\u003evdev_mutex);\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+\tstatic_assert(sizeof_field(struct virtblk_ctrl_request, in_resp.secret.secret) \u003e=\n+\t\t BLK_CRYPTO_SW_SECRET_SIZE);\n+\n+\tmutex_lock(\u0026vblk-\u003evdev_mutex);\n+\tif (!vblk-\u003evdev) {\n+\t\terr = -ENXIO;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\tif (eph_key_size \u003e VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) {\n+\t\terr = -EOVERFLOW;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\t/*\n+\t * GFP_NOIO: avoid a self-deadlock. vdev_mutex, held above, is also\n+\t * required by virtblk_crypto_keyslot_program()/_evict(), which direct\n+\t * reclaim could invoke synchronously (writeback needing a keyslot)\n+\t * while this allocation is still in progress under the same mutex.\n+\t */\n+\tcreq = kzalloc_obj(*creq, GFP_NOIO);\n+\tif (!creq) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out_unlock;\n+\t}\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 == -ETIMEDOUT)\n+\t\tgoto out_unlock;\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_sensitive(creq);\n+out_unlock:\n+\tmutex_unlock(\u0026vblk-\u003evdev_mutex);\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+\tmutex_lock(\u0026vblk-\u003evdev_mutex);\n+\tif (!vblk-\u003evdev) {\n+\t\terr = -ENXIO;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\tcreq = kzalloc_obj(*creq, GFP_NOIO);\n+\tif (!creq) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out_unlock;\n+\t}\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 == -ETIMEDOUT)\n+\t\tgoto out_unlock;\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_sensitive(creq);\n+out_unlock:\n+\tmutex_unlock(\u0026vblk-\u003evdev_mutex);\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+\tmutex_lock(\u0026vblk-\u003evdev_mutex);\n+\tif (!vblk-\u003evdev) {\n+\t\terr = -ENXIO;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\tif (lt_key_size \u003e VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) {\n+\t\terr = -EOVERFLOW;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\tcreq = kzalloc_obj(*creq, GFP_NOIO);\n+\tif (!creq) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out_unlock;\n+\t}\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 == -ETIMEDOUT)\n+\t\tgoto out_unlock;\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_sensitive(creq);\n+out_unlock:\n+\tmutex_unlock(\u0026vblk-\u003evdev_mutex);\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+\tmutex_lock(\u0026vblk-\u003evdev_mutex);\n+\tif (!vblk-\u003evdev) {\n+\t\terr = -ENXIO;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\tif (raw_key_size \u003e VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) {\n+\t\terr = -EOVERFLOW;\n+\t\tgoto out_unlock;\n+\t}\n+\n+\tcreq = kzalloc_obj(*creq, GFP_NOIO);\n+\tif (!creq) {\n+\t\terr = -ENOMEM;\n+\t\tgoto out_unlock;\n+\t}\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 == -ETIMEDOUT)\n+\t\tgoto out_unlock;\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_sensitive(creq);\n+out_unlock:\n+\tmutex_unlock(\u0026vblk-\u003evdev_mutex);\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 unsigned int get_supported_blk_key_types(u8 virtio_key_types)\n+{\n+\tunsigned int supported = 0;\n+\n+\tif (virtio_key_types \u0026 VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW)\n+\t\tsupported |= virtio_key_type_to_blk(VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW);\n+\tif (virtio_key_types \u0026 VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED)\n+\t\tsupported |= virtio_key_type_to_blk(VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED);\n+\n+\treturn supported;\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;\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+\tkey_type_supported = get_supported_blk_key_types(key_types);\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 +1790,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 +1820,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 +1849,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 +1864,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@@ -1444,6 +2286,7 @@ static int virtblk_probe(struct virtio_device *vdev)\n \t};\n \tint err, index;\n \tunsigned int queue_depth;\n+\tbool zoned_disk = false;\n \n \tif (!vdev-\u003econfig-\u003eget) {\n \t\tdev_err(\u0026vdev-\u003edev, \"%s failure: config access disabled\\n\",\n@@ -1464,14 +2307,19 @@ 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-\u003ecrypto_profile_initialized = false;\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 +2386,28 @@ 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+\n+\t\tzoned_disk = true;\n+\t}\n+\n+\tif (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\tif (zoned_disk) {\n+\t\t\tdev_info(\u0026vdev-\u003edev,\n+\t\t\t\t\"inline crypto not supported on zoned device\\n\");\n+\t\t} else {\n+\t\t\terr = virtblk_init_crypto(vblk);\n+\t\t\tif (!err) {\n+\t\t\t\tif (!blk_crypto_register(\u0026vblk-\u003eprofile, vblk-\u003edisk-\u003equeue))\n+\t\t\t\t\tdev_warn(\u0026vdev-\u003edev,\n+\t\t\t\t\t\t\"failed to register inline crypto profile\\n\");\n+\t\t\t} else {\n+\t\t\t\tdev_warn(\u0026vdev-\u003edev,\n+\t\t\t\t\t\"inline crypto init failed: %d, continuing without inline crypto support\\n\",\n+\t\t\t\t\terr);\n+\t\t\t}\n+\t\t}\n \t}\n \n \terr = device_add_disk(\u0026vdev-\u003edev, vblk-\u003edisk, virtblk_attr_groups);\n@@ -1553,6 +2423,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 +2442,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@@ -1598,8 +2474,11 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)\n \tblk_mq_quiesce_queue_nowait(q);\n \tblk_mq_unfreeze_queue(q, memflags);\n \n+\tvirtblk_ctrl_vq_quiesce(vblk);\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 +2491,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@@ -1626,6 +2506,11 @@ static int virtblk_restore_priv(struct virtio_device *vdev)\n \t\treturn ret;\n \n \tvirtio_device_ready(vdev);\n+\n+\t/* Reprogram the keys to keyslots. */\n+\tif (vblk-\u003ecrypto_profile_initialized)\n+\t\tblk_crypto_reprogram_all_keys(\u0026vblk-\u003eprofile);\n+\n \tblk_mq_unquiesce_queue(vblk-\u003edisk-\u003equeue);\n \n \treturn 0;\n@@ -1672,6 +2557,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/linux/virtio_blk.h b/include/linux/virtio_blk.h\nnew file mode 100644\nindex 0000000000000..9f5aecad1907f\n--- /dev/null\n+++ b/include/linux/virtio_blk.h\n@@ -0,0 +1,87 @@\n+/* SPDX-License-Identifier: GPL-2.0 */\n+#ifndef _LINUX_VIRTIO_BLK_H\n+#define _LINUX_VIRTIO_BLK_H\n+\n+#include \u003clinux/blk-crypto.h\u003e\n+#include \u003cuapi/linux/virtio_blk.h\u003e\n+\n+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)\n+/**\n+ * virtio_mode_to_blk() - Convert a virtio_blk crypto mode number to a block mode\n+ * @vmode: The virtio_blk crypto mode number (VIRTIO_BLK_CRYPTO_MODE_*).\n+ *\n+ * Return: The corresponding \u0026enum blk_crypto_mode_num, or\n+ * %BLK_ENCRYPTION_MODE_INVALID if @vmode is out of range.\n+ */\n+static inline enum blk_crypto_mode_num virtio_mode_to_blk(unsigned int vmode)\n+{\n+\t/* Indexed by virtio_blk crypto mode number; unlisted entries are 0 (INVALID). */\n+\tstatic const enum blk_crypto_mode_num modes[__VIRTIO_BLK_CRYPTO_MODE_MAX] = {\n+\t\t[VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS] = BLK_ENCRYPTION_MODE_AES_256_XTS,\n+\t};\n+\n+\tif (vmode \u003e= __VIRTIO_BLK_CRYPTO_MODE_MAX)\n+\t\treturn BLK_ENCRYPTION_MODE_INVALID;\n+\treturn modes[vmode];\n+}\n+\n+/**\n+ * blk_mode_to_virtio() - Convert a block crypto mode to a virtio_blk crypto mode number\n+ * @bmode: The kernel \u0026enum blk_crypto_mode_num.\n+ *\n+ * Return: The corresponding virtio_blk crypto mode number, or\n+ * %VIRTIO_BLK_CRYPTO_MODE_INVALID if @bmode is out of range.\n+ */\n+static inline unsigned int blk_mode_to_virtio(enum blk_crypto_mode_num bmode)\n+{\n+\t/* Indexed by blk_crypto_mode_num; unlisted entries are 0 (INVALID). */\n+\tstatic const unsigned int modes[BLK_ENCRYPTION_MODE_MAX] = {\n+\t\t[BLK_ENCRYPTION_MODE_AES_256_XTS] = VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS,\n+\t};\n+\n+\tif (bmode \u003e= BLK_ENCRYPTION_MODE_MAX)\n+\t\treturn VIRTIO_BLK_CRYPTO_MODE_INVALID;\n+\treturn modes[bmode];\n+}\n+\n+/**\n+ * virtio_key_type_to_blk() - Convert a virtio_blk crypto key type to a block key type\n+ * @vtype: The virtio_blk crypto key type (VIRTIO_BLK_CRYPTO_KEY_TYPE_*).\n+ *\n+ * Return: The corresponding \u0026enum blk_crypto_key_type, or 0 if @vtype does\n+ * not name a single supported key type.\n+ */\n+static inline enum blk_crypto_key_type virtio_key_type_to_blk(unsigned int vtype)\n+{\n+\tswitch (vtype) {\n+\tcase VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW:\n+\t\treturn BLK_CRYPTO_KEY_TYPE_RAW;\n+\tcase VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED:\n+\t\treturn BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+\n+/**\n+ * blk_key_type_to_virtio() - Convert a block key type to a virtio_blk crypto key type\n+ * @btype: The kernel \u0026enum blk_crypto_key_type.\n+ *\n+ * Return: The corresponding virtio_blk crypto key type\n+ * (VIRTIO_BLK_CRYPTO_KEY_TYPE_*), or 0 if @btype does not name a\n+ * single supported key type.\n+ */\n+static inline unsigned int blk_key_type_to_virtio(enum blk_crypto_key_type btype)\n+{\n+\tswitch (btype) {\n+\tcase BLK_CRYPTO_KEY_TYPE_RAW:\n+\t\treturn VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW;\n+\tcase BLK_CRYPTO_KEY_TYPE_HW_WRAPPED:\n+\t\treturn VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;\n+\tdefault:\n+\t\treturn 0;\n+\t}\n+}\n+#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */\n+\n+#endif /* _LINUX_VIRTIO_BLK_H */\ndiff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_blk.h\nindex 3744e4da1b2a7..87300907abca8 100644\n--- a/include/uapi/linux/virtio_blk.h\n+++ b/include/uapi/linux/virtio_blk.h\n@@ -1,5 +1,5 @@\n-#ifndef _LINUX_VIRTIO_BLK_H\n-#define _LINUX_VIRTIO_BLK_H\n+#ifndef _UAPI_LINUX_VIRTIO_BLK_H\n+#define _UAPI_LINUX_VIRTIO_BLK_H\n /* This header is BSD licensed so anyone can use the definitions to implement\n * compatible drivers/servers.\n *\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\t20\t/* Control queue */\n+#define VIRTIO_BLK_F_INLINE_ENCRYPTION\t\t21\t/* Inline encryption */\n \n /* Legacy feature bits */\n #ifndef VIRTIO_BLK_NO_LEGACY\n@@ -148,6 +150,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 +217,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 +263,86 @@ 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+/* Key type for VIRTIO_BLK_F_INLINE_ENCRYPTION. */\n+enum virtio_blk_crypto_key_type {\n+\tVIRTIO_BLK_CRYPTO_KEY_TYPE_RAW = 1,\n+\tVIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED,\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, in\n+ * struct virtio_blk_crypto_key_desc.crypto_mode and in indexing struct\n+ * virtio_blk_crypto_modes.modes[] below. These numbers are assigned by\n+ * the virtio spec and are stable: a number is never reused for a different\n+ * crypto mode, and additional crypto modes are assigned new, 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@@ -324,4 +442,4 @@ struct virtio_scsi_inhdr {\n #define VIRTIO_BLK_S_ZONE_OPEN_RESOURCE 5\n #define VIRTIO_BLK_S_ZONE_ACTIVE_RESOURCE 6\n \n-#endif /* _LINUX_VIRTIO_BLK_H */\n+#endif /* _UAPI_LINUX_VIRTIO_BLK_H */\n"
} |
| 2/1 | 2026/10/09 05:44 | agent | patch-evaluator |
2mModel:core Args: null Results: {
"EnableConfigs": [
"VIRTIO_BLK_INLINE_ENCRYPTION",
"VIRTIO_BLK",
"BLK_INLINE_ENCRYPTION",
"VIRTIO"
],
"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_ctrlq_callback",
"virtblk_ctrl_vq_request",
"virtblk_init_crypto"
],
"Reasoning": "The patch introduces inline encryption support and a control virtqueue to the virtio-blk driver (under CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION). It adds new request handling, state tracking, and timeout/abandonment logic for control queue operations, blk-crypto callbacks, and new WARN_ON_ONCE assertions in the callback and drain paths. Because virtio devices are emulated and exercised in virtualized environments, this code is reachable and warrants fuzzing.",
"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 6ea11839f29c8ce9d6ca1010a93b4181e5bed46f
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 9 05:44:02 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..df24c28d8ca59 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,41 @@ struct virtblk_req {
struct scatterlist sg[];
};
+/*
+ * Software-only completion state for a control-queue request.
+ */
+struct virtblk_ctrl_completion {
+ struct completion done;
+ /*
+ * Set when virtblk_ctrl_vq_request()'s waiter timed out and moved on
+ * without freeing this request. Whichever of virtblk_ctrlq_callback()
+ * or virtblk_ctrl_vq_drain() later retrieves the buffer must free
+ * this struct and the request instead of calling complete() on it.
+ */
+ bool abandoned;
+};
+
+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 virtblk_ctrl_completion *compl;
+};
+
static inline blk_status_t virtblk_result(u8 status)
{
switch (status) {
@@ -140,12 +200,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);
- sg_init_one(&out_hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
+ 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, 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 +300,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 +324,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 +397,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 +406,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 +668,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 +917,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 +963,736 @@ static int virtblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)
return ret;
}
+#define VIRTBLK_CTRL_VQ_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_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) {
+ bool abandoned = creq->compl->abandoned;
+
+ if (!WARN_ON_ONCE(!vblk->ctrl_vq.inflight))
+ vblk->ctrl_vq.inflight--;
+
+ if (abandoned) {
+ kfree(creq->compl);
+ kfree_sensitive(creq);
+ } else {
+ creq->status = VIRTIO_BLK_S_IOERR;
+ complete(&creq->compl->done);
+ }
+ }
+ 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;
+ bool abandoned = creq->compl->abandoned;
+
+ /*
+ * Skip the inflight decrement/drained check when
+ * inflight was already 0 (a bug, hence WARN_ON_ONCE)
+ * — but always fall through to resolve @creq below
+ * regardless. Never leave a synchronous caller
+ * blocked because the accounting state was already
+ * inconsistent.
+ */
+ if (!WARN_ON_ONCE(!vblk->ctrl_vq.inflight) &&
+ --vblk->ctrl_vq.inflight == 0 && vblk->ctrl_vq.dead)
+ drained = true;
+
+ /*
+ * Decide and act on @abandoned without dropping the lock
+ * to avoid the memory leakage of @creq and its completion
+ * in the race condition that virtblk_ctrl_vq_request()
+ * wakes up from the timeout, and the callback resumes at
+ * the same time.
+ */
+ if (abandoned) {
+ kfree(creq->compl);
+ kfree_sensitive(creq);
+ } else {
+ complete(&creq->compl->done);
+ }
+ if (drained)
+ complete(&vblk->ctrl_vq.drained);
+ }
+ } 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)
+{
+ struct virtblk_ctrl_completion *comp;
+ unsigned long flags;
+ int err;
+
+ /*
+ * GFP_NOIO: this may be reached on the bio-submission path
+ * (memory reclaim writing back dirty pages to this same device),
+ * so GFP_KERNEL could self-deadlock.
+ */
+ comp = kmalloc_obj(*comp, GFP_NOIO);
+ if (!comp)
+ return -ENOMEM;
+ init_completion(&comp->done);
+ comp->abandoned = false;
+
+ mutex_lock(&vblk->ctrl_vq.mutex);
+ creq->compl = comp;
+
+ 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);
+ kfree(comp);
+ creq->compl = NULL;
+ 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);
+ kfree(comp);
+ creq->compl = NULL;
+ return err;
+ }
+
+ if (wait_for_completion_timeout(&comp->done, VIRTBLK_CTRL_VQ_TIMEOUT)) {
+ mutex_unlock(&vblk->ctrl_vq.mutex);
+ kfree(comp);
+ creq->compl = NULL;
+ return 0;
+ }
+
+ /*
+ * The host hasn't responded within the timeout. @creq is still
+ * owned by the device, so don't touch its DMA-target fields or
+ * free it here. Mark it abandoned and hand ownership of both @creq
+ * and @comp to whichever of virtblk_ctrlq_callback() or
+ * virtblk_ctrl_vq_drain() retrieves the buffer later; unlock the
+ * mutex so subsequent requests aren't serialized behind an
+ * unresponsive host.
+ */
+ spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+ comp->abandoned = true;
+ spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+ mutex_unlock(&vblk->ctrl_vq.mutex);
+
+ dev_warn(&vblk->vdev->dev,
+ "control queue request timed out, abandoning\n");
+ return -ETIMEDOUT;
+}
+
+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)
+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 == -ETIMEDOUT)
+ return err;
+ 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_mode_to_blk(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;
+ unsigned int vtype = blk_key_type_to_virtio(key->crypto_cfg.key_type);
+
+ if (sizeof(desc->bytes) < key->size)
+ return -EOVERFLOW;
+ if (!vtype)
+ return -EOPNOTSUPP;
+
+ 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,
+ blk_mode_to_virtio(key->crypto_cfg.crypto_mode));
+ desc->key_type = cpu_to_virtio32(vdev, vtype);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ /*
+ * GFP_NOIO: this callback runs on the bio-submission path, which
+ * memory reclaim can reach while writing back dirty pages to this
+ * same device; GFP_KERNEL here could recurse into that same reclaim
+ * and self-deadlock.
+ */
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ if (err)
+ goto out_free;
+
+ err = blk_status_to_errno(virtblk_result(creq->status));
+out_free:
+ kfree_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ if (err)
+ goto out_free;
+
+ err = blk_status_to_errno(virtblk_result(creq->status));
+out_free:
+ kfree_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ static_assert(sizeof_field(struct virtblk_ctrl_request, in_resp.secret.secret) >=
+ BLK_CRYPTO_SW_SECRET_SIZE);
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ if (eph_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) {
+ err = -EOVERFLOW;
+ goto out_unlock;
+ }
+
+ /*
+ * GFP_NOIO: avoid a self-deadlock. vdev_mutex, held above, is also
+ * required by virtblk_crypto_keyslot_program()/_evict(), which direct
+ * reclaim could invoke synchronously (writeback needing a keyslot)
+ * while this allocation is still in progress under the same mutex.
+ */
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ 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_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ 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_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ if (lt_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) {
+ err = -EOVERFLOW;
+ goto out_unlock;
+ }
+
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ 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_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ if (raw_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) {
+ err = -EOVERFLOW;
+ goto out_unlock;
+ }
+
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ 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_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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 unsigned int get_supported_blk_key_types(u8 virtio_key_types)
+{
+ unsigned int supported = 0;
+
+ if (virtio_key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW)
+ supported |= virtio_key_type_to_blk(VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW);
+ if (virtio_key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED)
+ supported |= virtio_key_type_to_blk(VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED);
+
+ return supported;
+}
+
+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;
+ 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;
+
+ key_type_supported = get_supported_blk_key_types(key_types);
+ 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 +1790,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 +1820,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 +1849,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 +1864,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);
@@ -1444,6 +2286,7 @@ static int virtblk_probe(struct virtio_device *vdev)
};
int err, index;
unsigned int queue_depth;
+ bool zoned_disk = false;
if (!vdev->config->get) {
dev_err(&vdev->dev, "%s failure: config access disabled\n",
@@ -1464,14 +2307,19 @@ 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->crypto_profile_initialized = false;
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 +2386,28 @@ static int virtblk_probe(struct virtio_device *vdev)
err = blk_revalidate_disk_zones(vblk->disk);
if (err)
goto out_cleanup_disk;
+
+ zoned_disk = true;
+ }
+
+ 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)) {
+ if (zoned_disk) {
+ dev_info(&vdev->dev,
+ "inline crypto not supported on zoned device\n");
+ } else {
+ 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 +2423,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 +2442,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);
@@ -1598,8 +2474,11 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
blk_mq_quiesce_queue_nowait(q);
blk_mq_unfreeze_queue(q, memflags);
+ virtblk_ctrl_vq_quiesce(vblk);
+
/* 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 +2491,7 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
* pointers safely.
*/
vblk->vqs = NULL;
+ vblk->ctrl_vq.vq = NULL;
return 0;
}
@@ -1626,6 +2506,11 @@ static int virtblk_restore_priv(struct virtio_device *vdev)
return ret;
virtio_device_ready(vdev);
+
+ /* Reprogram the keys to keyslots. */
+ if (vblk->crypto_profile_initialized)
+ blk_crypto_reprogram_all_keys(&vblk->profile);
+
blk_mq_unquiesce_queue(vblk->disk->queue);
return 0;
@@ -1672,6 +2557,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/linux/virtio_blk.h b/include/linux/virtio_blk.h
new file mode 100644
index 0000000000000..9f5aecad1907f
--- /dev/null
+++ b/include/linux/virtio_blk.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_VIRTIO_BLK_H
+#define _LINUX_VIRTIO_BLK_H
+
+#include <linux/blk-crypto.h>
+#include <uapi/linux/virtio_blk.h>
+
+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)
+/**
+ * virtio_mode_to_blk() - Convert a virtio_blk crypto mode number to a block mode
+ * @vmode: The virtio_blk crypto mode number (VIRTIO_BLK_CRYPTO_MODE_*).
+ *
+ * Return: The corresponding &enum blk_crypto_mode_num, or
+ * %BLK_ENCRYPTION_MODE_INVALID if @vmode is out of range.
+ */
+static inline enum blk_crypto_mode_num virtio_mode_to_blk(unsigned int vmode)
+{
+ /* Indexed by virtio_blk crypto mode number; unlisted entries are 0 (INVALID). */
+ static const enum blk_crypto_mode_num modes[__VIRTIO_BLK_CRYPTO_MODE_MAX] = {
+ [VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS] = BLK_ENCRYPTION_MODE_AES_256_XTS,
+ };
+
+ if (vmode >= __VIRTIO_BLK_CRYPTO_MODE_MAX)
+ return BLK_ENCRYPTION_MODE_INVALID;
+ return modes[vmode];
+}
+
+/**
+ * blk_mode_to_virtio() - Convert a block crypto mode to a virtio_blk crypto mode number
+ * @bmode: The kernel &enum blk_crypto_mode_num.
+ *
+ * Return: The corresponding virtio_blk crypto mode number, or
+ * %VIRTIO_BLK_CRYPTO_MODE_INVALID if @bmode is out of range.
+ */
+static inline unsigned int blk_mode_to_virtio(enum blk_crypto_mode_num bmode)
+{
+ /* Indexed by blk_crypto_mode_num; unlisted entries are 0 (INVALID). */
+ static const unsigned int modes[BLK_ENCRYPTION_MODE_MAX] = {
+ [BLK_ENCRYPTION_MODE_AES_256_XTS] = VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS,
+ };
+
+ if (bmode >= BLK_ENCRYPTION_MODE_MAX)
+ return VIRTIO_BLK_CRYPTO_MODE_INVALID;
+ return modes[bmode];
+}
+
+/**
+ * virtio_key_type_to_blk() - Convert a virtio_blk crypto key type to a block key type
+ * @vtype: The virtio_blk crypto key type (VIRTIO_BLK_CRYPTO_KEY_TYPE_*).
+ *
+ * Return: The corresponding &enum blk_crypto_key_type, or 0 if @vtype does
+ * not name a single supported key type.
+ */
+static inline enum blk_crypto_key_type virtio_key_type_to_blk(unsigned int vtype)
+{
+ switch (vtype) {
+ case VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW:
+ return BLK_CRYPTO_KEY_TYPE_RAW;
+ case VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED:
+ return BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * blk_key_type_to_virtio() - Convert a block key type to a virtio_blk crypto key type
+ * @btype: The kernel &enum blk_crypto_key_type.
+ *
+ * Return: The corresponding virtio_blk crypto key type
+ * (VIRTIO_BLK_CRYPTO_KEY_TYPE_*), or 0 if @btype does not name a
+ * single supported key type.
+ */
+static inline unsigned int blk_key_type_to_virtio(enum blk_crypto_key_type btype)
+{
+ switch (btype) {
+ case BLK_CRYPTO_KEY_TYPE_RAW:
+ return VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW;
+ case BLK_CRYPTO_KEY_TYPE_HW_WRAPPED:
+ return VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
+ default:
+ return 0;
+ }
+}
+#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */
+
+#endif /* _LINUX_VIRTIO_BLK_H */
diff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_blk.h
index 3744e4da1b2a7..87300907abca8 100644
--- a/include/uapi/linux/virtio_blk.h
+++ b/include/uapi/linux/virtio_blk.h
@@ -1,5 +1,5 @@
-#ifndef _LINUX_VIRTIO_BLK_H
-#define _LINUX_VIRTIO_BLK_H
+#ifndef _UAPI_LINUX_VIRTIO_BLK_H
+#define _UAPI_LINUX_VIRTIO_BLK_H
/* This header is BSD licensed so anyone can use the definitions to implement
* compatible drivers/servers.
*
@@ -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 20 /* Control queue */
+#define VIRTIO_BLK_F_INLINE_ENCRYPTION 21 /* Inline encryption */
/* Legacy feature bits */
#ifndef VIRTIO_BLK_NO_LEGACY
@@ -148,6 +150,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 +217,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 +263,86 @@ 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];
+};
+
+/* Key type for VIRTIO_BLK_F_INLINE_ENCRYPTION. */
+enum virtio_blk_crypto_key_type {
+ VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW = 1,
+ VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED,
+};
+
+/*
+ * 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, in
+ * struct virtio_blk_crypto_key_desc.crypto_mode 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.
*/
@@ -324,4 +442,4 @@ struct virtio_scsi_inhdr {
#define VIRTIO_BLK_S_ZONE_OPEN_RESOURCE 5
#define VIRTIO_BLK_S_ZONE_ACTIVE_RESOURCE 6
-#endif /* _LINUX_VIRTIO_BLK_H */
+#endif /* _UAPI_LINUX_VIRTIO_BLK_H */
|
| 3/2 | 2026/10/09 05:44 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 21404 output: 26 thoughts: 520 Thoughts: **Evaluating the Kernel Patch for Syzkaller Fuzzing Potential** Okay, so I need to assess this kernel patch and determine if it's a good candidate for fuzzing with syzkaller. The user wants a thorough evaluation, and that means being methodical. First things first: let's dive into the diff and the instructions, making sure I haven't missed any details. The instructions are clear - reachability is the primary gate. If the code can't *execute* in a standard virtualized environment like GCE or QEMU, then it's a no-go. No point fuzzing unreachable code, that's just a waste of cycles. The patch is touching `drivers/block/virtio_blk.c`, `drivers/block/Kconfig`, `include/linux/virtio_blk.h`, and `include/uapi/linux/virtio_blk.h`. Okay, so it's related to the virtio block driver, which immediately piques my interest. It introduces `CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION`. The key question becomes: can standard QEMU or syzkaller actually *emulate* or test this new functionality? Does it support the flags `VIRTIO_BLK_F_INLINE_ENCRYPTION` or `VIRTIO_BLK_F_CTRL_VQ`? I need to verify that before I go any further. I need to quickly figure out if syzkaller even *considers* virtio devices reachable. My mental checklist kicks in: Can standard QEMU create or simulate virtio devices? Are there any virtio drivers in syzkaller that could be leveraged? Are virtio devices even part of syzkaller's test suite? Time to double-check the provided instructions. Let's make sure I didn't misinterpret the guidelines. "Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.)" - Yep, virtio is explicitly listed. Now, to get more specific: How exactly *is* `CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION` reached? I need to check the Kconfig and the relevant code. Time to fire up `grepper` or check git grep and track down the conditional compilation to confirm its conditions. |
| 4/2 | 2026/10/09 05:44 | tool | grepper |
0mArgs:{
"Expression": "VIRTIO_BLK_INLINE_ENCRYPTION"
}Results: {
"Output": "drivers/block/Kconfig=356=config BLK_DEV_ZONED_LOOP\n--\ndrivers/block/Kconfig-374-\ndrivers/block/Kconfig:375:config VIRTIO_BLK_INLINE_ENCRYPTION\ndrivers/block/Kconfig-376-\ttristate \"Virtio block inline encryption support\"\n--\ndrivers/block/virtio_blk.c=297=static void virtblk_cleanup_cmd(struct request *req)\n--\ndrivers/block/virtio_blk.c-302-\ndrivers/block/virtio_blk.c:303:#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)\ndrivers/block/virtio_blk.c-304-static bool is_crypto_request(struct request *req)\n--\ndrivers/block/virtio_blk.c=1065=static int virtblk_ctrl_vq_request(struct virtio_blk *vblk,\n--\ndrivers/block/virtio_blk.c-1134-\ndrivers/block/virtio_blk.c:1135:#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)\ndrivers/block/virtio_blk.c-1136-static int virtblk_get_crypto_modes(struct virtio_blk *vblk,\n--\ndrivers/block/virtio_blk.c=1684=static inline void virtblk_destroy_crypto(struct virtio_blk *vblk)\n--\ndrivers/block/virtio_blk.c-1686-}\ndrivers/block/virtio_blk.c:1687:#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */\ndrivers/block/virtio_blk.c-1688-\n--\ndrivers/block/virtio_blk.c=2280=static int virtblk_probe(struct virtio_device *vdev)\n--\ndrivers/block/virtio_blk.c-2392-\ndrivers/block/virtio_blk.c:2393:\tif (IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION) \u0026\u0026\ndrivers/block/virtio_blk.c-2394-\t\t virtio_has_feature(vdev, VIRTIO_BLK_F_INLINE_ENCRYPTION) \u0026\u0026\n--\ninclude/linux/virtio_blk.h-7-\ninclude/linux/virtio_blk.h:8:#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)\ninclude/linux/virtio_blk.h-9-/**\n--\ninclude/linux/virtio_blk.h=74=static inline unsigned int blk_key_type_to_virtio(enum blk_crypto_key_type btype)\n--\ninclude/linux/virtio_blk.h-84-}\ninclude/linux/virtio_blk.h:85:#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */\ninclude/linux/virtio_blk.h-86-\n"
} |
| 5/2 | 2026/10/09 05:44 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23214 output: 20 thoughts: 1311 Thoughts: **Analyzing Virtio-blk Crypto Integration** Okay, let's dive into these changes outside the `CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION` conditional. I'm focusing on `drivers/block/virtio_blk.c`. First, I'm noting the structural changes: `struct virtio_blk_ctrl_vq` within `struct virtio_blk`, `struct virtblk_req` which now has a union with `crypto_append` in `out_hdr`, and `struct virtblk_ctrl_completion` and `struct virtblk_ctrl_request`. Then I'm keying in on `virtblk_add_req` where the `out_hdr_len` is conditionally set based on the `type` field of `vbr->out_hdr`. If it's `VIRTIO_BLK_T_CRYPTO_IN` or `VIRTIO_BLK_T_CRYPTO_OUT`, the length uses `crypto_append`; otherwise, it's the base structure. The scatter-gather list is initialized accordingly. Next up is `virtblk_setup_cmd` where the operation type (read or write) is set to `VIRTIO_BLK_T_CRYPTO_IN` or `VIRTIO_BLK_T_CRYPTO_OUT` if the request is a "crypto request," otherwise it defaults to the standard `VIRTIO_BLK_T_IN` or `VIRTIO_BLK_T_OUT`. The sector is taken from `blk_rq_pos(req)`. Now, the control-related functions – `virtblk_ctrl_vq_quiesce`, `virtblk_ctrl_vq_drain`, `virtblk_ctrlq_callback`, and `virtblk_ctrl_vq_request` – are intriguing. I'm wondering if their usage is *exclusively* tied to crypto, so I'm checking their call sites. I've found they're used in `virtblk_remove` and `virtblk_freeze_priv`. Looking at `init_vq`, I see `VIRTIO_BLK_F_CTRL_VQ` is negotiated. This suggests that even without crypto enabled, there's control queue infrastructure that can be set up, and it's initialized with mutexes and a spinlock in the probe function. The `ctrl_vq.vq` field is conditional on having the feature enabled. Here's the critical bit: the `probe` function has a conditional block that executes if `CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION` *and* both `VIRTIO_BLK_F_INLINE_ENCRYPTION` *and* `VIRTIO_BLK_F_CTRL_VQ` are present. This triggers `virtblk_init_crypto`, followed by registering a crypto profile using `blk_crypto_register`. This registration is the trigger, it seems! This is where the standard block crypto stuff will kick in; the various `virtblk_crypto_*` functions are called by the kernel via the block layer, and then the requests are translated and sent to QEMU via virtio. The `VIRTIO_BLK_T_CRYPTO_IN` and `VIRTIO_BLK_T_CRYPTO_OUT` types are used in these requests. Also `virtblk_restore_priv` calls `blk_crypto_reprogram_all_keys`. My real question now is: is this reachable in a standard virtualized environment (QEMU, GCE, etc.)? I need to check if QEMU supports `VIRTIO_BLK_F_INLINE_ENCRYPTION` or `VIRTIO_BLK_F_CTRL_VQ`. Is this something that needs to be manually configured, or is it automatic? Does QEMU create virtio devices that support this, or can this be enabled in Linux? I'm thinking about vhost, virtio-net, virtio-blk test, or something like vringh. And I'm also thinking about whether this could be tested/fuzzed using syzkaller, or if there's a virtio device simulator in the kernel itself. Time to do some more digging within the kernel source and maybe start grepping through drivers and syzkaller to see what I can find. |
| 6/2 | 2026/10/09 05:44 | tool | grepper |
0mArgs:{
"Expression": "register_virtio_device"
}Results: {
"Output": "Documentation/driver-api/virtio/virtio.rst=96=device is registered to the virtio bus::\n--\nDocumentation/driver-api/virtio/virtio.rst-119-\nDocumentation/driver-api/virtio/virtio.rst:120:\t\trc = register_virtio_device(\u0026vp_dev-\u003evdev);\nDocumentation/driver-api/virtio/virtio.rst-121-\n--\narch/um/drivers/virtio_uml.c=1208=static int virtio_uml_probe(struct platform_device *pdev)\n--\narch/um/drivers/virtio_uml.c-1252-\narch/um/drivers/virtio_uml.c:1253:\trc = register_virtio_device(\u0026vu_dev-\u003evdev);\narch/um/drivers/virtio_uml.c-1254-\tif (rc) {\n--\narch/um/drivers/virtio_uml.c=1268=static void virtio_uml_remove(struct platform_device *pdev)\n--\narch/um/drivers/virtio_uml.c-1271-\narch/um/drivers/virtio_uml.c:1272:\tunregister_virtio_device(\u0026vu_dev-\u003evdev);\narch/um/drivers/virtio_uml.c-1273-}\n--\ndrivers/platform/mellanox/mlxbf-tmfifo.c=1189=static int mlxbf_tmfifo_create_vdev(struct device *dev,\n--\ndrivers/platform/mellanox/mlxbf-tmfifo.c-1233-\t/* Register the virtio device. */\ndrivers/platform/mellanox/mlxbf-tmfifo.c:1234:\tret = register_virtio_device(\u0026tm_vdev-\u003evdev);\ndrivers/platform/mellanox/mlxbf-tmfifo.c-1235-\treg_dev = tm_vdev;\ndrivers/platform/mellanox/mlxbf-tmfifo.c-1236-\tif (ret) {\ndrivers/platform/mellanox/mlxbf-tmfifo.c:1237:\t\tdev_err(dev, \"register_virtio_device failed\\n\");\ndrivers/platform/mellanox/mlxbf-tmfifo.c-1238-\t\tgoto vdev_fail;\n--\ndrivers/platform/mellanox/mlxbf-tmfifo.c=1257=static int mlxbf_tmfifo_delete_vdev(struct mlxbf_tmfifo *fifo, int vdev_id)\n--\ndrivers/platform/mellanox/mlxbf-tmfifo.c-1265-\tif (tm_vdev) {\ndrivers/platform/mellanox/mlxbf-tmfifo.c:1266:\t\tunregister_virtio_device(\u0026tm_vdev-\u003evdev);\ndrivers/platform/mellanox/mlxbf-tmfifo.c-1267-\t\tmlxbf_tmfifo_free_vrings(fifo, tm_vdev);\n--\ndrivers/remoteproc/remoteproc_virtio.c=370=static int rproc_add_virtio_dev(struct rproc_vdev *rvdev, int id)\n--\ndrivers/remoteproc/remoteproc_virtio.c-445-\ndrivers/remoteproc/remoteproc_virtio.c:446:\tret = register_virtio_device(vdev);\ndrivers/remoteproc/remoteproc_virtio.c-447-\tif (ret) {\n--\ndrivers/remoteproc/remoteproc_virtio.c=468=static int rproc_remove_virtio_dev(struct device *dev, void *data)\n--\ndrivers/remoteproc/remoteproc_virtio.c-471-\ndrivers/remoteproc/remoteproc_virtio.c:472:\tunregister_virtio_device(vdev);\ndrivers/remoteproc/remoteproc_virtio.c-473-\treturn 0;\n--\ndrivers/s390/virtio/virtio_ccw.c=1289=static void virtio_ccw_remove(struct ccw_device *cdev)\n--\ndrivers/s390/virtio/virtio_ccw.c-1296-\t\t\tvirtio_break_device(\u0026vcdev-\u003evdev);\ndrivers/s390/virtio/virtio_ccw.c:1297:\t\tunregister_virtio_device(\u0026vcdev-\u003evdev);\ndrivers/s390/virtio/virtio_ccw.c-1298-\t\tspin_lock_irqsave(get_ccwdev_lock(cdev), flags);\n--\ndrivers/s390/virtio/virtio_ccw.c=1305=static int virtio_ccw_offline(struct ccw_device *cdev)\n--\ndrivers/s390/virtio/virtio_ccw.c-1313-\t\tvirtio_break_device(\u0026vcdev-\u003evdev);\ndrivers/s390/virtio/virtio_ccw.c:1314:\tunregister_virtio_device(\u0026vcdev-\u003evdev);\ndrivers/s390/virtio/virtio_ccw.c-1315-\tspin_lock_irqsave(get_ccwdev_lock(cdev), flags);\n--\ndrivers/s390/virtio/virtio_ccw.c=1367=static int virtio_ccw_online(struct ccw_device *cdev)\n--\ndrivers/s390/virtio/virtio_ccw.c-1409-\ndrivers/s390/virtio/virtio_ccw.c:1410:\tret = register_virtio_device(\u0026vcdev-\u003evdev);\ndrivers/s390/virtio/virtio_ccw.c-1411-\tif (ret) {\n--\ndrivers/virtio/virtio.c=488=static int virtio_device_of_init(struct virtio_device *dev)\n--\ndrivers/virtio/virtio.c-530-/**\ndrivers/virtio/virtio.c:531: * register_virtio_device - register virtio device\ndrivers/virtio/virtio.c-532- * @dev : virtio device to be registered\n--\ndrivers/virtio/virtio.c-538- */\ndrivers/virtio/virtio.c:539:int register_virtio_device(struct virtio_device *dev)\ndrivers/virtio/virtio.c-540-{\n--\ndrivers/virtio/virtio.c-594-}\ndrivers/virtio/virtio.c:595:EXPORT_SYMBOL_GPL(register_virtio_device);\ndrivers/virtio/virtio.c-596-\n--\ndrivers/virtio/virtio.c=601=EXPORT_SYMBOL_GPL(is_virtio_device);\ndrivers/virtio/virtio.c-602-\ndrivers/virtio/virtio.c:603:void unregister_virtio_device(struct virtio_device *dev)\ndrivers/virtio/virtio.c-604-{\n--\ndrivers/virtio/virtio.c-610-}\ndrivers/virtio/virtio.c:611:EXPORT_SYMBOL_GPL(unregister_virtio_device);\ndrivers/virtio/virtio.c-612-\n--\ndrivers/virtio/virtio_mmio.c=579=static int virtio_mmio_probe(struct platform_device *pdev)\n--\ndrivers/virtio/virtio_mmio.c-648-\ndrivers/virtio/virtio_mmio.c:649:\trc = register_virtio_device(\u0026vm_dev-\u003evdev);\ndrivers/virtio/virtio_mmio.c-650-\tif (rc)\n--\ndrivers/virtio/virtio_mmio.c=660=static void virtio_mmio_remove(struct platform_device *pdev)\n--\ndrivers/virtio/virtio_mmio.c-662-\tstruct virtio_mmio_device *vm_dev = platform_get_drvdata(pdev);\ndrivers/virtio/virtio_mmio.c:663:\tunregister_virtio_device(\u0026vm_dev-\u003evdev);\ndrivers/virtio/virtio_mmio.c-664-}\n--\ndrivers/virtio/virtio_pci_common.c=681=static int virtio_pci_probe(struct pci_dev *pci_dev,\n--\ndrivers/virtio/virtio_pci_common.c-721-\ndrivers/virtio/virtio_pci_common.c:722:\trc = register_virtio_device(\u0026vp_dev-\u003evdev);\ndrivers/virtio/virtio_pci_common.c-723-\treg_dev = vp_dev;\n--\ndrivers/virtio/virtio_pci_common.c=744=static void virtio_pci_remove(struct pci_dev *pci_dev)\n--\ndrivers/virtio/virtio_pci_common.c-757-\ndrivers/virtio/virtio_pci_common.c:758:\tunregister_virtio_device(\u0026vp_dev-\u003evdev);\ndrivers/virtio/virtio_pci_common.c-759-\n--\ndrivers/virtio/virtio_vdpa.c=459=static int virtio_vdpa_probe(struct vdpa_device *vdpa)\n--\ndrivers/virtio/virtio_vdpa.c-480-\tvd_dev-\u003evdev.id.vendor = ops-\u003eget_vendor_id(vdpa);\ndrivers/virtio/virtio_vdpa.c:481:\tret = register_virtio_device(\u0026vd_dev-\u003evdev);\ndrivers/virtio/virtio_vdpa.c-482-\treg_dev = vd_dev;\n--\ndrivers/virtio/virtio_vdpa.c=498=static void virtio_vdpa_remove(struct vdpa_device *vdpa)\n--\ndrivers/virtio/virtio_vdpa.c-501-\ndrivers/virtio/virtio_vdpa.c:502:\tunregister_virtio_device(\u0026vd_dev-\u003evdev);\ndrivers/virtio/virtio_vdpa.c-503-}\n--\ninclude/linux/virtio.h=195=void virtio_add_status(struct virtio_device *dev, unsigned int status);\ninclude/linux/virtio.h:196:int register_virtio_device(struct virtio_device *dev);\ninclude/linux/virtio.h:197:void unregister_virtio_device(struct virtio_device *dev);\ninclude/linux/virtio.h-198-bool is_virtio_device(struct device *dev);\n"
} |
| 7/2 | 2026/10/09 05:44 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 28261 output: 20 thoughts: 317 Thoughts: **My Virtio Device Brainstorm** Okay, so I'm seeing "virtio" pop up a lot, specifically in the context of virtualized environments like QEMU and Google Compute Engine (GCE). It's explicitly listed among drivers accessible via virtual buses, which makes sense. I see it mentioned right in the instructions, clear as day: `Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.)`. My next thought is, can syzkaller actually interact with `virtio_blk`? Is it used as a root or secondary disk within QEMU or GCE? I need to clarify how syzkaller boots the kernel in QEMU. Ah, yes, syzkaller does boot with `virtio-blk` drives! For example, it would use `-drive file=...,if=virtio` or `/dev/vda`. But wait, does the *host* QEMU configuration negotiate features like `VIRTIO_BLK_F_INLINE_ENCRYPTION`? Or are there other options I need to consider? Can we use `vdpa`, `vhost-user`, or `virtio_vdpa` or even mock devices? Ah, and of course, how does syzkaller test block devices: is it through ioctls, `blk-crypto`, `fscrypt`, or some other mechanism? But then I thought, wait a minute...can we simulate a virtio device *in userspace* using something like `vdpa` or `vhost`? Yes! We should be able to create `vdpa` devices in userspace with something like `vdpa-sim`. Okay, let's look into `vdpa_sim_blk`. |
| 8/2 | 2026/10/09 05:44 | tool | grepper |
0mArgs:{
"Expression": "vdpa_sim_blk"
}Results: {
"Output": "drivers/vdpa/vdpa_sim/Makefile=3=obj-$(CONFIG_VDPA_SIM_NET) += vdpa_sim_net.o\ndrivers/vdpa/vdpa_sim/Makefile:4:obj-$(CONFIG_VDPA_SIM_BLOCK) += vdpa_sim_blk.o\n"
} |
| 9/2 | 2026/10/09 05:44 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29059 output: 24 thoughts: 458 Thoughts: **My Initial Assessment and Fuzzing Strategy** Okay, so `vdpa_sim_blk` exists, which is excellent news! The fact that vdpa devices can be created through netlink, and that syzkaller already has netlink descriptions for vdpa, is a huge boost. It means we're starting from a solid base of supported functionality. Plus, virtio-blk is a standard in virtualized environments, making it a prime candidate for fuzzing based on the core guideline: REACHABILITY IS THE PRIMARY GATE. Virtio is explicitly mentioned as a reachable virtual bus! This is good. But before I get too carried away, I need to analyze the specific changes in detail. I have to look at what's being touched, any added assertions or complex logic, the required configurations, and determine the right focus symbols for fuzzing. Let's dive into `drivers/block/virtio_blk.c` and `drivers/block/Kconfig`. First, let me check the `Kconfig`: ``` 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. ``` Okay, the `VIRTIO_BLK_INLINE_ENCRYPTION` config option looks interesting. It depends on `VIRTIO_BLK` and `BLK_INLINE_ENCRYPTION`. What does `BLK_INLINE_ENCRYPTION` depend on? Let me check its definition in `Kconfig` as well. I need to understand all dependencies before making any fuzzing decisions. |
| 10/2 | 2026/10/09 05:44 | tool | grepper |
0mArgs:{
"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/10/09 05:44 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30118 output: 32 thoughts: 17 Thoughts: Okay, let's dive into this. Here's my thought process on this: **Examining `block/Kconfig` - Line 195** Alright, I'm thinking we need to take a look at the `block/Kconfig` file. Specifically, I'm going to zero in on around line 195. Given my experience, that's often a good area to start when dealing with block device configuration, features, or perhaps dependencies. This might be where some key settings related to a particular feature or driver are defined, enabled, or disabled. I expect to find some configuration symbols or dependencies there, and I need to see how they're set and why. Let's see what's there. |
| 12/2 | 2026/10/09 05:45 | tool | read-file |
0mArgs:{
"File": "block/Kconfig",
"FirstLine": 190,
"LineCount": 20
}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"
} |
| 13/2 | 2026/10/09 05:45 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30633 output: 20 thoughts: 49 Thoughts: Okay, here's my take on those thoughts, framed as my own: **Investigating Block Device Encryption in the Kernel Configuration** Alright, so `BLK_INLINE_ENCRYPTION` is a straightforward boolean option, and it doesn't seem to rely on anything else. My immediate thought is, what's the interaction with virtual block devices, specifically `VIRTIO_BLK`? I need to understand how inline encryption plays with the virtualized block layer. Let's dive into the `drivers/block/Kconfig` file. I'm going to quickly check the configuration options associated with `VIRTIO_BLK` there. This should help me see if there are any obvious dependencies, implications, or conflicts between these two configurations. I need to ensure that inline encryption works appropriately, or, if not compatible, that there are clear warnings or graceful handling in place when `VIRTIO_BLK` is enabled. Time to get my hands dirty. |
| 14/2 | 2026/10/09 05:45 | tool | grepper |
0mArgs:{
"Expression": "config VIRTIO_BLK"
}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--\ndrivers/block/Kconfig=356=config BLK_DEV_ZONED_LOOP\n--\ndrivers/block/Kconfig-374-\ndrivers/block/Kconfig:375:config VIRTIO_BLK_INLINE_ENCRYPTION\ndrivers/block/Kconfig-376-\ttristate \"Virtio block inline encryption support\"\n"
} |
| 15/2 | 2026/10/09 05:45 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31080 output: 34 thoughts: 18 Thoughts: Here's a summary of my thought process, as requested: **Navigating the Block Driver Configuration Landscape** Okay, so I'm diving into this block driver configuration. Specifically, I need to get a handle on how this particular feature is controlled, so I'm going to look into `drivers/block/Kconfig`. That file is the central point for block device configuration options. And since I know my way around the kernel source, I can navigate quickly - aiming straight for line 307. I'm expecting that line, or the surrounding lines, will give me the key to understanding the relevant configuration options that determine if this feature is enabled or not, depending on the other configurations that are active. I am expecting that that section will tell me what dependencies it has, and perhaps the default value associated with the configuration setting. From there, I can determine how this feature ties into the overall block driver architecture. |
| 16/2 | 2026/10/09 05:45 | tool | read-file |
0mArgs:{
"File": "drivers/block/Kconfig",
"FirstLine": 305,
"LineCount": 20
}Results: {
"Contents": " 305:\t\n 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"
} |
| 17/2 | 2026/10/09 05:45 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31616 output: 29 thoughts: 14 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Diving into the Block Driver Makefile** Right, let's get down to brass tacks. I'm going to head over to the `drivers/block/` directory and immediately zero in on the `Makefile`. Given the complexity of the block layer, I'm expecting this to be a crucial file. It's the nerve center for how all those individual block driver modules are built, linked, and potentially managed during the kernel compilation process. I want to quickly understand the structure of this thing: Does it follow a standard pattern, or are there any quirky custom rules specific to this block subsystem? What variables are defined? How are dependencies handled? Are they using any clever tricks to optimize the build or conditionally compile different drivers based on the kernel config? I need to understand this `Makefile` to quickly grasp the build process for a specific module, or for debugging issues that could stem from the build configuration. I will be looking to see if any of the newer facilities are being used such as `$(obj)/` and `.depend` or if legacy makefile idioms are being deployed. |
| 18/2 | 2026/10/09 05:45 | tool | grepper |
0mArgs:{
"Expression": "virtio_blk",
"PathPrefixes": [
"drivers/block/"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 492 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (2 files in total):\ndrivers/block/Makefile:1\ndrivers/block/virtio_blk.c:120\n\ndrivers/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=152=struct virtblk_ctrl_request {\n--\ndrivers/block/virtio_blk.c-156-\tunion {\ndrivers/block/virtio_blk.c:157:\t\tstruct virtio_blk_crypto_key_desc key_desc;\ndrivers/block/virtio_blk.c:158:\t\tstruct virtio_blk_crypto_key_blob blob;\ndrivers/block/virtio_blk.c-159-\t} out_req;\n--\ndrivers/block/virtio_blk.c-162-\tunion {\ndrivers/block/virtio_blk.c:163:\t\tstruct virtio_blk_crypto_key_blob blob;\ndrivers/block/virtio_blk.c:164:\t\tstruct virtio_blk_crypto_sw_secret secret;\ndrivers/block/virtio_blk.c:165:\t\tstruct virtio_blk_crypto_modes modes;\ndrivers/block/virtio_blk.c-166-\t} in_resp;\n--\ndrivers/block/virtio_blk.c=173=static inline blk_status_t virtblk_result(u8 status)\n--\ndrivers/block/virtio_blk.c-190-\ndrivers/block/virtio_blk.c:191:static inline struct virtio_blk_vq *get_virtio_blk_vq(struct blk_mq_hw_ctx *hctx)\ndrivers/block/virtio_blk.c-192-{\ndrivers/block/virtio_blk.c:193:\tstruct virtio_blk *vblk = hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c:194:\tstruct virtio_blk_vq *vq = \u0026vblk-\u003evqs[hctx-\u003equeue_num];\ndrivers/block/virtio_blk.c-195-\n--\ndrivers/block/virtio_blk.c=225=static int virtblk_setup_discard_write_zeroes_erase(struct request *req, bool unmap)\n--\ndrivers/block/virtio_blk.c-228-\tunsigned short n = 0;\ndrivers/block/virtio_blk.c:229:\tstruct virtio_blk_discard_write_zeroes *range;\ndrivers/block/virtio_blk.c-230-\tstruct bio *bio;\n--\ndrivers/block/virtio_blk.c=434=static inline void virtblk_request_done(struct request *req)\n--\ndrivers/block/virtio_blk.c-437-\tblk_status_t status = virtblk_result(virtblk_vbr_status(vbr));\ndrivers/block/virtio_blk.c:438:\tstruct virtio_blk *vblk = req-\u003emq_hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c-439-\n--\ndrivers/block/virtio_blk.c=450=static void virtblk_done(struct virtqueue *vq)\ndrivers/block/virtio_blk.c-451-{\ndrivers/block/virtio_blk.c:452:\tstruct virtio_blk *vblk = vq-\u003evdev-\u003epriv;\ndrivers/block/virtio_blk.c-453-\tbool req_done = false;\n--\ndrivers/block/virtio_blk.c=477=static void virtio_commit_rqs(struct blk_mq_hw_ctx *hctx)\ndrivers/block/virtio_blk.c-478-{\ndrivers/block/virtio_blk.c:479:\tstruct virtio_blk *vblk = hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c:480:\tstruct virtio_blk_vq *vq = \u0026vblk-\u003evqs[hctx-\u003equeue_num];\ndrivers/block/virtio_blk.c-481-\tbool kick;\n--\ndrivers/block/virtio_blk.c=504=static blk_status_t virtblk_prep_rq(struct blk_mq_hw_ctx *hctx,\ndrivers/block/virtio_blk.c:505:\t\t\t\t\tstruct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-506-\t\t\t\t\tstruct request *req,\n--\ndrivers/block/virtio_blk.c=526=static blk_status_t virtio_queue_rq(struct blk_mq_hw_ctx *hctx,\n--\ndrivers/block/virtio_blk.c-528-{\ndrivers/block/virtio_blk.c:529:\tstruct virtio_blk *vblk = hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c-530-\tstruct request *req = bd-\u003erq;\n--\ndrivers/block/virtio_blk.c=565=static bool virtblk_prep_rq_batch(struct request *req)\ndrivers/block/virtio_blk.c-566-{\ndrivers/block/virtio_blk.c:567:\tstruct virtio_blk *vblk = req-\u003emq_hctx-\u003equeue-\u003equeuedata;\ndrivers/block/virtio_blk.c-568-\tstruct virtblk_req *vbr = blk_mq_rq_to_pdu(req);\n--\ndrivers/block/virtio_blk.c-572-\ndrivers/block/virtio_blk.c:573:static void virtblk_add_req_batch(struct virtio_blk_vq *vq,\ndrivers/block/virtio_blk.c-574-\t\tstruct rq_list *rqlist)\n--\ndrivers/block/virtio_blk.c=601=static void virtio_queue_rqs(struct rq_list *rqlist)\n--\ndrivers/block/virtio_blk.c-604-\tstruct rq_list requeue_list = { };\ndrivers/block/virtio_blk.c:605:\tstruct virtio_blk_vq *vq = NULL;\ndrivers/block/virtio_blk.c-606-\tstruct request *req;\n--\ndrivers/block/virtio_blk.c-608-\twhile ((req = rq_list_pop(rqlist))) {\ndrivers/block/virtio_blk.c:609:\t\tstruct virtio_blk_vq *this_vq = get_virtio_blk_vq(req-\u003emq_hctx);\ndrivers/block/virtio_blk.c-610-\n--\ndrivers/block/virtio_blk.c-626-#ifdef CONFIG_BLK_DEV_ZONED\ndrivers/block/virtio_blk.c:627:static void *virtblk_alloc_report_buffer(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-628-\t\t\t\t\t unsigned int nr_zones,\n--\ndrivers/block/virtio_blk.c-637-\ndrivers/block/virtio_blk.c:638:\tbufsize = sizeof(struct virtio_blk_zone_report) +\ndrivers/block/virtio_blk.c:639:\t\tnr_zones * sizeof(struct virtio_blk_zone_descriptor);\ndrivers/block/virtio_blk.c-640-\tbufsize = min_t(size_t, bufsize,\n--\ndrivers/block/virtio_blk.c-643-\ndrivers/block/virtio_blk.c:644:\twhile (bufsize \u003e= sizeof(struct virtio_blk_zone_report)) {\ndrivers/block/virtio_blk.c-645-\t\tbuf = __vmalloc(bufsize, GFP_KERNEL | __GFP_NORETRY);\n--\ndrivers/block/virtio_blk.c-655-\ndrivers/block/virtio_blk.c:656:static int virtblk_submit_zone_report(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-657-\t\t\t\t char *report_buf, size_t report_len,\n--\ndrivers/block/virtio_blk.c-684-\ndrivers/block/virtio_blk.c:685:static int virtblk_parse_zone(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c:686:\t\t\t struct virtio_blk_zone_descriptor *entry,\ndrivers/block/virtio_blk.c-687-\t\t\t unsigned int idx,\n--\ndrivers/block/virtio_blk.c=757=static int virtblk_report_zones(struct gendisk *disk, sector_t sector,\n--\ndrivers/block/virtio_blk.c-760-{\ndrivers/block/virtio_blk.c:761:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c:762:\tstruct virtio_blk_zone_report *report;\ndrivers/block/virtio_blk.c-763-\tunsigned long long nz, i;\n--\ndrivers/block/virtio_blk.c-819-\ndrivers/block/virtio_blk.c:820:static int virtblk_read_zoned_limits(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-821-\t\tstruct queue_limits *lim)\n--\ndrivers/block/virtio_blk.c-829-\ndrivers/block/virtio_blk.c:830:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-831-\t\t zoned.max_open_zones, \u0026v);\n--\ndrivers/block/virtio_blk.c-834-\ndrivers/block/virtio_blk.c:835:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-836-\t\t zoned.max_active_zones, \u0026v);\n--\ndrivers/block/virtio_blk.c-839-\ndrivers/block/virtio_blk.c:840:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-841-\t\t zoned.write_granularity, \u0026wg);\n--\ndrivers/block/virtio_blk.c-854-\t */\ndrivers/block/virtio_blk.c:855:\tvirtio_cread(vdev, struct virtio_blk_config, zoned.zone_sectors,\ndrivers/block/virtio_blk.c-856-\t\t \u0026vblk-\u003ezone_sectors);\n--\ndrivers/block/virtio_blk.c-871-\ndrivers/block/virtio_blk.c:872:\tvirtio_cread(vdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-873-\t\t zoned.max_append_sectors, \u0026v);\n--\ndrivers/block/virtio_blk.c-894-#define virtblk_report_zones NULL\ndrivers/block/virtio_blk.c:895:static inline int virtblk_read_zoned_limits(struct virtio_blk *vblk,\ndrivers/block/virtio_blk.c-896-\t\tstruct queue_limits *lim)\n--\ndrivers/block/virtio_blk.c-898-\tdev_err(\u0026vblk-\u003evdev-\u003edev,\ndrivers/block/virtio_blk.c:899:\t\t\"virtio_blk: zoned devices are not supported\");\ndrivers/block/virtio_blk.c-900-\treturn -EOPNOTSUPP;\n--\ndrivers/block/virtio_blk.c=906=static int virtblk_get_id(struct gendisk *disk, char *id_str)\ndrivers/block/virtio_blk.c-907-{\ndrivers/block/virtio_blk.c:908:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c-909-\tstruct request_queue *q = vblk-\u003edisk-\u003equeue;\n--\ndrivers/block/virtio_blk.c=935=static int virtblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)\ndrivers/block/virtio_blk.c-936-{\ndrivers/block/virtio_blk.c:937:\tstruct virtio_blk *vblk = disk-\u003eprivate_data;\ndrivers/block/virtio_blk.c-938-\tint ret = 0;\n--\ndrivers/block/virtio_blk.c-948-\tif (virtio_has_feature(vblk-\u003evdev, VIRTIO_BLK_F_GEOMETRY)) {\ndrivers/block/virtio_blk.c:949:\t\tvirtio_cread(vblk-\u003evdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-950-\t\t\t geometry.cylinders, \u0026geo-\u003ecylinders);\ndrivers/block/virtio_blk.c:951:\t\tvirtio_cread(vblk-\u003evdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-952-\t\t\t geometry.heads, \u0026geo-\u003eheads);\ndrivers/block/virtio_blk.c:953:\t\tvirtio_cread(vblk-\u003evdev, struct virtio_blk_config,\ndrivers/block/virtio_blk.c-954-\t\t\t geometry.sectors, \u0026geo-\u003esectors);\n--\ndrivers/block/virtio_blk.c-968-/* Prevent new submissions and wait for in-flight requests to complete. */\ndrivers/block/virtio_blk.c:969:static void virtblk_ctrl_vq_quiesce(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-970-{\n--\ndrivers/block/virtio_blk.c-991-/* Fail requests left in the control queue after reset. */\ndrivers/block/virtio_blk.c:992:static void virtblk_ctrl_vq_drain(struct virtio_blk *vblk)\ndrivers/block/virtio_blk.c-993-{\n--\n\n"
} |
| 19/2 | 2026/10/09 05:45 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 36223 output: 32 thoughts: 661 Thoughts: **Analyzing the VIRTIO_BLK_INLINE_ENCRYPTION Configuration** Okay, let's break down how this `VIRTIO_BLK_INLINE_ENCRYPTION` is built. First, checking the `drivers/block/Makefile`, I see that `virtio_blk.o` is built when `CONFIG_VIRTIO_BLK` is set. Then, looking at `drivers/block/virtio_blk.c`, the code uses `#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)`. Makes sense so far. Now, peeking into `drivers/block/Kconfig`, it defines `VIRTIO_BLK_INLINE_ENCRYPTION` as `tristate` and dependent on both `VIRTIO_BLK` and `BLK_INLINE_ENCRYPTION`. That's a key observation: it's *tristate*. A slight wrinkle though. Despite its `tristate` status, there's no separate object file like `obj-$(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)` in the `Makefile`. That means it's directly compiled into `virtio_blk.o`. Got it. Thinking this through, if `CONFIG_VIRTIO_BLK` is `y` and `CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION` is `m`, `IS_ENABLED()` will still evaluate to true. In fact, it works if both are `y`, or if `VIRTIO_BLK` is `m` and `VIRTIO_BLK_INLINE_ENCRYPTION` is `m`. A slight design quirk, as bools are usually preferred for sub-features, but since `IS_ENABLED()` works correctly for both, it's not a major issue. So, what are the *essential* configs required for the code to be compiled? It comes down to: 1. `VIRTIO_BLK` (and its implicit dependency, `VIRTIO`) 2. `BLK_INLINE_ENCRYPTION` 3. `VIRTIO_BLK_INLINE_ENCRYPTION` Next step, check whether `BLK_INLINE_ENCRYPTION` has other dependencies, or picks any other config. Scanning through `block/Kconfig`, it's defined as a simple `bool` option, without any `depends on` clauses. Also, looking in that `Kconfig` for the `BLK_INLINE_ENCRYPTION` config, I see that it does not `select` anything. Alright, that simplifies things! |
| 20/2 | 2026/10/09 05:45 | tool | read-file |
0mArgs:{
"File": "block/Kconfig",
"FirstLine": 195,
"LineCount": 20
}Results: {
"Contents": " 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"
} |
| 21/2 | 2026/10/09 05:45 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 37780 output: 31 thoughts: 28 Thoughts: Okay, here's my interpretation of those thoughts, tailored for an expert audience, presented in the first person: **Investigating Block Device Crypto Profiles** Alright, time to dig into the block device crypto configuration. My immediate thought is to check for the macro definition related to crypto profiles, specifically `BLK_CRYPTO_PROFILE` – I'll also double-check, in case I'm off on the casing and need to confirm `blk_crypto_profile`. This is likely where the core feature flag or settings reside. Then I'll dive into `block/Makefile` to see how that macro is being used. This Makefile is the obvious location to see its effects on the build process and what modules might be included or excluded based on its value. Time to see what's what. |
| 22/2 | 2026/10/09 05:46 | tool | grepper |
0mArgs:{
"Expression": "BLK_INLINE_ENCRYPTION",
"PathPrefixes": [
"block/"
]
}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\"\nblock/Kconfig:204:\tdepends on BLK_INLINE_ENCRYPTION\nblock/Kconfig-205-\tselect CRYPTO\n--\nblock/Makefile=37=obj-$(CONFIG_BLK_PM)\t\t+= blk-pm.o\nblock/Makefile:38:obj-$(CONFIG_BLK_INLINE_ENCRYPTION)\t+= blk-crypto.o blk-crypto-profile.o \\\nblock/Makefile-39-\t\t\t\t\t blk-crypto-sysfs.o\nblock/Makefile:40:obj-$(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK)\t+= blk-crypto-fallback.o\nblock/Makefile-41-obj-$(CONFIG_BLOCK_HOLDER_DEPRECATED)\t+= holder.o\n--\nblock/bio.c=217=void bio_init(struct bio *bio, struct block_device *bdev, struct bio_vec *table,\n--\nblock/bio.c-243-#endif\nblock/bio.c:244:#ifdef CONFIG_BLK_INLINE_ENCRYPTION\nblock/bio.c-245-\tbio-\u003ebi_crypt_context = NULL;\n--\nblock/blk-crypto-internal.h=21=extern const struct blk_crypto_mode blk_crypto_modes[];\nblock/blk-crypto-internal.h-22-\nblock/blk-crypto-internal.h:23:#ifdef CONFIG_BLK_INLINE_ENCRYPTION\nblock/blk-crypto-internal.h-24-\n--\nblock/blk-crypto-internal.h=86=static inline bool blk_crypto_supported(struct bio *bio)\n--\nblock/blk-crypto-internal.h-91-\nblock/blk-crypto-internal.h:92:#else /* CONFIG_BLK_INLINE_ENCRYPTION */\nblock/blk-crypto-internal.h-93-\n--\nblock/blk-crypto-internal.h=145=static inline bool blk_crypto_supported(struct bio *bio)\n--\nblock/blk-crypto-internal.h-149-\nblock/blk-crypto-internal.h:150:#endif /* CONFIG_BLK_INLINE_ENCRYPTION */\nblock/blk-crypto-internal.h-151-\n--\nblock/blk-crypto-internal.h=166=static inline void bio_crypt_do_front_merge(struct request *rq,\n--\nblock/blk-crypto-internal.h-168-{\nblock/blk-crypto-internal.h:169:#ifdef CONFIG_BLK_INLINE_ENCRYPTION\nblock/blk-crypto-internal.h-170-\tif (bio_has_crypt_ctx(bio))\n--\nblock/blk-crypto-internal.h=218=bool blk_crypto_fallback_bio_prep(struct bio *bio);\nblock/blk-crypto-internal.h-219-\nblock/blk-crypto-internal.h:220:#ifdef CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK\nblock/blk-crypto-internal.h-221-\n--\nblock/blk-crypto-internal.h=224=int blk_crypto_fallback_evict_key(const struct blk_crypto_key *key);\nblock/blk-crypto-internal.h-225-\nblock/blk-crypto-internal.h:226:#else /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */\nblock/blk-crypto-internal.h-227-\n--\nblock/blk-crypto-internal.h=236=blk_crypto_fallback_evict_key(const struct blk_crypto_key *key)\n--\nblock/blk-crypto-internal.h-240-\nblock/blk-crypto-internal.h:241:#endif /* CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK */\nblock/blk-crypto-internal.h-242-\n--\nblock/blk-crypto.c=252=bool __blk_crypto_submit_bio(struct bio *bio)\n--\nblock/blk-crypto.c-267-\tif (!blk_crypto_config_supported_natively(bdev, \u0026bc_key-\u003ecrypto_cfg)) {\nblock/blk-crypto.c:268:\t\tif (!IS_ENABLED(CONFIG_BLK_INLINE_ENCRYPTION_FALLBACK)) {\nblock/blk-crypto.c-269-\t\t\tpr_warn_once(\"%pg: crypto API fallback disabled; failing request.\\n\",\n--\nblock/blk-settings.c=545=int queue_limits_commit_update(struct request_queue *q,\n--\nblock/blk-settings.c-555-\nblock/blk-settings.c:556:#ifdef CONFIG_BLK_INLINE_ENCRYPTION\nblock/blk-settings.c-557-\tif (q-\u003ecrypto_profile \u0026\u0026 lim-\u003eintegrity.tag_size) {\n"
} |
| 23/2 | 2026/10/09 05:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39332 output: 32 thoughts: 61 Thoughts: **Investigating Inline Encryption in Block Devices** Okay, let's dive into this inline encryption business within block devices. My immediate question is, how does this actually get fuzzed? Can userspace even *trigger* inline encryption operations directly? I need to figure out the user-facing interface, the exposed entry points. So, let's get down to brass tacks: time to grep the codebase. I'm thinking I'll start with calls to `blk_crypto_`, any keyslot programming functions, and anything related to `fscrypt`. That should give me a solid starting point for understanding where userspace interacts (or *can* interact) with this inline encryption functionality. Gotta trace the data flow from there. |
| 24/2 | 2026/10/09 05:46 | tool | grepper |
0mArgs:{
"Expression": "blk_crypto_",
"PathPrefixes": [
"fs/",
"drivers/"
]
}Results: {
"Output": "\nFull output is too long, showing 200 out of 705 lines.\nUse more precise expression or PathPrefixes if possible.\n\nNumber of matching lines per file (21 files in total):\ndrivers/block/virtio_blk.c:40\ndrivers/md/dm-core.h:1\ndrivers/md/dm-inlinecrypt.c:10\ndrivers/md/dm-table.c:27\ndrivers/mmc/core/crypto.c:4\ndrivers/mmc/host/cqhci-crypto.c:14\ndrivers/mmc/host/sdhci-msm.c:14\ndrivers/soc/qcom/ice.c:3\ndrivers/ufs/core/ufshcd-crypto.c:13\ndrivers/ufs/core/ufshcd-crypto.h:1\ndrivers/ufs/host/ufs-exynos.c:2\ndrivers/ufs/host/ufs-qcom.c:13\nfs/buffer.c:1\nfs/crypto/block.c:16\nfs/crypto/fscrypt_private.h:4\nfs/crypto/keysetup.c:4\nfs/ext4/page-io.c:1\nfs/ext4/readpage.c:4\nfs/f2fs/data.c:2\nfs/f2fs/file.c:2\nfs/iomap/direct-io.c:1\n\ndrivers/block/virtio_blk.c=66=struct virtio_blk {\n--\ndrivers/block/virtio_blk.c-98-\t/* For inline encryption support */\ndrivers/block/virtio_blk.c:99:\tstruct blk_crypto_profile profile;\ndrivers/block/virtio_blk.c-100-\tbool crypto_profile_initialized;\n--\ndrivers/block/virtio_blk.c=106=struct virtblk_req {\n--\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=152=struct virtblk_ctrl_request {\n--\ndrivers/block/virtio_blk.c-156-\tunion {\ndrivers/block/virtio_blk.c:157:\t\tstruct virtio_blk_crypto_key_desc key_desc;\ndrivers/block/virtio_blk.c:158:\t\tstruct virtio_blk_crypto_key_blob blob;\ndrivers/block/virtio_blk.c-159-\t} out_req;\n--\ndrivers/block/virtio_blk.c-162-\tunion {\ndrivers/block/virtio_blk.c:163:\t\tstruct virtio_blk_crypto_key_blob blob;\ndrivers/block/virtio_blk.c:164:\t\tstruct virtio_blk_crypto_sw_secret secret;\ndrivers/block/virtio_blk.c:165:\t\tstruct virtio_blk_crypto_modes modes;\ndrivers/block/virtio_blk.c-166-\t} in_resp;\n--\ndrivers/block/virtio_blk.c=319=static blk_status_t virtblk_setup_cmd(struct virtio_device *vdev,\n--\ndrivers/block/virtio_blk.c-413-\t\t\tcpu_to_virtio32(vdev,\ndrivers/block/virtio_blk.c:414:\t\t\t\tblk_crypto_keyslot_index(req-\u003ecrypt_keyslot));\ndrivers/block/virtio_blk.c-415-\t\tfor (i = 0; i \u003c ARRAY_SIZE(vbr-\u003eout_hdr.crypto_append.msg.dun); i++) {\n--\ndrivers/block/virtio_blk.c=1136=static int virtblk_get_crypto_modes(struct virtio_blk *vblk,\n--\ndrivers/block/virtio_blk.c-1170-\t\t\t\t\t\t creq-\u003ein_resp.modes.modes[i]);\ndrivers/block/virtio_blk.c:1171:\t\tenum blk_crypto_mode_num mode = virtio_mode_to_blk(i);\ndrivers/block/virtio_blk.c-1172-\n--\ndrivers/block/virtio_blk.c-1187-\ndrivers/block/virtio_blk.c:1188:static int set_virtblk_crypto_key_desc(struct virtio_device *vdev,\ndrivers/block/virtio_blk.c-1189-\t\t\t\t struct virtblk_ctrl_request *creq,\ndrivers/block/virtio_blk.c:1190:\t\t\t\t const struct blk_crypto_key *key,\ndrivers/block/virtio_blk.c-1191-\t\t\t\t unsigned int slot)\ndrivers/block/virtio_blk.c-1192-{\ndrivers/block/virtio_blk.c:1193:\tstruct virtio_blk_crypto_key_desc *desc = \u0026creq-\u003eout_req.key_desc;\ndrivers/block/virtio_blk.c-1194-\tunsigned int vtype = blk_key_type_to_virtio(key-\u003ecrypto_cfg.key_type);\n--\ndrivers/block/virtio_blk.c-1213-\ndrivers/block/virtio_blk.c:1214:static inline struct virtio_blk *virtblk_from_profile(struct blk_crypto_profile *profile)\ndrivers/block/virtio_blk.c-1215-{\n--\ndrivers/block/virtio_blk.c-1218-\ndrivers/block/virtio_blk.c:1219:static int virtblk_crypto_keyslot_program(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c:1220:\t\t\t\t\t const struct blk_crypto_key *key,\ndrivers/block/virtio_blk.c-1221-\t\t\t\t\t unsigned int slot)\n--\ndrivers/block/virtio_blk.c-1247-\ndrivers/block/virtio_blk.c:1248:\terr = set_virtblk_crypto_key_desc(vblk-\u003evdev, creq, key, slot);\ndrivers/block/virtio_blk.c-1249-\tif (err)\n--\ndrivers/block/virtio_blk.c-1272-\ndrivers/block/virtio_blk.c:1273:static int virtblk_crypto_keyslot_evict(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c:1274:\t\t\t\t\t const struct blk_crypto_key *key,\ndrivers/block/virtio_blk.c-1275-\t\t\t\t\t unsigned int slot)\n--\ndrivers/block/virtio_blk.c-1295-\ndrivers/block/virtio_blk.c:1296:\terr = set_virtblk_crypto_key_desc(vblk-\u003evdev, creq, key, slot);\ndrivers/block/virtio_blk.c-1297-\tif (err)\n--\ndrivers/block/virtio_blk.c-1320-\ndrivers/block/virtio_blk.c:1321:static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c-1322-\t\t\t\t\t const u8 *eph_key, size_t eph_key_size,\n--\ndrivers/block/virtio_blk.c-1345-\t * GFP_NOIO: avoid a self-deadlock. vdev_mutex, held above, is also\ndrivers/block/virtio_blk.c:1346:\t * required by virtblk_crypto_keyslot_program()/_evict(), which direct\ndrivers/block/virtio_blk.c-1347-\t * reclaim could invoke synchronously (writeback needing a keyslot)\n--\ndrivers/block/virtio_blk.c-1386-\ndrivers/block/virtio_blk.c:1387:static int virtblk_crypto_generate_key(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c-1388-\t\t\t\t\tu8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])\n--\ndrivers/block/virtio_blk.c-1443-\ndrivers/block/virtio_blk.c:1444:static int virtblk_crypto_prepare_key(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c-1445-\t\t\t\t const u8 *lt_key, size_t lt_key_size,\n--\ndrivers/block/virtio_blk.c-1510-\ndrivers/block/virtio_blk.c:1511:static int virtblk_crypto_import_key(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c-1512-\t\t\t\t const u8 *raw_key, size_t raw_key_size,\n--\ndrivers/block/virtio_blk.c-1577-\ndrivers/block/virtio_blk.c:1578:static const struct blk_crypto_ll_ops virtblk_crypto_ops = {\ndrivers/block/virtio_blk.c:1579:\t.keyslot_program\t= virtblk_crypto_keyslot_program,\ndrivers/block/virtio_blk.c:1580:\t.keyslot_evict\t\t= virtblk_crypto_keyslot_evict,\ndrivers/block/virtio_blk.c:1581:\t.derive_sw_secret\t= virtblk_crypto_derive_sw_secret,\ndrivers/block/virtio_blk.c:1582:\t.generate_key\t\t= virtblk_crypto_generate_key,\ndrivers/block/virtio_blk.c:1583:\t.prepare_key\t\t= virtblk_crypto_prepare_key,\ndrivers/block/virtio_blk.c:1584:\t.import_key\t\t= virtblk_crypto_import_key,\ndrivers/block/virtio_blk.c-1585-};\n--\ndrivers/block/virtio_blk.c=1599=static int virtblk_init_crypto(struct virtio_blk *vblk)\n--\ndrivers/block/virtio_blk.c-1623-\t/*\ndrivers/block/virtio_blk.c:1624:\t * struct virtio_blk_crypto_msg.dun is a fixed array of four __virtio64\ndrivers/block/virtio_blk.c-1625-\t * values (32 bytes total), matching the size of\ndrivers/block/virtio_blk.c:1626:\t * blk_crypto_ctx::bc_dun[4]. Refuse to advertise more than that as\ndrivers/block/virtio_blk.c-1627-\t * supported, or blk-crypto could negotiate a larger dun_bytes with the\n--\ndrivers/block/virtio_blk.c-1630-\t */\ndrivers/block/virtio_blk.c:1631:\tif (max_dun_bytes \u003e sizeof_field(struct virtio_blk_crypto_msg, dun))\ndrivers/block/virtio_blk.c-1632-\t\treturn -EINVAL;\n--\ndrivers/block/virtio_blk.c-1648-\t * destroy callback after virtblk_remove() has already freed vblk.\ndrivers/block/virtio_blk.c:1649:\t * virtblk_free_disk() calls blk_crypto_profile_destroy() explicitly\ndrivers/block/virtio_blk.c-1650-\t * instead, guarded by crypto_profile_initialized below.\ndrivers/block/virtio_blk.c-1651-\t */\ndrivers/block/virtio_blk.c:1652:\terr = blk_crypto_profile_init(\u0026vblk-\u003eprofile, max_slots);\ndrivers/block/virtio_blk.c-1653-\tif (err) {\n--\ndrivers/block/virtio_blk.c-1657-\ndrivers/block/virtio_blk.c:1658:\tvblk-\u003eprofile.ll_ops = virtblk_crypto_ops;\ndrivers/block/virtio_blk.c-1659-\tvblk-\u003eprofile.max_dun_bytes_supported = max_dun_bytes;\n--\ndrivers/block/virtio_blk.c=1672=static void virtblk_destroy_crypto(struct virtio_blk *vblk)\n--\ndrivers/block/virtio_blk.c-1674-\tif (vblk-\u003ecrypto_profile_initialized)\ndrivers/block/virtio_blk.c:1675:\t\tblk_crypto_profile_destroy(\u0026vblk-\u003eprofile);\ndrivers/block/virtio_blk.c-1676-}\n--\ndrivers/block/virtio_blk.c=2280=static int virtblk_probe(struct virtio_device *vdev)\n--\ndrivers/block/virtio_blk.c-2401-\t\t\tif (!err) {\ndrivers/block/virtio_blk.c:2402:\t\t\t\tif (!blk_crypto_register(\u0026vblk-\u003eprofile, vblk-\u003edisk-\u003equeue))\ndrivers/block/virtio_blk.c-2403-\t\t\t\t\tdev_warn(\u0026vdev-\u003edev,\n--\ndrivers/block/virtio_blk.c=2499=static int virtblk_restore_priv(struct virtio_device *vdev)\n--\ndrivers/block/virtio_blk.c-2511-\tif (vblk-\u003ecrypto_profile_initialized)\ndrivers/block/virtio_blk.c:2512:\t\tblk_crypto_reprogram_all_keys(\u0026vblk-\u003eprofile);\ndrivers/block/virtio_blk.c-2513-\n--\ndrivers/md/dm-core.h=189=struct dm_table {\n--\ndrivers/md/dm-core.h-225-#ifdef CONFIG_BLK_INLINE_ENCRYPTION\ndrivers/md/dm-core.h:226:\tstruct blk_crypto_profile *crypto_profile;\ndrivers/md/dm-core.h-227-#endif\n--\ndrivers/md/dm-inlinecrypt.c=15=static const struct dm_inlinecrypt_cipher {\ndrivers/md/dm-inlinecrypt.c-16-\tconst char *name;\ndrivers/md/dm-inlinecrypt.c:17:\tenum blk_crypto_mode_num mode_num;\ndrivers/md/dm-inlinecrypt.c-18-} dm_inlinecrypt_ciphers[] = {\n--\ndrivers/md/dm-inlinecrypt.c=40=struct inlinecrypt_ctx {\n--\ndrivers/md/dm-inlinecrypt.c-47-\tunsigned int sector_bits;\ndrivers/md/dm-inlinecrypt.c:48:\tenum blk_crypto_key_type key_type;\ndrivers/md/dm-inlinecrypt.c:49:\tstruct blk_crypto_key key;\ndrivers/md/dm-inlinecrypt.c-50-\tu64 max_dun;\n--\ndrivers/md/dm-inlinecrypt.c=65=static void inlinecrypt_dtr(struct dm_target *ti)\n--\ndrivers/md/dm-inlinecrypt.c-70-\t\tif (ctx-\u003ekey.size)\ndrivers/md/dm-inlinecrypt.c:71:\t\t\tblk_crypto_evict_key(ctx-\u003edev-\u003ebdev, \u0026ctx-\u003ekey);\ndrivers/md/dm-inlinecrypt.c-72-\t\tdm_put_device(ti, ctx-\u003edev);\n--\ndrivers/md/dm-inlinecrypt.c=310=static int inlinecrypt_ctr(struct dm_target *ti, unsigned int argc, char **argv)\n--\ndrivers/md/dm-inlinecrypt.c-407-\ndrivers/md/dm-inlinecrypt.c:408:\terr = blk_crypto_init_key(\u0026ctx-\u003ekey, key_bytes, ctx-\u003ekey_size,\ndrivers/md/dm-inlinecrypt.c-409-\t\t\t\t ctx-\u003ekey_type, cipher-\u003emode_num,\n--\ndrivers/md/dm-inlinecrypt.c-416-\ndrivers/md/dm-inlinecrypt.c:417:\terr = blk_crypto_start_using_key(ctx-\u003edev-\u003ebdev, \u0026ctx-\u003ekey);\ndrivers/md/dm-inlinecrypt.c-418-\tif (err) {\n--\ndrivers/md/dm-inlinecrypt.c=435=static int inlinecrypt_map(struct dm_target *ti, struct bio *bio)\n--\ndrivers/md/dm-inlinecrypt.c-489-\t * To get the correct accounting for a dm target in the case where\ndrivers/md/dm-inlinecrypt.c:490:\t * __blk_crypto_submit_bio() doesn't take ownership of the bio (returns\ndrivers/md/dm-inlinecrypt.c:491:\t * true), call __blk_crypto_submit_bio() directly and return\ndrivers/md/dm-inlinecrypt.c-492-\t * DM_MAPIO_REMAPPED in that case, rather than relying on\ndrivers/md/dm-inlinecrypt.c:493:\t * blk_crypto_submit_bio() which calls submit_bio() in that case.\ndrivers/md/dm-inlinecrypt.c-494-\t *\n--\ndrivers/md/dm-inlinecrypt.c-499-\t */\ndrivers/md/dm-inlinecrypt.c:500:\tif (__blk_crypto_submit_bio(bio))\ndrivers/md/dm-inlinecrypt.c-501-\t\treturn DM_MAPIO_REMAPPED;\n--\ndrivers/md/dm-table.c=1139=struct dm_crypto_profile {\ndrivers/md/dm-table.c:1140:\tstruct blk_crypto_profile profile;\ndrivers/md/dm-table.c-1141-\tstruct mapped_device *md;\n--\ndrivers/md/dm-table.c=1144=static int dm_keyslot_evict_callback(struct dm_target *ti, struct dm_dev *dev,\n--\ndrivers/md/dm-table.c-1146-{\ndrivers/md/dm-table.c:1147:\tconst struct blk_crypto_key *key = data;\ndrivers/md/dm-table.c-1148-\ndrivers/md/dm-table.c:1149:\tblk_crypto_evict_key(dev-\u003ebdev, key);\ndrivers/md/dm-table.c-1150-\treturn 0;\n--\ndrivers/md/dm-table.c-1156- */\ndrivers/md/dm-table.c:1157:static int dm_keyslot_evict(struct blk_crypto_profile *profile,\n\n"
} |
| 25/2 | 2026/10/09 05:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44336 output: 20 thoughts: 1855 Thoughts: **Fuzzing Target Identification: virtio-blk Inline Encryption** Okay, so I'm looking at this patch and immediately see the potential for some serious fuzzing. The fact that `dm-inlinecrypt`, and `fscrypt` all use inline encryption profiles on block devices is interesting, and the presence of `syzkaller` descriptions for these areas (like `FS_IOC_ADD_ENCRYPTION_KEY`) is a good sign. The implementation for a control virtqueue added in `drivers/block/virtio_blk.c` with new synchronization, queue handling, and a timeout mechanism are clearly important. I'm focusing on the functions specifically related to crypto key management and how they interact with the control queue, which are: * `virtblk_ctrl_vq_quiesce` * `virtblk_ctrl_vq_drain` * `virtblk_ctrlq_callback` * `virtblk_ctrl_vq_request` * `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` * `virtblk_crypto_import_key` * `virtblk_init_crypto` * `virtblk_destroy_crypto` * and in `virtblk_setup_cmd`: handling `VIRTIO_BLK_T_CRYPTO_IN` and `VIRTIO_BLK_T_CRYPTO_OUT` * and `virtblk_add_req`: handling `crypto_append` * and `virtblk_restore_priv`: `blk_crypto_reprogram_all_keys` The concurrency and assertions within `virtblk_ctrlq_callback` and `virtblk_ctrl_vq_drain` are particularly juicy. The `WARN_ON_ONCE` assertions, especially the one checking `vblk->ctrl_vq.inflight`, are screaming for targeted fuzzing. I have to make sure that these assertions are reachable and that the control queue is appropriately handled during I/O operations. I'm following the fuzzing guidance, and I'm checking if the new or modified assertions are in reachable code. Based on the documentation, this means I need to make sure the patch introduces or modifies runtime checks or assertions (e.g., WARN_ON\*, VM_WARN_ON\*, BUG_ON\*, lockdep\_assert\*) in reachable code paths, enforcing new or stricter invariants. Now, let's determine if this code is worth fuzzing and, if so, what config options are needed. The code isn't structurally unreachable. It's not a vendor-specific driver. `virtio` is an emulated virtual bus, and the code is definitely reachable. Therefore, `WorthFuzzing` is definitely `true`. To enable this, I need to know the specific Kconfig flags that enable and compile this code, and based on the dependencies and the code, I see that these are: * `VIRTIO_BLK_INLINE_ENCRYPTION` * `VIRTIO_BLK` * `BLK_INLINE_ENCRYPTION` * `VIRTIO` (since VIRTIO_BLK depends on VIRTIO) And I'll need to check if there are other flags like `VIRTIO_PCI` as these are usually used by QEMU. |
| 26/2 | 2026/10/09 05:46 | tool | grepper |
0mArgs:{
"Expression": "config VIRTIO_PCI"
}Results: {
"Output": "drivers/virtio/Kconfig=5=config VIRTIO\n--\ndrivers/virtio/Kconfig-12-\ndrivers/virtio/Kconfig:13:config VIRTIO_PCI_LIB\ndrivers/virtio/Kconfig-14-\ttristate\n--\ndrivers/virtio/Kconfig-20-\ndrivers/virtio/Kconfig:21:config VIRTIO_PCI_LIB_LEGACY\ndrivers/virtio/Kconfig-22-\ttristate\n--\ndrivers/virtio/Kconfig=36=config VIRTIO_HARDEN_NOTIFICATION\n--\ndrivers/virtio/Kconfig-49-\ndrivers/virtio/Kconfig:50:config VIRTIO_PCI\ndrivers/virtio/Kconfig-51-\ttristate \"PCI driver for virtio devices\"\n--\ndrivers/virtio/Kconfig-62-\ndrivers/virtio/Kconfig:63:config VIRTIO_PCI_ADMIN_LEGACY\ndrivers/virtio/Kconfig-64-\tbool\n--\ndrivers/virtio/Kconfig-67-\ndrivers/virtio/Kconfig:68:config VIRTIO_PCI_LEGACY\ndrivers/virtio/Kconfig-69-\tbool \"Support for legacy virtio draft 0.9.X and older devices\"\n"
} |
| 27/2 | 2026/10/09 05:46 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47341 output: 271 thoughts: 1690 Thoughts: **Fuzzing Configuration for Virtio-blk Inline Encryption** Okay, let's break this down. First, regarding `EnableConfigs`, the task is to identify the necessary `CONFIG_` options to enable and reach the modified code. I've checked the `Kconfig` files and any `#ifdef` guards, and haven't made any assumptions. From the `Kconfig` files, I've identified the following dependencies: `VIRTIO_BLK_INLINE_ENCRYPTION` depends on `VIRTIO_BLK` and `BLK_INLINE_ENCRYPTION`, `VIRTIO_BLK` depends on `VIRTIO`. `BLK_INLINE_ENCRYPTION` has no further dependencies. Therefore, after stripping the `CONFIG_` prefix, the relevant configs are: `"VIRTIO_BLK_INLINE_ENCRYPTION"`, `"VIRTIO_BLK"`, `"BLK_INLINE_ENCRYPTION"`, and `"VIRTIO"`. Next, the critical part - `FocusSymbols`. The goal here is to guide the fuzzer by pinpointing specific functions involved in the feature's logic. I need to avoid the 'ubiquitous lifecycle hot-paths' and concentrate on the specific logic being added or altered, or entry points for the subsystem feature under review. Static inline functions in headers require special consideration – I need to target their non-static callers in `.c` files. Looking at the changes in `drivers/block/virtio_blk.c`, I've carefully reviewed the added and modified functions. I've considered: `virtblk_ctrl_vq_quiesce`, `virtblk_ctrl_vq_drain`, `virtblk_ctrlq_callback`, `virtblk_ctrl_vq_request`, `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`, `virtblk_destroy_crypto`, `set_virtblk_crypto_key_desc`, `get_supported_blk_key_types`, `is_crypto_request`, and the modified functions `virtblk_setup_cmd`, `virtblk_add_req`, `virtblk_probe`, `init_vq`, `virtblk_remove`, `virtblk_freeze_priv` and `virtblk_restore_priv`. I've paid careful attention to see if static functions are inlined by the compiler. Because their addresses are taken, certain functions like `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` are assigned to function pointers and will be distinct symbols. Similarly `virtblk_ctrlq_callback`, and `virtblk_ctrl_vq_request` will also likely be distinct symbols. `virtblk_init_crypto` and `virtblk_ctrl_vq_drain` are good candidates too. After careful consideration and ensuring these functions are not ubiquitous, I've selected the following as `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_ctrlq_callback` * `virtblk_ctrl_vq_request` * `virtblk_init_crypto` These functions are specific to the new inline encryption and control virtqueue functionality, and are therefore appropriate focus symbols. I've made certain the selected configuration fulfills all requirements, and created a concise reason as follows: The patch introduces support for inline encryption and a control virtqueue to the virtio block driver (`CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION`). It implements new control queue request submission and completion handling, timeout and abandonment logic, keyslot programming/eviction, wrapped key operations, and associated runtime assertions (`WARN_ON_ONCE` in control queue drain and callback paths). Since virtio devices are emulated and accessible in virtualized test environments, these changes modify reachable driver and block crypto code worth fuzzing. Now, it's time to call `set-results |
| 28/2 | 2026/10/09 05:46 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"VIRTIO_BLK_INLINE_ENCRYPTION",
"VIRTIO_BLK",
"BLK_INLINE_ENCRYPTION",
"VIRTIO"
],
"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_ctrlq_callback",
"virtblk_ctrl_vq_request",
"virtblk_init_crypto"
],
"Reasoning": "The patch introduces inline encryption support and a control virtqueue to the virtio-blk driver (under CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION). It adds new request handling, state tracking, and timeout/abandonment logic for control queue operations, blk-crypto callbacks, and new WARN_ON_ONCE assertions in the callback and drain paths. Because virtio devices are emulated and exercised in virtualized environments, this code is reachable and warrants fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"VIRTIO_BLK_INLINE_ENCRYPTION",
"VIRTIO_BLK",
"BLK_INLINE_ENCRYPTION",
"VIRTIO"
],
"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_ctrlq_callback",
"virtblk_ctrl_vq_request",
"virtblk_init_crypto"
],
"Reasoning": "The patch introduces inline encryption support and a control virtqueue to the virtio-blk driver (under CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION). It adds new request handling, state tracking, and timeout/abandonment logic for control queue operations, blk-crypto callbacks, and new WARN_ON_ONCE assertions in the callback and drain paths. Because virtio devices are emulated and exercised in virtualized environments, this code is reachable and warrants fuzzing.",
"WorthFuzzing": true
} |
| 29/1 | 2026/10/09 05:46 | action | If |
3mArgs:{
"WorthFuzzing": true
}Results: null |
| 30/2 | 2026/10/09 05:46 | agent | kmsan-evaluator |
3mModel:core Args: null Results: {
"KMSANReasoning": "The patch introduces inline encryption support and a control virtqueue to the virtio-blk driver (`drivers/block/virtio_blk.c`).\n\nAnalysis of uninitialized memory and KMSAN relevance:\n1. Heap allocations:\n - Control queue requests (`creq`) in all crypto operations (`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`) are allocated using `kzalloc_obj`, ensuring all fields and padding are zero-initialized.\n - The completion object `comp` in `virtblk_ctrl_vq_request()` is allocated via `kmalloc_obj`, but all of its fields (`done` and `abandoned`) are immediately initialized before submission.\n - Request structures in `init_vq()` use `kzalloc_objs` for `vqs_info`.\n2. Stack variables and structures:\n - All stack variables (e.g. `crypto_modes_supported`, `max_slots`, `max_dun_bytes`, `key_types`) are explicitly initialized with `= 0` or `= { 0 }`.\n - In `virtblk_setup_cmd()`, `vbr-\u003eout_hdr.crypto_append.msg` is explicitly zeroed via `memset()` before its fields are populated.\n3. Information leaks:\n - Wrapped keys generated or imported and returned to userspace via block device ioctls (`BLKCRYPTOGENERATEKEY`, `BLKCRYPTOIMPORTKEY`) copy only `key_size` bytes from the kzalloc'd response buffers.\n4. KASAN vs KMSAN:\n - The primary risks introduced by this patch involve concurrency, request lifetime handling on timeouts/drain (`abandoned` requests), locking (`vblk-\u003ectrl_vq.mutex`, `lock`), and virtqueue buffer management. These potential issues (use-after-free, double-free, leaks, or deadlocks) are strictly within the scope of KASAN, LOCKDEP, and standard kernel debugging facilities, not KMSAN.\n\nTherefore, no uninitialized memory risks are introduced, and a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false
}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 6ea11839f29c8ce9d6ca1010a93b4181e5bed46f
Author: syz-cluster <triage@syzkaller.com>
Date: Fri Oct 9 05:44:02 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..df24c28d8ca59 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,41 @@ struct virtblk_req {
struct scatterlist sg[];
};
+/*
+ * Software-only completion state for a control-queue request.
+ */
+struct virtblk_ctrl_completion {
+ struct completion done;
+ /*
+ * Set when virtblk_ctrl_vq_request()'s waiter timed out and moved on
+ * without freeing this request. Whichever of virtblk_ctrlq_callback()
+ * or virtblk_ctrl_vq_drain() later retrieves the buffer must free
+ * this struct and the request instead of calling complete() on it.
+ */
+ bool abandoned;
+};
+
+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 virtblk_ctrl_completion *compl;
+};
+
static inline blk_status_t virtblk_result(u8 status)
{
switch (status) {
@@ -140,12 +200,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);
- sg_init_one(&out_hdr, &vbr->out_hdr, sizeof(vbr->out_hdr));
+ 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, 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 +300,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 +324,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 +397,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 +406,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 +668,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 +917,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 +963,736 @@ static int virtblk_getgeo(struct gendisk *disk, struct hd_geometry *geo)
return ret;
}
+#define VIRTBLK_CTRL_VQ_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_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) {
+ bool abandoned = creq->compl->abandoned;
+
+ if (!WARN_ON_ONCE(!vblk->ctrl_vq.inflight))
+ vblk->ctrl_vq.inflight--;
+
+ if (abandoned) {
+ kfree(creq->compl);
+ kfree_sensitive(creq);
+ } else {
+ creq->status = VIRTIO_BLK_S_IOERR;
+ complete(&creq->compl->done);
+ }
+ }
+ 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;
+ bool abandoned = creq->compl->abandoned;
+
+ /*
+ * Skip the inflight decrement/drained check when
+ * inflight was already 0 (a bug, hence WARN_ON_ONCE)
+ * — but always fall through to resolve @creq below
+ * regardless. Never leave a synchronous caller
+ * blocked because the accounting state was already
+ * inconsistent.
+ */
+ if (!WARN_ON_ONCE(!vblk->ctrl_vq.inflight) &&
+ --vblk->ctrl_vq.inflight == 0 && vblk->ctrl_vq.dead)
+ drained = true;
+
+ /*
+ * Decide and act on @abandoned without dropping the lock
+ * to avoid the memory leakage of @creq and its completion
+ * in the race condition that virtblk_ctrl_vq_request()
+ * wakes up from the timeout, and the callback resumes at
+ * the same time.
+ */
+ if (abandoned) {
+ kfree(creq->compl);
+ kfree_sensitive(creq);
+ } else {
+ complete(&creq->compl->done);
+ }
+ if (drained)
+ complete(&vblk->ctrl_vq.drained);
+ }
+ } 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)
+{
+ struct virtblk_ctrl_completion *comp;
+ unsigned long flags;
+ int err;
+
+ /*
+ * GFP_NOIO: this may be reached on the bio-submission path
+ * (memory reclaim writing back dirty pages to this same device),
+ * so GFP_KERNEL could self-deadlock.
+ */
+ comp = kmalloc_obj(*comp, GFP_NOIO);
+ if (!comp)
+ return -ENOMEM;
+ init_completion(&comp->done);
+ comp->abandoned = false;
+
+ mutex_lock(&vblk->ctrl_vq.mutex);
+ creq->compl = comp;
+
+ 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);
+ kfree(comp);
+ creq->compl = NULL;
+ 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);
+ kfree(comp);
+ creq->compl = NULL;
+ return err;
+ }
+
+ if (wait_for_completion_timeout(&comp->done, VIRTBLK_CTRL_VQ_TIMEOUT)) {
+ mutex_unlock(&vblk->ctrl_vq.mutex);
+ kfree(comp);
+ creq->compl = NULL;
+ return 0;
+ }
+
+ /*
+ * The host hasn't responded within the timeout. @creq is still
+ * owned by the device, so don't touch its DMA-target fields or
+ * free it here. Mark it abandoned and hand ownership of both @creq
+ * and @comp to whichever of virtblk_ctrlq_callback() or
+ * virtblk_ctrl_vq_drain() retrieves the buffer later; unlock the
+ * mutex so subsequent requests aren't serialized behind an
+ * unresponsive host.
+ */
+ spin_lock_irqsave(&vblk->ctrl_vq.lock, flags);
+ comp->abandoned = true;
+ spin_unlock_irqrestore(&vblk->ctrl_vq.lock, flags);
+ mutex_unlock(&vblk->ctrl_vq.mutex);
+
+ dev_warn(&vblk->vdev->dev,
+ "control queue request timed out, abandoning\n");
+ return -ETIMEDOUT;
+}
+
+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)
+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 == -ETIMEDOUT)
+ return err;
+ 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_mode_to_blk(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;
+ unsigned int vtype = blk_key_type_to_virtio(key->crypto_cfg.key_type);
+
+ if (sizeof(desc->bytes) < key->size)
+ return -EOVERFLOW;
+ if (!vtype)
+ return -EOPNOTSUPP;
+
+ 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,
+ blk_mode_to_virtio(key->crypto_cfg.crypto_mode));
+ desc->key_type = cpu_to_virtio32(vdev, vtype);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ /*
+ * GFP_NOIO: this callback runs on the bio-submission path, which
+ * memory reclaim can reach while writing back dirty pages to this
+ * same device; GFP_KERNEL here could recurse into that same reclaim
+ * and self-deadlock.
+ */
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ if (err)
+ goto out_free;
+
+ err = blk_status_to_errno(virtblk_result(creq->status));
+out_free:
+ kfree_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ if (err)
+ goto out_free;
+
+ err = blk_status_to_errno(virtblk_result(creq->status));
+out_free:
+ kfree_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ static_assert(sizeof_field(struct virtblk_ctrl_request, in_resp.secret.secret) >=
+ BLK_CRYPTO_SW_SECRET_SIZE);
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ if (eph_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) {
+ err = -EOVERFLOW;
+ goto out_unlock;
+ }
+
+ /*
+ * GFP_NOIO: avoid a self-deadlock. vdev_mutex, held above, is also
+ * required by virtblk_crypto_keyslot_program()/_evict(), which direct
+ * reclaim could invoke synchronously (writeback needing a keyslot)
+ * while this allocation is still in progress under the same mutex.
+ */
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ 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_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ 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_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ if (lt_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) {
+ err = -EOVERFLOW;
+ goto out_unlock;
+ }
+
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ 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_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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;
+
+ mutex_lock(&vblk->vdev_mutex);
+ if (!vblk->vdev) {
+ err = -ENXIO;
+ goto out_unlock;
+ }
+
+ if (raw_key_size > VIRTIO_BLK_CRYPTO_MAX_KEY_SIZE) {
+ err = -EOVERFLOW;
+ goto out_unlock;
+ }
+
+ creq = kzalloc_obj(*creq, GFP_NOIO);
+ if (!creq) {
+ err = -ENOMEM;
+ goto out_unlock;
+ }
+
+ 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 == -ETIMEDOUT)
+ goto out_unlock;
+ 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_sensitive(creq);
+out_unlock:
+ mutex_unlock(&vblk->vdev_mutex);
+ 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 unsigned int get_supported_blk_key_types(u8 virtio_key_types)
+{
+ unsigned int supported = 0;
+
+ if (virtio_key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW)
+ supported |= virtio_key_type_to_blk(VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW);
+ if (virtio_key_types & VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED)
+ supported |= virtio_key_type_to_blk(VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED);
+
+ return supported;
+}
+
+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;
+ 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;
+
+ key_type_supported = get_supported_blk_key_types(key_types);
+ 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 +1790,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 +1820,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 +1849,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 +1864,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);
@@ -1444,6 +2286,7 @@ static int virtblk_probe(struct virtio_device *vdev)
};
int err, index;
unsigned int queue_depth;
+ bool zoned_disk = false;
if (!vdev->config->get) {
dev_err(&vdev->dev, "%s failure: config access disabled\n",
@@ -1464,14 +2307,19 @@ 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->crypto_profile_initialized = false;
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 +2386,28 @@ static int virtblk_probe(struct virtio_device *vdev)
err = blk_revalidate_disk_zones(vblk->disk);
if (err)
goto out_cleanup_disk;
+
+ zoned_disk = true;
+ }
+
+ 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)) {
+ if (zoned_disk) {
+ dev_info(&vdev->dev,
+ "inline crypto not supported on zoned device\n");
+ } else {
+ 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 +2423,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 +2442,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);
@@ -1598,8 +2474,11 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
blk_mq_quiesce_queue_nowait(q);
blk_mq_unfreeze_queue(q, memflags);
+ virtblk_ctrl_vq_quiesce(vblk);
+
/* 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 +2491,7 @@ static int virtblk_freeze_priv(struct virtio_device *vdev)
* pointers safely.
*/
vblk->vqs = NULL;
+ vblk->ctrl_vq.vq = NULL;
return 0;
}
@@ -1626,6 +2506,11 @@ static int virtblk_restore_priv(struct virtio_device *vdev)
return ret;
virtio_device_ready(vdev);
+
+ /* Reprogram the keys to keyslots. */
+ if (vblk->crypto_profile_initialized)
+ blk_crypto_reprogram_all_keys(&vblk->profile);
+
blk_mq_unquiesce_queue(vblk->disk->queue);
return 0;
@@ -1672,6 +2557,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/linux/virtio_blk.h b/include/linux/virtio_blk.h
new file mode 100644
index 0000000000000..9f5aecad1907f
--- /dev/null
+++ b/include/linux/virtio_blk.h
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LINUX_VIRTIO_BLK_H
+#define _LINUX_VIRTIO_BLK_H
+
+#include <linux/blk-crypto.h>
+#include <uapi/linux/virtio_blk.h>
+
+#if IS_ENABLED(CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION)
+/**
+ * virtio_mode_to_blk() - Convert a virtio_blk crypto mode number to a block mode
+ * @vmode: The virtio_blk crypto mode number (VIRTIO_BLK_CRYPTO_MODE_*).
+ *
+ * Return: The corresponding &enum blk_crypto_mode_num, or
+ * %BLK_ENCRYPTION_MODE_INVALID if @vmode is out of range.
+ */
+static inline enum blk_crypto_mode_num virtio_mode_to_blk(unsigned int vmode)
+{
+ /* Indexed by virtio_blk crypto mode number; unlisted entries are 0 (INVALID). */
+ static const enum blk_crypto_mode_num modes[__VIRTIO_BLK_CRYPTO_MODE_MAX] = {
+ [VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS] = BLK_ENCRYPTION_MODE_AES_256_XTS,
+ };
+
+ if (vmode >= __VIRTIO_BLK_CRYPTO_MODE_MAX)
+ return BLK_ENCRYPTION_MODE_INVALID;
+ return modes[vmode];
+}
+
+/**
+ * blk_mode_to_virtio() - Convert a block crypto mode to a virtio_blk crypto mode number
+ * @bmode: The kernel &enum blk_crypto_mode_num.
+ *
+ * Return: The corresponding virtio_blk crypto mode number, or
+ * %VIRTIO_BLK_CRYPTO_MODE_INVALID if @bmode is out of range.
+ */
+static inline unsigned int blk_mode_to_virtio(enum blk_crypto_mode_num bmode)
+{
+ /* Indexed by blk_crypto_mode_num; unlisted entries are 0 (INVALID). */
+ static const unsigned int modes[BLK_ENCRYPTION_MODE_MAX] = {
+ [BLK_ENCRYPTION_MODE_AES_256_XTS] = VIRTIO_BLK_CRYPTO_MODE_AES_256_XTS,
+ };
+
+ if (bmode >= BLK_ENCRYPTION_MODE_MAX)
+ return VIRTIO_BLK_CRYPTO_MODE_INVALID;
+ return modes[bmode];
+}
+
+/**
+ * virtio_key_type_to_blk() - Convert a virtio_blk crypto key type to a block key type
+ * @vtype: The virtio_blk crypto key type (VIRTIO_BLK_CRYPTO_KEY_TYPE_*).
+ *
+ * Return: The corresponding &enum blk_crypto_key_type, or 0 if @vtype does
+ * not name a single supported key type.
+ */
+static inline enum blk_crypto_key_type virtio_key_type_to_blk(unsigned int vtype)
+{
+ switch (vtype) {
+ case VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW:
+ return BLK_CRYPTO_KEY_TYPE_RAW;
+ case VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED:
+ return BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
+ default:
+ return 0;
+ }
+}
+
+/**
+ * blk_key_type_to_virtio() - Convert a block key type to a virtio_blk crypto key type
+ * @btype: The kernel &enum blk_crypto_key_type.
+ *
+ * Return: The corresponding virtio_blk crypto key type
+ * (VIRTIO_BLK_CRYPTO_KEY_TYPE_*), or 0 if @btype does not name a
+ * single supported key type.
+ */
+static inline unsigned int blk_key_type_to_virtio(enum blk_crypto_key_type btype)
+{
+ switch (btype) {
+ case BLK_CRYPTO_KEY_TYPE_RAW:
+ return VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW;
+ case BLK_CRYPTO_KEY_TYPE_HW_WRAPPED:
+ return VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED;
+ default:
+ return 0;
+ }
+}
+#endif /* CONFIG_VIRTIO_BLK_INLINE_ENCRYPTION */
+
+#endif /* _LINUX_VIRTIO_BLK_H */
diff --git a/include/uapi/linux/virtio_blk.h b/include/uapi/linux/virtio_blk.h
index 3744e4da1b2a7..87300907abca8 100644
--- a/include/uapi/linux/virtio_blk.h
+++ b/include/uapi/linux/virtio_blk.h
@@ -1,5 +1,5 @@
-#ifndef _LINUX_VIRTIO_BLK_H
-#define _LINUX_VIRTIO_BLK_H
+#ifndef _UAPI_LINUX_VIRTIO_BLK_H
+#define _UAPI_LINUX_VIRTIO_BLK_H
/* This header is BSD licensed so anyone can use the definitions to implement
* compatible drivers/servers.
*
@@ -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 20 /* Control queue */
+#define VIRTIO_BLK_F_INLINE_ENCRYPTION 21 /* Inline encryption */
/* Legacy feature bits */
#ifndef VIRTIO_BLK_NO_LEGACY
@@ -148,6 +150,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 +217,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 +263,86 @@ 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];
+};
+
+/* Key type for VIRTIO_BLK_F_INLINE_ENCRYPTION. */
+enum virtio_blk_crypto_key_type {
+ VIRTIO_BLK_CRYPTO_KEY_TYPE_RAW = 1,
+ VIRTIO_BLK_CRYPTO_KEY_TYPE_HW_WRAPPED,
+};
+
+/*
+ * 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, in
+ * struct virtio_blk_crypto_key_desc.crypto_mode 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.
*/
@@ -324,4 +442,4 @@ struct virtio_scsi_inhdr {
#define VIRTIO_BLK_S_ZONE_OPEN_RESOURCE 5
#define VIRTIO_BLK_S_ZONE_ACTIVE_RESOURCE 6
-#endif /* _LINUX_VIRTIO_BLK_H */
+#endif /* _UAPI_LINUX_VIRTIO_BLK_H */
|
| 31/3 | 2026/10/09 05:46 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20616 output: 51 thoughts: 2069 Thoughts: **KMSAN Evaluation for Virtio-blk Inline Encryption Patch** Okay, so I need to decide if a dedicated KMSAN fuzzing session is required for this virtio-blk patch. The key here is to determine if the changes introduce the risk of uninitialized memory reads that KASAN, LOCKDEP, or other standard detectors *won't* catch. KMSAN is expensive, so it's only warranted if there's a unique risk of information leaks due to uninitialized memory. The patch introduces inline encryption and a control queue. My initial thought is to carefully dissect the code changes, focusing on how data structures are allocated, initialized, and passed around, especially when interactions with user space are involved. First, I examined the `virtblk_req` structure change and how it's used. The introduction of the `crypto_append` union is the first area to watch. The code in `virtblk_setup_cmd` carefully initializes the `crypto_append.msg` struct with `memset` when the crypto paths are taken. The `out_hdr_len` is then correctly calculated and passed to `sg_init_one` to be sent to virtqueue. So far, this seems safe, given the explicit initialization. Next, I looked at the control queue and `virtblk_get_crypto_modes`, focusing on the responses. The hypervisor writes responses directly to `creq->in_resp.modes` and `creq->status` via DMA and the virtqueue. I need to understand if this exposes uninitialized memory to user space. DMA with KMSAN is particularly important, but here it is code *within* the guest kernel! However, it's possible that data is passed up to user space via `blk_crypto`? I need to look into how the `blk_crypto_ll_ops` work and where the buffers originate and go. To be sure, I need to check the following `blk_crypto_ll_ops` functions with `grepper`: `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` to see how the buffers are handled. |
| 32/3 | 2026/10/09 05:47 | tool | grepper |
0mArgs:{
"Expression": "struct blk_crypto_ll_ops",
"PathPrefixes": [
"include/linux/blk-crypto-profile.h",
"block/blk-crypto.c"
]
}Results: {
"Output": "include/linux/blk-crypto-profile.h=12=struct blk_crypto_profile;\n--\ninclude/linux/blk-crypto-profile.h-14-/**\ninclude/linux/blk-crypto-profile.h:15: * struct blk_crypto_ll_ops - functions to control inline encryption hardware\ninclude/linux/blk-crypto-profile.h-16- *\n--\ninclude/linux/blk-crypto-profile.h-21- */\ninclude/linux/blk-crypto-profile.h:22:struct blk_crypto_ll_ops {\ninclude/linux/blk-crypto-profile.h-23-\n--\ninclude/linux/blk-crypto-profile.h=126=struct blk_crypto_profile {\n--\ninclude/linux/blk-crypto-profile.h-133-\t */\ninclude/linux/blk-crypto-profile.h:134:\tstruct blk_crypto_ll_ops ll_ops;\ninclude/linux/blk-crypto-profile.h-135-\n"
} |
| 33/3 | 2026/10/09 05:47 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23500 output: 38 thoughts: 28 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Diving into the Block Crypto Profiling Header** Right, let's take a look at the `include/linux/blk-crypto-profile.h` header file. Specifically, I'm zeroing in on the vicinity of line 22 – that's often where the core data structures or function definitions related to the profiling functionality are initialized or declared. Using `read-file` to grab that section should give me a good starting point. I need to quickly refresh my understanding of how block layer crypto profiling is implemented, and that header file is the logical place to begin. I'm expecting to see definitions for the relevant structures (e.g., counters, timestamps, context information) and possibly inline functions for instrumentation. I'll need to then quickly jump to related source files to see how those data structures are used. I am hoping the layout of the include file is logical. |
| 34/3 | 2026/10/09 05:47 | tool | read-file |
0mArgs:{
"File": "include/linux/blk-crypto-profile.h",
"FirstLine": 15,
"LineCount": 90
}Results: {
"Contents": " 15:\t * struct blk_crypto_ll_ops - functions to control inline encryption hardware\n 16:\t *\n 17:\t * Low-level operations for controlling inline encryption hardware. This\n 18:\t * interface must be implemented by storage drivers that support inline\n 19:\t * encryption. All functions may sleep, are serialized by profile-\u003elock, and\n 20:\t * are never called while profile-\u003edev (if set) is runtime-suspended.\n 21:\t */\n 22:\tstruct blk_crypto_ll_ops {\n 23:\t\n 24:\t\t/**\n 25:\t\t * @keyslot_program: Program a key into the inline encryption hardware.\n 26:\t\t *\n 27:\t\t * Program @key into the specified @slot in the inline encryption\n 28:\t\t * hardware, overwriting any key that the keyslot may already contain.\n 29:\t\t * The keyslot is guaranteed to not be in-use by any I/O.\n 30:\t\t *\n 31:\t\t * This is required if the device has keyslots. Otherwise (i.e. if the\n 32:\t\t * device is a layered device, or if the device is real hardware that\n 33:\t\t * simply doesn't have the concept of keyslots) it is never called.\n 34:\t\t *\n 35:\t\t * Must return 0 on success, or -errno on failure.\n 36:\t\t */\n 37:\t\tint (*keyslot_program)(struct blk_crypto_profile *profile,\n 38:\t\t\t\t const struct blk_crypto_key *key,\n 39:\t\t\t\t unsigned int slot);\n 40:\t\n 41:\t\t/**\n 42:\t\t * @keyslot_evict: Evict a key from the inline encryption hardware.\n 43:\t\t *\n 44:\t\t * If the device has keyslots, this function must evict the key from the\n 45:\t\t * specified @slot. The slot will contain @key, but there should be no\n 46:\t\t * need for the @key argument to be used as @slot should be sufficient.\n 47:\t\t * The keyslot is guaranteed to not be in-use by any I/O.\n 48:\t\t *\n 49:\t\t * If the device doesn't have keyslots itself, this function must evict\n 50:\t\t * @key from any underlying devices. @slot won't be valid in this case.\n 51:\t\t *\n 52:\t\t * If there are no keyslots and no underlying devices, this function\n 53:\t\t * isn't required.\n 54:\t\t *\n 55:\t\t * Must return 0 on success, or -errno on failure.\n 56:\t\t */\n 57:\t\tint (*keyslot_evict)(struct blk_crypto_profile *profile,\n 58:\t\t\t\t const struct blk_crypto_key *key,\n 59:\t\t\t\t unsigned int slot);\n 60:\t\n 61:\t\t/**\n 62:\t\t * @derive_sw_secret: Derive the software secret from a hardware-wrapped\n 63:\t\t *\t\t key in ephemerally-wrapped form.\n 64:\t\t *\n 65:\t\t * This only needs to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED\n 66:\t\t * is supported.\n 67:\t\t *\n 68:\t\t * Must return 0 on success, -EBADMSG if the key is invalid, or another\n 69:\t\t * -errno code on other errors.\n 70:\t\t */\n 71:\t\tint (*derive_sw_secret)(struct blk_crypto_profile *profile,\n 72:\t\t\t\t\tconst u8 *eph_key, size_t eph_key_size,\n 73:\t\t\t\t\tu8 sw_secret[BLK_CRYPTO_SW_SECRET_SIZE]);\n 74:\t\n 75:\t\t/**\n 76:\t\t * @import_key: Create a hardware-wrapped key by importing a raw key.\n 77:\t\t *\n 78:\t\t * This only needs to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED\n 79:\t\t * is supported.\n 80:\t\t *\n 81:\t\t * On success, must write the new key in long-term wrapped form to\n 82:\t\t * @lt_key and return its size in bytes. On failure, must return a\n 83:\t\t * -errno value.\n 84:\t\t */\n 85:\t\tint (*import_key)(struct blk_crypto_profile *profile,\n 86:\t\t\t\t const u8 *raw_key, size_t raw_key_size,\n 87:\t\t\t\t u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);\n 88:\t\n 89:\t\t/**\n 90:\t\t * @generate_key: Generate a hardware-wrapped key.\n 91:\t\t *\n 92:\t\t * This only needs to be implemented if BLK_CRYPTO_KEY_TYPE_HW_WRAPPED\n 93:\t\t * is supported.\n 94:\t\t *\n 95:\t\t * On success, must write the new key in long-term wrapped form to\n 96:\t\t * @lt_key and return its size in bytes. On failure, must return a\n 97:\t\t * -errno value.\n 98:\t\t */\n 99:\t\tint (*generate_key)(struct blk_crypto_profile *profile,\n 100:\t\t\t\t u8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE]);\n 101:\t\n 102:\t\t/**\n 103:\t\t * @prepare_key: Prepare a hardware-wrapped key to be used.\n 104:\t\t *\n"
} |
| 35/3 | 2026/10/09 05:47 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25207 output: 26 thoughts: 56 Thoughts: Okay, here's my assessment of the situation: **Investigating Key Management in the Block Layer** Alright, let's dive into this. My initial focus is on understanding the interplay between those key generation and management functions: `derive_sw_secret`, `generate_key`, and `import_key`. I need to map their usage within the block layer – see how they are invoked, and where those calls ultimately lead. First things first, I'll leverage `grep` to quickly identify all instances of `import_key` and `generate_key` across the kernel source. This should give me a high-level view of their distribution and potential contexts. Then, I can start drilling down into the block layer specifically, tracing their execution paths and data flows to construct a solid mental model. From there, I'll probably be able to determine what the next steps are. |
| 36/3 | 2026/10/09 05:47 | tool | grepper |
0mArgs:{
"Expression": "\\.generate_key|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-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=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=1321=static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1386-\ndrivers/block/virtio_blk.c:1387:static int virtblk_crypto_generate_key(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c-1388-\t\t\t\t\tu8 lt_key[BLK_CRYPTO_MAX_HW_WRAPPED_KEY_SIZE])\n--\ndrivers/block/virtio_blk.c=1578=static const struct blk_crypto_ll_ops virtblk_crypto_ops = {\n--\ndrivers/block/virtio_blk.c-1581-\t.derive_sw_secret\t= virtblk_crypto_derive_sw_secret,\ndrivers/block/virtio_blk.c:1582:\t.generate_key\t\t= virtblk_crypto_generate_key,\ndrivers/block/virtio_blk.c-1583-\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--\ndrivers/md/dm-table.c=1320=static int dm_generate_key(struct blk_crypto_profile *profile,\n--\ndrivers/md/dm-table.c-1324-\t\t.op = GENERATE_KEY,\ndrivers/md/dm-table.c:1325:\t\t.generate_key = {\ndrivers/md/dm-table.c-1326-\t\t\t.lt_key = lt_key,\n--\ndrivers/md/dm-table.c=1387=static int dm_table_construct_crypto_profile(struct dm_table *t)\n--\ndrivers/md/dm-table.c-1423-\t\tprofile-\u003ell_ops.import_key = dm_import_key;\ndrivers/md/dm-table.c:1424:\t\tprofile-\u003ell_ops.generate_key = dm_generate_key;\ndrivers/md/dm-table.c-1425-\t\tprofile-\u003ell_ops.prepare_key = dm_prepare_key;\n--\ndrivers/mmc/host/sdhci-msm.c=2120=static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = {\n--\ndrivers/mmc/host/sdhci-msm.c-2124-\t.import_key\t\t= sdhci_msm_ice_import_key,\ndrivers/mmc/host/sdhci-msm.c:2125:\t.generate_key\t\t= sdhci_msm_ice_generate_key,\ndrivers/mmc/host/sdhci-msm.c-2126-\t.prepare_key\t\t= sdhci_msm_ice_prepare_key,\n--\ndrivers/ufs/host/ufs-qcom.c=309=static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops = {\n--\ndrivers/ufs/host/ufs-qcom.c-313-\t.import_key\t\t= ufs_qcom_ice_import_key,\ndrivers/ufs/host/ufs-qcom.c:314:\t.generate_key\t\t= ufs_qcom_ice_generate_key,\ndrivers/ufs/host/ufs-qcom.c-315-\t.prepare_key\t\t= ufs_qcom_ice_prepare_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"
} |
| 37/3 | 2026/10/09 05:47 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 27536 output: 35 thoughts: 20 Thoughts: Okay, here's my attempt at summarizing the thought process, as requested, geared toward an expert audience, and written in the first person: **Diving into the Block Crypto Code** Alright, time to dig in. My gut feeling tells me the answer to this issue lies within the `block/blk-crypto.c` file. Given my experience with block device encryption, particularly around performance bottlenecks, I have a specific area in mind. Let's start around line 500. This region often deals with the core encryption/decryption routines, key handling, and potentially the I/O path. That's a logical place to begin poking around for either the implementation details, or perhaps to assess a bottleneck that could exist due to a bad implementation. Let's see what's there... |
| 38/3 | 2026/10/09 05:47 | tool | read-file |
0mArgs:{
"File": "block/blk-crypto.c",
"FirstLine": 460,
"LineCount": 95
}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 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 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"
} |
| 39/3 | 2026/10/09 05:47 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 29280 output: 19 thoughts: 1273 Thoughts: **Analyzing the Potential Vulnerability in `virtblk_crypto_generate_key`** Okay, so I'm looking at this code and immediately my expert senses are tingling. I see this `blk_crypto_ioctl_generate_key` function and its interaction with `virtblk_crypto_generate_key`, and I'm zeroing in on a potential flaw. Specifically, I'm concerned about the `virtblk_crypto_generate_key` function where `creq` is allocated using `kzalloc_obj`, so it's zeroed out. Then, there's a virtqueue request. What really grabs my attention is the possibility of uninitialized memory being exposed. If the device, during the `virtblk_ctrl_vq_request`, doesn't fully populate the `creq` structure—or even if it only provides a status—we could have an information leak. It's a classic race condition in my field. DMA and virtqueue interactions are always tricky. I know the device uses virtio and doesn't use dma_map_* unless VIRTIO_F_ACCESS_PLATFORM is negotiated. That makes me wonder if KMSAN's DMA handling is even relevant here. But even more important, virtqueue! Does KMSAN mark the memory used in virtqueue as initialized? I'm familiar with KMSAN's `kmsan_handle_dma`, but what about virtio? It's all a little hazy. Wait, syzkaller runs in QEMU! And even more importantly, syzkaller tests the syscall interface, ioctls, etc.! The fact that userspace *can* invoke the `BLKCRYPTOGENERATEKEY` ioctl, and on `/dev/vd*` devices... that's crucial. I need to understand how the `BLKCRYPTOGENERATEKEY` ioctl itself works, in order to track the flow of data. I know that ioctl is how we're reaching this function. I need to review the `block/ioctl.c` and `block/blk-crypto.c` files for how it is handled in there. I'll grep for `BLKCRYPTOGENERATEKEY` to start. |
| 40/3 | 2026/10/09 05:47 | tool | grepper |
0mArgs:{
"Expression": "BLKCRYPTOGENERATEKEY"
}Results: {
"Output": "Documentation/block/inline-encryption.rst=494=provide a generic interface to creating and preparing such keys:\n--\nDocumentation/block/inline-encryption.rst-506-\nDocumentation/block/inline-encryption.rst:507:- ``BLKCRYPTOGENERATEKEY`` is like ``BLKCRYPTOIMPORTKEY``, but it has the\nDocumentation/block/inline-encryption.rst-508- hardware generate the key instead of importing one. It takes in a pointer to\n--\nDocumentation/block/inline-encryption.rst-522-\nDocumentation/block/inline-encryption.rst:523:Userspace needs to use either ``BLKCRYPTOIMPORTKEY`` or ``BLKCRYPTOGENERATEKEY``\nDocumentation/block/inline-encryption.rst-524-once to create a key, and then ``BLKCRYPTOPREPAREKEY`` each time the key is\n--\nblock/blk-crypto.c=579=int blk_crypto_ioctl(struct block_device *bdev, unsigned int cmd,\n--\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);\n--\nblock/ioctl.c=651=static int blkdev_common_ioctl(struct block_device *bdev, blk_mode_t mode,\n--\nblock/ioctl.c-719-\tcase BLKCRYPTOIMPORTKEY:\nblock/ioctl.c:720:\tcase BLKCRYPTOGENERATEKEY:\nblock/ioctl.c-721-\tcase BLKCRYPTOPREPAREKEY:\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"
} |
| 41/3 | 2026/10/09 05:47 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 31661 output: 41 thoughts: 1364 Thoughts: **Uninitialized Memory Access in VirtIO Block Crypto?** Okay, let's dissect this `BLKCRYPTOGENERATEKEY` issue. The critical path seems to be within `blk_crypto_ioctl_generate_key`. I'm concerned about the `lt_key` stack array. It's declared but *not* initialized before being passed to `blk_crypto_generate_key`. The function calls `virtblk_crypto_generate_key` which `kzalloc`'s a `creq` struct and copies from `creq->in_resp.blob.key`, which I would assume comes from device's response after generation. Ultimately the `lt_key` array is used in `copy_to_user`. This means only `key_size` bytes are actually copied to user space, so that shouldn't leak uninitialized memory. Still, the lack of initialization prior to the function call bugs me. Next, I checked `virtblk_get_crypto_modes`. The `creq` is allocated with `kzalloc_obj`, which *should* ensure it's zeroed, so that's okay. Similar checks for `virtblk_ctrl_vq_request` were performed, where `comp` is allocated, but at the very least it's initialized before usage, which is good. In contrast, `virtblk_init_crypto` shows a much better pattern: all stack variables like `max_slots`, `max_dun_bytes`, `key_types` and `crypto_modes_supported` are explicitly initialized. That's what I want to see. I need to confirm the allocation for `virtblk_req` struct. Let's use `grepper` to see where `virtblk_req` is allocated. I need to understand where the `virtblk_req` struct is allocated and if it's properly initialized *before* any sensitive data is written to it or copied from it. |
| 42/3 | 2026/10/09 05:48 | tool | grepper |
0mArgs:{
"Expression": "cmd_size|sizeof.*virtblk_req",
"PathPrefixes": [
"drivers/block/virtio_blk.c"
]
}Results: {
"Output": "drivers/block/virtio_blk.c=2280=static int virtblk_probe(struct virtio_device *vdev)\n--\ndrivers/block/virtio_blk.c-2337-\tvblk-\u003etag_set.numa_node = NUMA_NO_NODE;\ndrivers/block/virtio_blk.c:2338:\tvblk-\u003etag_set.cmd_size =\ndrivers/block/virtio_blk.c:2339:\t\tsizeof(struct virtblk_req) +\ndrivers/block/virtio_blk.c-2340-\t\tsizeof(struct scatterlist) * VIRTIO_BLK_INLINE_SG_CNT;\n"
} |
| 43/3 | 2026/10/09 05:48 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33661 output: 38 thoughts: 113 Thoughts: **Investigating Virtio-blk Request Initialization in blk-mq** Okay, so I'm diving into how virtio-blk requests are handled within the blk-mq framework. I see this line setting the `cmd_size` based on the size of `struct virtblk_req` along with some other stuff. It immediately triggers a question in my mind: When a request is allocated in blk-mq, and the driver's private data (`pdu`) is part of that request, how *exactly* is the request, including that `pdu` area, initialized? Is it zeroed out automatically, or does the driver need to handle that? I need to be sure! Let's dig deeper and verify. I need to understand what's happening during request setup. My next step is to examine the `virtblk_setup_cmd` function. Time to crack open `virtio_blk.c` and see what that function is actually doing with that allocated memory, so I can ensure the `pdu` gets handled as required! |
| 44/3 | 2026/10/09 05:48 | tool | read-file |
0mArgs:{
"File": "drivers/block/virtio_blk.c",
"FirstLine": 320,
"LineCount": 95
}Results: {
"Contents": " 320:\t\t\t\t\t struct request *req,\n 321:\t\t\t\t\t struct virtblk_req *vbr)\n 322:\t{\n 323:\t\tsize_t in_hdr_len = sizeof(vbr-\u003ein_hdr.status);\n 324:\t\tbool unmap = false;\n 325:\t\tu32 type;\n 326:\t\tu64 sector = 0;\n 327:\t\tint i;\n 328:\t\n 329:\t\tif (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) \u0026\u0026 op_is_zone_mgmt(req_op(req)))\n 330:\t\t\treturn BLK_STS_NOTSUPP;\n 331:\t\n 332:\t\t/* Set fields for all request types */\n 333:\t\tvbr-\u003eout_hdr.base.ioprio = cpu_to_virtio32(vdev, req_get_ioprio(req));\n 334:\t\n 335:\t\tswitch (req_op(req)) {\n 336:\t\tcase REQ_OP_READ:\n 337:\t\t\tif (is_crypto_request(req))\n 338:\t\t\t\ttype = VIRTIO_BLK_T_CRYPTO_IN;\n 339:\t\t\telse\n 340:\t\t\t\ttype = VIRTIO_BLK_T_IN;\n 341:\t\t\tsector = blk_rq_pos(req);\n 342:\t\t\tbreak;\n 343:\t\tcase REQ_OP_WRITE:\n 344:\t\t\tif (is_crypto_request(req))\n 345:\t\t\t\ttype = VIRTIO_BLK_T_CRYPTO_OUT;\n 346:\t\t\telse\n 347:\t\t\t\ttype = VIRTIO_BLK_T_OUT;\n 348:\t\t\tsector = blk_rq_pos(req);\n 349:\t\t\tbreak;\n 350:\t\tcase REQ_OP_FLUSH:\n 351:\t\t\ttype = VIRTIO_BLK_T_FLUSH;\n 352:\t\t\tbreak;\n 353:\t\tcase REQ_OP_DISCARD:\n 354:\t\t\ttype = VIRTIO_BLK_T_DISCARD;\n 355:\t\t\tbreak;\n 356:\t\tcase REQ_OP_WRITE_ZEROES:\n 357:\t\t\ttype = VIRTIO_BLK_T_WRITE_ZEROES;\n 358:\t\t\tunmap = !(req-\u003ecmd_flags \u0026 REQ_NOUNMAP);\n 359:\t\t\tbreak;\n 360:\t\tcase REQ_OP_SECURE_ERASE:\n 361:\t\t\ttype = VIRTIO_BLK_T_SECURE_ERASE;\n 362:\t\t\tbreak;\n 363:\t\tcase REQ_OP_ZONE_OPEN:\n 364:\t\t\ttype = VIRTIO_BLK_T_ZONE_OPEN;\n 365:\t\t\tsector = blk_rq_pos(req);\n 366:\t\t\tbreak;\n 367:\t\tcase REQ_OP_ZONE_CLOSE:\n 368:\t\t\ttype = VIRTIO_BLK_T_ZONE_CLOSE;\n 369:\t\t\tsector = blk_rq_pos(req);\n 370:\t\t\tbreak;\n 371:\t\tcase REQ_OP_ZONE_FINISH:\n 372:\t\t\ttype = VIRTIO_BLK_T_ZONE_FINISH;\n 373:\t\t\tsector = blk_rq_pos(req);\n 374:\t\t\tbreak;\n 375:\t\tcase REQ_OP_ZONE_APPEND:\n 376:\t\t\ttype = VIRTIO_BLK_T_ZONE_APPEND;\n 377:\t\t\tsector = blk_rq_pos(req);\n 378:\t\t\tin_hdr_len = sizeof(vbr-\u003ein_hdr.zone_append);\n 379:\t\t\tbreak;\n 380:\t\tcase REQ_OP_ZONE_RESET:\n 381:\t\t\ttype = VIRTIO_BLK_T_ZONE_RESET;\n 382:\t\t\tsector = blk_rq_pos(req);\n 383:\t\t\tbreak;\n 384:\t\tcase REQ_OP_ZONE_RESET_ALL:\n 385:\t\t\ttype = VIRTIO_BLK_T_ZONE_RESET_ALL;\n 386:\t\t\tbreak;\n 387:\t\tcase REQ_OP_DRV_IN:\n 388:\t\t\t/*\n 389:\t\t\t * Out header has already been prepared by the caller (virtblk_get_id()\n 390:\t\t\t * or virtblk_submit_zone_report()), nothing to do here.\n 391:\t\t\t */\n 392:\t\t\treturn 0;\n 393:\t\tdefault:\n 394:\t\t\tWARN_ON_ONCE(1);\n 395:\t\t\treturn BLK_STS_IOERR;\n 396:\t\t}\n 397:\t\n 398:\t\t/* Set fields for non-REQ_OP_DRV_IN request types */\n 399:\t\tvbr-\u003ein_hdr_len = in_hdr_len;\n 400:\t\tvbr-\u003eout_hdr.base.type = cpu_to_virtio32(vdev, type);\n 401:\t\tvbr-\u003eout_hdr.base.sector = cpu_to_virtio64(vdev, sector);\n 402:\t\n 403:\t\tif (type == VIRTIO_BLK_T_DISCARD || type == VIRTIO_BLK_T_WRITE_ZEROES ||\n 404:\t\t type == VIRTIO_BLK_T_SECURE_ERASE) {\n 405:\t\t\tif (virtblk_setup_discard_write_zeroes_erase(req, unmap))\n 406:\t\t\t\treturn BLK_STS_RESOURCE;\n 407:\t\t}\n 408:\t\n 409:\t\tif (type == VIRTIO_BLK_T_CRYPTO_IN || type == VIRTIO_BLK_T_CRYPTO_OUT) {\n 410:\t\t\tmemset(\u0026vbr-\u003eout_hdr.crypto_append.msg, 0,\n 411:\t\t\t\tsizeof(vbr-\u003eout_hdr.crypto_append.msg));\n 412:\t\t\tvbr-\u003eout_hdr.crypto_append.msg.slot =\n 413:\t\t\t\tcpu_to_virtio32(vdev,\n 414:\t\t\t\t\tblk_crypto_keyslot_index(req-\u003ecrypt_keyslot));\n"
} |
| 45/3 | 2026/10/09 05:48 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35681 output: 19 thoughts: 1627 Thoughts: **Analyzing Virtio Block Crypto Command Initialization** Okay, so I'm diving into the virtio-blk crypto implementation to make sure I understand the memory initialization flow, specifically to ensure no uninitialized bytes are present in sensitive data structures, which could lead to vulnerabilities. First, I examine `virtblk_setup_cmd`. I see that when dealing with crypto operations (`VIRTIO_BLK_T_CRYPTO_IN` or `VIRTIO_BLK_T_CRYPTO_OUT`), the `crypto_append.msg` field of `vbr->out_hdr` is zeroed out using `memset`. My initial thought is to make sure I understood the structure, so I checked the `struct virtio_blk_outhdr` definition, confirming my expectations regarding the `type`, `ioprio`, and `sector` fields in the `vbr->out_hdr.base`. Then I am satisfied that those are all initialized before the `memset`. I conclude there are no uninitialized bytes in `out_hdr` because of `memset`. Next, I check the function `set_virtblk_crypto_key_desc`. Here, a `virtio_blk_crypto_key_desc` structure is populated. I note the use of `memset` at the beginning, clearing the entire structure before anything is copied into it. This means the sensitive `key->bytes` is copied into memory that is zeroed. I then move on to `virtblk_crypto_keyslot_program` and `virtblk_crypto_keyslot_evict`. Both functions allocate a control request (`creq`) using `kzalloc_obj` (which initializes the memory to zero), then set the type, and finally call `set_virtblk_crypto_key_desc`, so they should be safe. Finally, I reach `virtblk_crypto_derive_sw_secret`. Here too, `creq` is allocated with `kzalloc_obj`. I focus on the `out_req.blob.key` and `out_req.blob.key_size` being populated from `eph_key` and its size. The secret itself is read from `creq->in_resp.secret.secret`. This looks good, as the `creq` is zeroed and the size looks like it won't overflow. However, it is always wise to double-check where this function is called from, so I'm going to look for all usages of `blk_crypto_derive_sw_secret` just to be sure it is called correctly. |
| 46/3 | 2026/10/09 05:49 | tool | grepper |
0mArgs:{
"Expression": "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-478-\nDocumentation/block/inline-encryption.rst:479:- A new method ``blk_crypto_ll_ops::derive_sw_secret`` is added. Device drivers\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-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--\ndrivers/block/virtio_blk.c=1273=static int virtblk_crypto_keyslot_evict(struct blk_crypto_profile *profile,\n--\ndrivers/block/virtio_blk.c-1320-\ndrivers/block/virtio_blk.c:1321:static int virtblk_crypto_derive_sw_secret(struct blk_crypto_profile *profile,\ndrivers/block/virtio_blk.c-1322-\t\t\t\t\t const u8 *eph_key, size_t eph_key_size,\n--\ndrivers/block/virtio_blk.c=1578=static const struct blk_crypto_ll_ops virtblk_crypto_ops = {\n--\ndrivers/block/virtio_blk.c-1580-\t.keyslot_evict\t\t= virtblk_crypto_keyslot_evict,\ndrivers/block/virtio_blk.c:1581:\t.derive_sw_secret\t= virtblk_crypto_derive_sw_secret,\ndrivers/block/virtio_blk.c-1582-\t.generate_key\t\t= virtblk_crypto_generate_key,\n--\ndrivers/firmware/qcom/qcom_scm.c=1582=EXPORT_SYMBOL_GPL(qcom_scm_has_wrapped_key_support);\n--\ndrivers/firmware/qcom/qcom_scm.c-1584-/**\ndrivers/firmware/qcom/qcom_scm.c:1585: * qcom_scm_derive_sw_secret() - Derive software secret from wrapped key\ndrivers/firmware/qcom/qcom_scm.c-1586- * @eph_key: an ephemerally-wrapped key\n--\ndrivers/firmware/qcom/qcom_scm.c-1599- */\ndrivers/firmware/qcom/qcom_scm.c:1600:int qcom_scm_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,\ndrivers/firmware/qcom/qcom_scm.c-1601-\t\t\t u8 *sw_secret, size_t sw_secret_size)\n--\ndrivers/firmware/qcom/qcom_scm.c-1637-}\ndrivers/firmware/qcom/qcom_scm.c:1638:EXPORT_SYMBOL_GPL(qcom_scm_derive_sw_secret);\ndrivers/firmware/qcom/qcom_scm.c-1639-\n--\ndrivers/md/dm-table.c=1190=struct dm_wrappedkey_op_args {\n--\ndrivers/md/dm-table.c-1197-\t\t\tu8 *sw_secret;\ndrivers/md/dm-table.c:1198:\t\t} derive_sw_secret;\ndrivers/md/dm-table.c-1199-\t\tstruct {\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,\ndrivers/md/dm-table.c:1228:\t\t\t\t\targs-\u003ederive_sw_secret.eph_key,\ndrivers/md/dm-table.c:1229:\t\t\t\t\targs-\u003ederive_sw_secret.eph_key_size,\ndrivers/md/dm-table.c:1230:\t\t\t\t\targs-\u003ederive_sw_secret.sw_secret);\ndrivers/md/dm-table.c-1231-\t\tbreak;\n--\ndrivers/md/dm-table.c=1253=static int dm_exec_wrappedkey_op(struct blk_crypto_profile *profile,\n--\ndrivers/md/dm-table.c-1289-\ndrivers/md/dm-table.c:1290:static int dm_derive_sw_secret(struct blk_crypto_profile *profile,\ndrivers/md/dm-table.c-1291-\t\t\t const u8 *eph_key, size_t eph_key_size,\n--\ndrivers/md/dm-table.c-1295-\t\t.op = DERIVE_SW_SECRET,\ndrivers/md/dm-table.c:1296:\t\t.derive_sw_secret = {\ndrivers/md/dm-table.c-1297-\t\t\t.eph_key = eph_key,\n--\ndrivers/md/dm-table.c=1387=static int dm_table_construct_crypto_profile(struct dm_table *t)\n--\ndrivers/md/dm-table.c-1421-\tif (profile-\u003ekey_types_supported \u0026 BLK_CRYPTO_KEY_TYPE_HW_WRAPPED) {\ndrivers/md/dm-table.c:1422:\t\tprofile-\u003ell_ops.derive_sw_secret = dm_derive_sw_secret;\ndrivers/md/dm-table.c-1423-\t\tprofile-\u003ell_ops.import_key = dm_import_key;\n--\ndrivers/mmc/host/sdhci-msm.c=2012=static int sdhci_msm_ice_keyslot_evict(struct blk_crypto_profile *profile,\n--\ndrivers/mmc/host/sdhci-msm.c-2021-\ndrivers/mmc/host/sdhci-msm.c:2022:static int sdhci_msm_ice_derive_sw_secret(struct blk_crypto_profile *profile,\ndrivers/mmc/host/sdhci-msm.c-2023-\t\t\t\t\t const u8 *eph_key, size_t eph_key_size,\n--\ndrivers/mmc/host/sdhci-msm.c-2027-\ndrivers/mmc/host/sdhci-msm.c:2028:\treturn qcom_ice_derive_sw_secret(msm_host-\u003eice, eph_key, eph_key_size,\ndrivers/mmc/host/sdhci-msm.c-2029-\t\t\t\t\t sw_secret);\n--\ndrivers/mmc/host/sdhci-msm.c=2120=static const struct blk_crypto_ll_ops sdhci_msm_crypto_ops = {\n--\ndrivers/mmc/host/sdhci-msm.c-2122-\t.keyslot_evict\t\t= sdhci_msm_ice_keyslot_evict,\ndrivers/mmc/host/sdhci-msm.c:2123:\t.derive_sw_secret\t= sdhci_msm_ice_derive_sw_secret,\ndrivers/mmc/host/sdhci-msm.c-2124-\t.import_key\t\t= sdhci_msm_ice_import_key,\n--\ndrivers/soc/qcom/ice.c=457=EXPORT_SYMBOL_GPL(qcom_ice_get_supported_key_type);\n--\ndrivers/soc/qcom/ice.c-459-/**\ndrivers/soc/qcom/ice.c:460: * qcom_ice_derive_sw_secret() - Derive software secret from wrapped key\ndrivers/soc/qcom/ice.c-461- * @ice: ICE driver data\n--\ndrivers/soc/qcom/ice.c-471- */\ndrivers/soc/qcom/ice.c:472:int qcom_ice_derive_sw_secret(struct qcom_ice *ice,\ndrivers/soc/qcom/ice.c-473-\t\t\t const u8 *eph_key, size_t eph_key_size,\n--\ndrivers/soc/qcom/ice.c-475-{\ndrivers/soc/qcom/ice.c:476:\tint err = qcom_scm_derive_sw_secret(eph_key, eph_key_size,\ndrivers/soc/qcom/ice.c-477-\t\t\t\t\t sw_secret,\n--\ndrivers/soc/qcom/ice.c-482-}\ndrivers/soc/qcom/ice.c:483:EXPORT_SYMBOL_GPL(qcom_ice_derive_sw_secret);\ndrivers/soc/qcom/ice.c-484-\n--\ndrivers/ufs/host/ufs-qcom.c=255=static int ufs_qcom_ice_keyslot_evict(struct blk_crypto_profile *profile,\n--\ndrivers/ufs/host/ufs-qcom.c-268-\ndrivers/ufs/host/ufs-qcom.c:269:static int ufs_qcom_ice_derive_sw_secret(struct blk_crypto_profile *profile,\ndrivers/ufs/host/ufs-qcom.c-270-\t\t\t\t\t const u8 *eph_key, size_t eph_key_size,\n--\ndrivers/ufs/host/ufs-qcom.c-275-\ndrivers/ufs/host/ufs-qcom.c:276:\treturn qcom_ice_derive_sw_secret(host-\u003eice, eph_key, eph_key_size,\ndrivers/ufs/host/ufs-qcom.c-277-\t\t\t\t\t sw_secret);\n--\ndrivers/ufs/host/ufs-qcom.c=309=static const struct blk_crypto_ll_ops ufs_qcom_crypto_ops = {\n--\ndrivers/ufs/host/ufs-qcom.c-311-\t.keyslot_evict\t\t= ufs_qcom_ice_keyslot_evict,\ndrivers/ufs/host/ufs-qcom.c:312:\t.derive_sw_secret\t= ufs_qcom_ice_derive_sw_secret,\ndrivers/ufs/host/ufs-qcom.c-313-\t.import_key\t\t= ufs_qcom_ice_import_key,\n--\nfs/crypto/block.c=147=void fscrypt_destroy_inline_crypt_key(struct super_block *sb,\n--\nfs/crypto/block.c-173- */\nfs/crypto/block.c:174:int fscrypt_derive_sw_secret(struct super_block *sb,\nfs/crypto/block.c-175-\t\t\t const u8 *wrapped_key, size_t wrapped_key_size,\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--\nfs/crypto/fscrypt_private.h=413=void fscrypt_destroy_inline_crypt_key(struct super_block *sb,\n--\nfs/crypto/fscrypt_private.h-415-\nfs/crypto/fscrypt_private.h:416:int fscrypt_derive_sw_secret(struct super_block *sb,\nfs/crypto/fscrypt_private.h-417-\t\t\t const u8 *wrapped_key, size_t wrapped_key_size,\n--\nfs/crypto/fscrypt_private.h=457=static inline int\nfs/crypto/fscrypt_private.h:458:fscrypt_derive_sw_secret(struct super_block *sb,\nfs/crypto/fscrypt_private.h-459-\t\t\t const u8 *wrapped_key, size_t wrapped_key_size,\n--\nfs/crypto/keyring.c=530=static int add_master_key(struct super_block *sb,\n--\nfs/crypto/keyring.c-548-\t\tif (secret-\u003eis_hw_wrapped) {\nfs/crypto/keyring.c:549:\t\t\terr = fscrypt_derive_sw_secret(sb, secret-\u003ebytes,\nfs/crypto/keyring.c-550-\t\t\t\t\t\t secret-\u003esize, sw_secret);\n--\ninclude/linux/blk-crypto-profile.h=22=struct blk_crypto_ll_ops {\n--\ninclude/linux/blk-crypto-profile.h-61-\t/**\ninclude/linux/blk-crypto-profile.h:62:\t * @derive_sw_secret: Derive the software secret from a hardware-wrapped\ninclude/linux/blk-crypto-profile.h-63-\t *\t\t key in ephemerally-wrapped form.\n--\ninclude/linux/blk-crypto-profile.h-70-\t */\ninclude/linux/blk-crypto-profile.h:71:\tint (*derive_sw_secret)(struct blk_crypto_profile *profile,\ninclude/linux/blk-crypto-profile.h-72-\t\t\t\tconst u8 *eph_key, size_t eph_key_size,\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--\ninclude/linux/firmware/qcom/qcom_scm.h=122=bool qcom_scm_has_wrapped_key_support(void);\ninclude/linux/firmware/qcom/qcom_scm.h:123:int qcom_scm_derive_sw_secret(const u8 *eph_key, size_t eph_key_size,\ninclude/linux/firmware/qcom/qcom_scm.h-124-\t\t\t u8 *sw_secret, size_t sw_secret_size);\n--\ninclude/soc/qcom/ice.h=20=enum blk_crypto_key_type qcom_ice_get_supported_key_type(struct qcom_ice *ice);\ninclude/soc/qcom/ice.h:21:int qcom_ice_derive_sw_secret(struct qcom_ice *ice,\ninclude/soc/qcom/ice.h-22-\t\t\t const u8 *eph_key, size_t eph_key_size,\n"
} |
| 47/3 | 2026/10/09 05:49 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42093 output: 21 thoughts: 4278 Thoughts: **Analysis of Potential Uninitialized Memory Reads in virtio-blk Crypto Operations** Okay, so I've been tracing the data flow within the virtio-blk driver, specifically focusing on potential vulnerabilities related to uninitialized memory reads or leaks, especially within the cryptographic functions. The primary concern is if the device doesn't fully initialize response buffers, leading to information leakage. My investigation started with the `derive_sw_secret` function, understanding its call paths. I've realized that the central struct used for communication, `virtblk_ctrl_request` (`creq`), is consistently allocated using `kzalloc_obj`, which guarantees zero-initialization. This is good because it eliminates the immediate risk of reading uninitialized data from the request buffer itself. I confirmed this across all critical functions like `virtblk_crypto_import_key`, `virtblk_crypto_prepare_key` and other key generation paths. I then looked at the `virtblk_ctrl_request` struct and its unions, and I can see that `creq`'s memory is zeroed and I also inspected the control completion struct `virtblk_ctrl_completion`. The core logic of `virtblk_ctrl_vq_request` and `virtblk_ctrl_vq_drain` shows that in case of timeout or errors, the `creq->status` is set to `VIRTIO_BLK_S_IOERR`, and the data in `in_resp` is *not* used. This is another safeguard: even if the device doesn't write to the `in_resp` union, the driver doesn't inadvertently expose garbage data to the caller. I moved on to examine what happens with the `virtqueue_get_buf` callback. The key insight is that since `creq` is allocated by kzalloc_obj, if the device doesn't write to `creq->status` and `creq->in_resp`, both buffers are *guaranteed* to be zeroed! I meticulously inspected all read/write paths, including crypto requests (`VIRTIO_BLK_T_CRYPTO_IN`, `VIRTIO_BLK_T_CRYPTO_OUT`) within the `virtblk_setup_cmd` function. The `vbr->out_hdr.crypto_append.msg` is memset to zero before use, so there is no issue here. Also I checked other structs. Regarding the responses from the virtio device, specifically the `in_resp.blob.key` and `in_resp.secret.secret` fields, the code always checks the received `key_size` (or relies on `BLK_CRYPTO_SW_SECRET_SIZE`) and *only* copies a bounded number of bytes. The fact that `creq` is kzalloc'd means that whatever the device *doesn't* write to is already zeroed. Critically, the functions that interact with the response data (e.g., `virtblk_crypto_generate_key`, `virtblk_crypto_import_key`, `virtblk_crypto_prepare_key`, `virtblk_crypto_derive_sw_secret`) correctly handle the `key_size` returned by the device. This implies that the driver is designed to handle partially-initialized responses from the device. Based on this analysis, I'm confident that the virtio-blk driver, as it stands, is reasonably robust against uninitialized memory reads and leaks in its cryptographic operations. The combination of `kzalloc_obj`, explicit size checks, bounded data copies, and the fallback to error handling in case of timeouts/failures provides a solid defense. |
| 48/3 | 2026/10/09 05:50 | tool | grepper |
0mArgs:{
"Expression": "#define virtio_cread"
}Results: {
"Output": "include/linux/virtio_config.h=441=static inline __virtio64 cpu_to_virtio64(struct virtio_device *vdev, u64 val)\n--\ninclude/linux/virtio_config.h-465-/* Config space accessors. */\ninclude/linux/virtio_config.h:466:#define virtio_cread(vdev, structname, member, ptr)\t\t\t\\\ninclude/linux/virtio_config.h-467-\tdo {\t\t\t\t\t\t\t\t\\\n--\ninclude/linux/virtio_config.h-529-/* LE (e.g. modern) Config space accessors. */\ninclude/linux/virtio_config.h:530:#define virtio_cread_le(vdev, structname, member, ptr)\t\t\t\\\ninclude/linux/virtio_config.h-531-\tdo {\t\t\t\t\t\t\t\t\\\n--\ninclude/linux/virtio_config.h=667=static inline void virtio_cwrite64(struct virtio_device *vdev,\n--\ninclude/linux/virtio_config.h-677-/* Conditional config space accessors. */\ninclude/linux/virtio_config.h:678:#define virtio_cread_feature(vdev, fbit, structname, member, ptr)\t\\\ninclude/linux/virtio_config.h-679-\t({\t\t\t\t\t\t\t\t\\\n--\ninclude/linux/virtio_config.h-688-/* Conditional config space accessors. */\ninclude/linux/virtio_config.h:689:#define virtio_cread_le_feature(vdev, fbit, structname, member, ptr)\t\\\ninclude/linux/virtio_config.h-690-\t({\t\t\t\t\t\t\t\t\\\n"
} |
| 49/3 | 2026/10/09 05:50 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 47603 output: 531 thoughts: 3822 Thoughts: **Analysis of Crypto Block Device Patch for Potential Uninitialized Memory Issues**
Okay, let's break this down. My primary concern here is always uninitialized memory, which could lead to information leaks or unpredictable behavior. We're dealing with a crypto block device, so key material and other sensitive data are definitely in the mix.
First, the direct writes to `ptr` via `virtio_cread` *seem* safe, but we need to verify all involved variables were initialized. Luckily, they were initialized before `virtio_cread`, so that looks good.
Next, I need to check how `crypto_modes_supported` and crypto modes are initialized and handled. The `crypto_modes_supported` array is initialized to zero, which is expected. The `virtblk_get_crypto_modes` function is interesting because it uses a control request. I see the allocation and usage of `creq` using `kzalloc_obj`, which *should* zero the memory, but let's carefully review the request handling. There is a potential timeout issue which does not go to `out_free` and just returns, but the `creq` pointer is zero allocated. I am not seeing any UAF or issues when the timeout happens.
Now, let's move on to the `virtblk_ctrl_vq_quiesce` and `virtblk_ctrl_vq_drain` functions, which are responsible for quiescing and draining the control queue. The `creq` is detached using `virtqueue_detach_unused_buf`, where an `abandoned` variable is available. The `creq->compl->abandoned` variable is essential for knowing whether a request was abandoned and the memory can be freed, and that happens in the queue callback. Memory is freed, so there are not any memory leaks here.
If there were a race condition or UAF, it would trigger KASAN, and this is standard. In the callback, if the request was abandoned, then `kfree_sensitive` is called, so this is handled correctly. If the completion was not abandoned, then the completion happens, and this is also fine. I feel safe that KASAN is sufficient for any potential bugs here.
The key question is whether we need KMSAN (Kernel Memory Sanitizer). KASAN catches a lot, but KMSAN specifically targets uninitialized memory reads, which is our biggest threat here. I must examine all four conditions that trigger KMSAN:
1. **Kernel structures to userspace:** Does this patch transmit kernel data directly to user space? I see several functions like `virtblk_crypto_keyslot_program`, `virtblk_crypto_keyslot_evict`, `virtblk_crypto_derive_sw_secret`, `virtblk_crypto_generate_key`, and `virtblk_crypto_import_key` related to crypto keys. I will check for uninitialized memory reads when any of these call an ioctl.
I checked `blk_crypto_ioctl_generate_key` and `blk_crypto_ioctl_import_key` and no uninitialized memory. The `kzalloc`'d `creq` is used, and then copies data.
2. **Conditional logic based on uninitialized variables:** I must review all the conditionals added or changed. I need to make sure any branching logic is safe. I checked `is_crypto_request`, `if (vbr->out_hdr.base.type == ...`, `if (vblk->ctrl_vq.dead)`, `comp->abandoned`, `creq->status`, `vblk->crypto_profile_initialized`, `zoned_disk`, `has_ctrl_vq`, `crypto_modes_supported`, `max_slots`, `max_dun_bytes`, `key_types`. All variables are explicitly initialized before use!
3. **Allocation of complex structures with potentially uninitialized fields:** I looked for any complex data structures allocation. I see `creq` allocations (`kzalloc_obj`). The `comp` is allocated, and the `done` and `abandoned` fields are initialized immediately with `kmalloc_obj`, which is good. There are also allocations of `vqs_info` (`kzalloc_objs`) and `vblk->out_hdr.crypto_append.msg`, but these are all explicitly `memset` to 0. All complex data structures are zeroed.
4. **Bounds checks or logic allowing access to uninitialized bytes:** I check the change of `out_hdr_len`. The bounds checks are related to `sizeof(vbr->out_hdr.base)` and `sizeof(vbr->out_hdr.crypto_append)`. The code explicitly expands the header length only for the right type, and it zero-initializes the `vbr->out_hdr.crypto
|
| 50/3 | 2026/10/09 05:50 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch introduces inline encryption support and a control virtqueue to the virtio-blk driver (`drivers/block/virtio_blk.c`).\n\nAnalysis of uninitialized memory and KMSAN relevance:\n1. Heap allocations:\n - Control queue requests (`creq`) in all crypto operations (`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`) are allocated using `kzalloc_obj`, ensuring all fields and padding are zero-initialized.\n - The completion object `comp` in `virtblk_ctrl_vq_request()` is allocated via `kmalloc_obj`, but all of its fields (`done` and `abandoned`) are immediately initialized before submission.\n - Request structures in `init_vq()` use `kzalloc_objs` for `vqs_info`.\n2. Stack variables and structures:\n - All stack variables (e.g. `crypto_modes_supported`, `max_slots`, `max_dun_bytes`, `key_types`) are explicitly initialized with `= 0` or `= { 0 }`.\n - In `virtblk_setup_cmd()`, `vbr-\u003eout_hdr.crypto_append.msg` is explicitly zeroed via `memset()` before its fields are populated.\n3. Information leaks:\n - Wrapped keys generated or imported and returned to userspace via block device ioctls (`BLKCRYPTOGENERATEKEY`, `BLKCRYPTOIMPORTKEY`) copy only `key_size` bytes from the kzalloc'd response buffers.\n4. KASAN vs KMSAN:\n - The primary risks introduced by this patch involve concurrency, request lifetime handling on timeouts/drain (`abandoned` requests), locking (`vblk-\u003ectrl_vq.mutex`, `lock`), and virtqueue buffer management. These potential issues (use-after-free, double-free, leaks, or deadlocks) are strictly within the scope of KASAN, LOCKDEP, and standard kernel debugging facilities, not KMSAN.\n\nTherefore, no uninitialized memory risks are introduced, and a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch introduces inline encryption support and a control virtqueue to the virtio-blk driver (`drivers/block/virtio_blk.c`).\n\nAnalysis of uninitialized memory and KMSAN relevance:\n1. Heap allocations:\n - Control queue requests (`creq`) in all crypto operations (`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`) are allocated using `kzalloc_obj`, ensuring all fields and padding are zero-initialized.\n - The completion object `comp` in `virtblk_ctrl_vq_request()` is allocated via `kmalloc_obj`, but all of its fields (`done` and `abandoned`) are immediately initialized before submission.\n - Request structures in `init_vq()` use `kzalloc_objs` for `vqs_info`.\n2. Stack variables and structures:\n - All stack variables (e.g. `crypto_modes_supported`, `max_slots`, `max_dun_bytes`, `key_types`) are explicitly initialized with `= 0` or `= { 0 }`.\n - In `virtblk_setup_cmd()`, `vbr-\u003eout_hdr.crypto_append.msg` is explicitly zeroed via `memset()` before its fields are populated.\n3. Information leaks:\n - Wrapped keys generated or imported and returned to userspace via block device ioctls (`BLKCRYPTOGENERATEKEY`, `BLKCRYPTOIMPORTKEY`) copy only `key_size` bytes from the kzalloc'd response buffers.\n4. KASAN vs KMSAN:\n - The primary risks introduced by this patch involve concurrency, request lifetime handling on timeouts/drain (`abandoned` requests), locking (`vblk-\u003ectrl_vq.mutex`, `lock`), and virtqueue buffer management. These potential issues (use-after-free, double-free, leaks, or deadlocks) are strictly within the scope of KASAN, LOCKDEP, and standard kernel debugging facilities, not KMSAN.\n\nTherefore, no uninitialized memory risks are introduced, and a dedicated KMSAN fuzzing session is not required.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|