From 89418520e59f2957dbb192c9db4eb45f108459a4 Mon Sep 17 00:00:00 2001 From: Alexander Potapenko Date: Thu, 8 Aug 2024 17:12:18 +0200 Subject: executor: pkg/vminfo: sys/linux: define syz_kvm_vgic_v3_setup The new pseudo-syscall sets up VGICv3 IRQ controller on the host. That still requires guest setup code, which will be submitted separately. --- executor/common_kvm_arm64.h | 80 +++++++++++++++++++++++++++++++++++++++++++- executor/common_linux.h | 10 ++++-- executor/kvm.h | 3 ++ pkg/vminfo/linux_syscalls.go | 8 +++++ pkg/vminfo/linux_test.go | 1 + sys/linux/dev_kvm.txt | 4 +++ 6 files changed, 103 insertions(+), 3 deletions(-) diff --git a/executor/common_kvm_arm64.h b/executor/common_kvm_arm64.h index a4fe7c57a..94f0ac9cb 100644 --- a/executor/common_kvm_arm64.h +++ b/executor/common_kvm_arm64.h @@ -5,14 +5,16 @@ // Implementation of syz_kvm_setup_cpu pseudo-syscall. -#include "common_kvm_arm64_syzos.h" #include "kvm.h" +#if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu + // Register encodings from https://docs.kernel.org/virt/kvm/api.html. #define KVM_ARM64_REGS_X0 0x6030000000100000UL #define KVM_ARM64_REGS_PC 0x6030000000100040UL #define KVM_ARM64_REGS_SP_EL1 0x6030000000100044UL +#include "common_kvm_arm64_syzos.h" struct kvm_text { uintptr_t typ; const void* text; @@ -149,3 +151,79 @@ static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volat return 0; } +#endif + +#if SYZ_EXECUTOR || __NR_syz_kvm_vgic_v3_setup +static int kvm_set_device_attr(int dev_fd, uint32 group, uint64 attr, void* val) +{ + struct kvm_device_attr kvmattr = { + .flags = 0, + .group = group, + .attr = attr, + .addr = (uintptr_t)val, + }; + + return ioctl(dev_fd, KVM_SET_DEVICE_ATTR, &kvmattr); +} + +static int kvm_create_device(int vm_fd, int type) +{ + struct kvm_create_device create_dev = { + .type = (uint32)type, + .fd = (uint32)-1, + .flags = 0, + }; + + if (ioctl(vm_fd, KVM_CREATE_DEVICE, &create_dev) != -1) + return create_dev.fd; + else + return -1; +} + +#define REDIST_REGION_ATTR_ADDR(count, base, flags, index) \ + (((uint64)(count) << 52) | \ + ((uint64)((base) >> 16) << 16) | \ + ((uint64)(flags) << 12) | \ + index) + +// Set up the VGICv3 interrupt controller. +// syz_kvm_vgic_v3_setup(fd fd_kvmvm, ncpus flags[kvm_num_cpus], nirqs flags[kvm_num_irqs]) +static long syz_kvm_vgic_v3_setup(volatile long a0, volatile long a1, volatile long a2) +{ + const int vm_fd = a0; + const int nr_vcpus = a1; + const int want_nr_irq = a2; + + int vgic_fd = kvm_create_device(vm_fd, KVM_DEV_TYPE_ARM_VGIC_V3); + if (vgic_fd == -1) + return -1; + + uint32 nr_irq = want_nr_irq; + int ret = kvm_set_device_attr(vgic_fd, KVM_DEV_ARM_VGIC_GRP_NR_IRQS, 0, &nr_irq); + if (ret == -1) { + close(vgic_fd); + return -1; + } + + uint64 gicd_base_gpa = ARM64_ADDR_GICD_BASE; + ret = kvm_set_device_attr(vgic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_DIST, &gicd_base_gpa); + if (ret == -1) { + close(vgic_fd); + return -1; + } + uint64 redist_attr = REDIST_REGION_ATTR_ADDR(nr_vcpus, ARM64_ADDR_GICR_BASE, 0, 0); + ret = kvm_set_device_attr(vgic_fd, KVM_DEV_ARM_VGIC_GRP_ADDR, KVM_VGIC_V3_ADDR_TYPE_REDIST_REGION, &redist_attr); + if (ret == -1) { + close(vgic_fd); + return -1; + } + + ret = kvm_set_device_attr(vgic_fd, KVM_DEV_ARM_VGIC_GRP_CTRL, KVM_DEV_ARM_VGIC_CTRL_INIT, NULL); + if (ret == -1) { + close(vgic_fd); + return -1; + } + + return vgic_fd; +} +#endif diff --git a/executor/common_linux.h b/executor/common_linux.h index 3669dee0f..9e7d9717d 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -3173,7 +3173,7 @@ error_clear_loop: } #endif -#if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu +#if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu || __NR_syz_kvm_vgic_v3_setup // KVM is not yet supported on RISC-V #if !GOARCH_riscv64 && !GOARCH_arm #include @@ -3190,12 +3190,18 @@ error_clear_loop: #include "common_kvm_arm64.h" #elif GOARCH_ppc64 || GOARCH_ppc64le #include "common_kvm_ppc64.h" -#elif !GOARCH_arm +#elif !GOARCH_arm && (SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu) static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volatile long a2, volatile long a3, volatile long a4, volatile long a5, volatile long a6, volatile long a7) { return 0; } #endif +#if !GOARCH_arm64 && (SYZ_EXECUTOR || __NR_syz_kvm_vgic_v3_setup) +static long syz_kvm_vgic_v3_setup(volatile long a0, volatile long a1, volatile long a2) +{ + return 0; +} +#endif #endif #endif diff --git a/executor/kvm.h b/executor/kvm.h index a66aa6727..32fda309d 100644 --- a/executor/kvm.h +++ b/executor/kvm.h @@ -76,6 +76,9 @@ #define NEXT_INSN $0xbadc0de #define PREFIX_SIZE 0xba1d +// GICv3 distributor/redistributor addresses. +#define ARM64_ADDR_GICD_BASE 0x08000000 +#define ARM64_ADDR_GICR_BASE 0x080A0000 // Write to this page to trigger a page fault and stop KVM_RUN. #define ARM64_ADDR_EXIT 0xdddd0000 // Dedicated address within the exit page for the uexit command. diff --git a/pkg/vminfo/linux_syscalls.go b/pkg/vminfo/linux_syscalls.go index 3f675c6a9..2b02ad7f9 100644 --- a/pkg/vminfo/linux_syscalls.go +++ b/pkg/vminfo/linux_syscalls.go @@ -80,6 +80,7 @@ var linuxSyscallChecks = map[string]func(*checkContext, *prog.Syscall) string{ "syz_usb_ep_write": linuxCheckUSBEmulation, "syz_usb_ep_read": linuxCheckUSBEmulation, "syz_kvm_setup_cpu": linuxSyzKvmSetupCPUSupported, + "syz_kvm_vgic_v3_setup": linuxSyzKvmVgicV3SetupSupported, "syz_emit_vhci": linuxVhciInjectionSupported, "syz_init_net_socket": linuxSyzInitNetSocketSupported, "syz_genetlink_get_family_id": linuxSyzGenetlinkGetFamilyIDSupported, @@ -184,6 +185,13 @@ func linuxSyzKvmSetupCPUSupported(ctx *checkContext, call *prog.Syscall) string return "unsupported arch" } +func linuxSyzKvmVgicV3SetupSupported(ctx *checkContext, call *prog.Syscall) string { + if ctx.target.Arch == targets.ARM64 { + return "" + } + return "unsupported arch" +} + func linuxSupportedMount(ctx *checkContext, call *prog.Syscall) string { return linuxSupportedFilesystem(ctx, call, 2) } diff --git a/pkg/vminfo/linux_test.go b/pkg/vminfo/linux_test.go index c026eb478..5295f4e94 100644 --- a/pkg/vminfo/linux_test.go +++ b/pkg/vminfo/linux_test.go @@ -48,6 +48,7 @@ func TestLinuxSyscalls(t *testing.T) { expectDisabled := map[string]bool{ "syz_kvm_setup_cpu$arm64": true, "syz_kvm_setup_cpu$ppc64": true, + "syz_kvm_vgic_v3_setup": true, } // All mount and syz_mount_image calls except for ext4 and binder will be disabled. for call := range disabled { diff --git a/sys/linux/dev_kvm.txt b/sys/linux/dev_kvm.txt index 3a8403eb5..1375c2c6a 100644 --- a/sys/linux/dev_kvm.txt +++ b/sys/linux/dev_kvm.txt @@ -196,6 +196,10 @@ syz_kvm_setup_cpu$x86(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in syz_kvm_setup_cpu$arm64(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in, array[kvm_text_arm64, 1]], ntext len[text], flags const[0], opts ptr[in, array[kvm_setup_opt_arm64, 1]], nopt len[opts]) syz_kvm_setup_cpu$ppc64(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in, array[kvm_text_ppc64, 1]], ntext len[text], flags flags[kvm_setup_flags_ppc64], opts ptr[in, array[kvm_setup_opt_ppc64, 1]], nopt len[opts]) +kvm_num_irqs = 32, 64, 128, 256, 512 +# This pseudo-syscall is ARM64-specific. +syz_kvm_vgic_v3_setup(fd fd_kvmvm, ncpus intptr[0:4], nirqs flags[kvm_num_irqs]) + resource kvm_run_ptr[int64] define KVM_RUN_SIZE sizeof(struct kvm_run) mmap$KVM_VCPU(addr vma, len vcpu_mmap_size, prot flags[mmap_prot], flags flags[mmap_flags], cpufd fd_kvmcpu, offset const[0]) kvm_run_ptr -- cgit mrf-deployment