From 06648d9ccf5ec6f9453ef09b0fdbdebf020ee0f8 Mon Sep 17 00:00:00 2001 From: 6eanut Date: Thu, 8 Jan 2026 10:04:33 +0800 Subject: executor, sys/linux, pkg: enable syz_kvm_setup_cpu for riscv64 This patch implements syz_kvm_setup_cpu for riscv64 architecture. The pseudo-syscall accepts VM fd, vCPU fd, host memory, and guest code as parameters. Additional parameters (ntext, flags, opts, nopt) are included for interface consistency with other architectures but are currently unused on riscv64. Implementation: - Set up guest memory via KVM_SET_USER_MEMORY_REGION - Copy guest code to guest memory - Initialize guest registers to enable code execution in S-mode - Return 0 on success, -1 on failure Testing: A test file syz_kvm_setup_cpu_riscv64 is included in sys/linux/test/ to verify basic functionality. Known limitations: - ifuzz is not yet compatible with riscv64. Temporary workaround: set text[riscv64] to TextTarget and return nil in createTargetIfuzzConfig for riscv64 to ensure generateText and mutateText work correctly. This patch also adds support for KVM_GET_ONE_REG ioctl. --- executor/common_kvm_riscv64.h | 157 +++++++++++++++++++++++++++++++ executor/common_linux.h | 2 + pkg/compiler/types.go | 4 +- pkg/vminfo/linux_syscalls.go | 4 + sys/linux/dev_kvm.txt | 14 +++ sys/linux/dev_kvm_riscv64.txt | 19 ++++ sys/linux/test/syz_kvm_setup_cpu_riscv64 | 24 +++++ 7 files changed, 223 insertions(+), 1 deletion(-) create mode 100644 executor/common_kvm_riscv64.h create mode 100644 sys/linux/test/syz_kvm_setup_cpu_riscv64 diff --git a/executor/common_kvm_riscv64.h b/executor/common_kvm_riscv64.h new file mode 100644 index 000000000..dcccd6dad --- /dev/null +++ b/executor/common_kvm_riscv64.h @@ -0,0 +1,157 @@ +// Copyright 2026 syzkaller project authors. All rights reserved. +// Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. + +#ifndef EXECUTOR_COMMON_KVM_RISCV64_H +#define EXECUTOR_COMMON_KVM_RISCV64_H + +// This file is shared between executor and csource package. + +// Implementation of syz_kvm_setup_cpu pseudo-syscall. + +#include +#include +#include + +struct kvm_text { + uintptr_t type; + const void* text; + uintptr_t size; +}; + +// Construct RISC-V register id for KVM. +#define RISCV_CORE_REG(idx) (KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CORE | (idx)) +#define RISCV_CSR_REG(idx) (KVM_REG_RISCV | KVM_REG_SIZE_U64 | KVM_REG_RISCV_CSR | (idx)) + +// Represent CSR indices in the kvm_riscv_csr structure. +enum riscv_csr_index { + CSR_SSTATUS = 0, + CSR_SIE, + CSR_STVEC, + CSR_SSCRATCH, + CSR_SEPC, + CSR_SCAUSE, + CSR_STVAL, + CSR_SIP, + CSR_SATP, + CSR_SCOUNTEREN, + CSR_SENVCFG +}; + +// Represent CORE register indices in the kvm_riscv_core structure. +enum riscv_core_index { + CORE_PC = 0x00, + CORE_RA, + CORE_SP, + CORE_GP, + CORE_TP, + CORE_T0, + CORE_T1, + CORE_T2, + CORE_S0, + CORE_S1, + CORE_A0, + CORE_A1, + CORE_A2, + CORE_A3, + CORE_A4, + CORE_A5, + CORE_A6, + CORE_A7, + CORE_S2, + CORE_S3, + CORE_S4, + CORE_S5, + CORE_S6, + CORE_S7, + CORE_S8, + CORE_S9, + CORE_S10, + CORE_S11, + CORE_T3, + CORE_T4, + CORE_T5, + CORE_T6, + // Store the privilege mode: 1=S-mode, 0=U-mode. + CORE_MODE +}; + +// Indicate the Supervisor Previous Privilege mode. +#define SSTATUS_SPP (1UL << 8) +// Indicate the Supervisor Previous Interrupt Enable state. +#define SSTATUS_SPIE (1UL << 5) +// Indicate the Supervisor Interrupt Enable state. +#define SSTATUS_SIE (1UL << 1) + +// Define the starting physical address for the guest code. +#define CODE_START 0x80000000ULL + +// Set a single register value for the specified CPU file descriptor. +static inline int kvm_set_reg(int cpufd, unsigned long id, unsigned long value) +{ + struct kvm_one_reg reg = { + .id = id, + .addr = (unsigned long)&value, + }; + return ioctl(cpufd, KVM_SET_ONE_REG, ®); +} + +// syz_kvm_setup_cpu$riscv64(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in, array[kvm_text_riscv64, 1]], ntext len[text], flags const[0], opts ptr[in, array[kvm_setup_opt_riscv64, 1]], nopt len[opts]) +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) +{ + const int vmfd = a0; + const int cpufd = a1; + char* const host_mem = (char*)a2; + const struct kvm_text* const text_array_ptr = (struct kvm_text*)a3; + + const uintptr_t page_size = 4096; + const uintptr_t guest_pages = 24; + const uintptr_t guest_mem_size = guest_pages * page_size; + + // Install guest memory. + for (uintptr_t i = 0; i < guest_pages; i++) { + struct kvm_userspace_memory_region mem = { + .slot = (unsigned int)i, + .flags = 0, + .guest_phys_addr = CODE_START + i * page_size, + .memory_size = page_size, + .userspace_addr = + (uintptr_t)(host_mem + i * page_size), + }; + + if (ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &mem)) + return -1; + } + + // Copy guest code. + const void* text = 0; + uintptr_t size = 0; + NONFAILING(text = text_array_ptr[0].text); + NONFAILING(size = text_array_ptr[0].size); + if (size > guest_mem_size) + size = guest_mem_size; + memcpy(host_mem, text, size); + + // Initialize VCPU registers. + // Set PC (program counter) to start of code. + if (kvm_set_reg(cpufd, RISCV_CORE_REG(CORE_PC), CODE_START)) + return -1; + // Set SP (stack pointer) at end of memory, reserving space for stack. + unsigned long stack_top = CODE_START + guest_mem_size - page_size; + if (kvm_set_reg(cpufd, RISCV_CORE_REG(CORE_SP), stack_top)) + return -1; + // Set privilege mode to S-mode. + if (kvm_set_reg(cpufd, RISCV_CORE_REG(CORE_MODE), 1)) + return -1; + // Set SSTATUS CSR with SPP and SPIE. + unsigned long sstatus = SSTATUS_SPP | SSTATUS_SPIE; + if (kvm_set_reg(cpufd, RISCV_CSR_REG(CSR_SSTATUS), sstatus)) + return -1; + // Set STVEC. + unsigned long stvec = CODE_START + page_size; + if (kvm_set_reg(cpufd, RISCV_CSR_REG(CSR_STVEC), stvec)) + return -1; + + return 0; +} + +#endif // EXECUTOR_COMMON_KVM_RISCV64_H \ No newline at end of file diff --git a/executor/common_linux.h b/executor/common_linux.h index de393227d..7c4fd9b68 100644 --- a/executor/common_linux.h +++ b/executor/common_linux.h @@ -3216,6 +3216,8 @@ error_clear_loop: #include "common_kvm_arm64.h" #elif GOARCH_ppc64 || GOARCH_ppc64le #include "common_kvm_ppc64.h" +#elif GOARCH_riscv64 +#include "common_kvm_riscv64.h" #elif 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) { diff --git a/pkg/compiler/types.go b/pkg/compiler/types.go index e022efafe..86876b69d 100644 --- a/pkg/compiler/types.go +++ b/pkg/compiler/types.go @@ -641,7 +641,7 @@ var typeText = &typeDesc{ var typeArgTextType = &typeArg{ Kind: kindIdent, - Names: []string{"target", "x86_real", "x86_16", "x86_32", "x86_64", "arm64", "ppc64"}, + Names: []string{"target", "x86_real", "x86_16", "x86_32", "x86_64", "arm64", "ppc64", "riscv64"}, } func genTextType(t *ast.Type) prog.TextKind { @@ -660,6 +660,8 @@ func genTextType(t *ast.Type) prog.TextKind { return prog.TextArm64 case "ppc64": return prog.TextPpc64 + case "riscv64": + return prog.TextTarget default: panic(fmt.Sprintf("unknown text type %q", t.Ident)) } diff --git a/pkg/vminfo/linux_syscalls.go b/pkg/vminfo/linux_syscalls.go index 605b939d2..11a7b9ddd 100644 --- a/pkg/vminfo/linux_syscalls.go +++ b/pkg/vminfo/linux_syscalls.go @@ -192,6 +192,10 @@ func linuxSyzKvmSupported(ctx *checkContext, call *prog.Syscall) string { if ctx.target.Arch == targets.ARM64 { return "" } + case "syz_kvm_setup_cpu$riscv64": + if ctx.target.Arch == targets.RiscV64 { + return "" + } case "syz_kvm_setup_cpu$ppc64": if ctx.target.Arch == targets.PPC64LE { return "" diff --git a/sys/linux/dev_kvm.txt b/sys/linux/dev_kvm.txt index 73e5edccc..aa13f723a 100644 --- a/sys/linux/dev_kvm.txt +++ b/sys/linux/dev_kvm.txt @@ -350,8 +350,17 @@ kvm_one_reg [ arm64_sve kvm_one_reg_arm64_range[0x6080000000150000:0x6080000000150620] arm64_sve_vls kvm_one_reg_arm64_range[0x606000000015ffff] other kvm_one_reg_other +# For riscv64 + riscv64_config kvm_one_reg_riscv64[kvm_regs_riscv64_config] + riscv64_core kvm_one_reg_riscv64[kvm_regs_riscv64_core] + riscv64_csr kvm_one_reg_riscv64[kvm_regs_riscv64_csr] ] +type kvm_one_reg_riscv64[FTYPE] { + id flags[FTYPE, int64] + addr ptr64[inout, int64] +} + type kvm_one_reg_arm64[FTYPE] { id flags[FTYPE, int64] addr ptr64[inout, int64] @@ -623,3 +632,8 @@ kvm_regs_arm64_sys = 0x6030000000138002, 0x6030000000138010, 0x6030000000138012, # Extra registers that KVM_GET_REG_LIST prints on QEMU kvm_regs_arm64_extra = 0x603000000013c01b, 0x603000000013c01f, 0x603000000013c022, 0x603000000013c023, 0x603000000013c025, 0x603000000013c026, 0x603000000013c027, 0x603000000013c02a, 0x603000000013c02b, 0x603000000013c02e, 0x603000000013c02f, 0x603000000013c033, 0x603000000013c034, 0x603000000013c035, 0x603000000013c036, 0x603000000013c037, 0x603000000013c03b, 0x603000000013c03c, 0x603000000013c03d, 0x603000000013c03e, 0x603000000013c03f, 0x603000000013c103, 0x603000000013c512, 0x603000000013c513 # End of register descriptions generated by tools/arm64/registers.go + +# For riscv64, https://elixir.bootlin.com/linux/v6.19-rc4/source/Documentation/virt/kvm/api.rst#L2765 +kvm_regs_riscv64_config = 0x8030000000100000 +kvm_regs_riscv64_core = 0x8030000000200000, 0x8030000000200001, 0x8030000000200002, 0x8030000000200003, 0x8030000000200004, 0x8030000000200005, 0x8030000000200006, 0x8030000000200007, 0x8030000000200008, 0x8030000000200009, 0x803000000020000a, 0x803000000020000b, 0x803000000020000c, 0x803000000020000d, 0x803000000020000e, 0x803000000020000f, 0x8030000000200010, 0x8030000000200011, 0x8030000000200012, 0x8030000000200013, 0x8030000000200014, 0x8030000000200015, 0x8030000000200016, 0x8030000000200017, 0x8030000000200018, 0x8030000000200019, 0x803000000020001a, 0x803000000020001b, 0x803000000020001c, 0x803000000020001d, 0x803000000020001e, 0x803000000020001f, 0x8030000000200020 +kvm_regs_riscv64_csr = 0x8030000000300000, 0x8030000000300001, 0x8030000000300002, 0x8030000000300003, 0x8030000000300004, 0x8030000000300005, 0x8030000000300006, 0x8030000000300007, 0x8030000000300008 diff --git a/sys/linux/dev_kvm_riscv64.txt b/sys/linux/dev_kvm_riscv64.txt index 1079853a7..c6ecde793 100644 --- a/sys/linux/dev_kvm_riscv64.txt +++ b/sys/linux/dev_kvm_riscv64.txt @@ -12,3 +12,22 @@ ioctl$KVM_SET_GUEST_DEBUG_riscv64(fd fd_kvmcpu, cmd const[KVM_SET_GUEST_DEBUG], kvm_guest_debug_arch_riscv64 { reg array[int64, 8] } + +syz_kvm_setup_cpu$riscv64(fd fd_kvmvm, cpufd fd_kvmcpu, usermem vma[24], text ptr[in, array[kvm_text_riscv64, 1]], ntext len[text], flags const[0], opts ptr[in, array[kvm_setup_opt_riscv64, 1]], nopt len[opts]) + +kvm_setup_opt_riscv64 [ +# unions need at least 2 fields, but we have only 1 now, but we want to have it as union for future extention + featur1 kvm_setup_opt_riscv64_feature + featur2 kvm_setup_opt_riscv64_feature +] + +kvm_setup_opt_riscv64_feature { + typ const[1, int64] + val int64 +} + +kvm_text_riscv64 { + typ const[0, intptr] + text ptr[in, text[riscv64]] + size len[text, intptr] +} diff --git a/sys/linux/test/syz_kvm_setup_cpu_riscv64 b/sys/linux/test/syz_kvm_setup_cpu_riscv64 new file mode 100644 index 000000000..3e7c222f1 --- /dev/null +++ b/sys/linux/test/syz_kvm_setup_cpu_riscv64 @@ -0,0 +1,24 @@ +# +# requires: arch=riscv64 +# + +r0 = openat$kvm(0xffffffffffffff9c, &(0x7f0000000000), 0x0, 0x0) +r1 = ioctl$KVM_CREATE_VM(r0, AUTO, 0x0) +r2 = ioctl$KVM_CREATE_VCPU(r1, AUTO, 0x0) + +# +# Set the register +# 0x04200513, li a0, 0x42 (addi a0, zero, 0x42) +# 0x06300593, li a1, 0x63 (addi a1, zero, 0x63) +# Load the MMIO address to t1 (without affecting a0, a1) +# 0x40000337, lui t1, 0x40000 (load 20 bits high to t1) +# Read from the MMIO address (this triggers KVM_EXIT_MMIO) +# 0x00032683, lw a3, 0(t1) (read from address 0x40000000 to a3) +# +syz_kvm_setup_cpu$riscv64(r1, r2, &(0x7f0000fe8000/0x180000)=nil,&(0x7f0000000000)=[{0x0, &(0x7f0000001000)="13052004930530063703004083260300", 0xf}], 0x1, 0x0, 0x0, 0x0) + +ioctl$KVM_RUN(r2, AUTO, 0x0) + +ioctl$KVM_GET_ONE_REG(r2, AUTO, &AUTO=@riscv64_core={0x803000000200000a, &AUTO}) +ioctl$KVM_GET_ONE_REG(r2, AUTO, &AUTO=@riscv64_config={0x8030000001000000, &AUTO}) +ioctl$KVM_GET_ONE_REG(r2, AUTO, &AUTO=@riscv64_csr={0x8030000003000000, &AUTO}) \ No newline at end of file -- cgit mrf-deployment