aboutsummaryrefslogtreecommitdiffstats
path: root/executor
diff options
context:
space:
mode:
authorAlexander Potapenko <glider@google.com>2025-10-01 16:35:10 +0200
committerAlexander Potapenko <glider@google.com>2025-10-17 06:51:20 +0000
commite69835fc40b5e00c0996ce3a85d8287eea57d162 (patch)
treed935f0f54b202ec2402f74852f7d44735b567653 /executor
parentb2cf58f4862666d3b14f051b1e8cddcde9f8186f (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.h12
-rw-r--r--executor/common_kvm_amd64_syzos.h71
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);
+}