On Fri, Jun 10, 2022, Andrew Jones wrote: > On Fri, Jun 03, 2022 at 12:43:31AM +0000, Sean Christopherson wrote: > > Add a static assert to the KVM/VM/vCPU ioctl() helpers to verify that the > > size of the argument provided matches the expected size of the IOCTL. > > Because ioctl() ultimately takes a "void *", it's all too easy to pass in > > garbage and not detect the error until runtime. E.g. while working on a > > CPUID rework, selftests happily compiled when vcpu_set_cpuid() > > unintentionally passed the cpuid() function as the parameter to ioctl() > > (a local "cpuid" parameter was removed, but its use was not replaced with > > "vcpu->cpuid" as intended). > > > > Tweak a variety of benign issues that aren't compatible with the sanity > > check, e.g. passing a non-pointer for ioctls(). > > > > Note, static_assert() requires a string on older versions of GCC. Feed > > it an empty string to make the compiler happy. > > > > Signed-off-by: Sean Christopherson <seanjc@xxxxxxxxxx> > > --- > > .../selftests/kvm/include/kvm_util_base.h | 61 +++++++++++++------ > > .../selftests/kvm/lib/aarch64/processor.c | 2 +- > > tools/testing/selftests/kvm/lib/guest_modes.c | 2 +- > > tools/testing/selftests/kvm/lib/kvm_util.c | 29 +-------- > > tools/testing/selftests/kvm/s390x/resets.c | 6 +- > > .../selftests/kvm/x86_64/mmio_warning_test.c | 2 +- > > .../kvm/x86_64/pmu_event_filter_test.c | 2 +- > > .../selftests/kvm/x86_64/xen_shinfo_test.c | 6 +- > > 8 files changed, 56 insertions(+), 54 deletions(-) > > > > diff --git a/tools/testing/selftests/kvm/include/kvm_util_base.h b/tools/testing/selftests/kvm/include/kvm_util_base.h > > index 04ddab322b6b..0eaf0c9b7612 100644 > > --- a/tools/testing/selftests/kvm/include/kvm_util_base.h > > +++ b/tools/testing/selftests/kvm/include/kvm_util_base.h > > @@ -180,29 +180,56 @@ static inline bool kvm_has_cap(long cap) > > #define __KVM_IOCTL_ERROR(_name, _ret) __KVM_SYSCALL_ERROR(_name, _ret) > > #define KVM_IOCTL_ERROR(_ioctl, _ret) __KVM_IOCTL_ERROR(#_ioctl, _ret) > > > > -#define __kvm_ioctl(kvm_fd, cmd, arg) \ > > - ioctl(kvm_fd, cmd, arg) > > +#define kvm_do_ioctl(fd, cmd, arg) \ > > +({ \ > > + static_assert(!_IOC_SIZE(cmd) || sizeof(*arg) == _IOC_SIZE(cmd), ""); \ > > + ioctl(fd, cmd, arg); \ > > +}) > > > > -static inline void _kvm_ioctl(int kvm_fd, unsigned long cmd, const char *name, > > - void *arg) > > -{ > > - int ret = __kvm_ioctl(kvm_fd, cmd, arg); > > +#define __kvm_ioctl(kvm_fd, cmd, arg) \ > > + kvm_do_ioctl(kvm_fd, cmd, arg) > > > > - TEST_ASSERT(!ret, __KVM_IOCTL_ERROR(name, ret)); > > -} > > + > > While we've gained the static asserts we've also lost the type checking > that the inline functions provided. Is there anyway we can bring them back > with more macro tricks? Gah, I overthought this. It doesn't even require macros, just a dummy helper. I wasn't trying to use static_assert() to enforce the type check, which is how I ended up with the sizeof() ugliness (not the one above). But it's far easier to let the compiler do the checking. I'll send a small fixup series to address this and your other feedback. static __always_inline void static_assert_is_vm(struct kvm_vm *vm) { } #define __vm_ioctl(vm, cmd, arg) \ ({ \ static_assert_is_vm(vm); \ kvm_do_ioctl((vm)->fd, cmd, arg); \ }) static __always_inline void static_assert_is_vcpu(struct kvm_vcpu *vcpu) { } #define __vcpu_ioctl(vcpu, cmd, arg) \ ({ \ static_assert_is_vcpu(vcpu); \ kvm_do_ioctl((vcpu)->fd, cmd, arg); \ }) In file included from include/kvm_util.h:10, from lib/x86_64/processor.c:9: lib/x86_64/processor.c: In function ‘_vcpu_set_msr’: lib/x86_64/processor.c:831:33: error: passing argument 1 of ‘static_assert_is_vcpu’ from incompatible pointer type [-Werror=incompatible-pointer-types] 831 | return __vcpu_ioctl(vcpu->vm, KVM_SET_MSRS, &buffer.header); | ~~~~^~~~ | | | struct kvm_vm * include/kvm_util_base.h:232:31: note: in definition of macro ‘__vcpu_ioctl’ 232 | static_assert_is_vcpu(vcpu); \ | ^~~~ include/kvm_util_base.h:225:68: note: expected ‘struct kvm_vcpu *’ but argument is of type ‘struct kvm_vm *’ 225 | static __always_inline void static_assert_is_vcpu(struct kvm_vcpu *vcpu) | ~~~~~~~~~~~~~~~~~^~~~ cc1: all warnings being treated as errors