99
1010#include "kvm.h"
1111#include "kvm_amd64.S.h"
12+ #include "common_kvm_amd64_syzos.h"
1213
1314#ifndef KVM_SMI
1415#define KVM_SMI _IO(KVMIO, 0xb7)
@@ -69,7 +70,9 @@ struct tss32 {
6970 uint16 trace ;
7071 uint16 io_bitmap ;
7172} __attribute__((packed ));
73+ #endif
7274
75+ #if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu || __NR_syz_kvm_add_vcpu
7376struct tss64 {
7477 uint32 reserved0 ;
7578 uint64 rsp [3 ];
@@ -79,9 +82,7 @@ struct tss64 {
7982 uint32 reserved3 ;
8083 uint32 io_bitmap ;
8184} __attribute__((packed ));
82- #endif
8385
84- #if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu
8586static void fill_segment_descriptor (uint64 * dt , uint64 * lt , struct kvm_segment * seg )
8687{
8788 uint16 index = seg -> selector >> 3 ;
@@ -200,7 +201,7 @@ static void setup_64bit_idt(struct kvm_sregs* sregs, char* host_mem, uintptr_t g
200201}
201202#endif
202203
203- #if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu
204+ #if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu || __NR_syz_kvm_add_vcpu
204205struct kvm_text {
205206 uintptr_t typ ;
206207 const void * text ;
@@ -215,6 +216,125 @@ struct kvm_opt {
215216};
216217#endif
217218
219+ #if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu || __NR_syz_kvm_add_vcpu
220+ #define GENMASK_ULL (h , l ) (((~_UL(0)) - (_UL(1) << (l)) + 1) & \
221+ (~_UL(0) >> (__BITS_PER_LONG - 1 - (h))))
222+ #define PAGE_MASK GENMASK_ULL(51, 12)
223+
224+ // We assume a 4-level page table, in the future we could add support for
225+ // n-level if needed.
226+ static void setup_pg_table (void * host_mem )
227+ {
228+ uint64 * pml4 = (uint64 * ) ((uint64 )host_mem + X86_ADDR_PML4 );
229+ uint64 * pdp = (uint64 * ) ((uint64 )host_mem + X86_ADDR_PDP );
230+ uint64 * pd = (uint64 * ) ((uint64 )host_mem + X86_ADDR_PD );
231+ uint64 * pd_ioapic = (uint64 * ) ((uint64 )host_mem + X86_ADDR_PD_IOAPIC );
232+
233+ pml4 [0 ] = X86_PDE64_PRESENT | X86_PDE64_RW | (X86_ADDR_PDP & PAGE_MASK );
234+ pdp [0 ] = X86_PDE64_PRESENT | X86_PDE64_RW | (X86_ADDR_PD & PAGE_MASK );
235+ pdp [3 ] = X86_PDE64_PRESENT | X86_PDE64_RW | (X86_ADDR_PD_IOAPIC & PAGE_MASK );
236+
237+ pd [0 ] = X86_PDE64_PRESENT | X86_PDE64_RW | X86_PDE64_PS ;
238+ pd_ioapic [502 ] = X86_PDE64_PRESENT | X86_PDE64_RW | X86_PDE64_PS ;
239+ }
240+
241+
242+ // This only sets up a 64-bit VCPU.
243+ // TODO: Should add support for other modes.
244+ static void setup_gdt_ldt_pg (int cpufd , void * host_mem )
245+ {
246+ struct kvm_sregs sregs ;
247+ ioctl (cpufd , KVM_GET_SREGS , & sregs );
248+
249+ sregs .gdt .base = X86_ADDR_GDT ;
250+ sregs .gdt .limit = 256 * sizeof (uint64 ) - 1 ;
251+ uint64 * gdt = (uint64 * )((uint64 )host_mem + sregs .gdt .base );
252+
253+ struct kvm_segment seg_ldt ;
254+ memset (& seg_ldt , 0 , sizeof (seg_ldt ));
255+ seg_ldt .selector = X86_SEL_LDT ;
256+ seg_ldt .type = 2 ;
257+ seg_ldt .base = X86_ADDR_LDT ;
258+ seg_ldt .limit = 256 * sizeof (uint64 ) - 1 ;
259+ seg_ldt .present = 1 ;
260+ seg_ldt .dpl = 0 ;
261+ seg_ldt .s = 0 ;
262+ seg_ldt .g = 0 ;
263+ seg_ldt .db = 1 ;
264+ seg_ldt .l = 0 ;
265+ sregs .ldt = seg_ldt ;
266+ uint64 * ldt = (uint64 * )((uint64 )host_mem + sregs .ldt .base );
267+
268+ struct kvm_segment seg_cs64 ;
269+ memset (& seg_cs64 , 0 , sizeof (seg_cs64 ));
270+ seg_cs64 .selector = X86_SEL_CS64 ;
271+ seg_cs64 .type = 11 ;
272+ seg_cs64 .base = 0 ;
273+ seg_cs64 .limit = 0xFFFFFFFFu ;
274+ seg_cs64 .present = 1 ;
275+ seg_cs64 .s = 1 ;
276+ seg_cs64 .g = 1 ;
277+ seg_cs64 .l = 1 ;
278+
279+ sregs .cs = seg_cs64 ;
280+
281+ struct kvm_segment seg_ds64 ;
282+ memset (& seg_ds64 , 0 , sizeof (struct kvm_segment ));
283+ seg_ds64 .selector = X86_SEL_DS64 ;
284+ seg_ds64 .type = 3 ;
285+ seg_ds64 .limit = 0xFFFFFFFFu ;
286+ seg_ds64 .present = 1 ;
287+ seg_ds64 .s = 1 ;
288+ seg_ds64 .g = 1 ;
289+
290+ sregs .ds = seg_ds64 ;
291+ sregs .es = seg_ds64 ;
292+
293+ struct kvm_segment seg_tss64 ;
294+ memset (& seg_tss64 , 0 , sizeof (seg_tss64 ));
295+ seg_tss64 .selector = X86_SEL_TSS64 ;
296+ seg_tss64 .base = X86_ADDR_VAR_TSS64 ;
297+ seg_tss64 .limit = 0x1ff ;
298+ seg_tss64 .type = 9 ;
299+ seg_tss64 .present = 1 ;
300+
301+ struct tss64 tss64 ;
302+ memset (& tss64 , 0 , sizeof (tss64 ));
303+ tss64 .rsp [0 ] = X86_ADDR_STACK0 ;
304+ tss64 .rsp [1 ] = X86_ADDR_STACK0 ;
305+ tss64 .rsp [2 ] = X86_ADDR_STACK0 ;
306+ tss64 .io_bitmap = offsetof(struct tss64 , io_bitmap );
307+ struct tss64 * tss64_addr = (struct tss64 * )((uint64 )host_mem + seg_tss64 .base );
308+ memcpy (tss64_addr , & tss64 , sizeof (tss64 ));
309+
310+ fill_segment_descriptor (gdt , ldt , & seg_ldt );
311+ fill_segment_descriptor (gdt , ldt , & seg_cs64 );
312+ fill_segment_descriptor (gdt , ldt , & seg_ds64 );
313+ fill_segment_descriptor_dword (gdt , ldt , & seg_tss64 );
314+
315+ setup_pg_table (host_mem );
316+
317+ sregs .cr0 = X86_CR0_PE | X86_CR0_NE | X86_CR0_PG ;
318+ sregs .cr4 |= X86_CR4_PAE | X86_CR4_OSFXSR ;
319+ sregs .efer |= (X86_EFER_LME | X86_EFER_LMA | X86_EFER_NXE );
320+ sregs .cr3 = X86_ADDR_PML4 ;
321+
322+ ioctl (cpufd , KVM_SET_SREGS , & sregs );
323+ }
324+
325+ static void setup_cpuid (int cpufd )
326+ {
327+ int kvmfd = open ("/dev/kvm" , O_RDWR );
328+ char buf [sizeof (struct kvm_cpuid2 ) + 128 * sizeof (struct kvm_cpuid_entry2 )];
329+ memset (buf , 0 , sizeof (buf ));
330+ struct kvm_cpuid2 * cpuid = (struct kvm_cpuid2 * )buf ;
331+ cpuid -> nent = 128 ;
332+ ioctl (kvmfd , KVM_GET_SUPPORTED_CPUID , cpuid );
333+ ioctl (cpufd , KVM_SET_CPUID2 , cpuid );
334+ close (kvmfd );
335+ }
336+ #endif
337+
218338#if SYZ_EXECUTOR || __NR_syz_kvm_setup_cpu
219339#define KVM_SETUP_PAGING (1 << 0)
220340#define KVM_SETUP_PAE (1 << 1)
@@ -764,18 +884,173 @@ static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volat
764884}
765885#endif
766886
887+ #if SYZ_EXECUTOR || __NR_syz_kvm_add_vcpu
888+ static void reset_cpu_regs (int cpufd , int cpu_id , size_t text_size )
889+ {
890+ struct kvm_regs regs ;
891+ memset (& regs , 0 , sizeof (regs ));
892+
893+ regs .rflags |= 2 ; // bit 1 is always set
894+ // PC points to the relative offset of guest_main() within the guest code.
895+ regs .rip = X86_ADDR_EXECUTOR_CODE + ((uint64 )guest_main - (uint64 )& __start_guest );
896+ regs .rsp = X86_ADDR_STACK0 ;
897+ // Pass parameters to guest_main().
898+ regs .rdi = text_size ;
899+ regs .rsi = cpu_id ;
900+ ioctl (cpufd , KVM_SET_REGS , & regs );
901+ }
902+
903+ static void install_user_code (int cpufd , void * user_text_slot , int cpu_id , const void * text , size_t text_size , void * host_mem )
904+ {
905+ if ((cpu_id < 0 ) || (cpu_id >= KVM_MAX_VCPU ))
906+ return ;
907+ if (!user_text_slot )
908+ return ;
909+ if (text_size > KVM_PAGE_SIZE )
910+ text_size = KVM_PAGE_SIZE ;
911+ void * target = (void * )((uint64 )user_text_slot + (KVM_PAGE_SIZE * cpu_id ));
912+ memcpy (target , text , text_size );
913+ setup_gdt_ldt_pg (cpufd , host_mem );
914+ setup_cpuid (cpufd );
915+ reset_cpu_regs (cpufd , cpu_id , text_size );
916+ }
917+ #endif
918+
919+ #if SYZ_EXECUTOR || __NR_syz_kvm_setup_syzos_vm
920+ struct addr_size {
921+ void * addr ;
922+ size_t size ;
923+ };
924+
925+ static struct addr_size alloc_guest_mem (struct addr_size * free , size_t size )
926+ {
927+ struct addr_size ret = {.addr = NULL , .size = 0 };
928+
929+ if (free -> size < size )
930+ return ret ;
931+ ret .addr = free -> addr ;
932+ ret .size = size ;
933+ free -> addr = (void * )((char * )free -> addr + size );
934+ free -> size -= size ;
935+ return ret ;
936+ }
937+
938+ // Call KVM_SET_USER_MEMORY_REGION for the given pages.
939+ static void vm_set_user_memory_region (int vmfd , uint32 slot , uint32 flags , uint64 guest_phys_addr , uint64 memory_size , uint64 userspace_addr )
940+ {
941+ struct kvm_userspace_memory_region memreg ;
942+ memreg .slot = slot ;
943+ memreg .flags = flags ;
944+ memreg .guest_phys_addr = guest_phys_addr ;
945+ memreg .memory_size = memory_size ;
946+ memreg .userspace_addr = userspace_addr ;
947+ ioctl (vmfd , KVM_SET_USER_MEMORY_REGION , & memreg );
948+ }
949+
950+ static void install_syzos_code (void * host_mem , size_t mem_size )
951+ {
952+ size_t size = (char * )& __stop_guest - (char * )& __start_guest ;
953+ if (size > mem_size )
954+ fail ("SyzOS size exceeds guest memory" );
955+ memcpy (host_mem , & __start_guest , size );
956+ }
957+
958+ static void setup_vm (int vmfd , void * host_mem , void * * text_slot )
959+ {
960+ // Guest virtual memory layout (must be in sync with executor/kvm.h):
961+ // 0x00000000 - AMD64 data structures (10 pages, see kvm.h)
962+ // 0x00030000 - SMRAM (10 pages)
963+ // 0x00040000 - unmapped region to trigger a page faults for uexits etc. (1 page)
964+ // 0x00041000 - writable region with KVM_MEM_LOG_DIRTY_PAGES to fuzz dirty ring (2 pages)
965+ // 0x00050000 - user code (4 pages)
966+ // 0x00054000 - executor guest code (4 pages)
967+ // 0000058000 - scratch memory for code generated at runtime (1 page)
968+ // 0xfec00000 - IOAPIC (1 page)
969+ struct addr_size allocator = {.addr = host_mem , .size = KVM_GUEST_MEM_SIZE };
970+ int slot = 0 ; // Slot numbers do not matter, they just have to be different.
971+
972+ // This *needs* to be the first allocation to avoid passing pointers
973+ // around for the gdt/ldt/page table setup.
974+ struct addr_size next = alloc_guest_mem (& allocator , 10 * KVM_PAGE_SIZE );
975+ vm_set_user_memory_region (vmfd , slot ++ , 0 , 0 , next .size , (uintptr_t )next .addr );
976+
977+ next = alloc_guest_mem (& allocator , 10 * KVM_PAGE_SIZE );
978+ vm_set_user_memory_region (vmfd , slot ++ , 0 , X86_ADDR_SMRAM , next .size , (uintptr_t )next .addr );
979+
980+ next = alloc_guest_mem (& allocator , 2 * KVM_PAGE_SIZE );
981+ vm_set_user_memory_region (vmfd , slot ++ , KVM_MEM_LOG_DIRTY_PAGES , X86_ADDR_DIRTY_PAGES , next .size , (uintptr_t )next .addr );
982+
983+ next = alloc_guest_mem (& allocator , KVM_MAX_VCPU * KVM_PAGE_SIZE );
984+ vm_set_user_memory_region (vmfd , slot ++ , KVM_MEM_READONLY , X86_ADDR_USER_CODE , next .size , (uintptr_t )next .addr );
985+ if (text_slot )
986+ * text_slot = next .addr ;
987+
988+ struct addr_size host_text = alloc_guest_mem (& allocator , 4 * KVM_PAGE_SIZE );
989+ install_syzos_code (host_text .addr , host_text .size );
990+ vm_set_user_memory_region (vmfd , slot ++ , KVM_MEM_READONLY , X86_ADDR_EXECUTOR_CODE , host_text .size , (uintptr_t )host_text .addr );
991+
992+ next = alloc_guest_mem (& allocator , KVM_PAGE_SIZE );
993+ vm_set_user_memory_region (vmfd , slot ++ , 0 , X86_ADDR_SCRATCH_CODE , next .size , (uintptr_t )next .addr );
994+
995+ next = alloc_guest_mem (& allocator , KVM_PAGE_SIZE );
996+ vm_set_user_memory_region (vmfd , slot ++ , 0 , X86_ADDR_IOAPIC , next .size , (uintptr_t )next .addr );
997+
998+ // Map the remaining pages at an unused address.
999+ next = alloc_guest_mem (& allocator , allocator .size );
1000+ vm_set_user_memory_region (vmfd , slot ++ , 0 , X86_ADDR_UNUSED , next .size , (uintptr_t )next .addr );
1001+ }
1002+ #endif
1003+
1004+ #if SYZ_EXECUTOR || __NR_syz_kvm_setup_syzos_vm || __NR_syz_kvm_add_vcpu_amd64
1005+ struct kvm_syz_vm {
1006+ int vmfd ;
1007+ int next_cpu_id ;
1008+ void * user_text ;
1009+ void * host_mem ;
1010+ };
1011+ #endif
1012+
7671013#if SYZ_EXECUTOR || __NR_syz_kvm_setup_syzos_vm
7681014static long syz_kvm_setup_syzos_vm (volatile long a0 , volatile long a1 )
7691015{
770- // Placeholder.
771- return 0 ;
1016+ const int vmfd = a0 ;
1017+ void * host_mem = (void * )a1 ;
1018+
1019+ void * user_text_slot = NULL ;
1020+ struct kvm_syz_vm * ret = (struct kvm_syz_vm * )host_mem ;
1021+ host_mem = (void * )((uint64 )host_mem + KVM_PAGE_SIZE );
1022+ setup_vm (vmfd , host_mem , & user_text_slot );
1023+ ret -> vmfd = vmfd ;
1024+ ret -> next_cpu_id = 0 ;
1025+ ret -> user_text = user_text_slot ;
1026+ ret -> host_mem = host_mem ;
1027+ return (long )ret ;
7721028}
7731029#endif
7741030
7751031#if SYZ_EXECUTOR || __NR_syz_kvm_add_vcpu
7761032static long syz_kvm_add_vcpu (volatile long a0 , volatile long a1 )
7771033{
778- // Placeholder.
779- return 0 ;
1034+ struct kvm_syz_vm * vm = (struct kvm_syz_vm * )a0 ;
1035+ struct kvm_text * utext = (struct kvm_text * )a1 ;
1036+ const void * text = utext -> text ;
1037+ size_t text_size = utext -> size ;
1038+
1039+ if (!vm ) {
1040+ errno = EINVAL ;
1041+ return -1 ;
1042+ }
1043+ if (vm -> next_cpu_id == KVM_MAX_VCPU ) {
1044+ errno = ENOMEM ;
1045+ return -1 ;
1046+ }
1047+ int cpu_id = vm -> next_cpu_id ;
1048+ int cpufd = ioctl (vm -> vmfd , KVM_CREATE_VCPU , cpu_id );
1049+ if (cpufd == -1 )
1050+ return -1 ;
1051+ // Only increment next_cpu_id if CPU creation succeeded.
1052+ vm -> next_cpu_id ++ ;
1053+ install_user_code (cpufd , vm -> user_text , cpu_id , text , text_size , vm -> host_mem );
1054+ return cpufd ;
7801055}
7811056#endif
0 commit comments