diff options
| author | Alexander Potapenko <glider@google.com> | 2025-10-01 16:35:10 +0200 |
|---|---|---|
| committer | Alexander Potapenko <glider@google.com> | 2025-10-17 06:51:20 +0000 |
| commit | e69835fc40b5e00c0996ce3a85d8287eea57d162 (patch) | |
| tree | d935f0f54b202ec2402f74852f7d44735b567653 /executor | |
| parent | b2cf58f4862666d3b14f051b1e8cddcde9f8186f (diff) | |
executor: sys/linux: implement SYZOS_API_SET_IRQ_HANDLER
The new API call allows to initialize the handler with one of the
three possible values:
- NULL (should cause a page fault)
- dummy_null_handler (should call iret)
- uexit_irq_handler (should perform guest_uexit(UEXIT_IRQ))
Also add a test for uexit_irq_handler()
Diffstat (limited to 'executor')
| -rw-r--r-- | executor/common_kvm_amd64.h | 12 | ||||
| -rw-r--r-- | executor/common_kvm_amd64_syzos.h | 71 |
2 files changed, 70 insertions, 13 deletions
diff --git a/executor/common_kvm_amd64.h b/executor/common_kvm_amd64.h index ce778792d..3e5200137 100644 --- a/executor/common_kvm_amd64.h +++ b/executor/common_kvm_amd64.h @@ -252,18 +252,6 @@ struct kvm_syz_vm { #endif #if SYZ_EXECUTOR || __NR_syz_kvm_add_vcpu -// See https://wiki.osdev.org/Interrupt_Descriptor_Table#Gate_Descriptor_2. -struct idt_entry_64 { - uint16 offset_low; - uint16 selector; - // Interrupt Stack Table offset in bits 0..2 - uint8 ist; - // Gate Type, P and DPL. - uint8 type_attr; - uint16 offset_mid; - uint32 offset_high; - uint32 reserved; -} __attribute__((packed)); // Post-processing code in pkg/csource/csource.go is very picky and won't let us directly pass // fail() to DEFINE_GUEST_FN_TO_GPA_FN. diff --git a/executor/common_kvm_amd64_syzos.h b/executor/common_kvm_amd64_syzos.h index 9baf9c5f5..d3b9ca421 100644 --- a/executor/common_kvm_amd64_syzos.h +++ b/executor/common_kvm_amd64_syzos.h @@ -22,6 +22,7 @@ typedef enum { SYZOS_API_WR_DRN = 110, SYZOS_API_IN_DX = 130, SYZOS_API_OUT_DX = 170, + SYZOS_API_SET_IRQ_HANDLER = 190, SYZOS_API_STOP, // Must be the last one } syzos_api_id; @@ -61,7 +62,13 @@ struct api_call_3 { uint64 args[3]; }; +#ifdef __cplusplus +extern "C" { +#endif static void guest_uexit(uint64 exit_code); +#ifdef __cplusplus +} +#endif static void guest_execute_code(uint8* insns, uint64 size); static void guest_handle_cpuid(uint32 eax, uint32 ecx); static void guest_handle_wrmsr(uint64 reg, uint64 val); @@ -70,6 +77,7 @@ static void guest_handle_wr_crn(struct api_call_2* cmd); static void guest_handle_wr_drn(struct api_call_2* cmd); static void guest_handle_in_dx(struct api_call_2* cmd); static void guest_handle_out_dx(struct api_call_3* cmd); +static void guest_handle_set_irq_handler(struct api_call_2* cmd); typedef enum { UEXIT_END = (uint64)-1, @@ -84,6 +92,17 @@ dummy_null_handler() asm("iretq"); } +__attribute__((naked)) GUEST_CODE static void uexit_irq_handler() +{ + asm volatile(R"( + // Call guest_uexit(UEXIT_IRQ). + movq $-2, %rdi + call guest_uexit + + iretq + )"); +} + // Main guest function that performs necessary setup and passes the control to the user-provided // payload. __attribute__((used)) @@ -140,6 +159,10 @@ guest_main(uint64 size, uint64 cpu) guest_handle_out_dx((struct api_call_3*)cmd); break; } + case SYZOS_API_SET_IRQ_HANDLER: { + guest_handle_set_irq_handler((struct api_call_2*)cmd); + break; + } } addr += cmd->size; size -= cmd->size; @@ -156,7 +179,12 @@ GUEST_CODE static noinline void guest_execute_code(uint8* insns, uint64 size) // Perform a userspace exit that can be handled by the host. // The host returns from ioctl(KVM_RUN) with kvm_run.exit_reason=KVM_EXIT_MMIO, // and can handle the call depending on the data passed as exit code. -GUEST_CODE static noinline void guest_uexit(uint64 exit_code) + +// Make sure the compiler does not optimize this function away, it is called from +// assembly. +__attribute__((used)) +GUEST_CODE static noinline void +guest_uexit(uint64 exit_code) { volatile uint64* ptr = (volatile uint64*)X86_SYZOS_ADDR_UEXIT; *ptr = exit_code; @@ -322,3 +350,44 @@ GUEST_CODE static noinline void guest_handle_out_dx(struct api_call_3* cmd) return; } } + +// See https://wiki.osdev.org/Interrupt_Descriptor_Table#Gate_Descriptor_2. +struct idt_entry_64 { + uint16 offset_low; + uint16 selector; + // Interrupt Stack Table offset in bits 0..2 + uint8 ist; + // Gate Type, P and DPL. + uint8 type_attr; + uint16 offset_mid; + uint32 offset_high; + uint32 reserved; +} __attribute__((packed)); + +// IDT gate setup should be similar to syzos_setup_idt() in the host code. +GUEST_CODE static void set_idt_gate(uint8 vector, uint64 handler) +{ + volatile struct idt_entry_64* idt = + (volatile struct idt_entry_64*)(X86_SYZOS_ADDR_VAR_IDT); + volatile struct idt_entry_64* idt_entry = &idt[vector]; + idt_entry->offset_low = (uint16)handler; + idt_entry->offset_mid = (uint16)(handler >> 16); + idt_entry->offset_high = (uint32)(handler >> 32); + idt_entry->selector = X86_SYZOS_SEL_CODE; + idt_entry->type_attr = 0x8E; + idt_entry->ist = 0; + idt_entry->reserved = 0; +} + +DEFINE_GUEST_FN_TO_GPA_FN(syzos_fn_address, X86_SYZOS_ADDR_EXECUTOR_CODE, guest_uexit(UEXIT_ASSERT)) +GUEST_CODE static noinline void guest_handle_set_irq_handler(struct api_call_2* cmd) +{ + uint8 vector = (uint8)cmd->args[0]; + uint64 type = cmd->args[1]; + volatile uint64 handler_addr = 0; + if (type == 1) + handler_addr = syzos_fn_address((uintptr_t)dummy_null_handler); + else if (type == 2) + handler_addr = syzos_fn_address((uintptr_t)uexit_irq_handler); + set_idt_gate(vector, handler_addr); +} |
