* [PATCH 1/1] kvm: reject unknown flags in device and dirty-log ioctls
2026-07-01 19:49 [PATCH 0/1] kvm: reject unknown flags in device and dirty-log ioctls Iván Ezequiel Rodriguez
@ 2026-07-01 19:49 ` Iván Ezequiel Rodriguez
2026-07-01 20:09 ` sashiko-bot
0 siblings, 1 reply; 3+ messages in thread
From: Iván Ezequiel Rodriguez @ 2026-07-01 19:49 UTC (permalink / raw)
To: pbonzini, seanjc; +Cc: kvm, linux-kselftest, Iván Ezequiel Rodriguez
KVM_CREATE_DEVICE only defines KVM_CREATE_DEVICE_TEST, but unknown
flag bits were silently ignored. kvm_device_attr.flags is documented
as unused yet was never checked centrally. KVM_ENABLE_CAP for
KVM_CAP_DIRTY_LOG_RING and KVM_CAP_DIRTY_LOG_RING_ACQ_REL requires
cap->flags to be zero per api.rst, but the generic handler did not
enforce it.
Reject unknown or non-zero flags with -EINVAL, consistent with other
KVM ioctls and dma-heap flag validation. Add a selftest covering all
three paths.
Signed-off-by: Iván Ezequiel Rodriguez <ivanrwcm25@gmail.com>
---
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../kvm/ioctl_flag_validation_test.c | 104 ++++++++++++++++++
virt/kvm/kvm_main.c | 9 ++
3 files changed, 114 insertions(+)
create mode 100644 tools/testing/selftests/kvm/ioctl_flag_validation_test.c
diff --git a/tools/testing/selftests/kvm/Makefile.kvm b/tools/testing/selftests/kvm/Makefile.kvm
index d28a057fa6c2..38c78838318c 100644
--- a/tools/testing/selftests/kvm/Makefile.kvm
+++ b/tools/testing/selftests/kvm/Makefile.kvm
@@ -59,6 +59,7 @@ TEST_PROGS_x86 += x86/nx_huge_pages_test.sh
TEST_GEN_PROGS_COMMON = demand_paging_test
TEST_GEN_PROGS_COMMON += dirty_log_test
TEST_GEN_PROGS_COMMON += guest_print_test
+TEST_GEN_PROGS_COMMON += ioctl_flag_validation_test
TEST_GEN_PROGS_COMMON += irqfd_test
TEST_GEN_PROGS_COMMON += kvm_binary_stats_test
TEST_GEN_PROGS_COMMON += kvm_create_max_vcpus
diff --git a/tools/testing/selftests/kvm/ioctl_flag_validation_test.c b/tools/testing/selftests/kvm/ioctl_flag_validation_test.c
new file mode 100644
index 000000000000..30c22435bb14
--- /dev/null
+++ b/tools/testing/selftests/kvm/ioctl_flag_validation_test.c
@@ -0,0 +1,104 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Test that selected KVM ioctls reject unknown flag bits with -EINVAL.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "test_util.h"
+#include "kvm_util.h"
+
+static void test_create_device_unknown_flags(struct kvm_vm *vm)
+{
+ struct kvm_create_device cd = {
+ .type = KVM_DEV_TYPE_VFIO,
+ .flags = 0x2,
+ };
+ int r;
+
+ TEST_REQUIRE(kvm_check_cap(KVM_CAP_DEVICE_CTRL));
+
+ r = __vm_ioctl(vm, KVM_CREATE_DEVICE, &cd);
+ TEST_ASSERT(r == -1 && errno == EINVAL,
+ "KVM_CREATE_DEVICE with unknown flags");
+
+ cd.flags = KVM_CREATE_DEVICE_TEST | 0x2;
+ r = __vm_ioctl(vm, KVM_CREATE_DEVICE, &cd);
+ TEST_ASSERT(r == -1 && errno == EINVAL,
+ "KVM_CREATE_DEVICE TEST with unknown flags");
+}
+
+static void test_device_attr_flags(struct kvm_vm *vm)
+{
+ struct kvm_device_attr attr = {
+ .flags = 1,
+ .group = KVM_DEV_VFIO_FILE,
+ .attr = KVM_DEV_VFIO_FILE_ADD,
+ .addr = 0,
+ };
+ int dev_fd, r;
+
+ TEST_REQUIRE(kvm_check_cap(KVM_CAP_DEVICE_CTRL));
+
+ dev_fd = __kvm_create_device(vm, KVM_DEV_TYPE_VFIO);
+ if (dev_fd < 0) {
+ pr_info("Skipping device_attr test, KVM_DEV_TYPE_VFIO unavailable\n");
+ return;
+ }
+
+ r = __kvm_ioctl(dev_fd, KVM_HAS_DEVICE_ATTR, &attr);
+ TEST_ASSERT(r == -1 && errno == EINVAL,
+ "KVM_HAS_DEVICE_ATTR with unknown flags");
+
+ close(dev_fd);
+}
+
+static void test_dirty_log_ring_cap_flags(struct kvm_vm *vm)
+{
+ struct kvm_enable_cap cap = {
+ .flags = 1,
+ .args = { 4096 },
+ };
+ int r;
+
+ if (!kvm_has_cap(KVM_CAP_DIRTY_LOG_RING) &&
+ !kvm_has_cap(KVM_CAP_DIRTY_LOG_RING_ACQ_REL)) {
+ pr_info("Skipping dirty log ring cap flag test, cap unavailable\n");
+ return;
+ }
+
+ if (kvm_has_cap(KVM_CAP_DIRTY_LOG_RING)) {
+ cap.cap = KVM_CAP_DIRTY_LOG_RING;
+ cap.flags = 1;
+ r = __vm_ioctl(vm, KVM_ENABLE_CAP, &cap);
+ TEST_ASSERT(r == -1 && errno == EINVAL,
+ "KVM_ENABLE_CAP DIRTY_LOG_RING with unknown flags");
+ }
+
+ if (kvm_has_cap(KVM_CAP_DIRTY_LOG_RING_ACQ_REL)) {
+ cap.cap = KVM_CAP_DIRTY_LOG_RING_ACQ_REL;
+ cap.flags = 1;
+ r = __vm_ioctl(vm, KVM_ENABLE_CAP, &cap);
+ TEST_ASSERT(r == -1 && errno == EINVAL,
+ "KVM_ENABLE_CAP DIRTY_LOG_RING_ACQ_REL with unknown flags");
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ struct kvm_vm *vm;
+
+ vm = vm_create_barebones();
+
+ test_create_device_unknown_flags(vm);
+ test_device_attr_flags(vm);
+ test_dirty_log_ring_cap_flags(vm);
+
+ kvm_vm_free(vm);
+ return 0;
+}
diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
index e44c20c04961..a2aecc06ab67 100644
--- a/virt/kvm/kvm_main.c
+++ b/virt/kvm/kvm_main.c
@@ -4719,6 +4719,9 @@ static int kvm_device_ioctl_attr(struct kvm_device *dev,
if (copy_from_user(&attr, (void __user *)arg, sizeof(attr)))
return -EFAULT;
+ if (attr.flags)
+ return -EINVAL;
+
return accessor(dev, &attr);
}
@@ -4811,6 +4814,9 @@ static int kvm_ioctl_create_device(struct kvm *kvm,
int type;
int ret;
+ if (cd->flags & ~KVM_CREATE_DEVICE_TEST)
+ return -EINVAL;
+
if (cd->type >= ARRAY_SIZE(kvm_device_ops_table))
return -ENODEV;
@@ -5061,6 +5067,9 @@ static int kvm_vm_ioctl_enable_cap_generic(struct kvm *kvm,
if (!kvm_vm_ioctl_check_extension_generic(kvm, cap->cap))
return -EINVAL;
+ if (cap->flags)
+ return -EINVAL;
+
return kvm_vm_ioctl_enable_dirty_log_ring(kvm, cap->args[0]);
case KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP: {
int r = -EINVAL;
--
2.43.0
^ permalink raw reply related [flat|nested] 3+ messages in thread