| Seq | Timestamp | Type | Name | Duration |
|---|---|---|---|---|
| 0/0 | 2026/10/29 06:16 | flow | patch-triage |
9mArgs:null Results: {
"EnableConfigs": [
"KVM",
"KVM_INTEL",
"KVM_AMD"
],
"FocusSymbols": [
"kvm_ioeventfd",
"kvm_assign_ioeventfd_idx",
"ioeventfd_write"
],
"KMSANReasoning": "The patch series introduces KVM_CAP_IOEVENTFD_POST_WRITE, allowing guest MMIO/PIO writes to be posted directly to a userspace address via put_user() before signaling an eventfd.\n\nKMSAN vs KASAN evaluation:\n1. No uninitialized memory copied to userspace: Although put_user() is used in ioeventfd_write(), it writes scalar primitive values (u8, u16, u32, or u64 based on the write length) obtained directly from the guest write buffer via get_unaligned(). There are no structs, structure padding, or uninitialized kernel buffers involved in this transfer.\n2. Structure allocations: The internal struct _ioeventfd is allocated using kzalloc_obj(), ensuring all fields (including the new post_addr pointer) are zero-initialized.\n3. Userspace IOCTL input: The kvm_ioeventfd struct is an input-only argument (_IOW) to KVM_IOEVENTFD copied via copy_from_user(); no kernel data or uninitialized fields are copied back to user space.\n4. Error modes: Any potential bugs introduced by this patch would be related to user pointer validation, concurrency, or device registration logic, which are covered by standard kernel bug detectors or KASAN.\n\nTherefore, the changes do not introduce or expose any uninitialized memory risks, and a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false,
"Reasoning": "The patch adds support for KVM_IOEVENTFD_FLAG_POST_WRITE (KVM_CAP_IOEVENTFD_POST_WRITE) in KVM's ioeventfd subsystem. It modifies reachable kernel code in virt/kvm/eventfd.c, adding user pointer validation in kvm_assign_ioeventfd_idx(), modifying ioeventfd matching logic, adding put_user() operations to write to guest-specified userspace addresses directly during vCPU MMIO/PIO exit handling in ioeventfd_write(), and adding a defensive WARN_ON_ONCE() assertion for unsupported lengths. This code is reachable from userspace via /dev/kvm ioctls and vCPU guest execution, making it a high-value target for fuzzing.",
"WorthFuzzing": true
} |
| 1/1 | 2026/10/29 06:16 | action | read-patch-diff |
0mArgs:null Results: {
"PatchDiff": "commit fdbb5357ab9de3115f5cde27eaaf6974e5b34459\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate: Thu Oct 29 06:16:49 2026 +0000\n\n syz-cluster: applied patch under review\n\ndiff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst\nindex e95c9cda085d94..a5e61ba7eaf583 100644\n--- a/Documentation/virt/kvm/api.rst\n+++ b/Documentation/virt/kvm/api.rst\n@@ -2132,7 +2132,9 @@ provided event instead of triggering an exit.\n \t__u32 len; /* 0, 1, 2, 4, or 8 bytes */\n \t__s32 fd;\n \t__u32 flags;\n-\t__u8 pad[36];\n+\t__u32 pad0;\n+\t__u64 post_addr; /* address to write to if POST_WRITE is set */\n+\t__u8 pad[24];\n };\n \n For the special case of virtio-ccw devices on s390, the ioevent is matched\n@@ -2145,6 +2147,7 @@ The following flags are defined::\n #define KVM_IOEVENTFD_FLAG_DEASSIGN (1 \u003c\u003c kvm_ioeventfd_flag_nr_deassign)\n #define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \\\n \t(1 \u003c\u003c kvm_ioeventfd_flag_nr_virtio_ccw_notify)\n+ #define KVM_IOEVENTFD_FLAG_POST_WRITE (1 \u003c\u003c kvm_ioeventfd_flag_nr_post_write)\n \n If datamatch flag is set, the event will be signaled only if the written value\n to the registered address is equal to datamatch in struct kvm_ioeventfd.\n@@ -2157,6 +2160,15 @@ the kernel will ignore the length of guest write and may get a faster vmexit.\n The speedup may only apply to specific architectures, but the ioeventfd will\n work anyway.\n \n+With KVM_IOEVENTFD_FLAG_POST_WRITE, the value being written is copied to the\n+userspace address specified by post_addr, and the eventfd is signaled. The\n+copy is guaranteed to complete before the eventfd is signaled, so a userspace\n+reader that wakes on the eventfd will observe the written value. When multiple\n+vCPUs write to the same ioeventfd concurrently, the value at post_addr reflects\n+one of the writes. If the copy to post_addr fails (e.g. the memory has been\n+unmapped), the eventfd is not signaled and the write is reported to userspace\n+as a regular MMIO/PIO exit.\n+\n 4.60 KVM_DIRTY_TLB\n ------------------\n \ndiff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h\nindex 8dff2fc1972e97..07ac3142ce17f6 100644\n--- a/include/uapi/linux/kvm.h\n+++ b/include/uapi/linux/kvm.h\n@@ -644,6 +644,7 @@ enum {\n \tkvm_ioeventfd_flag_nr_deassign,\n \tkvm_ioeventfd_flag_nr_virtio_ccw_notify,\n \tkvm_ioeventfd_flag_nr_fast_mmio,\n+\tkvm_ioeventfd_flag_nr_post_write,\n \tkvm_ioeventfd_flag_nr_max,\n };\n \n@@ -652,6 +653,7 @@ enum {\n #define KVM_IOEVENTFD_FLAG_DEASSIGN (1 \u003c\u003c kvm_ioeventfd_flag_nr_deassign)\n #define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \\\n \t(1 \u003c\u003c kvm_ioeventfd_flag_nr_virtio_ccw_notify)\n+#define KVM_IOEVENTFD_FLAG_POST_WRITE (1 \u003c\u003c kvm_ioeventfd_flag_nr_post_write)\n \n #define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 \u003c\u003c kvm_ioeventfd_flag_nr_max) - 1)\n \n@@ -661,7 +663,9 @@ struct kvm_ioeventfd {\n \t__u32 len; /* 1, 2, 4, or 8 bytes; or 0 to ignore length */\n \t__s32 fd;\n \t__u32 flags;\n-\t__u8 pad[36];\n+\t__u32 pad0;\n+\t__u64 post_addr; /* address to write to if POST_WRITE is set */\n+\t__u8 pad[24];\n };\n \n #define KVM_X86_DISABLE_EXITS_MWAIT (1 \u003c\u003c 0)\n@@ -1000,6 +1004,7 @@ struct kvm_enable_cap {\n #define KVM_CAP_PPC_COMPAT_CAPS 250\n #define KVM_CAP_ARM_PMU_V3_STRICT 251\n #define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 252\n+#define KVM_CAP_IOEVENTFD_POST_WRITE 253\n \n struct kvm_irq_routing_irqchip {\n \t__u32 irqchip;\ndiff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm\nindex 3cb35a6963d513..38860f2f08b0c8 100644\n--- a/tools/testing/selftests/kvm/Makefile.kvm\n+++ b/tools/testing/selftests/kvm/Makefile.kvm\n@@ -92,6 +92,7 @@ TEST_GEN_PROGS_x86 += x86/hyperv_features\n TEST_GEN_PROGS_x86 += x86/hyperv_ipi\n TEST_GEN_PROGS_x86 += x86/hyperv_svm_test\n TEST_GEN_PROGS_x86 += x86/hyperv_tlb_flush\n+TEST_GEN_PROGS_x86 += x86/ioeventfd_posted_write_test\n TEST_GEN_PROGS_x86 += x86/kvm_clock_test\n TEST_GEN_PROGS_x86 += x86/kvm_pv_test\n TEST_GEN_PROGS_x86 += x86/kvm_buslock_test\ndiff --git a/tools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c b/tools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c\nnew file mode 100644\nindex 00000000000000..f429f875c023e2\n--- /dev/null\n+++ b/tools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c\n@@ -0,0 +1,632 @@\n+// SPDX-License-Identifier: GPL-2.0\n+/*\n+ * ioeventfd_posted_write_test.c - Tests for KVM_IOEVENTFD_FLAG_POST_WRITE.\n+ *\n+ * Tests that when KVM_IOEVENTFD_FLAG_POST_WRITE is set and the MMIO/PIO\n+ * address is written to, the value is copied to the user-provided address\n+ * and the eventfd is signaled. Also tests negative cases and interactions\n+ * with DATAMATCH.\n+ *\n+ * Copyright Nutanix, 2026\n+ *\n+ * Author: Thanos Makatos \u003cthanos.makatos@nutanix.com\u003e\n+ */\n+\n+#include \u003cerrno.h\u003e\n+#include \u003cpoll.h\u003e\n+#include \u003cstring.h\u003e\n+\n+#include \"kvm_util.h\"\n+#include \"processor.h\"\n+#include \"ucall_common.h\"\n+\n+#define MMIO_GPA\t(1UL \u003c\u003c 30)\n+#define PIO_PORT\t0xe000\n+#define TEST_VAL\t0xDEADBEEFCAFEBABEULL\n+#define MATCH_VAL\t0x42U\n+#define NOMATCH_VAL\t(MATCH_VAL + 1)\n+#define POISON_VAL\t0xFFFFFFFFU\n+\n+/*\n+ * Check that the most recent vCPU exit is a ucall (delivered as KVM_EXIT_IO\n+ * on x86) matching @expected_cmd. The caller must have already called\n+ * vcpu_run().\n+ *\n+ * @expected_cmd: UCALL_SYNC, UCALL_DONE, etc.\n+ * @expected_stage: for UCALL_SYNC, the stage number passed by GUEST_SYNC().\n+ * Ignored for other ucall types.\n+ *\n+ * Aborts the test on UCALL_ABORT (a guest-side assertion failure).\n+ */\n+static void assert_ucall(struct kvm_vcpu *vcpu, uint64_t expected_cmd,\n+ uint64_t expected_stage)\n+{\n+\tstruct ucall uc;\n+\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\n+\tswitch (get_ucall(vcpu, \u0026uc)) {\n+\tcase UCALL_ABORT:\n+\t\tREPORT_GUEST_ASSERT(uc);\n+\t\tbreak;\n+\tcase UCALL_SYNC:\n+\t\tTEST_ASSERT(expected_cmd == UCALL_SYNC,\n+\t\t\t \"got UCALL_SYNC, expected %lu\",\n+\t\t\t expected_cmd);\n+\t\tTEST_ASSERT(uc.args[1] == expected_stage,\n+\t\t\t \"expected stage %lu, got %lu\",\n+\t\t\t expected_stage, uc.args[1]);\n+\t\tbreak;\n+\tcase UCALL_DONE:\n+\t\tTEST_ASSERT(expected_cmd == UCALL_DONE,\n+\t\t\t \"got UCALL_DONE, expected %lu\",\n+\t\t\t expected_cmd);\n+\t\tbreak;\n+\tdefault:\n+\t\tTEST_FAIL(\"unexpected ucall %lu\", uc.cmd);\n+\t}\n+}\n+\n+/*\n+ * Verify that KVM_IOEVENTFD rejects invalid POST_WRITE configurations:\n+ * - len=0: the kernel needs a non-zero length to know how many bytes to copy.\n+ * - post_addr=NULL: there is no destination for the copy.\n+ * - post_addr outside the process address space: access_ok() rejects it.\n+ * - post_addr unaligned: IS_ALIGNED() rejects it\n+ * All three must fail with EINVAL.\n+ */\n+static void test_post_write_negative(void)\n+{\n+\tstruct kvm_ioeventfd ioeventfd;\n+\tstruct kvm_vm *vm;\n+\tuint64_t dummy;\n+\tint ret;\n+\tint fd;\n+\n+\tvm = vm_create_barebones();\n+\tfd = kvm_new_eventfd();\n+\n+\t/* length cannot be zero */\n+\tioeventfd = (struct kvm_ioeventfd) {\n+\t\t.addr = MMIO_GPA,\n+\t\t.len = 0,\n+\t\t.fd = fd,\n+\t\t.flags = KVM_IOEVENTFD_FLAG_POST_WRITE,\n+\t\t.post_addr = (u64)\u0026dummy,\n+\t};\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(ret \u0026\u0026 errno == EINVAL,\n+\t\t \"len=0: expected EINVAL, got ret=%d errno=%d\", ret, errno);\n+\n+\t/* post_addr cannot be NULL */\n+\tioeventfd.len = 4;\n+\tioeventfd.post_addr = 0ULL;\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(ret \u0026\u0026 errno == EINVAL,\n+\t\t \"NULL post_addr: expected EINVAL, got ret=%d errno=%d\",\n+\t\t ret, errno);\n+\n+\t/* bogus post_addr */\n+\tioeventfd.post_addr = (u64)0xdeaddeaddeaddeadULL;\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(ret \u0026\u0026 errno == EINVAL,\n+\t\t \"bad post_addr: expected EINVAL, got ret=%d errno=%d\",\n+\t\t ret, errno);\n+\n+\t/* unaligned post_addr */\n+\tioeventfd.post_addr = (uintptr_t)((char *)\u0026dummy + 1);\n+\tioeventfd.len = sizeof dummy;\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(ret \u0026\u0026 errno == EINVAL,\n+\t\t \"bad post_addr: expected EINVAL, got ret=%d errno=%d\",\n+\t\t ret, errno);\n+\n+\tclose(fd);\n+\tkvm_vm_free(vm);\n+}\n+\n+#define DEFINE_GUEST_WRITE_FN(suffix, type) \\\n+static void guest_code_w##suffix(void) { \\\n+\t*(volatile type *)MMIO_GPA = (type)TEST_VAL; \\\n+\tGUEST_DONE(); \\\n+}\n+\n+DEFINE_GUEST_WRITE_FN(1, uint8_t)\n+DEFINE_GUEST_WRITE_FN(2, uint16_t)\n+DEFINE_GUEST_WRITE_FN(4, uint32_t)\n+DEFINE_GUEST_WRITE_FN(8, uint64_t)\n+\n+/*\n+ * Verify that ioeventfd_write copies exactly @width bytes to post_addr for\n+ * each supported MMIO write width (1, 2, 4, 8). The guest writes the low\n+ * @width bytes of TEST_VAL; the host checks that exactly those bytes land\n+ * at post_addr and the eventfd is signaled.\n+ */\n+static void test_post_write_width(int width, void (*guest_fn)(void))\n+{\n+\tuint64_t actual, expected, count;\n+\tstruct kvm_ioeventfd ioeventfd;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tint fd, ret;\n+\n+\t/* need to initialize to 0 because the guest writes the low @width bytes */\n+\tactual = 0;\n+\texpected = 0;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_fn);\n+\tvirt_map(vm, MMIO_GPA, MMIO_GPA, 1);\n+\n+\tfd = kvm_new_eventfd();\n+\n+\tioeventfd = (struct kvm_ioeventfd) {\n+\t\t.addr = MMIO_GPA,\n+\t\t.len = width,\n+\t\t.fd = fd,\n+\t\t.flags = KVM_IOEVENTFD_FLAG_POST_WRITE,\n+\t\t.post_addr = (u64)\u0026actual,\n+\t};\n+\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(!ret, \"KVM_IOEVENTFD failed: %s\", strerror(errno));\n+\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_DONE, 0);\n+\n+\tret = read(fd, \u0026count, sizeof(count));\n+\tTEST_ASSERT(ret == sizeof(count),\n+\t\t \"eventfd read failed: ret=%d errno=%d\", ret, errno);\n+\n+\tmemcpy(\u0026expected, \u0026(uint64_t){TEST_VAL}, width);\n+\tTEST_ASSERT_EQ(actual, expected);\n+\n+\tclose(fd);\n+\tkvm_vm_free(vm);\n+}\n+\n+static void guest_code_datamatch(void)\n+{\n+\t*(volatile uint32_t *)MMIO_GPA = MATCH_VAL;\n+\tGUEST_SYNC(1);\n+\t*(volatile uint32_t *)MMIO_GPA = NOMATCH_VAL;\n+\tGUEST_SYNC(2);\n+\tGUEST_DONE();\n+}\n+\n+/*\n+ * Test the interaction between DATAMATCH and POST_WRITE. When both flags are\n+ * set, ioeventfd_write should only fire (signal eventfd + copy value) when the\n+ * written value matches datamatch. A non-matching write must leave the eventfd\n+ * unsignaled and post_addr untouched, and fall through to KVM_EXIT_MMIO.\n+ */\n+static void test_post_write_datamatch(void)\n+{\n+\tstruct kvm_ioeventfd ioeventfd;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_run *run;\n+\tstruct kvm_vm *vm;\n+\tstruct pollfd pfd;\n+\tuint64_t count;\n+\tuint32_t actual;\n+\tint fd, ret;\n+\n+\tactual = POISON_VAL;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code_datamatch);\n+\tvirt_map(vm, MMIO_GPA, MMIO_GPA, 1);\n+\trun = vcpu-\u003erun;\n+\n+\tfd = kvm_new_eventfd();\n+\tpfd = (struct pollfd){ .fd = fd, .events = POLLIN };\n+\n+\tioeventfd = (struct kvm_ioeventfd) {\n+\t\t.datamatch = MATCH_VAL,\n+\t\t.addr = MMIO_GPA,\n+\t\t.len = 4,\n+\t\t.fd = fd,\n+\t\t.flags = KVM_IOEVENTFD_FLAG_POST_WRITE |\n+\t\t\t KVM_IOEVENTFD_FLAG_DATAMATCH,\n+\t\t.post_addr = (u64)\u0026actual,\n+\t};\n+\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(!ret, \"KVM_IOEVENTFD failed: %s\", strerror(errno));\n+\n+\t/*\n+\t * Guest writes MATCH_VAL → ioeventfd fires (value copied, eventfd\n+\t * signaled), vCPU continues, then GUEST_SYNC(1).\n+\t */\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_SYNC, 1);\n+\tTEST_ASSERT(read(fd, \u0026count, sizeof(count)) == sizeof(count),\n+\t \"eventfd read failed: errno=%d\", errno);\n+\tTEST_ASSERT_EQ(actual, MATCH_VAL);\n+\n+\tactual = POISON_VAL;\n+\n+\t/*\n+\t * Guest writes NOMATCH_VAL → ioeventfd_in_range() returns false, bus\n+\t * returns -EOPNOTSUPP → KVM_EXIT_MMIO to userspace.\n+\t */\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);\n+\tTEST_ASSERT(run-\u003emmio.is_write, \"expected MMIO write\");\n+\tTEST_ASSERT(run-\u003emmio.phys_addr == MMIO_GPA,\n+\t \"expected MMIO at 0x%lx, got 0x%llx\",\n+\t MMIO_GPA, run-\u003emmio.phys_addr);\n+\n+\t/* Re-enter: KVM completes the MMIO, guest runs to GUEST_SYNC(2). */\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_SYNC, 2);\n+\n+\tTEST_ASSERT(poll(\u0026pfd, 1, 0) == 0,\n+\t \"eventfd should not be signaled after non-matching write\");\n+\tTEST_ASSERT_EQ(actual, (uint32_t)POISON_VAL);\n+\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_DONE, 0);\n+\n+\tclose(fd);\n+\tkvm_vm_free(vm);\n+}\n+\n+static void guest_code_multi(void)\n+{\n+\t*(volatile uint32_t *)MMIO_GPA = 0x11111111;\n+\tGUEST_SYNC(1);\n+\t*(volatile uint32_t *)MMIO_GPA = 0x22222222;\n+\tGUEST_SYNC(2);\n+\t*(volatile uint32_t *)MMIO_GPA = 0x33333333;\n+\tGUEST_SYNC(3);\n+\tGUEST_DONE();\n+}\n+\n+/*\n+ * Verify that post_addr is updated on every MMIO write, not just the first.\n+ * The guest writes three distinct values in sequence; the host checks after\n+ * each one that post_addr holds the latest value and the eventfd is signaled\n+ * each time.\n+ */\n+static void test_post_write_multi(void)\n+{\n+\tstatic const uint32_t expected[] = {\n+\t\t0x11111111, 0x22222222, 0x33333333,\n+\t};\n+\tstruct kvm_ioeventfd ioeventfd;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tuint64_t count;\n+\tuint32_t actual;\n+\tint fd, ret, i;\n+\n+\tactual = POISON_VAL;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code_multi);\n+\tvirt_map(vm, MMIO_GPA, MMIO_GPA, 1);\n+\n+\tfd = kvm_new_eventfd();\n+\n+\tioeventfd = (struct kvm_ioeventfd) {\n+\t\t.addr = MMIO_GPA,\n+\t\t.len = 4,\n+\t\t.fd = fd,\n+\t\t.flags = KVM_IOEVENTFD_FLAG_POST_WRITE,\n+\t\t.post_addr = (u64)\u0026actual,\n+\t};\n+\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(!ret, \"KVM_IOEVENTFD failed: %s\", strerror(errno));\n+\n+\tfor (i = 0; i \u003c ARRAY_SIZE(expected); i++) {\n+\t\tvcpu_run(vcpu);\n+\t\tassert_ucall(vcpu, UCALL_SYNC, i + 1);\n+\t\tTEST_ASSERT(read(fd, \u0026count, sizeof(count)) == sizeof(count),\n+\t\t \"eventfd read failed: errno=%d\", errno);\n+\t\tTEST_ASSERT_EQ(actual, expected[i]);\n+\t}\n+\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_DONE, 0);\n+\n+\tclose(fd);\n+\tkvm_vm_free(vm);\n+}\n+\n+static void guest_code_multi_nosync(void)\n+{\n+\t*(volatile uint32_t *)MMIO_GPA = 0x11111111;\n+\t*(volatile uint32_t *)MMIO_GPA = 0x22222222;\n+\t*(volatile uint32_t *)MMIO_GPA = 0x33333333;\n+\tGUEST_DONE();\n+}\n+\n+/*\n+ * Variant of the multi-write test where the guest performs three consecutive\n+ * MMIO writes with no GUEST_SYNC in between. All three are handled in-kernel\n+ * by ioeventfd before the vCPU exits at GUEST_DONE. Verify that:\n+ * - post_addr reflects the last written value (0x33333333).\n+ * - A single eventfd read() returns a counter of 3 (one signal per write).\n+ */\n+static void test_post_write_multi_nosync(void)\n+{\n+\tstruct kvm_ioeventfd ioeventfd;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tuint64_t count;\n+\tuint32_t actual;\n+\tint fd, ret;\n+\n+\tactual = POISON_VAL;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code_multi_nosync);\n+\tvirt_map(vm, MMIO_GPA, MMIO_GPA, 1);\n+\n+\tfd = kvm_new_eventfd();\n+\n+\tioeventfd = (struct kvm_ioeventfd) {\n+\t\t.addr = MMIO_GPA,\n+\t\t.len = 4,\n+\t\t.fd = fd,\n+\t\t.flags = KVM_IOEVENTFD_FLAG_POST_WRITE,\n+\t\t.post_addr = (u64)\u0026actual,\n+\t};\n+\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(!ret, \"KVM_IOEVENTFD failed: %s\", strerror(errno));\n+\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_DONE, 0);\n+\n+\tret = read(fd, \u0026count, sizeof(count));\n+\tTEST_ASSERT(ret == sizeof(count),\n+\t\t \"eventfd read failed: ret=%d errno=%d\", ret, errno);\n+\tTEST_ASSERT_EQ(count, (uint64_t)3);\n+\tTEST_ASSERT_EQ(actual, (uint32_t)0x33333333);\n+\n+\tclose(fd);\n+\tkvm_vm_free(vm);\n+}\n+\n+static void guest_code_deassign(void)\n+{\n+\t*(volatile uint32_t *)MMIO_GPA = MATCH_VAL;\n+\tGUEST_SYNC(1);\n+\t*(volatile uint32_t *)MMIO_GPA = MATCH_VAL;\n+\tGUEST_DONE();\n+}\n+\n+/*\n+ * Verify that deassigning an ioeventfd with POST_WRITE fully removes it from\n+ * the I/O bus.\n+ */\n+static void test_post_write_deassign(void)\n+{\n+\tstruct kvm_ioeventfd ioeventfd;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_run *run;\n+\tstruct kvm_vm *vm;\n+\tstruct pollfd pfd;\n+\tuint64_t count;\n+\tuint32_t actual;\n+\tint fd, ret;\n+\n+\tactual = POISON_VAL;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code_deassign);\n+\tvirt_map(vm, MMIO_GPA, MMIO_GPA, 1);\n+\trun = vcpu-\u003erun;\n+\n+\tfd = kvm_new_eventfd();\n+\tpfd = (struct pollfd){ .fd = fd, .events = POLLIN };\n+\n+\tioeventfd = (struct kvm_ioeventfd) {\n+\t\t.addr = MMIO_GPA,\n+\t\t.len = 4,\n+\t\t.fd = fd,\n+\t\t.flags = KVM_IOEVENTFD_FLAG_POST_WRITE,\n+\t\t.post_addr = (u64)\u0026actual,\n+\t};\n+\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(!ret, \"KVM_IOEVENTFD assign failed: %s\", strerror(errno));\n+\n+\t/*\n+\t * Guest writes MATCH_VAL → ioeventfd fires, then GUEST_SYNC(1).\n+\t */\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_SYNC, 1);\n+\tTEST_ASSERT(read(fd, \u0026count, sizeof(count)) == sizeof(count),\n+\t \"eventfd read failed: errno=%d\", errno);\n+\tTEST_ASSERT_EQ(actual, MATCH_VAL);\n+\n+\t/* Deassign the ioeventfd. */\n+\tioeventfd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(!ret, \"KVM_IOEVENTFD deassign failed: %s\", strerror(errno));\n+\n+\tactual = POISON_VAL;\n+\n+\t/*\n+\t * Guest writes MATCH_VAL again → no handler on the bus →\n+\t * KVM_EXIT_MMIO to userspace.\n+\t */\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);\n+\tTEST_ASSERT(run-\u003emmio.is_write, \"expected MMIO write\");\n+\tTEST_ASSERT(run-\u003emmio.phys_addr == MMIO_GPA,\n+\t \"expected MMIO at 0x%lx, got 0x%llx\",\n+\t MMIO_GPA, run-\u003emmio.phys_addr);\n+\n+\t/* Re-enter: KVM completes MMIO, guest runs to GUEST_DONE. */\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_DONE, 0);\n+\n+\tTEST_ASSERT(poll(\u0026pfd, 1, 0) == 0,\n+\t\t \"eventfd should not be signaled after deassign\");\n+\tTEST_ASSERT_EQ(actual, (uint32_t)POISON_VAL);\n+\n+\tclose(fd);\n+\tkvm_vm_free(vm);\n+}\n+\n+static void guest_code_pio(void)\n+{\n+\toutl(PIO_PORT, (uint32_t)TEST_VAL);\n+\tGUEST_DONE();\n+}\n+\n+/*\n+ * Verify that POST_WRITE works on the PIO bus (KVM_PIO_BUS), not just MMIO.\n+ * The guest does an outl to PIO_PORT; the host checks that the written value\n+ * is copied to post_addr and the eventfd is signaled.\n+ */\n+static void test_post_write_pio(void)\n+{\n+\tstruct kvm_ioeventfd ioeventfd;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_vm *vm;\n+\tuint64_t count;\n+\tuint32_t actual;\n+\tint fd, ret;\n+\n+\tactual = POISON_VAL;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code_pio);\n+\n+\tfd = kvm_new_eventfd();\n+\n+\tioeventfd = (struct kvm_ioeventfd) {\n+\t\t.addr = PIO_PORT,\n+\t\t.len = 4,\n+\t\t.fd = fd,\n+\t\t.flags = KVM_IOEVENTFD_FLAG_POST_WRITE |\n+\t\t\t KVM_IOEVENTFD_FLAG_PIO,\n+\t\t.post_addr = (u64)\u0026actual,\n+\t};\n+\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(!ret, \"KVM_IOEVENTFD failed: %s\", strerror(errno));\n+\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_DONE, 0);\n+\n+\tret = read(fd, \u0026count, sizeof(count));\n+\tTEST_ASSERT(ret == sizeof(count),\n+\t \"eventfd read failed: ret=%d errno=%d\", ret, errno);\n+\n+\tTEST_ASSERT_EQ(actual, (uint32_t)TEST_VAL);\n+\n+\tclose(fd);\n+\tkvm_vm_free(vm);\n+}\n+\n+static void guest_code_pio_datamatch(void)\n+{\n+\toutl(PIO_PORT, MATCH_VAL);\n+\tGUEST_SYNC(1);\n+\toutl(PIO_PORT, NOMATCH_VAL);\n+\tGUEST_SYNC(2);\n+\tGUEST_DONE();\n+}\n+\n+/*\n+ * Test POST_WRITE + PIO + DATAMATCH together. When all three flags are set,\n+ * the ioeventfd should only fire when the outl value matches datamatch.\n+ * A non-matching outl must fall through to KVM_EXIT_IO (port I/O exit),\n+ * leaving the eventfd unsignaled and post_addr untouched.\n+ */\n+static void test_post_write_pio_datamatch(void)\n+{\n+\tstruct kvm_ioeventfd ioeventfd;\n+\tstruct kvm_vcpu *vcpu;\n+\tstruct kvm_run *run;\n+\tstruct kvm_vm *vm;\n+\tstruct pollfd pfd;\n+\tuint64_t count;\n+\tuint32_t actual;\n+\tint fd, ret;\n+\n+\tactual = POISON_VAL;\n+\n+\tvm = vm_create_with_one_vcpu(\u0026vcpu, guest_code_pio_datamatch);\n+\trun = vcpu-\u003erun;\n+\n+\tfd = kvm_new_eventfd();\n+\tpfd = (struct pollfd){ .fd = fd, .events = POLLIN };\n+\n+\tioeventfd = (struct kvm_ioeventfd) {\n+\t\t.datamatch = MATCH_VAL,\n+\t\t.addr = PIO_PORT,\n+\t\t.len = 4,\n+\t\t.fd = fd,\n+\t\t.flags = KVM_IOEVENTFD_FLAG_POST_WRITE |\n+\t\t KVM_IOEVENTFD_FLAG_PIO |\n+\t\t KVM_IOEVENTFD_FLAG_DATAMATCH,\n+\t\t.post_addr = (u64)\u0026actual,\n+\t};\n+\n+\tret = __vm_ioctl(vm, KVM_IOEVENTFD, \u0026ioeventfd);\n+\tTEST_ASSERT(!ret, \"KVM_IOEVENTFD failed: %s\", strerror(errno));\n+\n+\t/*\n+\t * Guest does outl MATCH_VAL → ioeventfd fires, then GUEST_SYNC(1).\n+\t */\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_SYNC, 1);\n+\tTEST_ASSERT(read(fd, \u0026count, sizeof(count)) == sizeof(count),\n+\t \"eventfd read failed: errno=%d\", errno);\n+\tTEST_ASSERT_EQ(actual, MATCH_VAL);\n+\n+\tactual = POISON_VAL;\n+\n+\t/*\n+\t * Guest does outl NOMATCH_VAL → no match → KVM_EXIT_IO.\n+\t */\n+\tvcpu_run(vcpu);\n+\tTEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);\n+\tTEST_ASSERT(run-\u003eio.direction == KVM_EXIT_IO_OUT,\n+\t \"expected PIO write\");\n+\tTEST_ASSERT(run-\u003eio.port == PIO_PORT,\n+\t \"expected PIO at 0x%x, got 0x%x\",\n+\t PIO_PORT, run-\u003eio.port);\n+\n+\t/* Re-enter: guest continues to GUEST_SYNC(2). */\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_SYNC, 2);\n+\n+\tTEST_ASSERT(poll(\u0026pfd, 1, 0) == 0,\n+\t\t \"eventfd should not be signaled after non-matching PIO write\");\n+\tTEST_ASSERT_EQ(actual, (uint32_t)POISON_VAL);\n+\n+\t/* GUEST_DONE */\n+\tvcpu_run(vcpu);\n+\tassert_ucall(vcpu, UCALL_DONE, 0);\n+\n+\tclose(fd);\n+\tkvm_vm_free(vm);\n+}\n+\n+int main(void)\n+{\n+\tTEST_REQUIRE(kvm_check_cap(KVM_CAP_IOEVENTFD_POST_WRITE));\n+\n+\ttest_post_write_negative();\n+\n+\ttest_post_write_width(1, guest_code_w1);\n+\ttest_post_write_width(2, guest_code_w2);\n+\ttest_post_write_width(4, guest_code_w4);\n+\ttest_post_write_width(8, guest_code_w8);\n+\n+\ttest_post_write_datamatch();\n+\n+\ttest_post_write_multi();\n+\n+\ttest_post_write_multi_nosync();\n+\n+\ttest_post_write_deassign();\n+\n+\ttest_post_write_pio();\n+\ttest_post_write_pio_datamatch();\n+\n+\treturn 0;\n+}\ndiff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c\nindex f43b8b25bc8549..6a221184b54a7c 100644\n--- a/virt/kvm/eventfd.c\n+++ b/virt/kvm/eventfd.c\n@@ -743,6 +743,7 @@ struct _ioeventfd {\n \tstruct kvm_io_device dev;\n \tu8 bus_idx;\n \tbool wildcard;\n+\tvoid __user *post_addr;\n };\n \n static inline struct _ioeventfd *\n@@ -759,11 +760,32 @@ ioeventfd_release(struct _ioeventfd *p)\n \tkfree(p);\n }\n \n+#define ioeventfd_get_val(__ptr, __len, __unsupported_len)\t\\\n+({\t\t\t\t\t\t\t\t\\\n+\tu64 __val;\t\t\t\t\t\t\\\n+\t\t\t\t\t\t\t\t\\\n+\tswitch (__len) {\t\t\t\t\t\t\\\n+\tcase 1:\t\t\t\t\t\t\t\\\n+\t\t__val = get_unaligned((u8 *)__ptr);\t\t\\\n+\t\tbreak;\t\t\t\t\t\t\\\n+\tcase 2:\t\t\t\t\t\t\t\\\n+\t\t__val = get_unaligned((u16 *)__ptr);\t\t\\\n+\t\tbreak;\t\t\t\t\t\t\\\n+\tcase 4:\t\t\t\t\t\t\t\\\n+\t\t__val = get_unaligned((u32 *)__ptr);\t\t\\\n+\t\tbreak;\t\t\t\t\t\t\\\n+\tcase 8:\t\t\t\t\t\t\t\\\n+\t\t__val = get_unaligned((u64 *)__ptr);\t\t\\\n+\t\tbreak;\t\t\t\t\t\t\\\n+\tdefault:\t\t\t\t\t\t\\\n+\t\tgoto __unsupported_len;\t\t\t\t\\\n+\t}\t\t\t\t\t\t\t\\\n+\t__val;\t\t\t\t\t\t\t\\\n+})\n+\n static bool\n ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)\n {\n-\tu64 _val;\n-\n \tif (addr != p-\u003eaddr)\n \t\t/* address must be precise for a hit */\n \t\treturn false;\n@@ -781,24 +803,10 @@ ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)\n \t\treturn true;\n \n \t/* otherwise, we have to actually compare the data */\n-\tswitch (len) {\n-\tcase 1:\n-\t\t_val = get_unaligned((u8 *)val);\n-\t\tbreak;\n-\tcase 2:\n-\t\t_val = get_unaligned((u16 *)val);\n-\t\tbreak;\n-\tcase 4:\n-\t\t_val = get_unaligned((u32 *)val);\n-\t\tbreak;\n-\tcase 8:\n-\t\t_val = get_unaligned((u64 *)val);\n-\t\tbreak;\n-\tdefault:\n-\t\treturn false;\n-\t}\n+\treturn ioeventfd_get_val(val, len, unsupported_len) == p-\u003edatamatch;\n \n-\treturn _val == p-\u003edatamatch;\n+unsupported_len:\n+\treturn false;\n }\n \n /* MMIO/PIO writes trigger an event if the addr/val match */\n@@ -811,8 +819,36 @@ ioeventfd_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this, gpa_t addr,\n \tif (!ioeventfd_in_range(p, addr, len, val))\n \t\treturn -EOPNOTSUPP;\n \n+\tif (p-\u003epost_addr) {\n+\t\tu64 _val = ioeventfd_get_val(val, len, unsupported_len);\n+\t\tint r;\n+\n+\t\tswitch (len) {\n+\t\tcase 1:\n+\t\t\tr = put_user(_val, (u8 __user *)p-\u003epost_addr);\n+\t\t\tbreak;\n+\t\tcase 2:\n+\t\t\tr = put_user(_val, (u16 __user *)p-\u003epost_addr);\n+\t\t\tbreak;\n+\t\tcase 4:\n+\t\t\tr = put_user(_val, (u32 __user *)p-\u003epost_addr);\n+\t\t\tbreak;\n+\t\tcase 8:\n+\t\t\tr = put_user(_val, (u64 __user *)p-\u003epost_addr);\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tWARN_ON_ONCE(1);\n+\t\t\tr = 0;\n+\t\t\tgoto unsupported_len;\n+\t\t}\n+\t\tif (r)\n+\t\t\treturn r;\n+\t}\n+\n \teventfd_signal(p-\u003eeventfd);\n \treturn 0;\n+unsupported_len:\n+\treturn -EOPNOTSUPP;\n }\n \n /*\n@@ -872,6 +908,19 @@ static int kvm_assign_ioeventfd_idx(struct kvm *kvm,\n \tif (IS_ERR(eventfd))\n \t\treturn PTR_ERR(eventfd);\n \n+\t/*\n+\t * Disallow posting to virtual address 0 so that KVM can treat\n+\t * a NULL user pointer as \"no posted write\".\n+\t */\n+\tif ((args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_POST_WRITE) \u0026\u0026\n+\t (!args-\u003elen || !u64_to_user_ptr(args-\u003epost_addr) ||\n+\t args-\u003epost_addr != untagged_addr(args-\u003epost_addr) ||\n+\t !access_ok(u64_to_user_ptr(args-\u003epost_addr), args-\u003elen) ||\n+\t !IS_ALIGNED(args-\u003epost_addr, args-\u003elen))) {\n+\t\tret = -EINVAL;\n+\t\tgoto fail;\n+\t}\n+\n \tp = kzalloc_obj(*p, GFP_KERNEL_ACCOUNT);\n \tif (!p) {\n \t\tret = -ENOMEM;\n@@ -890,6 +939,9 @@ static int kvm_assign_ioeventfd_idx(struct kvm *kvm,\n \telse\n \t\tp-\u003ewildcard = true;\n \n+\tif (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_POST_WRITE)\n+\t\tp-\u003epost_addr = u64_to_user_ptr(args-\u003epost_addr);\n+\n \tmutex_lock(\u0026kvm-\u003eslots_lock);\n \n \t/* Verify that there isn't a match already */\n@@ -941,6 +993,11 @@ kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,\n \tmutex_lock(\u0026kvm-\u003eslots_lock);\n \n \tlist_for_each_entry(p, \u0026kvm-\u003eioeventfds, list) {\n+\t\t/*\n+\t\t * No need to match post_addr, ioeventfd_check_collision\n+\t\t * prevents duplicate registrations that only differ by\n+\t\t * post_addr.\n+\t\t */\n \t\tif (p-\u003ebus_idx != bus_idx ||\n \t\t p-\u003eeventfd != eventfd ||\n \t\t p-\u003eaddr != args-\u003eaddr ||\ndiff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c\nindex cf4cb586f1407b..7c8b013fad7778 100644\n--- a/virt/kvm/kvm_main.c\n+++ b/virt/kvm/kvm_main.c\n@@ -4982,6 +4982,7 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)\n \tcase KVM_CAP_IRQFD:\n #endif\n \tcase KVM_CAP_IOEVENTFD_ANY_LENGTH:\n+\tcase KVM_CAP_IOEVENTFD_POST_WRITE:\n \tcase KVM_CAP_CHECK_EXTENSION_VM:\n \tcase KVM_CAP_ENABLE_CAP_VM:\n \tcase KVM_CAP_HALT_POLL:\n"
} |
| 2/1 | 2026/10/29 06:16 | agent | patch-evaluator |
3mModel:core Args: null Results: {
"EnableConfigs": [
"KVM",
"KVM_INTEL",
"KVM_AMD"
],
"FocusSymbols": [
"kvm_ioeventfd",
"kvm_assign_ioeventfd_idx",
"ioeventfd_write"
],
"Reasoning": "The patch adds support for KVM_IOEVENTFD_FLAG_POST_WRITE (KVM_CAP_IOEVENTFD_POST_WRITE) in KVM's ioeventfd subsystem. It modifies reachable kernel code in virt/kvm/eventfd.c, adding user pointer validation in kvm_assign_ioeventfd_idx(), modifying ioeventfd matching logic, adding put_user() operations to write to guest-specified userspace addresses directly during vCPU MMIO/PIO exit handling in ioeventfd_write(), and adding a defensive WARN_ON_ONCE() assertion for unsupported lengths. This code is reachable from userspace via /dev/kvm ioctls and vCPU guest execution, making it a high-value target for 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 fdbb5357ab9de3115f5cde27eaaf6974e5b34459
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 29 06:16:49 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index e95c9cda085d94..a5e61ba7eaf583 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -2132,7 +2132,9 @@ provided event instead of triggering an exit.
__u32 len; /* 0, 1, 2, 4, or 8 bytes */
__s32 fd;
__u32 flags;
- __u8 pad[36];
+ __u32 pad0;
+ __u64 post_addr; /* address to write to if POST_WRITE is set */
+ __u8 pad[24];
};
For the special case of virtio-ccw devices on s390, the ioevent is matched
@@ -2145,6 +2147,7 @@ The following flags are defined::
#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign)
#define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \
(1 << kvm_ioeventfd_flag_nr_virtio_ccw_notify)
+ #define KVM_IOEVENTFD_FLAG_POST_WRITE (1 << kvm_ioeventfd_flag_nr_post_write)
If datamatch flag is set, the event will be signaled only if the written value
to the registered address is equal to datamatch in struct kvm_ioeventfd.
@@ -2157,6 +2160,15 @@ the kernel will ignore the length of guest write and may get a faster vmexit.
The speedup may only apply to specific architectures, but the ioeventfd will
work anyway.
+With KVM_IOEVENTFD_FLAG_POST_WRITE, the value being written is copied to the
+userspace address specified by post_addr, and the eventfd is signaled. The
+copy is guaranteed to complete before the eventfd is signaled, so a userspace
+reader that wakes on the eventfd will observe the written value. When multiple
+vCPUs write to the same ioeventfd concurrently, the value at post_addr reflects
+one of the writes. If the copy to post_addr fails (e.g. the memory has been
+unmapped), the eventfd is not signaled and the write is reported to userspace
+as a regular MMIO/PIO exit.
+
4.60 KVM_DIRTY_TLB
------------------
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 8dff2fc1972e97..07ac3142ce17f6 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -644,6 +644,7 @@ enum {
kvm_ioeventfd_flag_nr_deassign,
kvm_ioeventfd_flag_nr_virtio_ccw_notify,
kvm_ioeventfd_flag_nr_fast_mmio,
+ kvm_ioeventfd_flag_nr_post_write,
kvm_ioeventfd_flag_nr_max,
};
@@ -652,6 +653,7 @@ enum {
#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign)
#define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \
(1 << kvm_ioeventfd_flag_nr_virtio_ccw_notify)
+#define KVM_IOEVENTFD_FLAG_POST_WRITE (1 << kvm_ioeventfd_flag_nr_post_write)
#define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 << kvm_ioeventfd_flag_nr_max) - 1)
@@ -661,7 +663,9 @@ struct kvm_ioeventfd {
__u32 len; /* 1, 2, 4, or 8 bytes; or 0 to ignore length */
__s32 fd;
__u32 flags;
- __u8 pad[36];
+ __u32 pad0;
+ __u64 post_addr; /* address to write to if POST_WRITE is set */
+ __u8 pad[24];
};
#define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0)
@@ -1000,6 +1004,7 @@ struct kvm_enable_cap {
#define KVM_CAP_PPC_COMPAT_CAPS 250
#define KVM_CAP_ARM_PMU_V3_STRICT 251
#define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 252
+#define KVM_CAP_IOEVENTFD_POST_WRITE 253
struct kvm_irq_routing_irqchip {
__u32 irqchip;
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 3cb35a6963d513..38860f2f08b0c8 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -92,6 +92,7 @@ TEST_GEN_PROGS_x86 += x86/hyperv_features
TEST_GEN_PROGS_x86 += x86/hyperv_ipi
TEST_GEN_PROGS_x86 += x86/hyperv_svm_test
TEST_GEN_PROGS_x86 += x86/hyperv_tlb_flush
+TEST_GEN_PROGS_x86 += x86/ioeventfd_posted_write_test
TEST_GEN_PROGS_x86 += x86/kvm_clock_test
TEST_GEN_PROGS_x86 += x86/kvm_pv_test
TEST_GEN_PROGS_x86 += x86/kvm_buslock_test
diff --git a/tools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c b/tools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c
new file mode 100644
index 00000000000000..f429f875c023e2
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ioeventfd_posted_write_test.c - Tests for KVM_IOEVENTFD_FLAG_POST_WRITE.
+ *
+ * Tests that when KVM_IOEVENTFD_FLAG_POST_WRITE is set and the MMIO/PIO
+ * address is written to, the value is copied to the user-provided address
+ * and the eventfd is signaled. Also tests negative cases and interactions
+ * with DATAMATCH.
+ *
+ * Copyright Nutanix, 2026
+ *
+ * Author: Thanos Makatos <thanos.makatos@nutanix.com>
+ */
+
+#include <errno.h>
+#include <poll.h>
+#include <string.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "ucall_common.h"
+
+#define MMIO_GPA (1UL << 30)
+#define PIO_PORT 0xe000
+#define TEST_VAL 0xDEADBEEFCAFEBABEULL
+#define MATCH_VAL 0x42U
+#define NOMATCH_VAL (MATCH_VAL + 1)
+#define POISON_VAL 0xFFFFFFFFU
+
+/*
+ * Check that the most recent vCPU exit is a ucall (delivered as KVM_EXIT_IO
+ * on x86) matching @expected_cmd. The caller must have already called
+ * vcpu_run().
+ *
+ * @expected_cmd: UCALL_SYNC, UCALL_DONE, etc.
+ * @expected_stage: for UCALL_SYNC, the stage number passed by GUEST_SYNC().
+ * Ignored for other ucall types.
+ *
+ * Aborts the test on UCALL_ABORT (a guest-side assertion failure).
+ */
+static void assert_ucall(struct kvm_vcpu *vcpu, uint64_t expected_cmd,
+ uint64_t expected_stage)
+{
+ struct ucall uc;
+
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ break;
+ case UCALL_SYNC:
+ TEST_ASSERT(expected_cmd == UCALL_SYNC,
+ "got UCALL_SYNC, expected %lu",
+ expected_cmd);
+ TEST_ASSERT(uc.args[1] == expected_stage,
+ "expected stage %lu, got %lu",
+ expected_stage, uc.args[1]);
+ break;
+ case UCALL_DONE:
+ TEST_ASSERT(expected_cmd == UCALL_DONE,
+ "got UCALL_DONE, expected %lu",
+ expected_cmd);
+ break;
+ default:
+ TEST_FAIL("unexpected ucall %lu", uc.cmd);
+ }
+}
+
+/*
+ * Verify that KVM_IOEVENTFD rejects invalid POST_WRITE configurations:
+ * - len=0: the kernel needs a non-zero length to know how many bytes to copy.
+ * - post_addr=NULL: there is no destination for the copy.
+ * - post_addr outside the process address space: access_ok() rejects it.
+ * - post_addr unaligned: IS_ALIGNED() rejects it
+ * All three must fail with EINVAL.
+ */
+static void test_post_write_negative(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vm *vm;
+ uint64_t dummy;
+ int ret;
+ int fd;
+
+ vm = vm_create_barebones();
+ fd = kvm_new_eventfd();
+
+ /* length cannot be zero */
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = 0,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&dummy,
+ };
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(ret && errno == EINVAL,
+ "len=0: expected EINVAL, got ret=%d errno=%d", ret, errno);
+
+ /* post_addr cannot be NULL */
+ ioeventfd.len = 4;
+ ioeventfd.post_addr = 0ULL;
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(ret && errno == EINVAL,
+ "NULL post_addr: expected EINVAL, got ret=%d errno=%d",
+ ret, errno);
+
+ /* bogus post_addr */
+ ioeventfd.post_addr = (u64)0xdeaddeaddeaddeadULL;
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(ret && errno == EINVAL,
+ "bad post_addr: expected EINVAL, got ret=%d errno=%d",
+ ret, errno);
+
+ /* unaligned post_addr */
+ ioeventfd.post_addr = (uintptr_t)((char *)&dummy + 1);
+ ioeventfd.len = sizeof dummy;
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(ret && errno == EINVAL,
+ "bad post_addr: expected EINVAL, got ret=%d errno=%d",
+ ret, errno);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+#define DEFINE_GUEST_WRITE_FN(suffix, type) \
+static void guest_code_w##suffix(void) { \
+ *(volatile type *)MMIO_GPA = (type)TEST_VAL; \
+ GUEST_DONE(); \
+}
+
+DEFINE_GUEST_WRITE_FN(1, uint8_t)
+DEFINE_GUEST_WRITE_FN(2, uint16_t)
+DEFINE_GUEST_WRITE_FN(4, uint32_t)
+DEFINE_GUEST_WRITE_FN(8, uint64_t)
+
+/*
+ * Verify that ioeventfd_write copies exactly @width bytes to post_addr for
+ * each supported MMIO write width (1, 2, 4, 8). The guest writes the low
+ * @width bytes of TEST_VAL; the host checks that exactly those bytes land
+ * at post_addr and the eventfd is signaled.
+ */
+static void test_post_write_width(int width, void (*guest_fn)(void))
+{
+ uint64_t actual, expected, count;
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ int fd, ret;
+
+ /* need to initialize to 0 because the guest writes the low @width bytes */
+ actual = 0;
+ expected = 0;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_fn);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+
+ fd = kvm_new_eventfd();
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = width,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ ret = read(fd, &count, sizeof(count));
+ TEST_ASSERT(ret == sizeof(count),
+ "eventfd read failed: ret=%d errno=%d", ret, errno);
+
+ memcpy(&expected, &(uint64_t){TEST_VAL}, width);
+ TEST_ASSERT_EQ(actual, expected);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_datamatch(void)
+{
+ *(volatile uint32_t *)MMIO_GPA = MATCH_VAL;
+ GUEST_SYNC(1);
+ *(volatile uint32_t *)MMIO_GPA = NOMATCH_VAL;
+ GUEST_SYNC(2);
+ GUEST_DONE();
+}
+
+/*
+ * Test the interaction between DATAMATCH and POST_WRITE. When both flags are
+ * set, ioeventfd_write should only fire (signal eventfd + copy value) when the
+ * written value matches datamatch. A non-matching write must leave the eventfd
+ * unsignaled and post_addr untouched, and fall through to KVM_EXIT_MMIO.
+ */
+static void test_post_write_datamatch(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_run *run;
+ struct kvm_vm *vm;
+ struct pollfd pfd;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_datamatch);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+ run = vcpu->run;
+
+ fd = kvm_new_eventfd();
+ pfd = (struct pollfd){ .fd = fd, .events = POLLIN };
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .datamatch = MATCH_VAL,
+ .addr = MMIO_GPA,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE |
+ KVM_IOEVENTFD_FLAG_DATAMATCH,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ /*
+ * Guest writes MATCH_VAL → ioeventfd fires (value copied, eventfd
+ * signaled), vCPU continues, then GUEST_SYNC(1).
+ */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 1);
+ TEST_ASSERT(read(fd, &count, sizeof(count)) == sizeof(count),
+ "eventfd read failed: errno=%d", errno);
+ TEST_ASSERT_EQ(actual, MATCH_VAL);
+
+ actual = POISON_VAL;
+
+ /*
+ * Guest writes NOMATCH_VAL → ioeventfd_in_range() returns false, bus
+ * returns -EOPNOTSUPP → KVM_EXIT_MMIO to userspace.
+ */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);
+ TEST_ASSERT(run->mmio.is_write, "expected MMIO write");
+ TEST_ASSERT(run->mmio.phys_addr == MMIO_GPA,
+ "expected MMIO at 0x%lx, got 0x%llx",
+ MMIO_GPA, run->mmio.phys_addr);
+
+ /* Re-enter: KVM completes the MMIO, guest runs to GUEST_SYNC(2). */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 2);
+
+ TEST_ASSERT(poll(&pfd, 1, 0) == 0,
+ "eventfd should not be signaled after non-matching write");
+ TEST_ASSERT_EQ(actual, (uint32_t)POISON_VAL);
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_multi(void)
+{
+ *(volatile uint32_t *)MMIO_GPA = 0x11111111;
+ GUEST_SYNC(1);
+ *(volatile uint32_t *)MMIO_GPA = 0x22222222;
+ GUEST_SYNC(2);
+ *(volatile uint32_t *)MMIO_GPA = 0x33333333;
+ GUEST_SYNC(3);
+ GUEST_DONE();
+}
+
+/*
+ * Verify that post_addr is updated on every MMIO write, not just the first.
+ * The guest writes three distinct values in sequence; the host checks after
+ * each one that post_addr holds the latest value and the eventfd is signaled
+ * each time.
+ */
+static void test_post_write_multi(void)
+{
+ static const uint32_t expected[] = {
+ 0x11111111, 0x22222222, 0x33333333,
+ };
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret, i;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_multi);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+
+ fd = kvm_new_eventfd();
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ for (i = 0; i < ARRAY_SIZE(expected); i++) {
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, i + 1);
+ TEST_ASSERT(read(fd, &count, sizeof(count)) == sizeof(count),
+ "eventfd read failed: errno=%d", errno);
+ TEST_ASSERT_EQ(actual, expected[i]);
+ }
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_multi_nosync(void)
+{
+ *(volatile uint32_t *)MMIO_GPA = 0x11111111;
+ *(volatile uint32_t *)MMIO_GPA = 0x22222222;
+ *(volatile uint32_t *)MMIO_GPA = 0x33333333;
+ GUEST_DONE();
+}
+
+/*
+ * Variant of the multi-write test where the guest performs three consecutive
+ * MMIO writes with no GUEST_SYNC in between. All three are handled in-kernel
+ * by ioeventfd before the vCPU exits at GUEST_DONE. Verify that:
+ * - post_addr reflects the last written value (0x33333333).
+ * - A single eventfd read() returns a counter of 3 (one signal per write).
+ */
+static void test_post_write_multi_nosync(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_multi_nosync);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+
+ fd = kvm_new_eventfd();
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ ret = read(fd, &count, sizeof(count));
+ TEST_ASSERT(ret == sizeof(count),
+ "eventfd read failed: ret=%d errno=%d", ret, errno);
+ TEST_ASSERT_EQ(count, (uint64_t)3);
+ TEST_ASSERT_EQ(actual, (uint32_t)0x33333333);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_deassign(void)
+{
+ *(volatile uint32_t *)MMIO_GPA = MATCH_VAL;
+ GUEST_SYNC(1);
+ *(volatile uint32_t *)MMIO_GPA = MATCH_VAL;
+ GUEST_DONE();
+}
+
+/*
+ * Verify that deassigning an ioeventfd with POST_WRITE fully removes it from
+ * the I/O bus.
+ */
+static void test_post_write_deassign(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_run *run;
+ struct kvm_vm *vm;
+ struct pollfd pfd;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_deassign);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+ run = vcpu->run;
+
+ fd = kvm_new_eventfd();
+ pfd = (struct pollfd){ .fd = fd, .events = POLLIN };
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD assign failed: %s", strerror(errno));
+
+ /*
+ * Guest writes MATCH_VAL → ioeventfd fires, then GUEST_SYNC(1).
+ */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 1);
+ TEST_ASSERT(read(fd, &count, sizeof(count)) == sizeof(count),
+ "eventfd read failed: errno=%d", errno);
+ TEST_ASSERT_EQ(actual, MATCH_VAL);
+
+ /* Deassign the ioeventfd. */
+ ioeventfd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD deassign failed: %s", strerror(errno));
+
+ actual = POISON_VAL;
+
+ /*
+ * Guest writes MATCH_VAL again → no handler on the bus →
+ * KVM_EXIT_MMIO to userspace.
+ */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);
+ TEST_ASSERT(run->mmio.is_write, "expected MMIO write");
+ TEST_ASSERT(run->mmio.phys_addr == MMIO_GPA,
+ "expected MMIO at 0x%lx, got 0x%llx",
+ MMIO_GPA, run->mmio.phys_addr);
+
+ /* Re-enter: KVM completes MMIO, guest runs to GUEST_DONE. */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ TEST_ASSERT(poll(&pfd, 1, 0) == 0,
+ "eventfd should not be signaled after deassign");
+ TEST_ASSERT_EQ(actual, (uint32_t)POISON_VAL);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_pio(void)
+{
+ outl(PIO_PORT, (uint32_t)TEST_VAL);
+ GUEST_DONE();
+}
+
+/*
+ * Verify that POST_WRITE works on the PIO bus (KVM_PIO_BUS), not just MMIO.
+ * The guest does an outl to PIO_PORT; the host checks that the written value
+ * is copied to post_addr and the eventfd is signaled.
+ */
+static void test_post_write_pio(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_pio);
+
+ fd = kvm_new_eventfd();
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = PIO_PORT,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE |
+ KVM_IOEVENTFD_FLAG_PIO,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ ret = read(fd, &count, sizeof(count));
+ TEST_ASSERT(ret == sizeof(count),
+ "eventfd read failed: ret=%d errno=%d", ret, errno);
+
+ TEST_ASSERT_EQ(actual, (uint32_t)TEST_VAL);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_pio_datamatch(void)
+{
+ outl(PIO_PORT, MATCH_VAL);
+ GUEST_SYNC(1);
+ outl(PIO_PORT, NOMATCH_VAL);
+ GUEST_SYNC(2);
+ GUEST_DONE();
+}
+
+/*
+ * Test POST_WRITE + PIO + DATAMATCH together. When all three flags are set,
+ * the ioeventfd should only fire when the outl value matches datamatch.
+ * A non-matching outl must fall through to KVM_EXIT_IO (port I/O exit),
+ * leaving the eventfd unsignaled and post_addr untouched.
+ */
+static void test_post_write_pio_datamatch(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_run *run;
+ struct kvm_vm *vm;
+ struct pollfd pfd;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_pio_datamatch);
+ run = vcpu->run;
+
+ fd = kvm_new_eventfd();
+ pfd = (struct pollfd){ .fd = fd, .events = POLLIN };
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .datamatch = MATCH_VAL,
+ .addr = PIO_PORT,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE |
+ KVM_IOEVENTFD_FLAG_PIO |
+ KVM_IOEVENTFD_FLAG_DATAMATCH,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ /*
+ * Guest does outl MATCH_VAL → ioeventfd fires, then GUEST_SYNC(1).
+ */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 1);
+ TEST_ASSERT(read(fd, &count, sizeof(count)) == sizeof(count),
+ "eventfd read failed: errno=%d", errno);
+ TEST_ASSERT_EQ(actual, MATCH_VAL);
+
+ actual = POISON_VAL;
+
+ /*
+ * Guest does outl NOMATCH_VAL → no match → KVM_EXIT_IO.
+ */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+ TEST_ASSERT(run->io.direction == KVM_EXIT_IO_OUT,
+ "expected PIO write");
+ TEST_ASSERT(run->io.port == PIO_PORT,
+ "expected PIO at 0x%x, got 0x%x",
+ PIO_PORT, run->io.port);
+
+ /* Re-enter: guest continues to GUEST_SYNC(2). */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 2);
+
+ TEST_ASSERT(poll(&pfd, 1, 0) == 0,
+ "eventfd should not be signaled after non-matching PIO write");
+ TEST_ASSERT_EQ(actual, (uint32_t)POISON_VAL);
+
+ /* GUEST_DONE */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+int main(void)
+{
+ TEST_REQUIRE(kvm_check_cap(KVM_CAP_IOEVENTFD_POST_WRITE));
+
+ test_post_write_negative();
+
+ test_post_write_width(1, guest_code_w1);
+ test_post_write_width(2, guest_code_w2);
+ test_post_write_width(4, guest_code_w4);
+ test_post_write_width(8, guest_code_w8);
+
+ test_post_write_datamatch();
+
+ test_post_write_multi();
+
+ test_post_write_multi_nosync();
+
+ test_post_write_deassign();
+
+ test_post_write_pio();
+ test_post_write_pio_datamatch();
+
+ return 0;
+}
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c
index f43b8b25bc8549..6a221184b54a7c 100644
--- a/virt/kvm/eventfd.c
+++ b/virt/kvm/eventfd.c
@@ -743,6 +743,7 @@ struct _ioeventfd {
struct kvm_io_device dev;
u8 bus_idx;
bool wildcard;
+ void __user *post_addr;
};
static inline struct _ioeventfd *
@@ -759,11 +760,32 @@ ioeventfd_release(struct _ioeventfd *p)
kfree(p);
}
+#define ioeventfd_get_val(__ptr, __len, __unsupported_len) \
+({ \
+ u64 __val; \
+ \
+ switch (__len) { \
+ case 1: \
+ __val = get_unaligned((u8 *)__ptr); \
+ break; \
+ case 2: \
+ __val = get_unaligned((u16 *)__ptr); \
+ break; \
+ case 4: \
+ __val = get_unaligned((u32 *)__ptr); \
+ break; \
+ case 8: \
+ __val = get_unaligned((u64 *)__ptr); \
+ break; \
+ default: \
+ goto __unsupported_len; \
+ } \
+ __val; \
+})
+
static bool
ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)
{
- u64 _val;
-
if (addr != p->addr)
/* address must be precise for a hit */
return false;
@@ -781,24 +803,10 @@ ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)
return true;
/* otherwise, we have to actually compare the data */
- switch (len) {
- case 1:
- _val = get_unaligned((u8 *)val);
- break;
- case 2:
- _val = get_unaligned((u16 *)val);
- break;
- case 4:
- _val = get_unaligned((u32 *)val);
- break;
- case 8:
- _val = get_unaligned((u64 *)val);
- break;
- default:
- return false;
- }
+ return ioeventfd_get_val(val, len, unsupported_len) == p->datamatch;
- return _val == p->datamatch;
+unsupported_len:
+ return false;
}
/* MMIO/PIO writes trigger an event if the addr/val match */
@@ -811,8 +819,36 @@ ioeventfd_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this, gpa_t addr,
if (!ioeventfd_in_range(p, addr, len, val))
return -EOPNOTSUPP;
+ if (p->post_addr) {
+ u64 _val = ioeventfd_get_val(val, len, unsupported_len);
+ int r;
+
+ switch (len) {
+ case 1:
+ r = put_user(_val, (u8 __user *)p->post_addr);
+ break;
+ case 2:
+ r = put_user(_val, (u16 __user *)p->post_addr);
+ break;
+ case 4:
+ r = put_user(_val, (u32 __user *)p->post_addr);
+ break;
+ case 8:
+ r = put_user(_val, (u64 __user *)p->post_addr);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ r = 0;
+ goto unsupported_len;
+ }
+ if (r)
+ return r;
+ }
+
eventfd_signal(p->eventfd);
return 0;
+unsupported_len:
+ return -EOPNOTSUPP;
}
/*
@@ -872,6 +908,19 @@ static int kvm_assign_ioeventfd_idx(struct kvm *kvm,
if (IS_ERR(eventfd))
return PTR_ERR(eventfd);
+ /*
+ * Disallow posting to virtual address 0 so that KVM can treat
+ * a NULL user pointer as "no posted write".
+ */
+ if ((args->flags & KVM_IOEVENTFD_FLAG_POST_WRITE) &&
+ (!args->len || !u64_to_user_ptr(args->post_addr) ||
+ args->post_addr != untagged_addr(args->post_addr) ||
+ !access_ok(u64_to_user_ptr(args->post_addr), args->len) ||
+ !IS_ALIGNED(args->post_addr, args->len))) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
p = kzalloc_obj(*p, GFP_KERNEL_ACCOUNT);
if (!p) {
ret = -ENOMEM;
@@ -890,6 +939,9 @@ static int kvm_assign_ioeventfd_idx(struct kvm *kvm,
else
p->wildcard = true;
+ if (args->flags & KVM_IOEVENTFD_FLAG_POST_WRITE)
+ p->post_addr = u64_to_user_ptr(args->post_addr);
+
mutex_lock(&kvm->slots_lock);
/* Verify that there isn't a match already */
@@ -941,6 +993,11 @@ kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,
mutex_lock(&kvm->slots_lock);
list_for_each_entry(p, &kvm->ioeventfds, list) {
+ /*
+ * No need to match post_addr, ioeventfd_check_collision
+ * prevents duplicate registrations that only differ by
+ * post_addr.
+ */
if (p->bus_idx != bus_idx ||
p->eventfd != eventfd ||
p->addr != args->addr ||
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index cf4cb586f1407b..7c8b013fad7778 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -4982,6 +4982,7 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
case KVM_CAP_IRQFD:
#endif
case KVM_CAP_IOEVENTFD_ANY_LENGTH:
+ case KVM_CAP_IOEVENTFD_POST_WRITE:
case KVM_CAP_CHECK_EXTENSION_VM:
case KVM_CAP_ENABLE_CAP_VM:
case KVM_CAP_HALT_POLL:
|
| 3/2 | 2026/10/29 06:16 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 13992 output: 30 thoughts: 1413 Thoughts: **KVM Post-Write Ioeventfd: Fuzzing Potential Assessment** Okay, let's break this down. My goal is to determine if this KVM patch series is a good candidate for syzkaller fuzzing. First, I need to understand the changes. The patch adds `KVM_IOEVENTFD_FLAG_POST_WRITE` to the KVM ioeventfd mechanism. It touches files related to the API documentation, headers, selftests, and the core `eventfd.c` and `kvm_main.c` files. This is definitely within the realm of KVM and potentially critical. Specifically, in `eventfd.c`, it introduces `post_addr`, and modifies how `ioeventfd_write` functions. I see they've added a `put_user` call to write values to a guest-specified address. This is interesting. Inside `ioeventfd_write`, we see the potential for a `put_user` fault based on the `post_addr` value. This is a crucial point to understand. The default case includes a `WARN_ON_ONCE(1)`. This *screams* "potential bug!" Especially since the `post_addr` is coming from user space, and within the context of MMIO/PIO exit handling. This is a very sensitive area. I need to figure out if `ioeventfd_write` is called in an atomic context and if the `post_addr` could belong to another memory model. Looking at `kvm_assign_ioeventfd_idx`, they've added checks for the `post_addr` when the `KVM_IOEVENTFD_FLAG_POST_WRITE` flag is set. They are checking for alignment and validity. That is good. The patch also checks for `KVM_CAP_IOEVENTFD_POST_WRITE`. Now for the triage criteria. KVM is *definitely* reachable. I can fuzz it on standard QEMU installations, or nested virtualization. Syzkaller is already heavily fuzzing KVM, so it's a good target. The modified code is in `virt/kvm/eventfd.c`, which is at the heart of the KVM subsystem. It handles `KVM_IOEVENTFD` with a new flag. The addition of `put_user` combined with that `WARN_ON_ONCE(1)` and its return value change make this a high-priority target. Based on my understanding and experience, this patch aligns perfectly with the fuzzing guidelines. KVM is a core virtualization subsystem that's accessible via `/dev/kvm`. It is a modification of a core part of the code and includes assertions. *WorthFuzzing=true* is a given. I'll need to double-check the Kconfig options now to be sure. I'll search for Kconfig requirements for KVM and eventfd in KVM using the available tools, but I'm quite confident that this will yield positive results. |
| 4/2 | 2026/10/29 06:17 | tool | grepper |
0mArgs:{
"Expression": "KVM.*eventfd",
"PathPrefixes": [
"virt/kvm"
]
}Results: {
"Output": "virt/kvm/Makefile.kvm=11=ccflags-y +=-DKVM_DEV_NAME=$(KVM_DEV_NAME) -DKVM_DEV_MINOR=$(KVM_DEV_MINOR)\nvirt/kvm/Makefile.kvm-12-\nvirt/kvm/Makefile.kvm:13:kvm-y := $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/binary_stats.o \\\nvirt/kvm/Makefile.kvm-14-\t $(KVM)/coalesced_mmio.o\n"
} |
| 5/2 | 2026/10/29 06:17 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 16222 output: 33 thoughts: 61 Thoughts: Here's my thought process, as it stands: **Locating the Build Dependencies for KVM Modules** Alright, I need to figure out how `eventfd.o` and `kvm_main.o` are getting built within the KVM infrastructure. Given my experience, the first place I'd check is likely going to be the Makefiles. Specifically, I'm thinking I should start digging into `virt/kvm/Makefile.kvm`. That directory seems like a logical place for KVM-specific build instructions. If it's not there, I'll branch out to `arch/x86/kvm/Makefile` and even `arch/x86/kvm/Kconfig`. `Kconfig` files often define dependencies and feature flags that might indirectly influence which object files get compiled. I'm expecting the build process will be defined with the usual Makefile syntax, so I'll be looking for source file entries and dependency definitions related to those specific `.o` files. I am anticipating a straightforward process that may reveal the source files that these modules are built from and the dependencies they need. |
| 6/2 | 2026/10/29 06:17 | tool | grepper |
0mArgs:{
"Expression": "Makefile.kvm",
"PathPrefixes": [
"arch/x86/kvm"
]
}Results: {
"Output": "arch/x86/kvm/Makefile=4=ccflags-$(CONFIG_KVM_WERROR) += -Werror\narch/x86/kvm/Makefile-5-\narch/x86/kvm/Makefile:6:include $(srctree)/virt/kvm/Makefile.kvm\narch/x86/kvm/Makefile-7-\n"
} |
| 7/2 | 2026/10/29 06:17 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 16651 output: 36 thoughts: 16 Thoughts: Here's a summary of my thought process, as requested: **Diving into the KVM x86 Makefile** Okay, time to get my hands dirty. I need to understand something about the KVM infrastructure, and a good starting point is usually the build system. Let's see... the architecture-specific `Makefile` is often a goldmine of information about the kernel modules and flags used to compile and link that part of the code. My hunch is that `arch/x86/kvm/Makefile` will reveal some core details about how KVM works on x86, probably dependencies, options, and potentially even some insights into the structure of the relevant KVM modules for this architecture. Let's open it up and see what it has to tell me. I expect to find some interesting tidbits about memory management, virtualization features, and perhaps even some hints about the interaction with the CPU's virtualization extensions. This Makefile should be key to understanding the build process and linking of the KVM modules within the x86 architecture. |
| 8/2 | 2026/10/29 06:17 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/Makefile",
"FirstLine": 1,
"LineCount": 30
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t\n 3:\tccflags-y += -I $(srctree)/arch/x86/kvm\n 4:\tccflags-$(CONFIG_KVM_WERROR) += -Werror\n 5:\t\n 6:\tinclude $(srctree)/virt/kvm/Makefile.kvm\n 7:\t\n 8:\tkvm-y\t\t\t+= x86.o emulate.o irq.o lapic.o cpuid.o msrs.o pmu.o regs.o \\\n 9:\t\t\t\t mtrr.o debugfs.o mmu/mmu.o mmu/page_track.o mmu/spte.o\n 10:\t\n 11:\tkvm-$(CONFIG_X86_64) += mmu/tdp_iter.o mmu/tdp_mmu.o\n 12:\tkvm-$(CONFIG_KVM_IOAPIC) += i8259.o i8254.o ioapic.o\n 13:\tkvm-$(CONFIG_KVM_HYPERV) += hyperv.o\n 14:\tkvm-$(CONFIG_KVM_XEN)\t+= xen.o\n 15:\tkvm-$(CONFIG_KVM_SMM)\t+= smm.o\n 16:\t\n 17:\tkvm-intel-y\t\t+= vmx/vmx.o vmx/vmenter.o vmx/pmu_intel.o vmx/vmcs12.o \\\n 18:\t\t\t\t vmx/nested.o vmx/posted_intr.o vmx/main.o\n 19:\t\n 20:\tkvm-intel-$(CONFIG_X86_SGX_KVM)\t+= vmx/sgx.o\n 21:\tkvm-intel-$(CONFIG_KVM_HYPERV)\t+= vmx/hyperv.o vmx/hyperv_evmcs.o\n 22:\tkvm-intel-$(CONFIG_KVM_INTEL_TDX)\t+= vmx/tdx.o\n 23:\t\n 24:\tkvm-amd-y\t\t+= svm/svm.o svm/vmenter.o svm/pmu.o svm/nested.o svm/avic.o\n 25:\t\n 26:\tkvm-amd-$(CONFIG_KVM_AMD_SEV)\t+= svm/sev.o\n 27:\tkvm-amd-$(CONFIG_KVM_HYPERV)\t+= svm/hyperv.o\n 28:\t\n 29:\tifdef CONFIG_HYPERV\n 30:\tkvm-y\t\t\t+= kvm_onhyperv.o\n"
} |
| 9/2 | 2026/10/29 06:17 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 17605 output: 35 thoughts: 30 Thoughts: Okay, here's my take on those thoughts, tailored for an expert audience: **Examining KVM Configuration for x86** Right, let's dive straight in. I need to refresh my memory on the specific configuration options we have available for KVM within the x86 architecture. Time to check `arch/x86/kvm/Kconfig`. I know the core KVM functionality is pretty robust, but I want to be certain I recall all the nuances of what's exposed through the config system. Things like nested virtualization, specific hardware extensions, and any vendor-specific options that might influence performance or feature sets. I'm especially keen to see if there have been any recent additions or changes that I need to be aware of. Gotta stay sharp on this stuff; it's always evolving. |
| 10/2 | 2026/10/29 06:17 | tool | grepper |
0mArgs:{
"Expression": "config KVM",
"PathPrefixes": [
"arch/x86/kvm/Kconfig"
]
}Results: {
"Output": "arch/x86/kvm/Kconfig=18=if VIRTUALIZATION\narch/x86/kvm/Kconfig-19-\narch/x86/kvm/Kconfig:20:config KVM_X86\narch/x86/kvm/Kconfig-21-\tdef_tristate KVM if (KVM_INTEL != n || KVM_AMD != n)\n--\narch/x86/kvm/Kconfig-47-\narch/x86/kvm/Kconfig:48:config KVM\narch/x86/kvm/Kconfig-49-\ttristate \"Kernel-based Virtual Machine (KVM) support\"\n--\narch/x86/kvm/Kconfig-64-\narch/x86/kvm/Kconfig:65:config KVM_WERROR\narch/x86/kvm/Kconfig-66-\tbool \"Compile KVM with -Werror\"\n--\narch/x86/kvm/Kconfig-78-\narch/x86/kvm/Kconfig:79:config KVM_PRIVATE_MEMATTR_VM\narch/x86/kvm/Kconfig-80-\tbool \"Enable per-VM PRIVATE vs. SHARED attributes (for CoCo VMs)\"\n--\narch/x86/kvm/Kconfig-92-\narch/x86/kvm/Kconfig:93:config KVM_SW_PROTECTED_VM\narch/x86/kvm/Kconfig-94-\tbool \"Enable support for KVM software-protected VMs\"\n--\narch/x86/kvm/Kconfig-104-\narch/x86/kvm/Kconfig:105:config KVM_INTEL\narch/x86/kvm/Kconfig-106-\ttristate \"KVM for Intel (and compatible) processors support\"\n--\narch/x86/kvm/Kconfig-115-\narch/x86/kvm/Kconfig:116:config KVM_INTEL_PROVE_VE\narch/x86/kvm/Kconfig-117- bool \"Check that guests do not receive #VE exceptions\"\n--\narch/x86/kvm/Kconfig=131=config X86_SGX_KVM\n--\narch/x86/kvm/Kconfig-142-\narch/x86/kvm/Kconfig:143:config KVM_INTEL_TDX\narch/x86/kvm/Kconfig-144-\tbool \"Intel Trust Domain Extensions (TDX) support\"\n--\narch/x86/kvm/Kconfig-153-\narch/x86/kvm/Kconfig:154:config KVM_AMD\narch/x86/kvm/Kconfig-155-\ttristate \"KVM for AMD processors support\"\n--\narch/x86/kvm/Kconfig-163-\narch/x86/kvm/Kconfig:164:config KVM_AMD_SEV\narch/x86/kvm/Kconfig-165-\tbool \"AMD Secure Encrypted Virtualization (SEV) support\"\n--\narch/x86/kvm/Kconfig-179-\narch/x86/kvm/Kconfig:180:config KVM_IOAPIC\narch/x86/kvm/Kconfig-181-\tbool \"I/O APIC, PIC, and PIT emulation\"\n--\narch/x86/kvm/Kconfig-189-\narch/x86/kvm/Kconfig:190:config KVM_SMM\narch/x86/kvm/Kconfig-191-\tbool \"System Management Mode emulation\"\n--\narch/x86/kvm/Kconfig-200-\narch/x86/kvm/Kconfig:201:config KVM_HYPERV\narch/x86/kvm/Kconfig-202-\tbool \"Support for Microsoft Hyper-V emulation\"\n--\narch/x86/kvm/Kconfig-214-\narch/x86/kvm/Kconfig:215:config KVM_XEN\narch/x86/kvm/Kconfig-216-\tbool \"Support for Xen hypercall interface\"\n--\narch/x86/kvm/Kconfig-223-\narch/x86/kvm/Kconfig:224:config KVM_PROVE_MMU\narch/x86/kvm/Kconfig-225-\tbool \"Prove KVM MMU correctness\"\n--\narch/x86/kvm/Kconfig-236-\narch/x86/kvm/Kconfig:237:config KVM_EXTERNAL_WRITE_TRACKING\narch/x86/kvm/Kconfig-238-\tbool\narch/x86/kvm/Kconfig-239-\narch/x86/kvm/Kconfig:240:config KVM_MAX_NR_VCPUS\narch/x86/kvm/Kconfig-241-\tint \"Maximum number of vCPUs per KVM guest\"\n"
} |
| 11/2 | 2026/10/29 06:17 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19140 output: 35 thoughts: 43 Thoughts: Okay, let's dive into this KVM eventfd question. **Eventfd in KVM: A Deep Dive** Alright, my immediate thought is to determine how eventfd is handled within KVM. Specifically, I need to know if it has its own dedicated configuration options or if its integration is intrinsically tied to the overall KVM build process. This level of granularity is essential for understanding its portability and any potential customization points. I’m going to revisit `virt/kvm/Makefile.kvm` now. That's the logical place to start poking around. I'll be looking for directives, flags, or conditional statements that provide insight into eventfd's compilation and linking within the KVM framework. I need to understand whether its inclusion is hardcoded or if there's a more nuanced control mechanism at play. |
| 12/2 | 2026/10/29 06:17 | tool | read-file |
0mArgs:{
"File": "virt/kvm/Makefile.kvm",
"FirstLine": 1,
"LineCount": 30
}Results: {
"Contents": " 1:\t# SPDX-License-Identifier: GPL-2.0\n 2:\t#\n 3:\t# Makefile for Kernel-based Virtual Machine module\n 4:\t#\n 5:\t\n 6:\tKVM ?= ../../../virt/kvm\n 7:\t\n 8:\tKVM_DEV_NAME ?= kvm\n 9:\tKVM_DEV_MINOR ?= KVM_MINOR\n 10:\t\n 11:\tccflags-y +=-DKVM_DEV_NAME=$(KVM_DEV_NAME) -DKVM_DEV_MINOR=$(KVM_DEV_MINOR)\n 12:\t\n 13:\tkvm-y := $(KVM)/kvm_main.o $(KVM)/eventfd.o $(KVM)/binary_stats.o \\\n 14:\t\t $(KVM)/coalesced_mmio.o\n 15:\tkvm-$(CONFIG_KVM_VFIO) += $(KVM)/vfio.o\n 16:\tkvm-$(CONFIG_KVM_ASYNC_PF) += $(KVM)/async_pf.o\n 17:\tkvm-$(CONFIG_HAVE_KVM_IRQ_ROUTING) += $(KVM)/irqchip.o\n 18:\tkvm-$(CONFIG_HAVE_KVM_DIRTY_RING) += $(KVM)/dirty_ring.o\n 19:\tkvm-$(CONFIG_HAVE_KVM_PFNCACHE) += $(KVM)/pfncache.o\n 20:\tkvm-$(CONFIG_KVM_GUEST_MEMFD) += $(KVM)/guest_memfd.o\n"
} |
| 13/2 | 2026/10/29 06:17 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 19803 output: 32 thoughts: 78 Thoughts: Okay, let me think this through. **Investigating the `eventfd.o` Dependency** Right, so `eventfd.o` is definitely part of the `kvm-y` variable, I can see that in the Makefile. But wait, this makes me think, are there any conditional builds happening around `ioeventfd` in the code? I should really confirm that, especially given the context of a build variable. Let's quickly double-check the source code itself, specifically `virt/kvm/eventfd.c` to see if there are any `#ifdef` directives or anything else potentially conditional that I'm overlooking. Need to make sure I understand the full dependency picture. |
| 14/2 | 2026/10/29 06:17 | tool | grepper |
0mArgs:{
"Expression": "CONFIG_",
"PathPrefixes": [
"virt/kvm/eventfd.c"
]
}Results: {
"Output": "virt/kvm/eventfd.c-31-\nvirt/kvm/eventfd.c:32:#ifdef CONFIG_HAVE_KVM_IRQCHIP\nvirt/kvm/eventfd.c-33-\n--\nvirt/kvm/eventfd.c=123=irqfd_shutdown(struct work_struct *work)\n--\nvirt/kvm/eventfd.c-152-\t */\nvirt/kvm/eventfd.c:153:#if IS_ENABLED(CONFIG_HAVE_KVM_IRQ_BYPASS)\nvirt/kvm/eventfd.c-154-\tirq_bypass_unregister_consumer(\u0026irqfd-\u003econsumer);\n--\nvirt/kvm/eventfd.c=301=static void kvm_irqfd_register(struct file *file, wait_queue_head_t *wqh,\n--\nvirt/kvm/eventfd.c-350-\nvirt/kvm/eventfd.c:351:#if IS_ENABLED(CONFIG_HAVE_KVM_IRQ_BYPASS)\nvirt/kvm/eventfd.c-352-void __attribute__((weak)) kvm_arch_irq_bypass_stop(\n--\nvirt/kvm/eventfd.c=371=kvm_irqfd_assign(struct kvm *kvm, struct kvm_irqfd *args)\n--\nvirt/kvm/eventfd.c-489-\nvirt/kvm/eventfd.c:490:#if IS_ENABLED(CONFIG_HAVE_KVM_IRQ_BYPASS)\nvirt/kvm/eventfd.c-491-\tif (kvm_arch_has_irq_bypass()) {\n--\nvirt/kvm/eventfd.c=659=void kvm_irq_routing_update(struct kvm *kvm)\n--\nvirt/kvm/eventfd.c-665-\tlist_for_each_entry(irqfd, \u0026kvm-\u003eirqfds.items, list) {\nvirt/kvm/eventfd.c:666:#if IS_ENABLED(CONFIG_HAVE_KVM_IRQ_BYPASS)\nvirt/kvm/eventfd.c-667-\t\t/* Under irqfds.lock, so can read irq_entry safely */\n--\nvirt/kvm/eventfd.c-672-\nvirt/kvm/eventfd.c:673:#if IS_ENABLED(CONFIG_HAVE_KVM_IRQ_BYPASS)\nvirt/kvm/eventfd.c-674-\t\tif (irqfd-\u003eproducer)\n--\nvirt/kvm/eventfd.c=1099=kvm_eventfd_init(struct kvm *kvm)\nvirt/kvm/eventfd.c-1100-{\nvirt/kvm/eventfd.c:1101:#ifdef CONFIG_HAVE_KVM_IRQCHIP\nvirt/kvm/eventfd.c-1102-\tspin_lock_init(\u0026kvm-\u003eirqfds.lock);\n"
} |
| 15/2 | 2026/10/29 06:17 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20819 output: 38 thoughts: 283 Thoughts: **Investigating KVM and Eventfd** Okay, so I'm digging into something related to `ioeventfd`, and the initial hunch is there's nothing immediately obvious linking the two. The first thing that jumps to mind is, "What configurations actually enable KVM in the first place?" Right, that's `CONFIG_KVM` and then either `CONFIG_KVM_INTEL` or `CONFIG_KVM_AMD`. That's the baseline. Now, let's confirm that and see how things are set up in `arch/x86/kvm/Kconfig`. Ah, here it is: `KVM` depends on either `KVM_INTEL` or `KVM_AMD` not being *n* - so either of those being active enables KVM. Looking further, `KVM` is just a tristate, and `KVM_INTEL` requires `IA32_FEAT_CTL` which is normal for this. So it boils down to the base `KVM` configuration and then Intel or AMD. Got it. Next, I need to understand where the changes are being made, so I'll be looking at `virt/kvm/eventfd.c` and `virt/kvm/kvm_main.c` specifically, focusing on the functions that this patch has modified. I'll start with `virt/kvm/eventfd.c` – specifically, I'll be reading through the code from roughly line 800 to 950. That should give me the context I need. |
| 16/2 | 2026/10/29 06:18 | tool | read-file |
0mArgs:{
"File": "virt/kvm/eventfd.c",
"FirstLine": 800,
"LineCount": 100
}Results: {
"Contents": " 800:\t\n 801:\t\tif (p-\u003ewildcard)\n 802:\t\t\t/* all else equal, wildcard is always a hit */\n 803:\t\t\treturn true;\n 804:\t\n 805:\t\t/* otherwise, we have to actually compare the data */\n 806:\t\treturn ioeventfd_get_val(val, len, unsupported_len) == p-\u003edatamatch;\n 807:\t\n 808:\tunsupported_len:\n 809:\t\treturn false;\n 810:\t}\n 811:\t\n 812:\t/* MMIO/PIO writes trigger an event if the addr/val match */\n 813:\tstatic int\n 814:\tioeventfd_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this, gpa_t addr,\n 815:\t\t\tint len, const void *val)\n 816:\t{\n 817:\t\tstruct _ioeventfd *p = to_ioeventfd(this);\n 818:\t\n 819:\t\tif (!ioeventfd_in_range(p, addr, len, val))\n 820:\t\t\treturn -EOPNOTSUPP;\n 821:\t\n 822:\t\tif (p-\u003epost_addr) {\n 823:\t\t\tu64 _val = ioeventfd_get_val(val, len, unsupported_len);\n 824:\t\t\tint r;\n 825:\t\n 826:\t\t\tswitch (len) {\n 827:\t\t\tcase 1:\n 828:\t\t\t\tr = put_user(_val, (u8 __user *)p-\u003epost_addr);\n 829:\t\t\t\tbreak;\n 830:\t\t\tcase 2:\n 831:\t\t\t\tr = put_user(_val, (u16 __user *)p-\u003epost_addr);\n 832:\t\t\t\tbreak;\n 833:\t\t\tcase 4:\n 834:\t\t\t\tr = put_user(_val, (u32 __user *)p-\u003epost_addr);\n 835:\t\t\t\tbreak;\n 836:\t\t\tcase 8:\n 837:\t\t\t\tr = put_user(_val, (u64 __user *)p-\u003epost_addr);\n 838:\t\t\t\tbreak;\n 839:\t\t\tdefault:\n 840:\t\t\t\tWARN_ON_ONCE(1);\n 841:\t\t\t\tr = 0;\n 842:\t\t\t\tgoto unsupported_len;\n 843:\t\t\t}\n 844:\t\t\tif (r)\n 845:\t\t\t\treturn r;\n 846:\t\t}\n 847:\t\n 848:\t\teventfd_signal(p-\u003eeventfd);\n 849:\t\treturn 0;\n 850:\tunsupported_len:\n 851:\t\treturn -EOPNOTSUPP;\n 852:\t}\n 853:\t\n 854:\t/*\n 855:\t * This function is called as KVM is completely shutting down. We do not\n 856:\t * need to worry about locking just nuke anything we have as quickly as possible\n 857:\t */\n 858:\tstatic void\n 859:\tioeventfd_destructor(struct kvm_io_device *this)\n 860:\t{\n 861:\t\tstruct _ioeventfd *p = to_ioeventfd(this);\n 862:\t\n 863:\t\tioeventfd_release(p);\n 864:\t}\n 865:\t\n 866:\tstatic const struct kvm_io_device_ops ioeventfd_ops = {\n 867:\t\t.write = ioeventfd_write,\n 868:\t\t.destructor = ioeventfd_destructor,\n 869:\t};\n 870:\t\n 871:\t/* assumes kvm-\u003eslots_lock held */\n 872:\tstatic bool\n 873:\tioeventfd_check_collision(struct kvm *kvm, struct _ioeventfd *p)\n 874:\t{\n 875:\t\tstruct _ioeventfd *_p;\n 876:\t\n 877:\t\tlist_for_each_entry(_p, \u0026kvm-\u003eioeventfds, list)\n 878:\t\t\tif (_p-\u003ebus_idx == p-\u003ebus_idx \u0026\u0026\n 879:\t\t\t _p-\u003eaddr == p-\u003eaddr \u0026\u0026\n 880:\t\t\t (!_p-\u003elength || !p-\u003elength ||\n 881:\t\t\t (_p-\u003elength == p-\u003elength \u0026\u0026\n 882:\t\t\t (_p-\u003ewildcard || p-\u003ewildcard ||\n 883:\t\t\t _p-\u003edatamatch == p-\u003edatamatch))))\n 884:\t\t\t\treturn true;\n 885:\t\n 886:\t\treturn false;\n 887:\t}\n 888:\t\n 889:\tstatic enum kvm_bus ioeventfd_bus_from_flags(__u32 flags)\n 890:\t{\n 891:\t\tif (flags \u0026 KVM_IOEVENTFD_FLAG_PIO)\n 892:\t\t\treturn KVM_PIO_BUS;\n 893:\t\tif (flags \u0026 KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY)\n 894:\t\t\treturn KVM_VIRTIO_CCW_NOTIFY_BUS;\n 895:\t\treturn KVM_MMIO_BUS;\n 896:\t}\n 897:\t\n 898:\tstatic int kvm_assign_ioeventfd_idx(struct kvm *kvm,\n 899:\t\t\t\t\tenum kvm_bus bus_idx,\n"
} |
| 17/2 | 2026/10/29 06:18 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 22948 output: 38 thoughts: 30 Thoughts: Okay, here's my interpretation of that thought process, assuming I'm deep in KVM internals: **Diving Deeper into Eventfd Handling in KVM** Alright, I'm thinking I need to understand this eventfd interaction better. I need to get a grip on how it works. I'm going to jump into the `virt/kvm/eventfd.c` file. Specifically, I'm going to zero in on lines 900 through 1000. That section should give me the core logic I'm interested in, whatever the particular function calls are and related data structures. This is where the magic happens, so I'm hoping to understand the nuances of this eventfd operation more completely. I expect to be focusing on the control flow within that code block. Let's see what gems are there, and I'll see what the code does from the inside. |
| 18/2 | 2026/10/29 06:18 | tool | read-file |
0mArgs:{
"File": "virt/kvm/eventfd.c",
"FirstLine": 900,
"LineCount": 100
}Results: {
"Contents": " 900:\t\t\t\t\tstruct kvm_ioeventfd *args)\n 901:\t{\n 902:\t\n 903:\t\tstruct eventfd_ctx *eventfd;\n 904:\t\tstruct _ioeventfd *p;\n 905:\t\tint ret;\n 906:\t\n 907:\t\teventfd = eventfd_ctx_fdget(args-\u003efd);\n 908:\t\tif (IS_ERR(eventfd))\n 909:\t\t\treturn PTR_ERR(eventfd);\n 910:\t\n 911:\t\t/*\n 912:\t\t * Disallow posting to virtual address 0 so that KVM can treat\n 913:\t\t * a NULL user pointer as \"no posted write\".\n 914:\t\t */\n 915:\t\tif ((args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_POST_WRITE) \u0026\u0026\n 916:\t\t (!args-\u003elen || !u64_to_user_ptr(args-\u003epost_addr) ||\n 917:\t\t args-\u003epost_addr != untagged_addr(args-\u003epost_addr) ||\n 918:\t\t !access_ok(u64_to_user_ptr(args-\u003epost_addr), args-\u003elen) ||\n 919:\t\t !IS_ALIGNED(args-\u003epost_addr, args-\u003elen))) {\n 920:\t\t\tret = -EINVAL;\n 921:\t\t\tgoto fail;\n 922:\t\t}\n 923:\t\n 924:\t\tp = kzalloc_obj(*p, GFP_KERNEL_ACCOUNT);\n 925:\t\tif (!p) {\n 926:\t\t\tret = -ENOMEM;\n 927:\t\t\tgoto fail;\n 928:\t\t}\n 929:\t\n 930:\t\tINIT_LIST_HEAD(\u0026p-\u003elist);\n 931:\t\tp-\u003eaddr = args-\u003eaddr;\n 932:\t\tp-\u003ebus_idx = bus_idx;\n 933:\t\tp-\u003elength = args-\u003elen;\n 934:\t\tp-\u003eeventfd = eventfd;\n 935:\t\n 936:\t\t/* The datamatch feature is optional, otherwise this is a wildcard */\n 937:\t\tif (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DATAMATCH)\n 938:\t\t\tp-\u003edatamatch = args-\u003edatamatch;\n 939:\t\telse\n 940:\t\t\tp-\u003ewildcard = true;\n 941:\t\n 942:\t\tif (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_POST_WRITE)\n 943:\t\t\tp-\u003epost_addr = u64_to_user_ptr(args-\u003epost_addr);\n 944:\t\n 945:\t\tmutex_lock(\u0026kvm-\u003eslots_lock);\n 946:\t\n 947:\t\t/* Verify that there isn't a match already */\n 948:\t\tif (ioeventfd_check_collision(kvm, p)) {\n 949:\t\t\tret = -EEXIST;\n 950:\t\t\tgoto unlock_fail;\n 951:\t\t}\n 952:\t\n 953:\t\tkvm_iodevice_init(\u0026p-\u003edev, \u0026ioeventfd_ops);\n 954:\t\n 955:\t\tret = kvm_io_bus_register_dev(kvm, bus_idx, p-\u003eaddr, p-\u003elength,\n 956:\t\t\t\t\t \u0026p-\u003edev);\n 957:\t\tif (ret \u003c 0)\n 958:\t\t\tgoto unlock_fail;\n 959:\t\n 960:\t\tkvm_get_bus(kvm, bus_idx)-\u003eioeventfd_count++;\n 961:\t\tlist_add_tail(\u0026p-\u003elist, \u0026kvm-\u003eioeventfds);\n 962:\t\n 963:\t\tmutex_unlock(\u0026kvm-\u003eslots_lock);\n 964:\t\n 965:\t\treturn 0;\n 966:\t\n 967:\tunlock_fail:\n 968:\t\tmutex_unlock(\u0026kvm-\u003eslots_lock);\n 969:\t\tkfree(p);\n 970:\t\n 971:\tfail:\n 972:\t\teventfd_ctx_put(eventfd);\n 973:\t\n 974:\t\treturn ret;\n 975:\t}\n 976:\t\n 977:\tstatic int\n 978:\tkvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,\n 979:\t\t\t\t struct kvm_ioeventfd *args)\n 980:\t{\n 981:\t\tstruct _ioeventfd *p;\n 982:\t\tstruct eventfd_ctx *eventfd;\n 983:\t\tstruct kvm_io_bus\t *bus;\n 984:\t\tint ret = -ENOENT;\n 985:\t\tbool wildcard;\n 986:\t\n 987:\t\teventfd = eventfd_ctx_fdget(args-\u003efd);\n 988:\t\tif (IS_ERR(eventfd))\n 989:\t\t\treturn PTR_ERR(eventfd);\n 990:\t\n 991:\t\twildcard = !(args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DATAMATCH);\n 992:\t\n 993:\t\tmutex_lock(\u0026kvm-\u003eslots_lock);\n 994:\t\n 995:\t\tlist_for_each_entry(p, \u0026kvm-\u003eioeventfds, list) {\n 996:\t\t\t/*\n 997:\t\t\t * No need to match post_addr, ioeventfd_check_collision\n 998:\t\t\t * prevents duplicate registrations that only differ by\n 999:\t\t\t * post_addr.\n"
} |
| 19/2 | 2026/10/29 06:18 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 24709 output: 36 thoughts: 41 Thoughts: Okay, let's dive into this `kvm_ioeventfd` situation. I need to understand what's going on under the hood here. My instinct is to start by examining the source code itself. Given that we're talking about a KVM-related function, I'll go directly to the `virt/kvm` directory and look in `eventfd.c`. That should be the logical place to find the implementation of `kvm_ioeventfd`. Let's see what insights that code provides. This will give a good grounding for further investigation, and point towards any relevant structures or functions. |
| 20/2 | 2026/10/29 06:18 | tool | grepper |
0mArgs:{
"Expression": "kvm_ioeventfd",
"PathPrefixes": [
"virt/kvm/eventfd.c"
]
}Results: {
"Output": "virt/kvm/eventfd.c=898=static int kvm_assign_ioeventfd_idx(struct kvm *kvm,\nvirt/kvm/eventfd.c-899-\t\t\t\tenum kvm_bus bus_idx,\nvirt/kvm/eventfd.c:900:\t\t\t\tstruct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-901-{\n--\nvirt/kvm/eventfd.c=978=kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,\nvirt/kvm/eventfd.c:979:\t\t\t struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-980-{\n--\nvirt/kvm/eventfd.c-1025-\nvirt/kvm/eventfd.c:1026:static int kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1027-{\n--\nvirt/kvm/eventfd.c=1037=static int\nvirt/kvm/eventfd.c:1038:kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1039-{\n--\nvirt/kvm/eventfd.c=1089=int\nvirt/kvm/eventfd.c:1090:kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1091-{\n"
} |
| 21/2 | 2026/10/29 06:18 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 25348 output: 38 thoughts: 21 Thoughts: Okay, let's dive into this. Here's my thought process on this specific code snippet: **Diving into `kvm_ioeventfd` Implementation** Alright, I need to get a handle on the `kvm_ioeventfd` implementation. My initial focus will be around line 1085, as that seems like a good starting point. I want to understand how it's structured, what the key functions are, and the overall logic flow. Given the context, I'm expecting to find the core mechanisms for handling I/O events with the `ioeventfd` mechanism. I'll be looking for how the kernel interacts with the userspace I/O event file descriptor, any data structures related to the event, and how the actual triggering of events is handled. It's time to get a clear picture of how this piece of the puzzle fits into the overall KVM architecture, specifically concerning I/O emulation. My goal is to quickly pinpoint any potential areas of concern, optimization opportunities, or potential bugs that might exist within this particular section of the code. Let's see what we find. |
| 22/2 | 2026/10/29 06:18 | tool | read-file |
0mArgs:{
"File": "virt/kvm/eventfd.c",
"FirstLine": 1070,
"LineCount": 35
}Results: {
"Contents": "1070:\t\t\tgoto fail;\n1071:\t\n1072:\t\t/* When length is ignored, MMIO is also put on a separate bus, for\n1073:\t\t * faster lookups.\n1074:\t\t */\n1075:\t\tif (!args-\u003elen \u0026\u0026 bus_idx == KVM_MMIO_BUS) {\n1076:\t\t\tret = kvm_assign_ioeventfd_idx(kvm, KVM_FAST_MMIO_BUS, args);\n1077:\t\t\tif (ret \u003c 0)\n1078:\t\t\t\tgoto fast_fail;\n1079:\t\t}\n1080:\t\n1081:\t\treturn 0;\n1082:\t\n1083:\tfast_fail:\n1084:\t\tkvm_deassign_ioeventfd_idx(kvm, bus_idx, args);\n1085:\tfail:\n1086:\t\treturn ret;\n1087:\t}\n1088:\t\n1089:\tint\n1090:\tkvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\n1091:\t{\n1092:\t\tif (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DEASSIGN)\n1093:\t\t\treturn kvm_deassign_ioeventfd(kvm, args);\n1094:\t\n1095:\t\treturn kvm_assign_ioeventfd(kvm, args);\n1096:\t}\n1097:\t\n1098:\tvoid\n1099:\tkvm_eventfd_init(struct kvm *kvm)\n1100:\t{\n1101:\t#ifdef CONFIG_HAVE_KVM_IRQCHIP\n1102:\t\tspin_lock_init(\u0026kvm-\u003eirqfds.lock);\n1103:\t\tINIT_LIST_HEAD(\u0026kvm-\u003eirqfds.items);\n1104:\t\tINIT_LIST_HEAD(\u0026kvm-\u003eirqfds.resampler_list);\n"
} |
| 23/2 | 2026/10/29 06:18 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 26177 output: 22 thoughts: 160 Thoughts: **Tracing the Invocation of `ioeventfd_write()`** Okay, let's break this down. My goal is to understand how `ioeventfd_write()` gets called in the kernel, specifically within the KVM environment. First, I need to trace the path. It looks like `ioeventfd_ops` is the key, so I need to find where that is invoked. Ah, here it is: `kvm_iodevice_init(&p->dev, &ioeventfd_ops);`. Then, that `dev` (which is our `ioeventfd_ops`) is registered with the I/O bus using `kvm_io_bus_register_dev(kvm, bus_idx, p->addr, p->length, &p->dev);`. Right, so when a guest VM executes an MMIO or PIO write, it's going through that I/O bus. My hypothesis is correct: `kvm_io_bus_write()` is then invoked, and that function, in turn, calls the `dev->ops->write()` function, passing along the virtual CPU, the device, the address, length, and the value to be written. And we've just seen that `dev->ops->write` is set to `ioeventfd_write`. So, there's our call chain! Now, to solidify my understanding, I'll examine the calls to `kvm_io_bus_write` in the kernel source to establish the contexts that lead to this path. |
| 24/2 | 2026/10/29 06:18 | tool | grepper |
0mArgs:{
"Expression": "kvm_io_bus_write"
}Results: {
"Output": "arch/arm64/kvm/mmio.c=156=int io_mem_abort(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa)\n--\narch/arm64/kvm/mmio.c-231-\narch/arm64/kvm/mmio.c:232:\t\tret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, fault_ipa, len,\narch/arm64/kvm/mmio.c-233-\t\t\t\t data_buf);\n--\narch/loongarch/kvm/exit.c=156=int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu)\n--\narch/loongarch/kvm/exit.c-208-\t\tidx = srcu_read_lock(\u0026vcpu-\u003ekvm-\u003esrcu);\narch/loongarch/kvm/exit.c:209:\t\tret = kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, run-\u003eiocsr_io.len, val);\narch/loongarch/kvm/exit.c-210-\t\tsrcu_read_unlock(\u0026vcpu-\u003ekvm-\u003esrcu, idx);\n--\narch/loongarch/kvm/exit.c=543=int kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst)\n--\narch/loongarch/kvm/exit.c-645-\t\tidx = srcu_read_lock(\u0026vcpu-\u003ekvm-\u003esrcu);\narch/loongarch/kvm/exit.c:646:\t\tret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, vcpu-\u003earch.badv, run-\u003emmio.len, data);\narch/loongarch/kvm/exit.c-647-\t\tsrcu_read_unlock(\u0026vcpu-\u003ekvm-\u003esrcu, idx);\n--\narch/loongarch/kvm/intc/ipi.c=125=static int send_ipi_data(struct kvm_vcpu *vcpu, gpa_t addr, uint64_t data)\n--\narch/loongarch/kvm/intc/ipi.c-152-\tidx = srcu_read_lock(\u0026vcpu-\u003ekvm-\u003esrcu);\narch/loongarch/kvm/intc/ipi.c:153:\tret = kvm_io_bus_write(vcpu, KVM_IOCSR_BUS, addr, 4, \u0026val);\narch/loongarch/kvm/intc/ipi.c-154-\tsrcu_read_unlock(\u0026vcpu-\u003ekvm-\u003esrcu, idx);\n--\narch/mips/kvm/emulate.c=968=enum emulation_result kvm_mips_emulate_store(union mips_instruction inst,\n--\narch/mips/kvm/emulate.c-1251-\narch/mips/kvm/emulate.c:1252:\tr = kvm_io_bus_write(vcpu, KVM_MMIO_BUS,\narch/mips/kvm/emulate.c-1253-\t\t\trun-\u003emmio.phys_addr, run-\u003emmio.len, data);\n--\narch/powerpc/kvm/book3s.c=976=int kvmppc_h_logical_ci_store(struct kvm_vcpu *vcpu)\n--\narch/powerpc/kvm/book3s.c-1006-\tsrcu_idx = srcu_read_lock(\u0026vcpu-\u003ekvm-\u003esrcu);\narch/powerpc/kvm/book3s.c:1007:\tret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, addr, size, \u0026buf);\narch/powerpc/kvm/book3s.c-1008-\tsrcu_read_unlock(\u0026vcpu-\u003ekvm-\u003esrcu, srcu_idx);\n--\narch/powerpc/kvm/book3s_64_mmu_hv.c=433=int kvmppc_hv_emulate_mmio(struct kvm_vcpu *vcpu,\n--\narch/powerpc/kvm/book3s_64_mmu_hv.c-447-\t\tidx = srcu_read_lock(\u0026vcpu-\u003ekvm-\u003esrcu);\narch/powerpc/kvm/book3s_64_mmu_hv.c:448:\t\tret = kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, (gpa_t) gpa, 0,\narch/powerpc/kvm/book3s_64_mmu_hv.c-449-\t\t\t\t NULL);\n--\narch/powerpc/kvm/powerpc.c=1356=int kvmppc_handle_store(struct kvm_vcpu *vcpu,\n--\narch/powerpc/kvm/powerpc.c-1401-\narch/powerpc/kvm/powerpc.c:1402:\tret = kvm_io_bus_write(vcpu, KVM_MMIO_BUS, run-\u003emmio.phys_addr,\narch/powerpc/kvm/powerpc.c-1403-\t\t\t bytes, \u0026run-\u003emmio.data);\n--\narch/riscv/kvm/vcpu_insn.c=501=int kvm_riscv_vcpu_mmio_store(struct kvm_vcpu *vcpu, struct kvm_run *run,\n--\narch/riscv/kvm/vcpu_insn.c-612-\t/* Try to handle MMIO access in the kernel */\narch/riscv/kvm/vcpu_insn.c:613:\tif (!kvm_io_bus_write(vcpu, KVM_MMIO_BUS,\narch/riscv/kvm/vcpu_insn.c-614-\t\t\t fault_addr, len, run-\u003emmio.data)) {\n--\narch/s390/kvm/s390/diag.c=269=static int __diag_virtio_hypercall(struct kvm_vcpu *vcpu)\n--\narch/s390/kvm/s390/diag.c-289-\t */\narch/s390/kvm/s390/diag.c:290:\tret = kvm_io_bus_write_cookie(vcpu, KVM_VIRTIO_CCW_NOTIFY_BUS,\narch/s390/kvm/s390/diag.c-291-\t\t\t\t vcpu-\u003erun-\u003es.regs.gprs[2] \u0026 0xffffffff,\n--\narch/s390/kvm/s390/diag.c-300-\t\tvcpu-\u003erun-\u003es.regs.gprs[2] = ret;\narch/s390/kvm/s390/diag.c:301:\t/* kvm_io_bus_write_cookie returns -EOPNOTSUPP if it found no match. */\narch/s390/kvm/s390/diag.c-302-\treturn ret \u003c 0 ? ret : 0;\n--\narch/x86/kvm/svm/svm.c=2014=static int npf_interception(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/svm.c-2042-\t\tif (nrips \u0026\u0026 svm-\u003evmcb-\u003econtrol.next_rip \u0026\u0026\narch/x86/kvm/svm/svm.c:2043:\t\t !kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {\narch/x86/kvm/svm/svm.c-2044-\t\t\ttrace_kvm_fast_mmio(gpa);\n--\narch/x86/kvm/vmx/tdx.c=1429=static inline int tdx_mmio_write(struct kvm_vcpu *vcpu, gpa_t gpa, int size,\n--\narch/x86/kvm/vmx/tdx.c-1431-{\narch/x86/kvm/vmx/tdx.c:1432:\tif (!kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {\narch/x86/kvm/vmx/tdx.c-1433-\t\ttrace_kvm_fast_mmio(gpa);\n--\narch/x86/kvm/vmx/tdx.c-1437-\ttrace_kvm_mmio(KVM_TRACE_MMIO_WRITE, size, gpa, \u0026val);\narch/x86/kvm/vmx/tdx.c:1438:\tif (kvm_io_bus_write(vcpu, KVM_MMIO_BUS, gpa, size, \u0026val))\narch/x86/kvm/vmx/tdx.c-1439-\t\treturn -EOPNOTSUPP;\n--\narch/x86/kvm/vmx/vmx.c=6009=static int handle_ept_misconfig(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/vmx/vmx.c-6021-\tif (!is_guest_mode(vcpu) \u0026\u0026\narch/x86/kvm/vmx/vmx.c:6022:\t !kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {\narch/x86/kvm/vmx/vmx.c-6023-\t\ttrace_kvm_fast_mmio(gpa);\n--\narch/x86/kvm/x86.c=4782=static int vcpu_mmio_write(struct kvm_vcpu *vcpu, gpa_t addr, int len,\n--\narch/x86/kvm/x86.c-4794-\t\t !kvm_iodevice_write(vcpu, \u0026vcpu-\u003earch.apic-\u003edev, addr, n, v))\narch/x86/kvm/x86.c:4795:\t\t \u0026\u0026 kvm_io_bus_write(vcpu, KVM_MMIO_BUS, addr, n, v))\narch/x86/kvm/x86.c-4796-\t\t\tbreak;\n--\narch/x86/kvm/x86.c=5386=static int emulator_pio_in_out(struct kvm_vcpu *vcpu, int size,\n--\narch/x86/kvm/x86.c-5397-\t\telse\narch/x86/kvm/x86.c:5398:\t\t\tr = kvm_io_bus_write(vcpu, KVM_PIO_BUS, port, size, data);\narch/x86/kvm/x86.c-5399-\n--\ninclude/linux/kvm_host.h=218=enum kvm_bus {\n--\ninclude/linux/kvm_host.h-226-\ninclude/linux/kvm_host.h:227:int kvm_io_bus_write(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,\ninclude/linux/kvm_host.h-228-\t\t int len, const void *val);\ninclude/linux/kvm_host.h:229:int kvm_io_bus_write_cookie(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx,\ninclude/linux/kvm_host.h-230-\t\t\t gpa_t addr, int len, const void *val, long cookie);\n--\nvirt/kvm/kvm_main.c=5943=static int kvm_io_bus_get_first_dev(struct kvm_io_bus *bus,\n--\nvirt/kvm/kvm_main.c-5966-\nvirt/kvm/kvm_main.c:5967:static int __kvm_io_bus_write(struct kvm_vcpu *vcpu, struct kvm_io_bus *bus,\nvirt/kvm/kvm_main.c-5968-\t\t\t struct kvm_io_range *range, const void *val)\n--\nvirt/kvm/kvm_main.c=5987=static struct kvm_io_bus *kvm_get_bus_srcu(struct kvm *kvm, enum kvm_bus idx)\n--\nvirt/kvm/kvm_main.c-5998-\nvirt/kvm/kvm_main.c:5999:int kvm_io_bus_write(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,\nvirt/kvm/kvm_main.c-6000-\t\t int len, const void *val)\n--\nvirt/kvm/kvm_main.c-6013-\t\treturn -ENOMEM;\nvirt/kvm/kvm_main.c:6014:\tr = __kvm_io_bus_write(vcpu, bus, \u0026range, val);\nvirt/kvm/kvm_main.c-6015-\treturn r \u003c 0 ? r : 0;\nvirt/kvm/kvm_main.c-6016-}\nvirt/kvm/kvm_main.c:6017:EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_io_bus_write);\nvirt/kvm/kvm_main.c-6018-\nvirt/kvm/kvm_main.c:6019:int kvm_io_bus_write_cookie(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx,\nvirt/kvm/kvm_main.c-6020-\t\t\t gpa_t addr, int len, const void *val, long cookie)\n--\nvirt/kvm/kvm_main.c-6044-\t */\nvirt/kvm/kvm_main.c:6045:\treturn __kvm_io_bus_write(vcpu, bus, \u0026range, val);\nvirt/kvm/kvm_main.c-6046-}\n"
} |
| 25/2 | 2026/10/29 06:18 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30358 output: 33 thoughts: 1041 Thoughts: **Analyzing Potential Io Eventfd Vulnerability** Okay, let's break this down. My initial observation is that `vcpu_mmio_write` calls `kvm_io_bus_write`, and `emulator_pio_in_out` does the same. `kvm_assign_ioeventfd_idx` registers devices onto the bus via `ioeventfd_ops`. This all leads to `ioeventfd_write`. Now, looking at `ioeventfd_write`, I see a clear path where the value is written to a user-space address (`p->post_addr`) if `p->post_addr` is set. The critical part is the `switch` statement based on `len`. The `WARN_ON_ONCE(1)` in the `default` case is what jumped out immediately. That suggests there are expected length restrictions. But *can* `len` be zero or something outside 1, 2, 4, or 8? Then, I examine `ioeventfd_in_range`. The `p->length` field is checked. If it's zero, *any* length is considered a hit. If it's not zero, the length has to match. The check for `p->wildcard` is also interesting. If `wildcard` is set, it's *always* a hit, regardless of the data, as long as the address matches and length is also correct, OR `p->length` is zero. The real question is, what happens during registration via `kvm_assign_ioeventfd_idx`? I need to check the length validation there. I see a bunch of checks on `args->len` and `args->post_addr`. If `KVM_IOEVENTFD_FLAG_POST_WRITE` is set, it checks: if `args->len` is zero or the post address is invalid (e.g. not user-space address), it returns an error. It looks for alignment on the post address as well, so there's *some* length checking during assignment. So, let me synthesize. If `p->wildcard` is *true* and `p->post_addr` is *set*, and `p->length` is *not* zero, the check in `ioeventfd_in_range` is that `len` must equal `p->length`. If `len` doesn't match `p->length` then `false` is returned and an event is *not* triggered. However, if `p->length` *is* zero, then *any* `len` value is a hit, meaning that an event will always be triggered, and the data will be written to `p->post_addr`. But, what if `p->wildcard` is true and `args->len` is, say, 3? Is the check for a zero length on the registration path, where it's assumed that some other path must also check the length? I need to see *precisely* how the `len` value from the guest is validated and used during the *register* process to trigger an event, and then if the data is written during the write process. It looks like the validation of `len` during registration relies on it *not* being zero, and alignment is also important. So I will see exactly how `args->len` is validated when registering an ioeventfd. |
| 26/2 | 2026/10/29 06:19 | tool | grepper |
0mArgs:{
"Expression": "args-\u003elen",
"PathPrefixes": [
"virt/kvm/eventfd.c"
]
}Results: {
"Output": "virt/kvm/eventfd.c=898=static int kvm_assign_ioeventfd_idx(struct kvm *kvm,\n--\nvirt/kvm/eventfd.c-915-\tif ((args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_POST_WRITE) \u0026\u0026\nvirt/kvm/eventfd.c:916:\t (!args-\u003elen || !u64_to_user_ptr(args-\u003epost_addr) ||\nvirt/kvm/eventfd.c-917-\t args-\u003epost_addr != untagged_addr(args-\u003epost_addr) ||\nvirt/kvm/eventfd.c:918:\t !access_ok(u64_to_user_ptr(args-\u003epost_addr), args-\u003elen) ||\nvirt/kvm/eventfd.c:919:\t !IS_ALIGNED(args-\u003epost_addr, args-\u003elen))) {\nvirt/kvm/eventfd.c-920-\t\tret = -EINVAL;\n--\nvirt/kvm/eventfd.c-932-\tp-\u003ebus_idx = bus_idx;\nvirt/kvm/eventfd.c:933:\tp-\u003elength = args-\u003elen;\nvirt/kvm/eventfd.c-934-\tp-\u003eeventfd = eventfd;\n--\nvirt/kvm/eventfd.c=978=kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,\n--\nvirt/kvm/eventfd.c-1003-\t\t p-\u003eaddr != args-\u003eaddr ||\nvirt/kvm/eventfd.c:1004:\t\t p-\u003elength != args-\u003elen ||\nvirt/kvm/eventfd.c-1005-\t\t p-\u003ewildcard != wildcard)\n--\nvirt/kvm/eventfd.c=1026=static int kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\n--\nvirt/kvm/eventfd.c-1030-\nvirt/kvm/eventfd.c:1031:\tif (!args-\u003elen \u0026\u0026 bus_idx == KVM_MMIO_BUS)\nvirt/kvm/eventfd.c-1032-\t\tkvm_deassign_ioeventfd_idx(kvm, KVM_FAST_MMIO_BUS, args);\n--\nvirt/kvm/eventfd.c=1038=kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\n--\nvirt/kvm/eventfd.c-1044-\t/* must be natural-word sized, or 0 to ignore length */\nvirt/kvm/eventfd.c:1045:\tswitch (args-\u003elen) {\nvirt/kvm/eventfd.c-1046-\tcase 0:\n--\nvirt/kvm/eventfd.c-1056-\t/* check for range overflow */\nvirt/kvm/eventfd.c:1057:\tif (args-\u003eaddr + args-\u003elen \u003c args-\u003eaddr)\nvirt/kvm/eventfd.c-1058-\t\treturn -EINVAL;\n--\nvirt/kvm/eventfd.c-1064-\t/* ioeventfd with no length can't be combined with DATAMATCH */\nvirt/kvm/eventfd.c:1065:\tif (!args-\u003elen \u0026\u0026 (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DATAMATCH))\nvirt/kvm/eventfd.c-1066-\t\treturn -EINVAL;\n--\nvirt/kvm/eventfd.c-1074-\t */\nvirt/kvm/eventfd.c:1075:\tif (!args-\u003elen \u0026\u0026 bus_idx == KVM_MMIO_BUS) {\nvirt/kvm/eventfd.c-1076-\t\tret = kvm_assign_ioeventfd_idx(kvm, KVM_FAST_MMIO_BUS, args);\n"
} |
| 27/2 | 2026/10/29 06:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 33184 output: 41 thoughts: 722 Thoughts: **Analyzing KVM's IOEventfd Write Path** Okay, let's break this down. My initial thought is, can `args->len` actually be zero? That's the first question that popped into my head. Then I saw in `kvm_assign_ioeventfd_idx` that a zero length *should* be caught, returning `-EINVAL` if `KVM_IOEVENTFD_FLAG_POST_WRITE` is also set. But wait, can `bus_idx` ever equal `KVM_FAST_MMIO_BUS`? It shouldn't, since `args->len` is enforced to be non-zero. Still, I wonder about direct registrations somehow bypassing that, even though `kvm_assign_ioeventfd_idx` is static. Now, what about the guest writing with a length that doesn't match `p->length`? That's crucial. The `ioeventfd_in_range(p, addr, len, val)` function is the gatekeeper here. It *allows* a write if `p->length` is zero (meaning any size is valid). And the key check is, `if (len != p->length) return false;`. So in `ioeventfd_write()` we have this: `if (!ioeventfd_in_range(p, addr, len, val)) return -EOPNOTSUPP;`. That's good; it *should* reject mismatched lengths. So, if `ioeventfd_in_range` passes *and* `p->post_addr` is set (which implies that `p->length` was 1, 2, 4, or 8, because `kvm_assign_ioeventfd` requires args->len to be 0, 1, 2, 4, 8, and POST_WRITE forbids 0), we know `len == p->length`. Then, the `WARN_ON_ONCE(1)` is what's nagging me. If `len` is *not* 1, 2, 4, or 8, that warning triggers, even though `ioeventfd_in_range()` should have prevented this. Then, there is the potential problem of calling `put_user` in `ioeventfd_write()`. Can `p->post_addr` be unmapped or cause page faults? I need to check how `put_user` is used. Looking closely at `ioeventfd_write()`: the code now seems sound as it uses a switch, and then returns r if anything goes wrong, with the switch controlling which size is written using `put_user`. So, if for any reason `len` is not 1, 2, 4, 8, then `WARN_ON_ONCE(1)` triggers! And if `r != 0`, then the code returns that error code. This leads me to wonder what `kvm_io_bus_write()` does when `ioeventfd_write()` returns `r`, which could be `-EFAULT`. So I need to go look at `__kvm_io_bus_write` in `virt/kvm/kvm_main.c` to understand the bigger picture when something goes wrong. I need to understand the error propagation from `ioeventfd_write` to the higher-level bus write function. |
| 28/2 | 2026/10/29 06:19 | tool | grepper |
0mArgs:{
"Expression": "__kvm_io_bus_write",
"PathPrefixes": [
"virt/kvm/kvm_main.c"
]
}Results: {
"Output": "virt/kvm/kvm_main.c=5943=static int kvm_io_bus_get_first_dev(struct kvm_io_bus *bus,\n--\nvirt/kvm/kvm_main.c-5966-\nvirt/kvm/kvm_main.c:5967:static int __kvm_io_bus_write(struct kvm_vcpu *vcpu, struct kvm_io_bus *bus,\nvirt/kvm/kvm_main.c-5968-\t\t\t struct kvm_io_range *range, const void *val)\n--\nvirt/kvm/kvm_main.c=5999=int kvm_io_bus_write(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,\n--\nvirt/kvm/kvm_main.c-6013-\t\treturn -ENOMEM;\nvirt/kvm/kvm_main.c:6014:\tr = __kvm_io_bus_write(vcpu, bus, \u0026range, val);\nvirt/kvm/kvm_main.c-6015-\treturn r \u003c 0 ? r : 0;\n--\nvirt/kvm/kvm_main.c=6019=int kvm_io_bus_write_cookie(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx,\n--\nvirt/kvm/kvm_main.c-6044-\t */\nvirt/kvm/kvm_main.c:6045:\treturn __kvm_io_bus_write(vcpu, bus, \u0026range, val);\nvirt/kvm/kvm_main.c-6046-}\n"
} |
| 29/2 | 2026/10/29 06:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35125 output: 40 thoughts: 30 Thoughts: Okay, let's dive into this. I'm going to grab `virt/kvm/kvm_main.c` and specifically focus on lines 5960 through 5990. My intuition tells me this area likely deals with a crucial aspect of KVM's core functionality, perhaps related to VM lifecycle management, interrupt handling, or resource allocation. Given my expertise, I'm expecting to find either complex control flow, detailed interaction with hardware, or intricate data structures. I'll be looking for any hints on how the code gracefully handles errors, optimizes performance, and maintains security. I'll need to really pay attention to the comments and variable names to quickly grasp the intention of the original developer. I'll try to relate the content to other code areas that might be referenced to gain a more complete understanding. Let's see what we've got. |
| 30/2 | 2026/10/29 06:19 | tool | read-file |
0mArgs:{
"File": "virt/kvm/kvm_main.c",
"FirstLine": 5960,
"LineCount": 35
}Results: {
"Contents": "5960:\t\n5961:\t\twhile (off \u003e 0 \u0026\u0026 kvm_io_bus_cmp(\u0026key, \u0026bus-\u003erange[off-1]) == 0)\n5962:\t\t\toff--;\n5963:\t\n5964:\t\treturn off;\n5965:\t}\n5966:\t\n5967:\tstatic int __kvm_io_bus_write(struct kvm_vcpu *vcpu, struct kvm_io_bus *bus,\n5968:\t\t\t\t struct kvm_io_range *range, const void *val)\n5969:\t{\n5970:\t\tint idx;\n5971:\t\n5972:\t\tidx = kvm_io_bus_get_first_dev(bus, range-\u003eaddr, range-\u003elen);\n5973:\t\tif (idx \u003c 0)\n5974:\t\t\treturn -EOPNOTSUPP;\n5975:\t\n5976:\t\twhile (idx \u003c bus-\u003edev_count \u0026\u0026\n5977:\t\t\tkvm_io_bus_cmp(range, \u0026bus-\u003erange[idx]) == 0) {\n5978:\t\t\tif (!kvm_iodevice_write(vcpu, bus-\u003erange[idx].dev, range-\u003eaddr,\n5979:\t\t\t\t\t\trange-\u003elen, val))\n5980:\t\t\t\treturn idx;\n5981:\t\t\tidx++;\n5982:\t\t}\n5983:\t\n5984:\t\treturn -EOPNOTSUPP;\n5985:\t}\n5986:\t\n5987:\tstatic struct kvm_io_bus *kvm_get_bus_srcu(struct kvm *kvm, enum kvm_bus idx)\n5988:\t{\n5989:\t\t/*\n5990:\t\t * Ensure that any updates to kvm_buses[] observed by the previous vCPU\n5991:\t\t * machine instruction are also visible to the vCPU machine instruction\n5992:\t\t * that triggered this call.\n5993:\t\t */\n5994:\t\tsmp_mb__after_srcu_read_lock();\n"
} |
| 31/2 | 2026/10/29 06:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 35933 output: 45 thoughts: 672 Thoughts: **My Immediate Thoughts on a Potential KVM Vulnerability** Okay, so I'm digging into this `__kvm_io_bus_write()` function and a potential flaw is really starting to crystallize in my mind. The code initially calls `kvm_iodevice_write`. If that call *fails* with a non-zero return (like `-EFAULT`), the loop continues. Eventually, if nothing handles it, the whole write operation returns `-EOPNOTSUPP`. That signals an exit to userspace via MMIO/PIO, which isn't the problem I'm concerned about. The thing that has my attention is how `put_user` gets used within `ioeventfd_write`. It accesses `p->post_addr`. What if `p->post_addr` *points into kernel memory*? It's all about memory safety, and `put_user` is a potential weak point. I see that `kvm_assign_ioeventfd_idx` attempts to validate `post_addr` at ioctl registration time. The code checks `access_ok` and alignment. Good, but what happens *during execution* when `ioeventfd_write()` is actually invoked? It's called from a vCPU thread during guest execution, meaning `current->mm` is the VM userspace process's memory map. Here's where it gets interesting. What if the `post_addr` is unmapped, `mremap`'d, or `mprotect`'d (especially read-only) *after* the ioctl registration but *before* the posted write? `put_user` will fail with `-EFAULT` in those cases! The `ioeventfd_write` function returns `-EFAULT`. Wait, what if the vCPU thread is running in another thread or task? This could easily cause issues. What if `p->post_addr` is mapped in one process (during the ioctl setup) but a *different* process invokes `KVM_RUN`? KVM VMs can have vCPUs run by threads sharing `mm` or even with file descriptor passing. I can immediately see potential issues, and I keep finding more. What if `post_addr` is near page boundaries (the code checks alignment so this is addressed)? What about races between multiple vCPUs concurrently performing posted writes? Also, what about datamatch combined with a posted write, how would that factor in? This smells like it could be exploitable, and given that syzkaller generates KVM ioctls, this needs to be checked. I'm going to search syzkaller and kernel source to see if there are existing descriptions for `KVM_IOEVENTFD`. |
| 32/2 | 2026/10/29 06:19 | tool | grepper |
0mArgs:{
"Expression": "KVM_IOEVENTFD",
"PathPrefixes": [
"virt/kvm",
"include/uapi/linux/kvm.h"
]
}Results: {
"Output": "include/uapi/linux/kvm.h=641=enum {\n--\ninclude/uapi/linux/kvm.h-650-\ninclude/uapi/linux/kvm.h:651:#define KVM_IOEVENTFD_FLAG_DATAMATCH (1 \u003c\u003c kvm_ioeventfd_flag_nr_datamatch)\ninclude/uapi/linux/kvm.h:652:#define KVM_IOEVENTFD_FLAG_PIO (1 \u003c\u003c kvm_ioeventfd_flag_nr_pio)\ninclude/uapi/linux/kvm.h:653:#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 \u003c\u003c kvm_ioeventfd_flag_nr_deassign)\ninclude/uapi/linux/kvm.h:654:#define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \\\ninclude/uapi/linux/kvm.h-655-\t(1 \u003c\u003c kvm_ioeventfd_flag_nr_virtio_ccw_notify)\ninclude/uapi/linux/kvm.h:656:#define KVM_IOEVENTFD_FLAG_POST_WRITE (1 \u003c\u003c kvm_ioeventfd_flag_nr_post_write)\ninclude/uapi/linux/kvm.h-657-\ninclude/uapi/linux/kvm.h:658:#define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 \u003c\u003c kvm_ioeventfd_flag_nr_max) - 1)\ninclude/uapi/linux/kvm.h-659-\n--\ninclude/uapi/linux/kvm.h=1260=struct kvm_s390_keyop {\n--\ninclude/uapi/linux/kvm.h-1305-#define KVM_SET_BOOT_CPU_ID _IO(KVMIO, 0x78)\ninclude/uapi/linux/kvm.h:1306:#define KVM_IOEVENTFD _IOW(KVMIO, 0x79, struct kvm_ioeventfd)\ninclude/uapi/linux/kvm.h-1307-#define KVM_XEN_HVM_CONFIG _IOW(KVMIO, 0x7a, struct kvm_xen_hvm_config)\n--\nvirt/kvm/eventfd.c=889=static enum kvm_bus ioeventfd_bus_from_flags(__u32 flags)\nvirt/kvm/eventfd.c-890-{\nvirt/kvm/eventfd.c:891:\tif (flags \u0026 KVM_IOEVENTFD_FLAG_PIO)\nvirt/kvm/eventfd.c-892-\t\treturn KVM_PIO_BUS;\nvirt/kvm/eventfd.c:893:\tif (flags \u0026 KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY)\nvirt/kvm/eventfd.c-894-\t\treturn KVM_VIRTIO_CCW_NOTIFY_BUS;\n--\nvirt/kvm/eventfd.c=898=static int kvm_assign_ioeventfd_idx(struct kvm *kvm,\n--\nvirt/kvm/eventfd.c-914-\t */\nvirt/kvm/eventfd.c:915:\tif ((args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_POST_WRITE) \u0026\u0026\nvirt/kvm/eventfd.c-916-\t (!args-\u003elen || !u64_to_user_ptr(args-\u003epost_addr) ||\n--\nvirt/kvm/eventfd.c-936-\t/* The datamatch feature is optional, otherwise this is a wildcard */\nvirt/kvm/eventfd.c:937:\tif (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DATAMATCH)\nvirt/kvm/eventfd.c-938-\t\tp-\u003edatamatch = args-\u003edatamatch;\n--\nvirt/kvm/eventfd.c-941-\nvirt/kvm/eventfd.c:942:\tif (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_POST_WRITE)\nvirt/kvm/eventfd.c-943-\t\tp-\u003epost_addr = u64_to_user_ptr(args-\u003epost_addr);\n--\nvirt/kvm/eventfd.c=978=kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,\n--\nvirt/kvm/eventfd.c-990-\nvirt/kvm/eventfd.c:991:\twildcard = !(args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DATAMATCH);\nvirt/kvm/eventfd.c-992-\n--\nvirt/kvm/eventfd.c=1038=kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\n--\nvirt/kvm/eventfd.c-1060-\t/* check for extra flags that we don't understand */\nvirt/kvm/eventfd.c:1061:\tif (args-\u003eflags \u0026 ~KVM_IOEVENTFD_VALID_FLAG_MASK)\nvirt/kvm/eventfd.c-1062-\t\treturn -EINVAL;\n--\nvirt/kvm/eventfd.c-1064-\t/* ioeventfd with no length can't be combined with DATAMATCH */\nvirt/kvm/eventfd.c:1065:\tif (!args-\u003elen \u0026\u0026 (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DATAMATCH))\nvirt/kvm/eventfd.c-1066-\t\treturn -EINVAL;\n--\nvirt/kvm/eventfd.c=1090=kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1091-{\nvirt/kvm/eventfd.c:1092:\tif (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DEASSIGN)\nvirt/kvm/eventfd.c-1093-\t\treturn kvm_deassign_ioeventfd(kvm, args);\n--\nvirt/kvm/kvm_main.c=5259=static long kvm_vm_ioctl(struct file *filp,\n--\nvirt/kvm/kvm_main.c-5364-\t}\nvirt/kvm/kvm_main.c:5365:\tcase KVM_IOEVENTFD: {\nvirt/kvm/kvm_main.c-5366-\t\tstruct kvm_ioeventfd data;\n"
} |
| 33/2 | 2026/10/29 06:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 38894 output: 40 thoughts: 44 Thoughts: Here's my analysis of the provided information, as if these were my internal thoughts: **Dissecting KVM IO Event Handling** Okay, so we're looking at `kvm_vm_ioctl` and specifically the `KVM_IOEVENTFD` case. This suggests we're dealing with a specific path within KVM related to I/O event file descriptors. My immediate instinct is to pinpoint the exact location of this logic, which is why I'm referencing line 5364 in `virt/kvm/kvm_main.c`. This is where the magic (or the potential problem!) with handling this particular IOCTL code probably lies. I need to understand what this code is doing. The `KVM_IOEVENTFD` ioctl is critical for efficiently managing events from the guest, which is a key part of KVM's performance. Knowing *how* it's handled is crucial to understanding the overall architecture. Now I need to know what's there on that line. |
| 34/2 | 2026/10/29 06:19 | tool | read-file |
0mArgs:{
"File": "virt/kvm/kvm_main.c",
"FirstLine": 5360,
"LineCount": 25
}Results: {
"Contents": "5360:\t\t\tif (copy_from_user(\u0026data, argp, sizeof(data)))\n5361:\t\t\t\tgoto out;\n5362:\t\t\tr = kvm_irqfd(kvm, \u0026data);\n5363:\t\t\tbreak;\n5364:\t\t}\n5365:\t\tcase KVM_IOEVENTFD: {\n5366:\t\t\tstruct kvm_ioeventfd data;\n5367:\t\n5368:\t\t\tr = -EFAULT;\n5369:\t\t\tif (copy_from_user(\u0026data, argp, sizeof(data)))\n5370:\t\t\t\tgoto out;\n5371:\t\t\tr = kvm_ioeventfd(kvm, \u0026data);\n5372:\t\t\tbreak;\n5373:\t\t}\n5374:\t#ifdef __KVM_HAVE_MSI\n5375:\t\tcase KVM_SIGNAL_MSI: {\n5376:\t\t\tstruct kvm_msi msi;\n5377:\t\n5378:\t\t\tr = -EFAULT;\n5379:\t\t\tif (copy_from_user(\u0026msi, argp, sizeof(msi)))\n5380:\t\t\t\tgoto out;\n5381:\t\t\tr = kvm_send_userspace_msi(kvm, \u0026msi);\n5382:\t\t\tbreak;\n5383:\t\t}\n5384:\t#endif\n"
} |
| 35/2 | 2026/10/29 06:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 39564 output: 38 thoughts: 1415 Thoughts: **Focus Symbol Determination for KVM ioeventfd Implementation** Okay, so I'm looking at this `kvm_vm_ioctl` function, and it seems to be directly dispatching `KVM_IOEVENTFD` to `kvm_ioeventfd`, which then triggers the registration via `kvm_assign_ioeventfd`. When a vCPU interacts with MMIO or PIO, it's the `ioeventfd_write` function that gets called. Now, I need to figure out the right *FocusSymbols* for fuzzing this specific functionality, given that the `WorthFuzzing` flag is true. The goal is to target the new or modified logic. First, I looked at all the modified functions in `virt/kvm/eventfd.c`: `ioeventfd_write`, `ioeventfd_in_range`, `kvm_assign_ioeventfd_idx`, `kvm_assign_ioeventfd`, `kvm_deassign_ioeventfd_idx`, and of course the top-level API function, `kvm_ioeventfd`. I also noted the modification to `kvm_vm_ioctl_check_extension_generic` in `kvm_main.c` to support `KVM_CAP_IOEVENTFD_POST_WRITE`. The key is to avoid "ubiquitous lifecycle hot-paths" like memory allocators or generic synchronization. Instead, I need functions that are *specific* to this feature, or the entry points to the subsystem. Let's break it down: `ioeventfd_write` is `static`, but its address is taken for function pointer calls. So it will likely not be inlined and the compiler generally retains the symbol. `kvm_ioeventfd` isn't static, and it's definitely the entry point from `kvm_vm_ioctl`. The static `kvm_assign_ioeventfd` and `kvm_assign_ioeventfd_idx` are more uncertain, as inlining is compiler-dependent. `kvm_deassign_ioeventfd_idx` is also static. And `ioeventfd_in_range` is static and called within `ioeventfd_write`, so inlining is probable. Now, which ones are good focus symbols? Is `kvm_ioeventfd` ubiquitous? No, it's *solely* for the `KVM_IOEVENTFD` ioctl. Same with `ioeventfd_write` - it's the *specific* callback for MMIO/PIO writes to an ioeventfd. `kvm_assign_ioeventfd` and `kvm_assign_ioeventfd_idx` appear targeted to this feature. Are there other relevant functions? `kvm_io_bus_write` is called on *every* MMIO/PIO exit, so that's too ubiquitous. So, I think a targeted list of FocusSymbols will be: * `kvm_ioeventfd` * `ioeventfd_write` * `kvm_assign_ioeventfd_idx` (or maybe `kvm_assign_ioeventfd`) Listing these three functions is very targeted. Finally, regarding configuration, it looks like this is for amd64 architecture, so I'll need `CONFIG_KVM` and either `CONFIG_KVM_INTEL` or `CONFIG_KVM_AMD`. I'll need to check `arch/x86/kvm/Kconfig` to confirm any dependencies. |
| 36/2 | 2026/10/29 06:19 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/Kconfig",
"FirstLine": 40,
"LineCount": 35
}Results: {
"Contents": " 40:\t\tselect KVM_GENERIC_DIRTYLOG_READ_PROTECT\n 41:\t\tselect KVM_VFIO\n 42:\t\tselect HAVE_KVM_PM_NOTIFIER if PM\n 43:\t\tselect KVM_GENERIC_HARDWARE_ENABLING\n 44:\t\tselect KVM_GENERIC_PRE_FAULT_MEMORY\n 45:\t\tselect KVM_WERROR if WERROR\n 46:\t\tselect KVM_GUEST_MEMFD if X86_64\n 47:\t\n 48:\tconfig KVM\n 49:\t\ttristate \"Kernel-based Virtual Machine (KVM) support\"\n 50:\t\tdepends on X86_LOCAL_APIC\n 51:\t\thelp\n 52:\t\t Support hosting fully virtualized guest machines using hardware\n 53:\t\t virtualization extensions. You will need a fairly recent\n 54:\t\t processor equipped with virtualization extensions. You will also\n 55:\t\t need to select one or more of the processor modules below.\n 56:\t\n 57:\t\t This module provides access to the hardware capabilities through\n 58:\t\t a character device node named /dev/kvm.\n 59:\t\n 60:\t\t To compile this as a module, choose M here: the module\n 61:\t\t will be called kvm.\n 62:\t\n 63:\t\t If unsure, say N.\n 64:\t\n 65:\tconfig KVM_WERROR\n 66:\t\tbool \"Compile KVM with -Werror\"\n 67:\t\t# Disallow KVM's -Werror if KASAN is enabled, e.g. to guard against\n 68:\t\t# randomized configs from selecting KVM_WERROR=y, which doesn't play\n 69:\t\t# nice with KASAN. KASAN builds generates warnings for the default\n 70:\t\t# FRAME_WARN, i.e. KVM_WERROR=y with KASAN=y requires special tuning.\n 71:\t\t# Building KVM with -Werror and KASAN is still doable via enabling\n 72:\t\t# the kernel-wide WERROR=y.\n 73:\t\tdepends on KVM_X86 \u0026\u0026 ((EXPERT \u0026\u0026 !KASAN) || WERROR)\n 74:\t\thelp\n"
} |
| 37/2 | 2026/10/29 06:19 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 42366 output: 38 thoughts: 41 Thoughts: Here's my take on the thought process: **Diving into Kconfig for KVM_X86 Dependencies** Okay, so I need to understand the dependencies and selections related to `KVM_X86`. My best bet is to go directly to the source, the `Kconfig` file in the architecture-specific KVM directory. Let's pinpoint the relevant lines, 15 through 40 in `arch/x86/kvm/Kconfig`. That range should give me the core logic behind what `KVM_X86` enables, and what it relies on. I'm expecting this will lay out the building blocks of the x86-specific KVM configuration. From there I'll be able to work backward and forward through any dependencies with greater understanding. |
| 38/2 | 2026/10/29 06:20 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/Kconfig",
"FirstLine": 15,
"LineCount": 30
}Results: {
"Contents": " 15:\t\n 16:\t\t If you say N, all options in this submenu will be skipped and disabled.\n 17:\t\n 18:\tif VIRTUALIZATION\n 19:\t\n 20:\tconfig KVM_X86\n 21:\t\tdef_tristate KVM if (KVM_INTEL != n || KVM_AMD != n)\n 22:\t\tselect KVM_COMMON\n 23:\t\tselect KVM_ELIDE_TLB_FLUSH_IF_YOUNG\n 24:\t\tselect KVM_MMU_LOCKLESS_AGING\n 25:\t\tselect HAVE_KVM_IRQCHIP\n 26:\t\tselect HAVE_KVM_PFNCACHE\n 27:\t\tselect HAVE_KVM_DIRTY_RING_TSO\n 28:\t\tselect HAVE_KVM_DIRTY_RING_ACQ_REL\n 29:\t\tselect HAVE_KVM_IRQ_BYPASS\n 30:\t\tselect HAVE_KVM_IRQ_ROUTING\n 31:\t\tselect VHOST_TASK\n 32:\t\tselect KVM_ASYNC_PF\n 33:\t\tselect USER_RETURN_NOTIFIER\n 34:\t\tselect SCHED_INFO\n 35:\t\tselect PERF_EVENTS\n 36:\t\tselect GUEST_PERF_EVENTS\n 37:\t\tselect PERF_GUEST_MEDIATED_PMU\n 38:\t\tselect HAVE_KVM_CPU_RELAX_INTERCEPT\n 39:\t\tselect VIRT_XFER_TO_GUEST_WORK\n 40:\t\tselect KVM_GENERIC_DIRTYLOG_READ_PROTECT\n 41:\t\tselect KVM_VFIO\n 42:\t\tselect HAVE_KVM_PM_NOTIFIER if PM\n 43:\t\tselect KVM_GENERIC_HARDWARE_ENABLING\n 44:\t\tselect KVM_GENERIC_PRE_FAULT_MEMORY\n"
} |
| 39/2 | 2026/10/29 06:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43097 output: 29 thoughts: 64 Thoughts: Okay, here's my take on those thoughts, framed as my own mental processing: **Virtualization Fundamentals Check** Alright, let's get down to brass tacks. I'm thinking about `VIRTUALIZATION`, specifically how it ties into `KVM`. And, of course, that inevitably brings up the architecture-specific modules, the `KVM_INTEL` and `KVM_AMD` pieces. I need to quickly establish if the `EVENTFD` mechanism is a core part of the standard KVM setup or if it's conditionally included based on some configuration option. To figure that out, I'm going to scan the kernel config files. I'll start with `init/Kconfig` and also check `fs/Kconfig`. That should provide the clearest picture. |
| 40/2 | 2026/10/29 06:20 | tool | grepper |
0mArgs:{
"Expression": "config EVENTFD",
"PathPrefixes": [
"fs",
"init"
]
}Results: {
"Output": "init/Kconfig=1928=config TIMERFD\n--\ninit/Kconfig-1936-\ninit/Kconfig:1937:config EVENTFD\ninit/Kconfig-1938-\tbool \"Enable eventfd() system call\" if EXPERT\n"
} |
| 41/2 | 2026/10/29 06:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 43436 output: 18 thoughts: 33 Thoughts: Okay, here's my interpretation of those thoughts, framed as an expert's internal monologue: **Investigating EVENTFD and KVM Interaction** Right, so `EVENTFD` is enabled by default, which is pretty standard. But the real question is: is `KVM` leveraging it for its select mechanisms? That's what I need to dig into. My intuition says there's probably a dependency, but I need to confirm it. Best way to do that is a quick and dirty cross-reference search. I'll fire up a global search across the codebase and specifically look for instances where `select` is being used in conjunction with `EVENTFD` within the `KVM` context. This will quickly reveal if there's any direct interaction going on, or if `KVM` is handling event notifications in a different way. Should give me the answer quickly. |
| 42/2 | 2026/10/29 06:20 | tool | grepper |
0mArgs:{
"Expression": "select.*EVENTFD"
}Results: {
"Output": "drivers/hv/Kconfig=68=config MSHV_ROOT\n--\ndrivers/hv/Kconfig-77-\tdepends on HYPERV_VMBUS if HYPERV_VMBUS\ndrivers/hv/Kconfig:78:\tselect EVENTFD\ndrivers/hv/Kconfig-79-\tselect VIRT_XFER_TO_GUEST_WORK\n--\ndrivers/vfio/Kconfig=79=config VFIO_VIRQFD\ndrivers/vfio/Kconfig-80-\tbool\ndrivers/vfio/Kconfig:81:\tselect EVENTFD\ndrivers/vfio/Kconfig-82-\tdefault n\n--\ndrivers/vfio/cdx/Kconfig=8=config VFIO_CDX\n--\ndrivers/vfio/cdx/Kconfig-10-\tdepends on CDX_BUS\ndrivers/vfio/cdx/Kconfig:11:\tselect EVENTFD\ndrivers/vfio/cdx/Kconfig-12-\thelp\n--\ndrivers/vfio/fsl-mc/Kconfig=4=config VFIO_FSL_MC\ndrivers/vfio/fsl-mc/Kconfig-5-\ttristate \"VFIO support for QorIQ DPAA2 fsl-mc bus devices\"\ndrivers/vfio/fsl-mc/Kconfig:6:\tselect EVENTFD\ndrivers/vfio/fsl-mc/Kconfig-7-\thelp\n--\ndrivers/virt/acrn/Kconfig=2=config ACRN_HSM\n--\ndrivers/virt/acrn/Kconfig-4-\tdepends on ACRN_GUEST\ndrivers/virt/acrn/Kconfig:5:\tselect EVENTFD\ndrivers/virt/acrn/Kconfig-6-\thelp\n--\ninit/Kconfig=1090=config MEMCG\n--\ninit/Kconfig-1092-\tselect PAGE_COUNTER\ninit/Kconfig:1093:\tselect EVENTFD\ninit/Kconfig-1094-\tselect SLAB_OBJ_EXT\n--\nvirt/kvm/Kconfig=4=config KVM_COMMON\nvirt/kvm/Kconfig-5- bool\nvirt/kvm/Kconfig:6: select EVENTFD\nvirt/kvm/Kconfig-7- select INTERVAL_TREE\n"
} |
| 43/2 | 2026/10/29 06:20 | llm | patch-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 44228 output: 241 thoughts: 835 Thoughts: **Analysis of KVM Patch for Fuzzing Focus** Okay, let's break this down. From a configuration perspective, enabling this code path on an amd64 system requires `KVM` and either `KVM_INTEL` or `KVM_AMD`. The `KVM_COMMON` config selects `EVENTFD`, and `KVM_X86` selects `KVM_COMMON`, so it all lines up. Now, let's confirm the fuzzing worthiness. The patch looks promising: 1. It's touching the core KVM virtualization subsystem. That's always interesting for potential bugs. 2. It introduces a new feature, `KVM_IOEVENTFD_FLAG_POST_WRITE`, in `virt/kvm/eventfd.c`. This suggests new code paths. 3. The functionality is directly exposed to userspace through `/dev/kvm` and the `KVM_IOEVENTFD` ioctl, followed by guest MMIO/PIO writes. This is a crucial aspect for reachability. 4. It introduces memory write logic within the kernel, using `put_user` – a classic area for potential issues. 5. There's a `WARN_ON_ONCE(1)` assertion. Always a good sign, especially if it indicates something unexpected. 6. The error reporting is modified, with a fallback to MMIO/PIO exits on failure. Important to consider error paths. 7. The ABI, specifically `struct kvm_ioeventfd`, is modified and `KVM_CAP_IOEVENTFD_POST_WRITE` extension is added. Important to notice ABI changes. The focus symbols are clear: `ioeventfd_write`, `kvm_assign_ioeventfd_idx`, `kvm_ioeventfd`. They are non-ubiquitous, which is what we need to minimize code surface. The code is not inlinable, especially `ioeventfd_write`, and `kvm_assign_ioeventfd_idx` has multiple call sites. All excellent candidates. The enable configs are as expected: `KVM`, `KVM_INTEL`, and `KVM_AMD`. These are our prerequisites to reach the focus symbols. So, for `set-results`, I can confidently define: * **EnableConfigs**: `["KVM", "KVM_INTEL"]` or `["KVM", "KVM_AMD"]` * **FocusSymbols**: `["kvm_ioeventfd", "kvm_assign_ioeventfd_idx", "ioeventfd_write"]` * **Reasoning**: The patch introduces a new feature with user-space interaction and in-kernel memory writes, new ABI and involves non-ubiquitous KVM functions. * **WorthFuzzing**: `True` I am ready to proceed. |
| 44/2 | 2026/10/29 06:20 | tool | set-results |
0mArgs:{
"EnableConfigs": [
"KVM",
"KVM_INTEL",
"KVM_AMD"
],
"FocusSymbols": [
"kvm_ioeventfd",
"kvm_assign_ioeventfd_idx",
"ioeventfd_write"
],
"Reasoning": "The patch adds support for KVM_IOEVENTFD_FLAG_POST_WRITE (KVM_CAP_IOEVENTFD_POST_WRITE) in KVM's ioeventfd subsystem. It modifies reachable kernel code in virt/kvm/eventfd.c, adding user pointer validation in kvm_assign_ioeventfd_idx(), modifying ioeventfd matching logic, adding put_user() operations to write to guest-specified userspace addresses directly during vCPU MMIO/PIO exit handling in ioeventfd_write(), and adding a defensive WARN_ON_ONCE() assertion for unsupported lengths. This code is reachable from userspace via /dev/kvm ioctls and vCPU guest execution, making it a high-value target for fuzzing.",
"WorthFuzzing": true
}Results: {
"EnableConfigs": [
"KVM",
"KVM_INTEL",
"KVM_AMD"
],
"FocusSymbols": [
"kvm_ioeventfd",
"kvm_assign_ioeventfd_idx",
"ioeventfd_write"
],
"Reasoning": "The patch adds support for KVM_IOEVENTFD_FLAG_POST_WRITE (KVM_CAP_IOEVENTFD_POST_WRITE) in KVM's ioeventfd subsystem. It modifies reachable kernel code in virt/kvm/eventfd.c, adding user pointer validation in kvm_assign_ioeventfd_idx(), modifying ioeventfd matching logic, adding put_user() operations to write to guest-specified userspace addresses directly during vCPU MMIO/PIO exit handling in ioeventfd_write(), and adding a defensive WARN_ON_ONCE() assertion for unsupported lengths. This code is reachable from userspace via /dev/kvm ioctls and vCPU guest execution, making it a high-value target for fuzzing.",
"WorthFuzzing": true
} |
| 45/1 | 2026/10/29 06:20 | action | If |
5mArgs:{
"WorthFuzzing": true
}Results: null |
| 46/2 | 2026/10/29 06:20 | agent | kmsan-evaluator |
5mModel:core Args: null Results: {
"KMSANReasoning": "The patch series introduces KVM_CAP_IOEVENTFD_POST_WRITE, allowing guest MMIO/PIO writes to be posted directly to a userspace address via put_user() before signaling an eventfd.\n\nKMSAN vs KASAN evaluation:\n1. No uninitialized memory copied to userspace: Although put_user() is used in ioeventfd_write(), it writes scalar primitive values (u8, u16, u32, or u64 based on the write length) obtained directly from the guest write buffer via get_unaligned(). There are no structs, structure padding, or uninitialized kernel buffers involved in this transfer.\n2. Structure allocations: The internal struct _ioeventfd is allocated using kzalloc_obj(), ensuring all fields (including the new post_addr pointer) are zero-initialized.\n3. Userspace IOCTL input: The kvm_ioeventfd struct is an input-only argument (_IOW) to KVM_IOEVENTFD copied via copy_from_user(); no kernel data or uninitialized fields are copied back to user space.\n4. Error modes: Any potential bugs introduced by this patch would be related to user pointer validation, concurrency, or device registration logic, which are covered by standard kernel bug detectors or KASAN.\n\nTherefore, the changes do not introduce or expose any uninitialized memory risks, and a dedicated KMSAN fuzzing session is not warranted.",
"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 fdbb5357ab9de3115f5cde27eaaf6974e5b34459
Author: syz-cluster <triage@syzkaller.com>
Date: Thu Oct 29 06:16:49 2026 +0000
syz-cluster: applied patch under review
diff --git a/Documentation/virt/kvm/api.rst b/Documentation/virt/kvm/api.rst
index e95c9cda085d94..a5e61ba7eaf583 100644
--- a/Documentation/virt/kvm/api.rst
+++ b/Documentation/virt/kvm/api.rst
@@ -2132,7 +2132,9 @@ provided event instead of triggering an exit.
__u32 len; /* 0, 1, 2, 4, or 8 bytes */
__s32 fd;
__u32 flags;
- __u8 pad[36];
+ __u32 pad0;
+ __u64 post_addr; /* address to write to if POST_WRITE is set */
+ __u8 pad[24];
};
For the special case of virtio-ccw devices on s390, the ioevent is matched
@@ -2145,6 +2147,7 @@ The following flags are defined::
#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign)
#define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \
(1 << kvm_ioeventfd_flag_nr_virtio_ccw_notify)
+ #define KVM_IOEVENTFD_FLAG_POST_WRITE (1 << kvm_ioeventfd_flag_nr_post_write)
If datamatch flag is set, the event will be signaled only if the written value
to the registered address is equal to datamatch in struct kvm_ioeventfd.
@@ -2157,6 +2160,15 @@ the kernel will ignore the length of guest write and may get a faster vmexit.
The speedup may only apply to specific architectures, but the ioeventfd will
work anyway.
+With KVM_IOEVENTFD_FLAG_POST_WRITE, the value being written is copied to the
+userspace address specified by post_addr, and the eventfd is signaled. The
+copy is guaranteed to complete before the eventfd is signaled, so a userspace
+reader that wakes on the eventfd will observe the written value. When multiple
+vCPUs write to the same ioeventfd concurrently, the value at post_addr reflects
+one of the writes. If the copy to post_addr fails (e.g. the memory has been
+unmapped), the eventfd is not signaled and the write is reported to userspace
+as a regular MMIO/PIO exit.
+
4.60 KVM_DIRTY_TLB
------------------
diff --git a/include/uapi/linux/kvm.h b/include/uapi/linux/kvm.h
index 8dff2fc1972e97..07ac3142ce17f6 100644
--- a/include/uapi/linux/kvm.h
+++ b/include/uapi/linux/kvm.h
@@ -644,6 +644,7 @@ enum {
kvm_ioeventfd_flag_nr_deassign,
kvm_ioeventfd_flag_nr_virtio_ccw_notify,
kvm_ioeventfd_flag_nr_fast_mmio,
+ kvm_ioeventfd_flag_nr_post_write,
kvm_ioeventfd_flag_nr_max,
};
@@ -652,6 +653,7 @@ enum {
#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 << kvm_ioeventfd_flag_nr_deassign)
#define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \
(1 << kvm_ioeventfd_flag_nr_virtio_ccw_notify)
+#define KVM_IOEVENTFD_FLAG_POST_WRITE (1 << kvm_ioeventfd_flag_nr_post_write)
#define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 << kvm_ioeventfd_flag_nr_max) - 1)
@@ -661,7 +663,9 @@ struct kvm_ioeventfd {
__u32 len; /* 1, 2, 4, or 8 bytes; or 0 to ignore length */
__s32 fd;
__u32 flags;
- __u8 pad[36];
+ __u32 pad0;
+ __u64 post_addr; /* address to write to if POST_WRITE is set */
+ __u8 pad[24];
};
#define KVM_X86_DISABLE_EXITS_MWAIT (1 << 0)
@@ -1000,6 +1004,7 @@ struct kvm_enable_cap {
#define KVM_CAP_PPC_COMPAT_CAPS 250
#define KVM_CAP_ARM_PMU_V3_STRICT 251
#define KVM_CAP_GUEST_MEMFD_MEMORY_ATTRIBUTES 252
+#define KVM_CAP_IOEVENTFD_POST_WRITE 253
struct kvm_irq_routing_irqchip {
__u32 irqchip;
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index 3cb35a6963d513..38860f2f08b0c8 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -92,6 +92,7 @@ TEST_GEN_PROGS_x86 += x86/hyperv_features
TEST_GEN_PROGS_x86 += x86/hyperv_ipi
TEST_GEN_PROGS_x86 += x86/hyperv_svm_test
TEST_GEN_PROGS_x86 += x86/hyperv_tlb_flush
+TEST_GEN_PROGS_x86 += x86/ioeventfd_posted_write_test
TEST_GEN_PROGS_x86 += x86/kvm_clock_test
TEST_GEN_PROGS_x86 += x86/kvm_pv_test
TEST_GEN_PROGS_x86 += x86/kvm_buslock_test
diff --git a/tools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c b/tools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c
new file mode 100644
index 00000000000000..f429f875c023e2
--- /dev/null
+++ b/tools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c
@@ -0,0 +1,632 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ioeventfd_posted_write_test.c - Tests for KVM_IOEVENTFD_FLAG_POST_WRITE.
+ *
+ * Tests that when KVM_IOEVENTFD_FLAG_POST_WRITE is set and the MMIO/PIO
+ * address is written to, the value is copied to the user-provided address
+ * and the eventfd is signaled. Also tests negative cases and interactions
+ * with DATAMATCH.
+ *
+ * Copyright Nutanix, 2026
+ *
+ * Author: Thanos Makatos <thanos.makatos@nutanix.com>
+ */
+
+#include <errno.h>
+#include <poll.h>
+#include <string.h>
+
+#include "kvm_util.h"
+#include "processor.h"
+#include "ucall_common.h"
+
+#define MMIO_GPA (1UL << 30)
+#define PIO_PORT 0xe000
+#define TEST_VAL 0xDEADBEEFCAFEBABEULL
+#define MATCH_VAL 0x42U
+#define NOMATCH_VAL (MATCH_VAL + 1)
+#define POISON_VAL 0xFFFFFFFFU
+
+/*
+ * Check that the most recent vCPU exit is a ucall (delivered as KVM_EXIT_IO
+ * on x86) matching @expected_cmd. The caller must have already called
+ * vcpu_run().
+ *
+ * @expected_cmd: UCALL_SYNC, UCALL_DONE, etc.
+ * @expected_stage: for UCALL_SYNC, the stage number passed by GUEST_SYNC().
+ * Ignored for other ucall types.
+ *
+ * Aborts the test on UCALL_ABORT (a guest-side assertion failure).
+ */
+static void assert_ucall(struct kvm_vcpu *vcpu, uint64_t expected_cmd,
+ uint64_t expected_stage)
+{
+ struct ucall uc;
+
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+
+ switch (get_ucall(vcpu, &uc)) {
+ case UCALL_ABORT:
+ REPORT_GUEST_ASSERT(uc);
+ break;
+ case UCALL_SYNC:
+ TEST_ASSERT(expected_cmd == UCALL_SYNC,
+ "got UCALL_SYNC, expected %lu",
+ expected_cmd);
+ TEST_ASSERT(uc.args[1] == expected_stage,
+ "expected stage %lu, got %lu",
+ expected_stage, uc.args[1]);
+ break;
+ case UCALL_DONE:
+ TEST_ASSERT(expected_cmd == UCALL_DONE,
+ "got UCALL_DONE, expected %lu",
+ expected_cmd);
+ break;
+ default:
+ TEST_FAIL("unexpected ucall %lu", uc.cmd);
+ }
+}
+
+/*
+ * Verify that KVM_IOEVENTFD rejects invalid POST_WRITE configurations:
+ * - len=0: the kernel needs a non-zero length to know how many bytes to copy.
+ * - post_addr=NULL: there is no destination for the copy.
+ * - post_addr outside the process address space: access_ok() rejects it.
+ * - post_addr unaligned: IS_ALIGNED() rejects it
+ * All three must fail with EINVAL.
+ */
+static void test_post_write_negative(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vm *vm;
+ uint64_t dummy;
+ int ret;
+ int fd;
+
+ vm = vm_create_barebones();
+ fd = kvm_new_eventfd();
+
+ /* length cannot be zero */
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = 0,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&dummy,
+ };
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(ret && errno == EINVAL,
+ "len=0: expected EINVAL, got ret=%d errno=%d", ret, errno);
+
+ /* post_addr cannot be NULL */
+ ioeventfd.len = 4;
+ ioeventfd.post_addr = 0ULL;
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(ret && errno == EINVAL,
+ "NULL post_addr: expected EINVAL, got ret=%d errno=%d",
+ ret, errno);
+
+ /* bogus post_addr */
+ ioeventfd.post_addr = (u64)0xdeaddeaddeaddeadULL;
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(ret && errno == EINVAL,
+ "bad post_addr: expected EINVAL, got ret=%d errno=%d",
+ ret, errno);
+
+ /* unaligned post_addr */
+ ioeventfd.post_addr = (uintptr_t)((char *)&dummy + 1);
+ ioeventfd.len = sizeof dummy;
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(ret && errno == EINVAL,
+ "bad post_addr: expected EINVAL, got ret=%d errno=%d",
+ ret, errno);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+#define DEFINE_GUEST_WRITE_FN(suffix, type) \
+static void guest_code_w##suffix(void) { \
+ *(volatile type *)MMIO_GPA = (type)TEST_VAL; \
+ GUEST_DONE(); \
+}
+
+DEFINE_GUEST_WRITE_FN(1, uint8_t)
+DEFINE_GUEST_WRITE_FN(2, uint16_t)
+DEFINE_GUEST_WRITE_FN(4, uint32_t)
+DEFINE_GUEST_WRITE_FN(8, uint64_t)
+
+/*
+ * Verify that ioeventfd_write copies exactly @width bytes to post_addr for
+ * each supported MMIO write width (1, 2, 4, 8). The guest writes the low
+ * @width bytes of TEST_VAL; the host checks that exactly those bytes land
+ * at post_addr and the eventfd is signaled.
+ */
+static void test_post_write_width(int width, void (*guest_fn)(void))
+{
+ uint64_t actual, expected, count;
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ int fd, ret;
+
+ /* need to initialize to 0 because the guest writes the low @width bytes */
+ actual = 0;
+ expected = 0;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_fn);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+
+ fd = kvm_new_eventfd();
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = width,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ ret = read(fd, &count, sizeof(count));
+ TEST_ASSERT(ret == sizeof(count),
+ "eventfd read failed: ret=%d errno=%d", ret, errno);
+
+ memcpy(&expected, &(uint64_t){TEST_VAL}, width);
+ TEST_ASSERT_EQ(actual, expected);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_datamatch(void)
+{
+ *(volatile uint32_t *)MMIO_GPA = MATCH_VAL;
+ GUEST_SYNC(1);
+ *(volatile uint32_t *)MMIO_GPA = NOMATCH_VAL;
+ GUEST_SYNC(2);
+ GUEST_DONE();
+}
+
+/*
+ * Test the interaction between DATAMATCH and POST_WRITE. When both flags are
+ * set, ioeventfd_write should only fire (signal eventfd + copy value) when the
+ * written value matches datamatch. A non-matching write must leave the eventfd
+ * unsignaled and post_addr untouched, and fall through to KVM_EXIT_MMIO.
+ */
+static void test_post_write_datamatch(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_run *run;
+ struct kvm_vm *vm;
+ struct pollfd pfd;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_datamatch);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+ run = vcpu->run;
+
+ fd = kvm_new_eventfd();
+ pfd = (struct pollfd){ .fd = fd, .events = POLLIN };
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .datamatch = MATCH_VAL,
+ .addr = MMIO_GPA,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE |
+ KVM_IOEVENTFD_FLAG_DATAMATCH,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ /*
+ * Guest writes MATCH_VAL → ioeventfd fires (value copied, eventfd
+ * signaled), vCPU continues, then GUEST_SYNC(1).
+ */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 1);
+ TEST_ASSERT(read(fd, &count, sizeof(count)) == sizeof(count),
+ "eventfd read failed: errno=%d", errno);
+ TEST_ASSERT_EQ(actual, MATCH_VAL);
+
+ actual = POISON_VAL;
+
+ /*
+ * Guest writes NOMATCH_VAL → ioeventfd_in_range() returns false, bus
+ * returns -EOPNOTSUPP → KVM_EXIT_MMIO to userspace.
+ */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);
+ TEST_ASSERT(run->mmio.is_write, "expected MMIO write");
+ TEST_ASSERT(run->mmio.phys_addr == MMIO_GPA,
+ "expected MMIO at 0x%lx, got 0x%llx",
+ MMIO_GPA, run->mmio.phys_addr);
+
+ /* Re-enter: KVM completes the MMIO, guest runs to GUEST_SYNC(2). */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 2);
+
+ TEST_ASSERT(poll(&pfd, 1, 0) == 0,
+ "eventfd should not be signaled after non-matching write");
+ TEST_ASSERT_EQ(actual, (uint32_t)POISON_VAL);
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_multi(void)
+{
+ *(volatile uint32_t *)MMIO_GPA = 0x11111111;
+ GUEST_SYNC(1);
+ *(volatile uint32_t *)MMIO_GPA = 0x22222222;
+ GUEST_SYNC(2);
+ *(volatile uint32_t *)MMIO_GPA = 0x33333333;
+ GUEST_SYNC(3);
+ GUEST_DONE();
+}
+
+/*
+ * Verify that post_addr is updated on every MMIO write, not just the first.
+ * The guest writes three distinct values in sequence; the host checks after
+ * each one that post_addr holds the latest value and the eventfd is signaled
+ * each time.
+ */
+static void test_post_write_multi(void)
+{
+ static const uint32_t expected[] = {
+ 0x11111111, 0x22222222, 0x33333333,
+ };
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret, i;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_multi);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+
+ fd = kvm_new_eventfd();
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ for (i = 0; i < ARRAY_SIZE(expected); i++) {
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, i + 1);
+ TEST_ASSERT(read(fd, &count, sizeof(count)) == sizeof(count),
+ "eventfd read failed: errno=%d", errno);
+ TEST_ASSERT_EQ(actual, expected[i]);
+ }
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_multi_nosync(void)
+{
+ *(volatile uint32_t *)MMIO_GPA = 0x11111111;
+ *(volatile uint32_t *)MMIO_GPA = 0x22222222;
+ *(volatile uint32_t *)MMIO_GPA = 0x33333333;
+ GUEST_DONE();
+}
+
+/*
+ * Variant of the multi-write test where the guest performs three consecutive
+ * MMIO writes with no GUEST_SYNC in between. All three are handled in-kernel
+ * by ioeventfd before the vCPU exits at GUEST_DONE. Verify that:
+ * - post_addr reflects the last written value (0x33333333).
+ * - A single eventfd read() returns a counter of 3 (one signal per write).
+ */
+static void test_post_write_multi_nosync(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_multi_nosync);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+
+ fd = kvm_new_eventfd();
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ ret = read(fd, &count, sizeof(count));
+ TEST_ASSERT(ret == sizeof(count),
+ "eventfd read failed: ret=%d errno=%d", ret, errno);
+ TEST_ASSERT_EQ(count, (uint64_t)3);
+ TEST_ASSERT_EQ(actual, (uint32_t)0x33333333);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_deassign(void)
+{
+ *(volatile uint32_t *)MMIO_GPA = MATCH_VAL;
+ GUEST_SYNC(1);
+ *(volatile uint32_t *)MMIO_GPA = MATCH_VAL;
+ GUEST_DONE();
+}
+
+/*
+ * Verify that deassigning an ioeventfd with POST_WRITE fully removes it from
+ * the I/O bus.
+ */
+static void test_post_write_deassign(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_run *run;
+ struct kvm_vm *vm;
+ struct pollfd pfd;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_deassign);
+ virt_map(vm, MMIO_GPA, MMIO_GPA, 1);
+ run = vcpu->run;
+
+ fd = kvm_new_eventfd();
+ pfd = (struct pollfd){ .fd = fd, .events = POLLIN };
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = MMIO_GPA,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD assign failed: %s", strerror(errno));
+
+ /*
+ * Guest writes MATCH_VAL → ioeventfd fires, then GUEST_SYNC(1).
+ */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 1);
+ TEST_ASSERT(read(fd, &count, sizeof(count)) == sizeof(count),
+ "eventfd read failed: errno=%d", errno);
+ TEST_ASSERT_EQ(actual, MATCH_VAL);
+
+ /* Deassign the ioeventfd. */
+ ioeventfd.flags |= KVM_IOEVENTFD_FLAG_DEASSIGN;
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD deassign failed: %s", strerror(errno));
+
+ actual = POISON_VAL;
+
+ /*
+ * Guest writes MATCH_VAL again → no handler on the bus →
+ * KVM_EXIT_MMIO to userspace.
+ */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_MMIO);
+ TEST_ASSERT(run->mmio.is_write, "expected MMIO write");
+ TEST_ASSERT(run->mmio.phys_addr == MMIO_GPA,
+ "expected MMIO at 0x%lx, got 0x%llx",
+ MMIO_GPA, run->mmio.phys_addr);
+
+ /* Re-enter: KVM completes MMIO, guest runs to GUEST_DONE. */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ TEST_ASSERT(poll(&pfd, 1, 0) == 0,
+ "eventfd should not be signaled after deassign");
+ TEST_ASSERT_EQ(actual, (uint32_t)POISON_VAL);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_pio(void)
+{
+ outl(PIO_PORT, (uint32_t)TEST_VAL);
+ GUEST_DONE();
+}
+
+/*
+ * Verify that POST_WRITE works on the PIO bus (KVM_PIO_BUS), not just MMIO.
+ * The guest does an outl to PIO_PORT; the host checks that the written value
+ * is copied to post_addr and the eventfd is signaled.
+ */
+static void test_post_write_pio(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_vm *vm;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_pio);
+
+ fd = kvm_new_eventfd();
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .addr = PIO_PORT,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE |
+ KVM_IOEVENTFD_FLAG_PIO,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ ret = read(fd, &count, sizeof(count));
+ TEST_ASSERT(ret == sizeof(count),
+ "eventfd read failed: ret=%d errno=%d", ret, errno);
+
+ TEST_ASSERT_EQ(actual, (uint32_t)TEST_VAL);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+static void guest_code_pio_datamatch(void)
+{
+ outl(PIO_PORT, MATCH_VAL);
+ GUEST_SYNC(1);
+ outl(PIO_PORT, NOMATCH_VAL);
+ GUEST_SYNC(2);
+ GUEST_DONE();
+}
+
+/*
+ * Test POST_WRITE + PIO + DATAMATCH together. When all three flags are set,
+ * the ioeventfd should only fire when the outl value matches datamatch.
+ * A non-matching outl must fall through to KVM_EXIT_IO (port I/O exit),
+ * leaving the eventfd unsignaled and post_addr untouched.
+ */
+static void test_post_write_pio_datamatch(void)
+{
+ struct kvm_ioeventfd ioeventfd;
+ struct kvm_vcpu *vcpu;
+ struct kvm_run *run;
+ struct kvm_vm *vm;
+ struct pollfd pfd;
+ uint64_t count;
+ uint32_t actual;
+ int fd, ret;
+
+ actual = POISON_VAL;
+
+ vm = vm_create_with_one_vcpu(&vcpu, guest_code_pio_datamatch);
+ run = vcpu->run;
+
+ fd = kvm_new_eventfd();
+ pfd = (struct pollfd){ .fd = fd, .events = POLLIN };
+
+ ioeventfd = (struct kvm_ioeventfd) {
+ .datamatch = MATCH_VAL,
+ .addr = PIO_PORT,
+ .len = 4,
+ .fd = fd,
+ .flags = KVM_IOEVENTFD_FLAG_POST_WRITE |
+ KVM_IOEVENTFD_FLAG_PIO |
+ KVM_IOEVENTFD_FLAG_DATAMATCH,
+ .post_addr = (u64)&actual,
+ };
+
+ ret = __vm_ioctl(vm, KVM_IOEVENTFD, &ioeventfd);
+ TEST_ASSERT(!ret, "KVM_IOEVENTFD failed: %s", strerror(errno));
+
+ /*
+ * Guest does outl MATCH_VAL → ioeventfd fires, then GUEST_SYNC(1).
+ */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 1);
+ TEST_ASSERT(read(fd, &count, sizeof(count)) == sizeof(count),
+ "eventfd read failed: errno=%d", errno);
+ TEST_ASSERT_EQ(actual, MATCH_VAL);
+
+ actual = POISON_VAL;
+
+ /*
+ * Guest does outl NOMATCH_VAL → no match → KVM_EXIT_IO.
+ */
+ vcpu_run(vcpu);
+ TEST_ASSERT_KVM_EXIT_REASON(vcpu, KVM_EXIT_IO);
+ TEST_ASSERT(run->io.direction == KVM_EXIT_IO_OUT,
+ "expected PIO write");
+ TEST_ASSERT(run->io.port == PIO_PORT,
+ "expected PIO at 0x%x, got 0x%x",
+ PIO_PORT, run->io.port);
+
+ /* Re-enter: guest continues to GUEST_SYNC(2). */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_SYNC, 2);
+
+ TEST_ASSERT(poll(&pfd, 1, 0) == 0,
+ "eventfd should not be signaled after non-matching PIO write");
+ TEST_ASSERT_EQ(actual, (uint32_t)POISON_VAL);
+
+ /* GUEST_DONE */
+ vcpu_run(vcpu);
+ assert_ucall(vcpu, UCALL_DONE, 0);
+
+ close(fd);
+ kvm_vm_free(vm);
+}
+
+int main(void)
+{
+ TEST_REQUIRE(kvm_check_cap(KVM_CAP_IOEVENTFD_POST_WRITE));
+
+ test_post_write_negative();
+
+ test_post_write_width(1, guest_code_w1);
+ test_post_write_width(2, guest_code_w2);
+ test_post_write_width(4, guest_code_w4);
+ test_post_write_width(8, guest_code_w8);
+
+ test_post_write_datamatch();
+
+ test_post_write_multi();
+
+ test_post_write_multi_nosync();
+
+ test_post_write_deassign();
+
+ test_post_write_pio();
+ test_post_write_pio_datamatch();
+
+ return 0;
+}
diff --git a/virt/kvm/eventfd.c b/virt/kvm/eventfd.c
index f43b8b25bc8549..6a221184b54a7c 100644
--- a/virt/kvm/eventfd.c
+++ b/virt/kvm/eventfd.c
@@ -743,6 +743,7 @@ struct _ioeventfd {
struct kvm_io_device dev;
u8 bus_idx;
bool wildcard;
+ void __user *post_addr;
};
static inline struct _ioeventfd *
@@ -759,11 +760,32 @@ ioeventfd_release(struct _ioeventfd *p)
kfree(p);
}
+#define ioeventfd_get_val(__ptr, __len, __unsupported_len) \
+({ \
+ u64 __val; \
+ \
+ switch (__len) { \
+ case 1: \
+ __val = get_unaligned((u8 *)__ptr); \
+ break; \
+ case 2: \
+ __val = get_unaligned((u16 *)__ptr); \
+ break; \
+ case 4: \
+ __val = get_unaligned((u32 *)__ptr); \
+ break; \
+ case 8: \
+ __val = get_unaligned((u64 *)__ptr); \
+ break; \
+ default: \
+ goto __unsupported_len; \
+ } \
+ __val; \
+})
+
static bool
ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)
{
- u64 _val;
-
if (addr != p->addr)
/* address must be precise for a hit */
return false;
@@ -781,24 +803,10 @@ ioeventfd_in_range(struct _ioeventfd *p, gpa_t addr, int len, const void *val)
return true;
/* otherwise, we have to actually compare the data */
- switch (len) {
- case 1:
- _val = get_unaligned((u8 *)val);
- break;
- case 2:
- _val = get_unaligned((u16 *)val);
- break;
- case 4:
- _val = get_unaligned((u32 *)val);
- break;
- case 8:
- _val = get_unaligned((u64 *)val);
- break;
- default:
- return false;
- }
+ return ioeventfd_get_val(val, len, unsupported_len) == p->datamatch;
- return _val == p->datamatch;
+unsupported_len:
+ return false;
}
/* MMIO/PIO writes trigger an event if the addr/val match */
@@ -811,8 +819,36 @@ ioeventfd_write(struct kvm_vcpu *vcpu, struct kvm_io_device *this, gpa_t addr,
if (!ioeventfd_in_range(p, addr, len, val))
return -EOPNOTSUPP;
+ if (p->post_addr) {
+ u64 _val = ioeventfd_get_val(val, len, unsupported_len);
+ int r;
+
+ switch (len) {
+ case 1:
+ r = put_user(_val, (u8 __user *)p->post_addr);
+ break;
+ case 2:
+ r = put_user(_val, (u16 __user *)p->post_addr);
+ break;
+ case 4:
+ r = put_user(_val, (u32 __user *)p->post_addr);
+ break;
+ case 8:
+ r = put_user(_val, (u64 __user *)p->post_addr);
+ break;
+ default:
+ WARN_ON_ONCE(1);
+ r = 0;
+ goto unsupported_len;
+ }
+ if (r)
+ return r;
+ }
+
eventfd_signal(p->eventfd);
return 0;
+unsupported_len:
+ return -EOPNOTSUPP;
}
/*
@@ -872,6 +908,19 @@ static int kvm_assign_ioeventfd_idx(struct kvm *kvm,
if (IS_ERR(eventfd))
return PTR_ERR(eventfd);
+ /*
+ * Disallow posting to virtual address 0 so that KVM can treat
+ * a NULL user pointer as "no posted write".
+ */
+ if ((args->flags & KVM_IOEVENTFD_FLAG_POST_WRITE) &&
+ (!args->len || !u64_to_user_ptr(args->post_addr) ||
+ args->post_addr != untagged_addr(args->post_addr) ||
+ !access_ok(u64_to_user_ptr(args->post_addr), args->len) ||
+ !IS_ALIGNED(args->post_addr, args->len))) {
+ ret = -EINVAL;
+ goto fail;
+ }
+
p = kzalloc_obj(*p, GFP_KERNEL_ACCOUNT);
if (!p) {
ret = -ENOMEM;
@@ -890,6 +939,9 @@ static int kvm_assign_ioeventfd_idx(struct kvm *kvm,
else
p->wildcard = true;
+ if (args->flags & KVM_IOEVENTFD_FLAG_POST_WRITE)
+ p->post_addr = u64_to_user_ptr(args->post_addr);
+
mutex_lock(&kvm->slots_lock);
/* Verify that there isn't a match already */
@@ -941,6 +993,11 @@ kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,
mutex_lock(&kvm->slots_lock);
list_for_each_entry(p, &kvm->ioeventfds, list) {
+ /*
+ * No need to match post_addr, ioeventfd_check_collision
+ * prevents duplicate registrations that only differ by
+ * post_addr.
+ */
if (p->bus_idx != bus_idx ||
p->eventfd != eventfd ||
p->addr != args->addr ||
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index cf4cb586f1407b..7c8b013fad7778 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -4982,6 +4982,7 @@ static int kvm_vm_ioctl_check_extension_generic(struct kvm *kvm, long arg)
case KVM_CAP_IRQFD:
#endif
case KVM_CAP_IOEVENTFD_ANY_LENGTH:
+ case KVM_CAP_IOEVENTFD_POST_WRITE:
case KVM_CAP_CHECK_EXTENSION_VM:
case KVM_CAP_ENABLE_CAP_VM:
case KVM_CAP_HALT_POLL:
|
| 47/3 | 2026/10/29 06:20 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 13204 output: 32 thoughts: 698 Thoughts: **KMSAN Fuzzing Session Evaluation** Okay, so I need to decide if this patch series warrants a dedicated KMSAN fuzzing session. I need to be efficient; there's no point in KMSAN running if standard bug detectors like KASAN, LOCKDEP, or the other sanitizers would already catch the issue. The goal here is to specifically look for uninitialized memory reads and kernel-to-user info-leaks. The rules are pretty clear: KMSAN is relevant if the patch touches kernel structures sent to userspace where fields might be uninitialized, if there's conditional logic based on potentially uninitialized variables, if complex data structures are allocated where uninitialized fields could be read later, or if the patch introduces changes that allow reading of uninitialized bytes via bounds checks or logic. Conversely, if it's primarily about out-of-bounds access, overflows, NULL pointer dereferences, deadlocks, or use-after-free, then KMSAN is likely unnecessary. Also, if everything is initialized properly, then it's a no-go. Now, let's dive into the diff. The `Documentation/virt/kvm/api.rst` file is the first thing. The change appears to be in the `struct kvm_ioeventfd`, which is interesting. It *seems* the only change is to how the padding is arranged within the struct. I need to make sure I am not missing anything, but it looks like the size didn't change, which is good. Okay, let's double-check the definition in `include/uapi/linux/kvm.h`. Yep, it confirms that it's a field in a `struct kvm_ioeventfd` that is changed. Now, the critical question: is this structure copied *to* user space, *from* user space, or both? This is the core of the matter! If it's *from* user space, KMSAN is probably not needed. If it's *to* user space, then there's a risk of info-leaks, and I need to investigate further. Time to trace the usage of `struct kvm_ioeventfd` in the kernel code. This will tell me if I need KMSAN! |
| 48/3 | 2026/10/29 06:21 | tool | grepper |
0mArgs:{
"Expression": "kvm_ioeventfd",
"PathPrefixes": [
"virt/kvm/"
]
}Results: {
"Output": "virt/kvm/eventfd.c=898=static int kvm_assign_ioeventfd_idx(struct kvm *kvm,\nvirt/kvm/eventfd.c-899-\t\t\t\tenum kvm_bus bus_idx,\nvirt/kvm/eventfd.c:900:\t\t\t\tstruct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-901-{\n--\nvirt/kvm/eventfd.c=978=kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,\nvirt/kvm/eventfd.c:979:\t\t\t struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-980-{\n--\nvirt/kvm/eventfd.c-1025-\nvirt/kvm/eventfd.c:1026:static int kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1027-{\n--\nvirt/kvm/eventfd.c=1037=static int\nvirt/kvm/eventfd.c:1038:kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1039-{\n--\nvirt/kvm/eventfd.c=1089=int\nvirt/kvm/eventfd.c:1090:kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1091-{\n--\nvirt/kvm/kvm_main.c=5259=static long kvm_vm_ioctl(struct file *filp,\n--\nvirt/kvm/kvm_main.c-5365-\tcase KVM_IOEVENTFD: {\nvirt/kvm/kvm_main.c:5366:\t\tstruct kvm_ioeventfd data;\nvirt/kvm/kvm_main.c-5367-\n--\nvirt/kvm/kvm_main.c-5370-\t\t\tgoto out;\nvirt/kvm/kvm_main.c:5371:\t\tr = kvm_ioeventfd(kvm, \u0026data);\nvirt/kvm/kvm_main.c-5372-\t\tbreak;\n"
} |
| 49/3 | 2026/10/29 06:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 15033 output: 40 thoughts: 33 Thoughts: Okay, let's dive into this. Here's how I'd approach that kernel code analysis, as though these are my internal thoughts: **Analyzing `kvm_vm_ioctl` Around Line 5365 in `virt/kvm/kvm_main.c`** Alright, time to get my hands dirty. My initial focus is on `kvm_vm_ioctl`, specifically around line 5365 in `virt/kvm/kvm_main.c`. I need to figure out what's happening in this particular section of code. Given my experience, I'm expecting this `ioctl` handler to be dealing with some sort of management or control operation related to the KVM virtual machine. I'll need to see what specific command it's handling at that line number. I'll also want to look for any interesting data structures being passed in or manipulated at that point. I'm expecting to see calls to other KVM functions which means I need to quickly grasp its context within the KVM system. Are we dealing with memory allocation, device assignment, CPU configuration, or something else entirely? A good start will be to understand the overall control flow and if there are any error conditions or potentially problematic areas that deserve more scrutiny. I will be looking for resource locks in the code. I am particularly interested in any synchronization primitives as those are important for KVM performance and reliability. |
| 50/3 | 2026/10/29 06:21 | tool | read-file |
0mArgs:{
"File": "virt/kvm/kvm_main.c",
"FirstLine": 5360,
"LineCount": 20
}Results: {
"Contents": "5360:\t\t\tif (copy_from_user(\u0026data, argp, sizeof(data)))\n5361:\t\t\t\tgoto out;\n5362:\t\t\tr = kvm_irqfd(kvm, \u0026data);\n5363:\t\t\tbreak;\n5364:\t\t}\n5365:\t\tcase KVM_IOEVENTFD: {\n5366:\t\t\tstruct kvm_ioeventfd data;\n5367:\t\n5368:\t\t\tr = -EFAULT;\n5369:\t\t\tif (copy_from_user(\u0026data, argp, sizeof(data)))\n5370:\t\t\t\tgoto out;\n5371:\t\t\tr = kvm_ioeventfd(kvm, \u0026data);\n5372:\t\t\tbreak;\n5373:\t\t}\n5374:\t#ifdef __KVM_HAVE_MSI\n5375:\t\tcase KVM_SIGNAL_MSI: {\n5376:\t\t\tstruct kvm_msi msi;\n5377:\t\n5378:\t\t\tr = -EFAULT;\n5379:\t\t\tif (copy_from_user(\u0026msi, argp, sizeof(msi)))\n"
} |
| 51/3 | 2026/10/29 06:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 15712 output: 55 thoughts: 479 Thoughts: **KVM IOEventFD Write Operation Analysis** Okay, so I'm looking at `KVM_IOEVENTFD` and something isn't sitting right. My initial understanding was that this only involves a `copy_from_user` operation, which is fine, but I need to understand what this patch is actually doing. Let's dig into the code, specifically `virt/kvm/eventfd.c`. I'm zeroing in on this `ioeventfd_write` function. Hmm, it looks like this function is triggered on MMIO/PIO writes, but only if the address and value match some pre-defined criteria. And look here, if `p->post_addr` is set, there is a `put_user` call! This is unexpected! I thought this was just a read operation! I have to understand the source of `val`. Where does `val` in `ioeventfd_write` come from? I need to trace this back. Let's see...I'll grep for how `ioeventfd_write` is called. Ah, it is a callback function on `struct kvm_io_device`. Interesting. Now I need to understand what's writing the data to the bus. I'm going to look for where `kvm_io_bus_write` or similar is being called. |
| 52/3 | 2026/10/29 06:21 | tool | grepper |
0mArgs:{
"Expression": "kvm_iodevice_write|kvm_io_bus_write",
"PathPrefixes": [
"virt/kvm/",
"arch/x86/kvm/"
]
}Results: {
"Output": "arch/x86/kvm/svm/svm.c=2014=static int npf_interception(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/svm/svm.c-2042-\t\tif (nrips \u0026\u0026 svm-\u003evmcb-\u003econtrol.next_rip \u0026\u0026\narch/x86/kvm/svm/svm.c:2043:\t\t !kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {\narch/x86/kvm/svm/svm.c-2044-\t\t\ttrace_kvm_fast_mmio(gpa);\n--\narch/x86/kvm/vmx/tdx.c=1429=static inline int tdx_mmio_write(struct kvm_vcpu *vcpu, gpa_t gpa, int size,\n--\narch/x86/kvm/vmx/tdx.c-1431-{\narch/x86/kvm/vmx/tdx.c:1432:\tif (!kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {\narch/x86/kvm/vmx/tdx.c-1433-\t\ttrace_kvm_fast_mmio(gpa);\n--\narch/x86/kvm/vmx/tdx.c-1437-\ttrace_kvm_mmio(KVM_TRACE_MMIO_WRITE, size, gpa, \u0026val);\narch/x86/kvm/vmx/tdx.c:1438:\tif (kvm_io_bus_write(vcpu, KVM_MMIO_BUS, gpa, size, \u0026val))\narch/x86/kvm/vmx/tdx.c-1439-\t\treturn -EOPNOTSUPP;\n--\narch/x86/kvm/vmx/vmx.c=6009=static int handle_ept_misconfig(struct kvm_vcpu *vcpu)\n--\narch/x86/kvm/vmx/vmx.c-6021-\tif (!is_guest_mode(vcpu) \u0026\u0026\narch/x86/kvm/vmx/vmx.c:6022:\t !kvm_io_bus_write(vcpu, KVM_FAST_MMIO_BUS, gpa, 0, NULL)) {\narch/x86/kvm/vmx/vmx.c-6023-\t\ttrace_kvm_fast_mmio(gpa);\n--\narch/x86/kvm/x86.c=4782=static int vcpu_mmio_write(struct kvm_vcpu *vcpu, gpa_t addr, int len,\n--\narch/x86/kvm/x86.c-4793-\t\tif (!(lapic_in_kernel(vcpu) \u0026\u0026\narch/x86/kvm/x86.c:4794:\t\t !kvm_iodevice_write(vcpu, \u0026vcpu-\u003earch.apic-\u003edev, addr, n, v))\narch/x86/kvm/x86.c:4795:\t\t \u0026\u0026 kvm_io_bus_write(vcpu, KVM_MMIO_BUS, addr, n, v))\narch/x86/kvm/x86.c-4796-\t\t\tbreak;\n--\narch/x86/kvm/x86.c=5386=static int emulator_pio_in_out(struct kvm_vcpu *vcpu, int size,\n--\narch/x86/kvm/x86.c-5397-\t\telse\narch/x86/kvm/x86.c:5398:\t\t\tr = kvm_io_bus_write(vcpu, KVM_PIO_BUS, port, size, data);\narch/x86/kvm/x86.c-5399-\n--\nvirt/kvm/kvm_main.c=5943=static int kvm_io_bus_get_first_dev(struct kvm_io_bus *bus,\n--\nvirt/kvm/kvm_main.c-5966-\nvirt/kvm/kvm_main.c:5967:static int __kvm_io_bus_write(struct kvm_vcpu *vcpu, struct kvm_io_bus *bus,\nvirt/kvm/kvm_main.c-5968-\t\t\t struct kvm_io_range *range, const void *val)\n--\nvirt/kvm/kvm_main.c-5977-\t\tkvm_io_bus_cmp(range, \u0026bus-\u003erange[idx]) == 0) {\nvirt/kvm/kvm_main.c:5978:\t\tif (!kvm_iodevice_write(vcpu, bus-\u003erange[idx].dev, range-\u003eaddr,\nvirt/kvm/kvm_main.c-5979-\t\t\t\t\trange-\u003elen, val))\n--\nvirt/kvm/kvm_main.c=5987=static struct kvm_io_bus *kvm_get_bus_srcu(struct kvm *kvm, enum kvm_bus idx)\n--\nvirt/kvm/kvm_main.c-5998-\nvirt/kvm/kvm_main.c:5999:int kvm_io_bus_write(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx, gpa_t addr,\nvirt/kvm/kvm_main.c-6000-\t\t int len, const void *val)\n--\nvirt/kvm/kvm_main.c-6013-\t\treturn -ENOMEM;\nvirt/kvm/kvm_main.c:6014:\tr = __kvm_io_bus_write(vcpu, bus, \u0026range, val);\nvirt/kvm/kvm_main.c-6015-\treturn r \u003c 0 ? r : 0;\nvirt/kvm/kvm_main.c-6016-}\nvirt/kvm/kvm_main.c:6017:EXPORT_SYMBOL_FOR_KVM_INTERNAL(kvm_io_bus_write);\nvirt/kvm/kvm_main.c-6018-\nvirt/kvm/kvm_main.c:6019:int kvm_io_bus_write_cookie(struct kvm_vcpu *vcpu, enum kvm_bus bus_idx,\nvirt/kvm/kvm_main.c-6020-\t\t\t gpa_t addr, int len, const void *val, long cookie)\n--\nvirt/kvm/kvm_main.c-6036-\t (kvm_io_bus_cmp(\u0026range, \u0026bus-\u003erange[cookie]) == 0))\nvirt/kvm/kvm_main.c:6037:\t\tif (!kvm_iodevice_write(vcpu, bus-\u003erange[cookie].dev, addr, len,\nvirt/kvm/kvm_main.c-6038-\t\t\t\t\tval))\n--\nvirt/kvm/kvm_main.c-6044-\t */\nvirt/kvm/kvm_main.c:6045:\treturn __kvm_io_bus_write(vcpu, bus, \u0026range, val);\nvirt/kvm/kvm_main.c-6046-}\n"
} |
| 53/3 | 2026/10/29 06:21 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 18430 output: 37 thoughts: 138 Thoughts: **Investigating a Suspected Memory Corruption Vulnerability** Okay, hold on a second! I need to really dig into this section of code. Specifically, I need to focus on `npf_interception`, `handle_ept_misconfig`, and that `tdx_mmio_write` function. I see that potentially dangerous write operation involving `kvm_io_bus_write` and I have to ask myself, "could `val` ever be NULL here?" That would definitely be a problem. Then another thing occurs to me, "can `KVM_FAST_MMIO_BUS` have a `post_addr`?" I don't think so, but better safe than sorry, so I'll need to confirm. I need to understand how `bus_idx` is actually selected in this context. And I have to nail down how the `FAST_MMIO` is ultimately assigned. That's crucial. I'm going to have to dive into how this bus is used, the logic surrounding it. Let's see...I think `virt/kvm/eventfd.c` is the right place to start looking at this bus assignment. Hopefully, it clarifies how that whole `post_addr` thing is supposed to work. This needs a methodical, thorough investigation. |
| 54/3 | 2026/10/29 06:22 | tool | read-file |
0mArgs:{
"File": "virt/kvm/eventfd.c",
"FirstLine": 890,
"LineCount": 80
}Results: {
"Contents": " 890:\t{\n 891:\t\tif (flags \u0026 KVM_IOEVENTFD_FLAG_PIO)\n 892:\t\t\treturn KVM_PIO_BUS;\n 893:\t\tif (flags \u0026 KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY)\n 894:\t\t\treturn KVM_VIRTIO_CCW_NOTIFY_BUS;\n 895:\t\treturn KVM_MMIO_BUS;\n 896:\t}\n 897:\t\n 898:\tstatic int kvm_assign_ioeventfd_idx(struct kvm *kvm,\n 899:\t\t\t\t\tenum kvm_bus bus_idx,\n 900:\t\t\t\t\tstruct kvm_ioeventfd *args)\n 901:\t{\n 902:\t\n 903:\t\tstruct eventfd_ctx *eventfd;\n 904:\t\tstruct _ioeventfd *p;\n 905:\t\tint ret;\n 906:\t\n 907:\t\teventfd = eventfd_ctx_fdget(args-\u003efd);\n 908:\t\tif (IS_ERR(eventfd))\n 909:\t\t\treturn PTR_ERR(eventfd);\n 910:\t\n 911:\t\t/*\n 912:\t\t * Disallow posting to virtual address 0 so that KVM can treat\n 913:\t\t * a NULL user pointer as \"no posted write\".\n 914:\t\t */\n 915:\t\tif ((args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_POST_WRITE) \u0026\u0026\n 916:\t\t (!args-\u003elen || !u64_to_user_ptr(args-\u003epost_addr) ||\n 917:\t\t args-\u003epost_addr != untagged_addr(args-\u003epost_addr) ||\n 918:\t\t !access_ok(u64_to_user_ptr(args-\u003epost_addr), args-\u003elen) ||\n 919:\t\t !IS_ALIGNED(args-\u003epost_addr, args-\u003elen))) {\n 920:\t\t\tret = -EINVAL;\n 921:\t\t\tgoto fail;\n 922:\t\t}\n 923:\t\n 924:\t\tp = kzalloc_obj(*p, GFP_KERNEL_ACCOUNT);\n 925:\t\tif (!p) {\n 926:\t\t\tret = -ENOMEM;\n 927:\t\t\tgoto fail;\n 928:\t\t}\n 929:\t\n 930:\t\tINIT_LIST_HEAD(\u0026p-\u003elist);\n 931:\t\tp-\u003eaddr = args-\u003eaddr;\n 932:\t\tp-\u003ebus_idx = bus_idx;\n 933:\t\tp-\u003elength = args-\u003elen;\n 934:\t\tp-\u003eeventfd = eventfd;\n 935:\t\n 936:\t\t/* The datamatch feature is optional, otherwise this is a wildcard */\n 937:\t\tif (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DATAMATCH)\n 938:\t\t\tp-\u003edatamatch = args-\u003edatamatch;\n 939:\t\telse\n 940:\t\t\tp-\u003ewildcard = true;\n 941:\t\n 942:\t\tif (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_POST_WRITE)\n 943:\t\t\tp-\u003epost_addr = u64_to_user_ptr(args-\u003epost_addr);\n 944:\t\n 945:\t\tmutex_lock(\u0026kvm-\u003eslots_lock);\n 946:\t\n 947:\t\t/* Verify that there isn't a match already */\n 948:\t\tif (ioeventfd_check_collision(kvm, p)) {\n 949:\t\t\tret = -EEXIST;\n 950:\t\t\tgoto unlock_fail;\n 951:\t\t}\n 952:\t\n 953:\t\tkvm_iodevice_init(\u0026p-\u003edev, \u0026ioeventfd_ops);\n 954:\t\n 955:\t\tret = kvm_io_bus_register_dev(kvm, bus_idx, p-\u003eaddr, p-\u003elength,\n 956:\t\t\t\t\t \u0026p-\u003edev);\n 957:\t\tif (ret \u003c 0)\n 958:\t\t\tgoto unlock_fail;\n 959:\t\n 960:\t\tkvm_get_bus(kvm, bus_idx)-\u003eioeventfd_count++;\n 961:\t\tlist_add_tail(\u0026p-\u003elist, \u0026kvm-\u003eioeventfds);\n 962:\t\n 963:\t\tmutex_unlock(\u0026kvm-\u003eslots_lock);\n 964:\t\n 965:\t\treturn 0;\n 966:\t\n 967:\tunlock_fail:\n 968:\t\tmutex_unlock(\u0026kvm-\u003eslots_lock);\n 969:\t\tkfree(p);\n"
} |
| 55/3 | 2026/10/29 06:22 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 20125 output: 38 thoughts: 26 Thoughts: Here's a summary of my immediate thought process: **Diving into the FAST_MMIO Details** Alright, let's get down to brass tacks. I need to understand what's going on with this `FAST_MMIO` setting. It's a key optimization, and I want to see how it's behaving. My focus is on the impact and how it's being leveraged in this specific scenario. My next step will be to examine the code around `kvm_assign_ioeventfd`. Understanding that function and its interactions is crucial to seeing how `FAST_MMIO` is being employed. Knowing this will give me a better understanding of performance characteristics. |
| 56/3 | 2026/10/29 06:22 | tool | read-file |
0mArgs:{
"File": "virt/kvm/eventfd.c",
"FirstLine": 1037,
"LineCount": 50
}Results: {
"Contents": "1037:\tstatic int\n1038:\tkvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\n1039:\t{\n1040:\t\tenum kvm_bus bus_idx;\n1041:\t\tint ret;\n1042:\t\n1043:\t\tbus_idx = ioeventfd_bus_from_flags(args-\u003eflags);\n1044:\t\t/* must be natural-word sized, or 0 to ignore length */\n1045:\t\tswitch (args-\u003elen) {\n1046:\t\tcase 0:\n1047:\t\tcase 1:\n1048:\t\tcase 2:\n1049:\t\tcase 4:\n1050:\t\tcase 8:\n1051:\t\t\tbreak;\n1052:\t\tdefault:\n1053:\t\t\treturn -EINVAL;\n1054:\t\t}\n1055:\t\n1056:\t\t/* check for range overflow */\n1057:\t\tif (args-\u003eaddr + args-\u003elen \u003c args-\u003eaddr)\n1058:\t\t\treturn -EINVAL;\n1059:\t\n1060:\t\t/* check for extra flags that we don't understand */\n1061:\t\tif (args-\u003eflags \u0026 ~KVM_IOEVENTFD_VALID_FLAG_MASK)\n1062:\t\t\treturn -EINVAL;\n1063:\t\n1064:\t\t/* ioeventfd with no length can't be combined with DATAMATCH */\n1065:\t\tif (!args-\u003elen \u0026\u0026 (args-\u003eflags \u0026 KVM_IOEVENTFD_FLAG_DATAMATCH))\n1066:\t\t\treturn -EINVAL;\n1067:\t\n1068:\t\tret = kvm_assign_ioeventfd_idx(kvm, bus_idx, args);\n1069:\t\tif (ret)\n1070:\t\t\tgoto fail;\n1071:\t\n1072:\t\t/* When length is ignored, MMIO is also put on a separate bus, for\n1073:\t\t * faster lookups.\n1074:\t\t */\n1075:\t\tif (!args-\u003elen \u0026\u0026 bus_idx == KVM_MMIO_BUS) {\n1076:\t\t\tret = kvm_assign_ioeventfd_idx(kvm, KVM_FAST_MMIO_BUS, args);\n1077:\t\t\tif (ret \u003c 0)\n1078:\t\t\t\tgoto fast_fail;\n1079:\t\t}\n1080:\t\n1081:\t\treturn 0;\n1082:\t\n1083:\tfast_fail:\n1084:\t\tkvm_deassign_ioeventfd_idx(kvm, bus_idx, args);\n1085:\tfail:\n1086:\t\treturn ret;\n"
} |
| 57/3 | 2026/10/29 06:22 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 21063 output: 43 thoughts: 869 Thoughts: **Analyzing KVM IOEventfd POST_WRITE Behavior** Okay, let's break this down. My initial observation is that `args->len` *cannot* be zero when the `POST_WRITE` flag is set. That's immediately clear from the `kvm_assign_ioeventfd_idx` function. The code explicitly checks for `!args->len` in conjunction with `KVM_IOEVENTFD_FLAG_POST_WRITE`, and if true, it returns `-EINVAL`. This means a zero length is a direct error condition when `POST_WRITE` is in play. This is key, because it implies certain assumptions within the subsequent logic. Furthermore, I recognize that zero length assignments seem specific to `KVM_FAST_MMIO_BUS`. Now, moving on to `ioeventfd_in_range`, the behavior is quite distinct. If the length is non-zero, it means a precise match on length is enforced. The incoming `len` from a write *must* equal the stored `p->length`. If `p->length` *is* set, then this code path will compare the provided `len` and the stored value. Looking at `ioeventfd_write`, I see that the `val` parameter is ultimately used to write data to `p->post_addr`. The function retrieves the data to be written and uses `put_user` based on the length of the transaction. The critical part is understanding where this `val` comes from. It's time to trace the execution path. Since this deals with MMIO writes, I need to check `vcpu_mmio_write` in `arch/x86/kvm/x86.c`. That function will reveal the source of this `val`. I'll need to figure out how `val` is populated from the actual MMIO or PIO write operations. |
| 58/3 | 2026/10/29 06:23 | tool | read-file |
0mArgs:{
"File": "arch/x86/kvm/x86.c",
"FirstLine": 4780,
"LineCount": 40
}Results: {
"Contents": "4780:\t}\n4781:\t\n4782:\tstatic int vcpu_mmio_write(struct kvm_vcpu *vcpu, gpa_t addr, int len,\n4783:\t\t\t\t void *__v)\n4784:\t{\n4785:\t\tconst void *v = __v;\n4786:\t\tint handled = 0;\n4787:\t\tint n;\n4788:\t\n4789:\t\ttrace_kvm_mmio(KVM_TRACE_MMIO_WRITE, len, addr, __v);\n4790:\t\n4791:\t\tdo {\n4792:\t\t\tn = min(len, 8);\n4793:\t\t\tif (!(lapic_in_kernel(vcpu) \u0026\u0026\n4794:\t\t\t !kvm_iodevice_write(vcpu, \u0026vcpu-\u003earch.apic-\u003edev, addr, n, v))\n4795:\t\t\t \u0026\u0026 kvm_io_bus_write(vcpu, KVM_MMIO_BUS, addr, n, v))\n4796:\t\t\t\tbreak;\n4797:\t\t\thandled += n;\n4798:\t\t\taddr += n;\n4799:\t\t\tlen -= n;\n4800:\t\t\tv += n;\n4801:\t\t} while (len);\n4802:\t\n4803:\t\treturn handled;\n4804:\t}\n4805:\t\n4806:\tstatic int vcpu_mmio_read(struct kvm_vcpu *vcpu, gpa_t addr, int len, void *v)\n4807:\t{\n4808:\t\tint handled = 0;\n4809:\t\tint n;\n4810:\t\n4811:\t\tdo {\n4812:\t\t\tn = min(len, 8);\n4813:\t\t\tif (!(lapic_in_kernel(vcpu) \u0026\u0026\n4814:\t\t\t !kvm_iodevice_read(vcpu, \u0026vcpu-\u003earch.apic-\u003edev,\n4815:\t\t\t\t\t\t addr, n, v))\n4816:\t\t\t \u0026\u0026 kvm_io_bus_read(vcpu, KVM_MMIO_BUS, addr, n, v))\n4817:\t\t\t\tbreak;\n4818:\t\t\ttrace_kvm_mmio(KVM_TRACE_MMIO_READ, n, addr, v);\n4819:\t\t\thandled += n;\n"
} |
| 59/3 | 2026/10/29 06:23 | llm | kmsan-evaluator |
0mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 23014 output: 20 thoughts: 1415 Thoughts: **Analyzing Potential Uninitialized Data in KVM ioeventfd Implementation** Okay, let's break this down systematically. I'm focusing on the `ioeventfd` mechanism within KVM, specifically the potential for uninitialized data being exposed to userspace. I started by looking at how the guest's data, written to an MMIO or PIO address, gets propagated to the user-space eventfd listener. First, I examined `emulator_pio_in_out`, where the guest data (`data`) is the write value. Then, I dug into `ioeventfd_write`. The core of the transfer seems to be `ioeventfd_get_val(val, len, unsupported_len)`. The input `val` is the pointer to the guest's data that is written. `ioeventfd_get_val` then reads `len` bytes from this pointer into a local `__val` using `get_unaligned`. This data will be written at the address `p->post_addr` via `put_user`. My initial thought was that there was a problem as I was seeing `_val` being potentially uninitialized. However, I realized `__val` is initialized using `get_unaligned`, which reads the guest data. This is where things get interesting. I scrutinized the macro `ioeventfd_get_val`. If `len` is 1, 2, 4, or 8, it reads the appropriate number of bytes and assigns this data to a `u64 __val`. Critically, when `len` is 1, 2, or 4, the read data gets zero-extended to `u64`. Then, regardless of the size (`len`), a `put_user` call is made. Thus, I confirmed that no uninitialized data is copied to the userspace. Next, I wanted to double-check that the `post_addr` itself wasn't initialized with junk. I went back to the `struct _ioeventfd`. It's allocated via `kzalloc_obj`, which guarantees it's zeroed. Then `p->post_addr` is set to user-space pointer based on the value passed in. So, the `post_addr` itself is clean. I then considered deallocation. Does `kvm_deassign_ioeventfd_idx` expose anything? Looking at the code, it iterates through a list to find and remove `ioeventfd` structures. The comparison happens against several fields in `struct _ioeventfd`: `bus_idx`, `eventfd`, `addr`, `len`, and `datamatch`. Critically, these comparisons *don't* involve reading uninitialized data from the structure. Therefore, the checks that I've performed point to no obvious path by which uninitialized data is exposed to user space. To be absolutely certain, I need to check the wider usage of `struct kvm_ioeventfd` in the kernel code. |
| 60/3 | 2026/10/29 06:23 | tool | grepper |
0mArgs:{
"Expression": "kvm_ioeventfd"
}Results: {
"Output": "Documentation/virt/kvm/api.rst=2111=See the note in KVM_GET_LAPIC.\n--\nDocumentation/virt/kvm/api.rst-2119-:Type: vm ioctl\nDocumentation/virt/kvm/api.rst:2120::Parameters: struct kvm_ioeventfd (in)\nDocumentation/virt/kvm/api.rst-2121-:Returns: 0 on success, !0 on error\n--\nDocumentation/virt/kvm/api.rst=2125=provided event instead of triggering an exit.\n--\nDocumentation/virt/kvm/api.rst-2128-\nDocumentation/virt/kvm/api.rst:2129: struct kvm_ioeventfd {\nDocumentation/virt/kvm/api.rst-2130-\t__u64 datamatch;\n--\nDocumentation/virt/kvm/api.rst=2143=The following flags are defined::\nDocumentation/virt/kvm/api.rst-2144-\nDocumentation/virt/kvm/api.rst:2145: #define KVM_IOEVENTFD_FLAG_DATAMATCH (1 \u003c\u003c kvm_ioeventfd_flag_nr_datamatch)\nDocumentation/virt/kvm/api.rst:2146: #define KVM_IOEVENTFD_FLAG_PIO (1 \u003c\u003c kvm_ioeventfd_flag_nr_pio)\nDocumentation/virt/kvm/api.rst:2147: #define KVM_IOEVENTFD_FLAG_DEASSIGN (1 \u003c\u003c kvm_ioeventfd_flag_nr_deassign)\nDocumentation/virt/kvm/api.rst-2148- #define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \\\nDocumentation/virt/kvm/api.rst:2149:\t(1 \u003c\u003c kvm_ioeventfd_flag_nr_virtio_ccw_notify)\nDocumentation/virt/kvm/api.rst:2150: #define KVM_IOEVENTFD_FLAG_POST_WRITE (1 \u003c\u003c kvm_ioeventfd_flag_nr_post_write)\nDocumentation/virt/kvm/api.rst-2151-\nDocumentation/virt/kvm/api.rst=2152=If datamatch flag is set, the event will be signaled only if the written value\nDocumentation/virt/kvm/api.rst:2153:to the registered address is equal to datamatch in struct kvm_ioeventfd.\nDocumentation/virt/kvm/api.rst-2154-\n--\ninclude/linux/kvm_host.h=2263=void kvm_eventfd_init(struct kvm *kvm);\ninclude/linux/kvm_host.h:2264:int kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args);\ninclude/linux/kvm_host.h-2265-\n--\ninclude/uapi/linux/kvm.h=641=enum {\ninclude/uapi/linux/kvm.h:642:\tkvm_ioeventfd_flag_nr_datamatch,\ninclude/uapi/linux/kvm.h:643:\tkvm_ioeventfd_flag_nr_pio,\ninclude/uapi/linux/kvm.h:644:\tkvm_ioeventfd_flag_nr_deassign,\ninclude/uapi/linux/kvm.h:645:\tkvm_ioeventfd_flag_nr_virtio_ccw_notify,\ninclude/uapi/linux/kvm.h:646:\tkvm_ioeventfd_flag_nr_fast_mmio,\ninclude/uapi/linux/kvm.h:647:\tkvm_ioeventfd_flag_nr_post_write,\ninclude/uapi/linux/kvm.h:648:\tkvm_ioeventfd_flag_nr_max,\ninclude/uapi/linux/kvm.h-649-};\ninclude/uapi/linux/kvm.h-650-\ninclude/uapi/linux/kvm.h:651:#define KVM_IOEVENTFD_FLAG_DATAMATCH (1 \u003c\u003c kvm_ioeventfd_flag_nr_datamatch)\ninclude/uapi/linux/kvm.h:652:#define KVM_IOEVENTFD_FLAG_PIO (1 \u003c\u003c kvm_ioeventfd_flag_nr_pio)\ninclude/uapi/linux/kvm.h:653:#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 \u003c\u003c kvm_ioeventfd_flag_nr_deassign)\ninclude/uapi/linux/kvm.h-654-#define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \\\ninclude/uapi/linux/kvm.h:655:\t(1 \u003c\u003c kvm_ioeventfd_flag_nr_virtio_ccw_notify)\ninclude/uapi/linux/kvm.h:656:#define KVM_IOEVENTFD_FLAG_POST_WRITE (1 \u003c\u003c kvm_ioeventfd_flag_nr_post_write)\ninclude/uapi/linux/kvm.h-657-\ninclude/uapi/linux/kvm.h:658:#define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 \u003c\u003c kvm_ioeventfd_flag_nr_max) - 1)\ninclude/uapi/linux/kvm.h-659-\ninclude/uapi/linux/kvm.h:660:struct kvm_ioeventfd {\ninclude/uapi/linux/kvm.h-661-\t__u64 datamatch;\n--\ninclude/uapi/linux/kvm.h=1260=struct kvm_s390_keyop {\n--\ninclude/uapi/linux/kvm.h-1305-#define KVM_SET_BOOT_CPU_ID _IO(KVMIO, 0x78)\ninclude/uapi/linux/kvm.h:1306:#define KVM_IOEVENTFD _IOW(KVMIO, 0x79, struct kvm_ioeventfd)\ninclude/uapi/linux/kvm.h-1307-#define KVM_XEN_HVM_CONFIG _IOW(KVMIO, 0x7a, struct kvm_xen_hvm_config)\n--\ntools/include/uapi/linux/kvm.h=641=enum {\ntools/include/uapi/linux/kvm.h:642:\tkvm_ioeventfd_flag_nr_datamatch,\ntools/include/uapi/linux/kvm.h:643:\tkvm_ioeventfd_flag_nr_pio,\ntools/include/uapi/linux/kvm.h:644:\tkvm_ioeventfd_flag_nr_deassign,\ntools/include/uapi/linux/kvm.h:645:\tkvm_ioeventfd_flag_nr_virtio_ccw_notify,\ntools/include/uapi/linux/kvm.h:646:\tkvm_ioeventfd_flag_nr_fast_mmio,\ntools/include/uapi/linux/kvm.h:647:\tkvm_ioeventfd_flag_nr_max,\ntools/include/uapi/linux/kvm.h-648-};\ntools/include/uapi/linux/kvm.h-649-\ntools/include/uapi/linux/kvm.h:650:#define KVM_IOEVENTFD_FLAG_DATAMATCH (1 \u003c\u003c kvm_ioeventfd_flag_nr_datamatch)\ntools/include/uapi/linux/kvm.h:651:#define KVM_IOEVENTFD_FLAG_PIO (1 \u003c\u003c kvm_ioeventfd_flag_nr_pio)\ntools/include/uapi/linux/kvm.h:652:#define KVM_IOEVENTFD_FLAG_DEASSIGN (1 \u003c\u003c kvm_ioeventfd_flag_nr_deassign)\ntools/include/uapi/linux/kvm.h-653-#define KVM_IOEVENTFD_FLAG_VIRTIO_CCW_NOTIFY \\\ntools/include/uapi/linux/kvm.h:654:\t(1 \u003c\u003c kvm_ioeventfd_flag_nr_virtio_ccw_notify)\ntools/include/uapi/linux/kvm.h-655-\ntools/include/uapi/linux/kvm.h:656:#define KVM_IOEVENTFD_VALID_FLAG_MASK ((1 \u003c\u003c kvm_ioeventfd_flag_nr_max) - 1)\ntools/include/uapi/linux/kvm.h-657-\ntools/include/uapi/linux/kvm.h:658:struct kvm_ioeventfd {\ntools/include/uapi/linux/kvm.h-659-\t__u64 datamatch;\n--\ntools/include/uapi/linux/kvm.h=1252=struct kvm_s390_keyop {\n--\ntools/include/uapi/linux/kvm.h-1297-#define KVM_SET_BOOT_CPU_ID _IO(KVMIO, 0x78)\ntools/include/uapi/linux/kvm.h:1298:#define KVM_IOEVENTFD _IOW(KVMIO, 0x79, struct kvm_ioeventfd)\ntools/include/uapi/linux/kvm.h-1299-#define KVM_XEN_HVM_CONFIG _IOW(KVMIO, 0x7a, struct kvm_xen_hvm_config)\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c=78=static void test_post_write_negative(void)\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-79-{\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:80:\tstruct kvm_ioeventfd ioeventfd;\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-81-\tstruct kvm_vm *vm;\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-89-\t/* length cannot be zero */\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:90:\tioeventfd = (struct kvm_ioeventfd) {\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-91-\t\t.addr = MMIO_GPA,\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c=145=static void test_post_write_width(int width, void (*guest_fn)(void))\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-147-\tuint64_t actual, expected, count;\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:148:\tstruct kvm_ioeventfd ioeventfd;\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-149-\tstruct kvm_vcpu *vcpu;\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-161-\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:162:\tioeventfd = (struct kvm_ioeventfd) {\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-163-\t\t.addr = MMIO_GPA,\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c=202=static void test_post_write_datamatch(void)\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-203-{\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:204:\tstruct kvm_ioeventfd ioeventfd;\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-205-\tstruct kvm_vcpu *vcpu;\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-221-\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:222:\tioeventfd = (struct kvm_ioeventfd) {\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-223-\t\t.datamatch = MATCH_VAL,\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c=290=static void test_post_write_multi(void)\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-294-\t};\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:295:\tstruct kvm_ioeventfd ioeventfd;\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-296-\tstruct kvm_vcpu *vcpu;\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-308-\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:309:\tioeventfd = (struct kvm_ioeventfd) {\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-310-\t\t.addr = MMIO_GPA,\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c=350=static void test_post_write_multi_nosync(void)\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-351-{\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:352:\tstruct kvm_ioeventfd ioeventfd;\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-353-\tstruct kvm_vcpu *vcpu;\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-365-\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:366:\tioeventfd = (struct kvm_ioeventfd) {\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-367-\t\t.addr = MMIO_GPA,\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c=402=static void test_post_write_deassign(void)\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-403-{\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:404:\tstruct kvm_ioeventfd ioeventfd;\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-405-\tstruct kvm_vcpu *vcpu;\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-421-\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:422:\tioeventfd = (struct kvm_ioeventfd) {\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-423-\t\t.addr = MMIO_GPA,\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c=483=static void test_post_write_pio(void)\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-484-{\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:485:\tstruct kvm_ioeventfd ioeventfd;\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-486-\tstruct kvm_vcpu *vcpu;\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-497-\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:498:\tioeventfd = (struct kvm_ioeventfd) {\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-499-\t\t.addr = PIO_PORT,\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c=538=static void test_post_write_pio_datamatch(void)\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-539-{\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:540:\tstruct kvm_ioeventfd ioeventfd;\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-541-\tstruct kvm_vcpu *vcpu;\n--\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-556-\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c:557:\tioeventfd = (struct kvm_ioeventfd) {\ntools/testing/selftests/kvm/x86/ioeventfd_posted_write_test.c-558-\t\t.datamatch = MATCH_VAL,\n--\nvirt/kvm/eventfd.c=898=static int kvm_assign_ioeventfd_idx(struct kvm *kvm,\nvirt/kvm/eventfd.c-899-\t\t\t\tenum kvm_bus bus_idx,\nvirt/kvm/eventfd.c:900:\t\t\t\tstruct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-901-{\n--\nvirt/kvm/eventfd.c=978=kvm_deassign_ioeventfd_idx(struct kvm *kvm, enum kvm_bus bus_idx,\nvirt/kvm/eventfd.c:979:\t\t\t struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-980-{\n--\nvirt/kvm/eventfd.c-1025-\nvirt/kvm/eventfd.c:1026:static int kvm_deassign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1027-{\n--\nvirt/kvm/eventfd.c=1037=static int\nvirt/kvm/eventfd.c:1038:kvm_assign_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1039-{\n--\nvirt/kvm/eventfd.c=1089=int\nvirt/kvm/eventfd.c:1090:kvm_ioeventfd(struct kvm *kvm, struct kvm_ioeventfd *args)\nvirt/kvm/eventfd.c-1091-{\n--\nvirt/kvm/kvm_main.c=5259=static long kvm_vm_ioctl(struct file *filp,\n--\nvirt/kvm/kvm_main.c-5365-\tcase KVM_IOEVENTFD: {\nvirt/kvm/kvm_main.c:5366:\t\tstruct kvm_ioeventfd data;\nvirt/kvm/kvm_main.c-5367-\n--\nvirt/kvm/kvm_main.c-5370-\t\t\tgoto out;\nvirt/kvm/kvm_main.c:5371:\t\tr = kvm_ioeventfd(kvm, \u0026data);\nvirt/kvm/kvm_main.c-5372-\t\tbreak;\n"
} |
| 61/3 | 2026/10/29 06:23 | llm | kmsan-evaluator |
2mModel:gemini-3.8-flash Args: null Results: null Tokens: input: 30467 output: 327 thoughts: 2976 Thoughts: **KMSAN Assessment: KVM Ioeventfd Post-Write Feature** Alright, let's break this down. The core of this patch introduces the ability for a KVM guest to write data to a userspace address via `ioeventfd` upon an MMIO write. The initial structure (`kvm_ioeventfd`) is input-only, and there's no data being copied back to userspace except via the new post-write functionality. We’re dealing with `put_user()` calls in `ioeventfd_write()`, specifically for writing a value to `p->post_addr`. My concern is whether `_val` in `put_user(_val, ...)` could be uninitialized. The code carefully reads a value using `ioeventfd_get_val()`. Looking into the `ioeventfd_get_val()` macro, it becomes apparent that the read operation uses `get_unaligned()` on the incoming `val`, and then the appropriate integer is read (1, 2, 4, or 8 bytes) based on `len`. The macro always ensures `__val` is initialized. If `len` is anything other than 1, 2, 4, or 8, it bails out via `unsupported_len` and returns an error. This means there's a direct copy of an initialized value. Therefore, it's clear `_val` is always initialized before `put_user` is called. It’s reading a fixed amount of bytes from the guest’s MMIO write data and writing it to user space. Now, consider the origin of `val`. In the MMIO write path, the vCPU emulator (e.g., in x86) fetches data from the guest registers and stores it in a local buffer. There’s no reason to believe that buffer will be uninitialized. That’s fundamental code which has been running for a decade. The patch itself doesn’t change any of that core initialization logic. The new changes focus on the post-write functionality. The patch itself boils down to the `KVM_IOEVENTFD_POST_WRITE` capability, a flag (`KVM_IOEVENTFD_FLAG_POST_WRITE`), and checks in `kvm_assign_ioeventfd_idx` to validate `post_addr`, `len` and alignment. When post-write is enabled, `ioeventfd_write()` now uses `put_user()`. The `access_ok()` check is in place, and `put_user()` handles potential errors, such as `-EFAULT` due to unmapped memory. The previous switch statement in `ioeventfd_in_range` was refactored into the macro `ioeventfd_get_val()`. So, what about the KMSAN-specific checks? 1. The `put_user` copies a primitive integer directly from `val`, not a struct, thus there are no padding concerns. 2. There's no conditional logic that depends on potentially uninitialized variables. `p` is allocated and zeroed via `kzalloc_obj`. 3. There are no complex data structures being initialized with the risk of later reads of uninitialized memory. 4. The code validates that the `len` matches the size being written and is one of the supported types (1, 2, 4, or 8), so no uninitialized bytes are accessible. To be thorough, I checked other potential sources of the `val`. - In `kvm_assign_ioeventfd_idx`, all the inputs are being copied from userspace via `copy_from_user` to the `data` which has all fields initialized. - `ioeventfd_get_val` always initializes `__val` before it's read by using `get_unaligned`. - Inside `ioeventfd_write`, `put_user` is used to write values (1, 2, 4, or 8 bytes). - In `ioeventfd_in_range`, the `ioeventfd_get_val` returns the value which has been correctly initialized. Regarding potential memory safety issues, such as invalid user pointers, the code uses `access_ok()` to validate the user pointer and `put_user()` handles page faults. Out-of-bounds accesses would be caught by KASAN. In conclusion, there are no risks of uninitialized memory usage or info leaks. The post-write feature is carefully constructed to prevent this. All data is initialized before use, and standard memory safety features are in place. There's no need for KMSAN. Therefore, `NeedsKMSAN = false`. I’m confident in that analysis. |
| 62/3 | 2026/10/29 06:26 | tool | set-results |
0mArgs:{
"KMSANReasoning": "The patch series introduces KVM_CAP_IOEVENTFD_POST_WRITE, allowing guest MMIO/PIO writes to be posted directly to a userspace address via put_user() before signaling an eventfd.\n\nKMSAN vs KASAN evaluation:\n1. No uninitialized memory copied to userspace: Although put_user() is used in ioeventfd_write(), it writes scalar primitive values (u8, u16, u32, or u64 based on the write length) obtained directly from the guest write buffer via get_unaligned(). There are no structs, structure padding, or uninitialized kernel buffers involved in this transfer.\n2. Structure allocations: The internal struct _ioeventfd is allocated using kzalloc_obj(), ensuring all fields (including the new post_addr pointer) are zero-initialized.\n3. Userspace IOCTL input: The kvm_ioeventfd struct is an input-only argument (_IOW) to KVM_IOEVENTFD copied via copy_from_user(); no kernel data or uninitialized fields are copied back to user space.\n4. Error modes: Any potential bugs introduced by this patch would be related to user pointer validation, concurrency, or device registration logic, which are covered by standard kernel bug detectors or KASAN.\n\nTherefore, the changes do not introduce or expose any uninitialized memory risks, and a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
}Results: {
"KMSANReasoning": "The patch series introduces KVM_CAP_IOEVENTFD_POST_WRITE, allowing guest MMIO/PIO writes to be posted directly to a userspace address via put_user() before signaling an eventfd.\n\nKMSAN vs KASAN evaluation:\n1. No uninitialized memory copied to userspace: Although put_user() is used in ioeventfd_write(), it writes scalar primitive values (u8, u16, u32, or u64 based on the write length) obtained directly from the guest write buffer via get_unaligned(). There are no structs, structure padding, or uninitialized kernel buffers involved in this transfer.\n2. Structure allocations: The internal struct _ioeventfd is allocated using kzalloc_obj(), ensuring all fields (including the new post_addr pointer) are zero-initialized.\n3. Userspace IOCTL input: The kvm_ioeventfd struct is an input-only argument (_IOW) to KVM_IOEVENTFD copied via copy_from_user(); no kernel data or uninitialized fields are copied back to user space.\n4. Error modes: Any potential bugs introduced by this patch would be related to user pointer validation, concurrency, or device registration logic, which are covered by standard kernel bug detectors or KASAN.\n\nTherefore, the changes do not introduce or expose any uninitialized memory risks, and a dedicated KMSAN fuzzing session is not warranted.",
"NeedsKMSAN": false
} |
| Total Calls | Total Tokens | Avg Tokens | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|
| Total Calls | Total Duration (Seconds) | Avg Duration (Seconds) |
|---|