* [PATCH 0/1] kvm: reject unknown flags in device and dirty-log ioctls
@ 2026-07-01 19:49 Iván Ezequiel Rodriguez
2026-07-01 19:49 ` [PATCH 1/1] " Iván Ezequiel Rodriguez
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
Three generic KVM paths silently ignored flag bits that uapi or
Documentation/virt/kvm/api.rst treat as reserved or zero-only:
- KVM_CREATE_DEVICE (only KVM_CREATE_DEVICE_TEST is defined)
- KVM_{SET,GET,HAS}_DEVICE_ATTR (kvm_device_attr.flags)
- KVM_ENABLE_CAP for KVM_CAP_DIRTY_LOG_RING{,_ACQ_REL} (cap->flags)
Reject with -EINVAL, matching dma-heap and other strict KVM ioctls.
Adds ioctl_flag_validation_test to tools/testing/selftests/kvm.
Tested: ioctl_flag_validation_test fails on the host kernel without
this patch (KVM_CREATE_DEVICE accepts flags=0x2); requires reloading
the patched kvm module to pass.
Iván Ezequiel Rodriguez (1):
kvm: reject unknown flags in device and dirty-log ioctls
tools/testing/selftests/kvm/Makefile.kvm | 1 +
.../selftests/kvm/ioctl_flag_validation_test.c | 104 +++++++++++++++++++++
virt/kvm/kvm_main.c | 9 ++
3 files changed, 114 insertions(+)
--
2.43.0
^ permalink raw reply [flat|nested] 3+ messages in thread
* [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
* Re: [PATCH 1/1] kvm: reject unknown flags in device and dirty-log ioctls
2026-07-01 19:49 ` [PATCH 1/1] " Iván Ezequiel Rodriguez
@ 2026-07-01 20:09 ` sashiko-bot
0 siblings, 0 replies; 3+ messages in thread
From: sashiko-bot @ 2026-07-01 20:09 UTC (permalink / raw)
To: Iván Ezequiel Rodriguez; +Cc: kvm
Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
New issues:
- [Critical] Adding strict flag validation to previously ignored ioctl parameters breaks backwards compatibility for existing userspace applications.
Pre-existing issues:
- [Medium] The validation for `kvm_device_attr.flags` is incomplete and misses the VM, VCPU, and System ioctl paths.
--
commit c3cf74adec51cd659e1e4d1a49a36240977087b9
Author: Iván Ezequiel Rodriguez <ivanrwcm25@gmail.com>
kvm: reject unknown flags in device and dirty-log ioctls
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.
> diff --git a/virt/kvm/kvm_main.c b/virt/kvm/kvm_main.c
> index e44c20c049610..a2aecc06ab67e 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;
[Severity: Critical]
Does adding strict flag validation here break backwards compatibility for
existing userspace applications? If userspace previously allocated
struct kvm_device_attr on the stack without explicitly zero-initializing the
flags field, won't it now fail with -EINVAL since the kernel used to
silently ignore it?
[Severity: Medium]
This is a pre-existing issue, but the commit message states
kvm_device_attr.flags is now checked "centrally". However, doesn't
kvm_device_ioctl_attr() only handle device file descriptors?
The same device attribute ioctls (KVM_HAS_DEVICE_ATTR, KVM_GET_DEVICE_ATTR,
KVM_SET_DEVICE_ATTR) can be called on VM or VCPU file descriptors, which
are handled in architecture-specific code like kvm_vcpu_ioctl_device_attr()
in arch/x86/kvm/x86.c or kvm_arch_vcpu_ioctl() in arch/arm64/kvm/arm.c.
Doesn't this bypass the new validation entirely?
> +
> 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;
[Severity: Critical]
Similar to the issue above, does rejecting unknown flags retroactively break
applications that pass uninitialized stack memory in cd->flags when calling
KVM_CREATE_DEVICE?
> +
> 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;
[Severity: Critical]
Will this retroactively break backwards compatibility for applications
enabling KVM_CAP_DIRTY_LOG_RING or KVM_CAP_DIRTY_LOG_RING_ACQ_REL if they
haven't explicitly zero-initialized cap->flags?
> +
> return kvm_vm_ioctl_enable_dirty_log_ring(kvm, cap->args[0]);
> case KVM_CAP_DIRTY_LOG_RING_WITH_BITMAP: {
> int r = -EINVAL;
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260701194946.768731-2-ivanrwcm25@gmail.com?part=1
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-07-01 20:09 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
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 ` [PATCH 1/1] " Iván Ezequiel Rodriguez
2026-07-01 20:09 ` sashiko-bot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox