Skip to content

Commit d8491a6

Browse files
executor: introduce __addrspace_guest
Apply __addrspace_guest to every guest function and use a C++ template to statically validate that host functions are not passed to executor_fn_guest_addr(). This only works in Clang builds of syz-executor, because GCC does not support address spaces, and C reproducers cannot use templates. The static check allows us to drop the dynamic checks in DEFINE_GUEST_FN_TO_GPA_FN(). While at it, replace DEFINE_GUEST_FN_TO_GPA_FN() with explicit declarations of host_fn_guest_addr() and guest_fn_guest_addr().
1 parent fdef407 commit d8491a6

File tree

6 files changed

+63
-64
lines changed

6 files changed

+63
-64
lines changed

executor/common_kvm.h

Lines changed: 28 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,23 +5,34 @@
55

66
// Common KVM-related definitions.
77

8-
extern char *__start_guest, *__stop_guest;
9-
10-
// Define a function that calculates the guest physical address for a guest function.
11-
// Execute failure_action if the function does not belong to the guest section.
12-
// This function is using volatile accesses, otherwise the compiler may attempt
13-
// to store e.g. &__start_guest + offset as a constant in .rodata.
14-
#define DEFINE_GUEST_FN_TO_GPA_FN(fn_name, offset, failure_action) \
15-
static inline uintptr_t fn_name(uintptr_t f) \
16-
{ \
17-
volatile uintptr_t start = (uintptr_t)&__start_guest; \
18-
volatile uintptr_t stop = (uintptr_t)&__stop_guest; \
19-
if (f >= start && f < stop) { \
20-
return f - start + (offset); \
21-
} \
22-
(failure_action); \
23-
return 0; \
24-
}
8+
#include "common_kvm_syzos.h"
9+
#include "kvm.h"
10+
11+
#if SYZ_EXECUTOR || __NR_syz_kvm_add_vcpu
12+
extern char* __start_guest;
13+
14+
// executor_fn_guest_addr() is compiled into both the host and the guest code.
15+
static inline uintptr_t executor_fn_guest_addr(void* fn)
16+
{
17+
// Prevent the compiler from creating a .rodata constant for
18+
// &__start_guest + SYZOS_ADDR_EXECUTOR_CODE.
19+
volatile uintptr_t start = (uintptr_t)&__start_guest;
20+
volatile uintptr_t offset = SYZOS_ADDR_EXECUTOR_CODE;
21+
return (uintptr_t)fn - start + offset;
22+
}
23+
24+
#if SYZ_EXECUTOR
25+
// In Clang-based C++ builds, use template magic to ensure that only guest functions can be passed
26+
// to executor_fn_guest_addr().
27+
template <typename R, typename... A>
28+
uintptr_t static inline executor_fn_guest_addr(__addrspace_guest R (*fn)(A...))
29+
{
30+
return executor_fn_guest_addr((void*)fn);
31+
}
32+
33+
#endif
34+
35+
#endif
2536

2637
#if SYZ_EXECUTOR || __NR_syz_kvm_assert_syzos_kvm_exit
2738
static long syz_kvm_assert_syzos_kvm_exit(volatile long a0, volatile long a1)

executor/common_kvm_amd64.h

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -253,23 +253,14 @@ struct kvm_syz_vm {
253253

254254
#if SYZ_EXECUTOR || __NR_syz_kvm_add_vcpu
255255

256-
// Post-processing code in pkg/csource/csource.go is very picky and won't let us directly pass
257-
// fail() to DEFINE_GUEST_FN_TO_GPA_FN.
258-
static inline void error_in_executor_fn_guest_addr()
259-
{
260-
fail("SYZOS: executor_fn_guest_addr: invalid guest address");
261-
}
262-
263-
DEFINE_GUEST_FN_TO_GPA_FN(executor_fn_guest_addr, X86_SYZOS_ADDR_EXECUTOR_CODE, error_in_executor_fn_guest_addr());
264-
265256
#define X86_NUM_IDT_ENTRIES 256
266257
static void syzos_setup_idt(struct kvm_syz_vm* vm, struct kvm_sregs* sregs)
267258
{
268259
sregs->idt.base = X86_SYZOS_ADDR_VAR_IDT;
269260
sregs->idt.limit = (X86_NUM_IDT_ENTRIES * sizeof(struct idt_entry_64)) - 1;
270261
volatile struct idt_entry_64* idt =
271262
(volatile struct idt_entry_64*)((uint64)vm->host_mem + sregs->idt.base);
272-
uint64 handler_addr = executor_fn_guest_addr((uintptr_t)dummy_null_handler);
263+
uint64 handler_addr = executor_fn_guest_addr(dummy_null_handler);
273264
for (int i = 0; i < X86_NUM_IDT_ENTRIES; i++) {
274265
idt[i].offset_low = (uint16)(handler_addr & 0xFFFF);
275266
idt[i].selector = X86_SYZOS_SEL_CODE;
@@ -1031,6 +1022,7 @@ static volatile long syz_kvm_setup_cpu(volatile long a0, volatile long a1, volat
10311022

10321023
#define RFLAGS_1_BIT (1ULL << 1)
10331024
#define RFLAGS_IF_BIT (1ULL << 9)
1025+
10341026
static void reset_cpu_regs(int cpufd, int cpu_id, size_t text_size)
10351027
{
10361028
struct kvm_regs regs;
@@ -1039,7 +1031,7 @@ static void reset_cpu_regs(int cpufd, int cpu_id, size_t text_size)
10391031
// RFLAGS.1 must be 1, RFLAGS.IF enables interrupts.
10401032
regs.rflags |= RFLAGS_1_BIT | RFLAGS_IF_BIT;
10411033
// PC points to the relative offset of guest_main() within the guest code.
1042-
regs.rip = executor_fn_guest_addr((uintptr_t)guest_main);
1034+
regs.rip = executor_fn_guest_addr(guest_main);
10431035
regs.rsp = X86_SYZOS_ADDR_STACK0;
10441036
// Pass parameters to guest_main().
10451037
regs.rdi = text_size;

executor/common_kvm_amd64_syzos.h

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -65,19 +65,19 @@ struct api_call_3 {
6565
#ifdef __cplusplus
6666
extern "C" {
6767
#endif
68-
static void guest_uexit(uint64 exit_code);
68+
GUEST_CODE static void guest_uexit(uint64 exit_code);
6969
#ifdef __cplusplus
7070
}
7171
#endif
72-
static void guest_execute_code(uint8* insns, uint64 size);
73-
static void guest_handle_cpuid(uint32 eax, uint32 ecx);
74-
static void guest_handle_wrmsr(uint64 reg, uint64 val);
75-
static void guest_handle_rdmsr(uint64 reg);
76-
static void guest_handle_wr_crn(struct api_call_2* cmd);
77-
static void guest_handle_wr_drn(struct api_call_2* cmd);
78-
static void guest_handle_in_dx(struct api_call_2* cmd);
79-
static void guest_handle_out_dx(struct api_call_3* cmd);
80-
static void guest_handle_set_irq_handler(struct api_call_2* cmd);
72+
GUEST_CODE static void guest_execute_code(uint8* insns, uint64 size);
73+
GUEST_CODE static void guest_handle_cpuid(uint32 eax, uint32 ecx);
74+
GUEST_CODE static void guest_handle_wrmsr(uint64 reg, uint64 val);
75+
GUEST_CODE static void guest_handle_rdmsr(uint64 reg);
76+
GUEST_CODE static void guest_handle_wr_crn(struct api_call_2* cmd);
77+
GUEST_CODE static void guest_handle_wr_drn(struct api_call_2* cmd);
78+
GUEST_CODE static void guest_handle_in_dx(struct api_call_2* cmd);
79+
GUEST_CODE static void guest_handle_out_dx(struct api_call_3* cmd);
80+
GUEST_CODE static void guest_handle_set_irq_handler(struct api_call_2* cmd);
8181

8282
typedef enum {
8383
UEXIT_END = (uint64)-1,
@@ -383,15 +383,14 @@ GUEST_CODE static void set_idt_gate(uint8 vector, uint64 handler)
383383
idt_entry->reserved = 0;
384384
}
385385

386-
DEFINE_GUEST_FN_TO_GPA_FN(syzos_fn_address, X86_SYZOS_ADDR_EXECUTOR_CODE, guest_uexit(UEXIT_ASSERT))
387386
GUEST_CODE static noinline void guest_handle_set_irq_handler(struct api_call_2* cmd)
388387
{
389388
uint8 vector = (uint8)cmd->args[0];
390389
uint64 type = cmd->args[1];
391390
volatile uint64 handler_addr = 0;
392391
if (type == 1)
393-
handler_addr = syzos_fn_address((uintptr_t)dummy_null_handler);
392+
handler_addr = executor_fn_guest_addr(dummy_null_handler);
394393
else if (type == 2)
395-
handler_addr = syzos_fn_address((uintptr_t)uexit_irq_handler);
394+
handler_addr = executor_fn_guest_addr(uexit_irq_handler);
396395
set_idt_gate(vector, handler_addr);
397396
}

executor/common_kvm_arm64.h

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -144,20 +144,11 @@ static void vcpu_set_reg(int vcpu_fd, uint64 id, uint64 val)
144144
ioctl(vcpu_fd, KVM_SET_ONE_REG, &reg);
145145
}
146146

147-
// Post-processing code in pkg/csource/csource.go is very picky and won't let us directly pass
148-
// fail() to DEFINE_GUEST_FN_TO_GPA_FN.
149-
static inline void error_in_executor_fn_guest_addr()
150-
{
151-
fail("SYZOS: executor_fn_guest_addr: invalid guest address");
152-
}
153-
154-
DEFINE_GUEST_FN_TO_GPA_FN(executor_fn_guest_addr, ARM64_ADDR_EXECUTOR_CODE, error_in_executor_fn_guest_addr());
155-
156147
// Set up CPU registers.
157148
static void reset_cpu_regs(int cpufd, int cpu_id, size_t text_size)
158149
{
159150
// PC points to the relative offset of guest_main() within the guest code.
160-
vcpu_set_reg(cpufd, KVM_ARM64_REGS_PC, executor_fn_guest_addr((uintptr_t)guest_main));
151+
vcpu_set_reg(cpufd, KVM_ARM64_REGS_PC, executor_fn_guest_addr(guest_main));
161152
vcpu_set_reg(cpufd, KVM_ARM64_REGS_SP_EL1, ARM64_ADDR_EL1_STACK_BOTTOM + KVM_PAGE_SIZE - 128);
162153
// Store the CPU ID in TPIDR_EL1.
163154
vcpu_set_reg(cpufd, KVM_ARM64_REGS_TPIDR_EL1, cpu_id);

executor/common_kvm_arm64_syzos.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -89,18 +89,18 @@ struct api_call_its_send_cmd {
8989
uint32 cpuid2;
9090
};
9191

92-
static void guest_uexit(uint64 exit_code);
93-
static void guest_execute_code(uint32* insns, uint64 size);
94-
static void guest_handle_mrs(uint64 reg);
95-
static void guest_handle_msr(uint64 reg, uint64 val);
96-
static void guest_handle_smc(struct api_call_smccc* cmd);
97-
static void guest_handle_hvc(struct api_call_smccc* cmd);
98-
static void guest_handle_svc(struct api_call_smccc* cmd);
99-
static void guest_handle_eret(uint64 unused);
100-
static void guest_handle_irq_setup(struct api_call_irq_setup* cmd);
101-
static void guest_handle_memwrite(struct api_call_memwrite* cmd);
102-
static void guest_handle_its_setup(struct api_call_3* cmd);
103-
static void guest_handle_its_send_cmd(struct api_call_its_send_cmd* cmd);
92+
GUEST_CODE static void guest_uexit(uint64 exit_code);
93+
GUEST_CODE static void guest_execute_code(uint32* insns, uint64 size);
94+
GUEST_CODE static void guest_handle_mrs(uint64 reg);
95+
GUEST_CODE static void guest_handle_msr(uint64 reg, uint64 val);
96+
GUEST_CODE static void guest_handle_smc(struct api_call_smccc* cmd);
97+
GUEST_CODE static void guest_handle_hvc(struct api_call_smccc* cmd);
98+
GUEST_CODE static void guest_handle_svc(struct api_call_smccc* cmd);
99+
GUEST_CODE static void guest_handle_eret(uint64 unused);
100+
GUEST_CODE static void guest_handle_irq_setup(struct api_call_irq_setup* cmd);
101+
GUEST_CODE static void guest_handle_memwrite(struct api_call_memwrite* cmd);
102+
GUEST_CODE static void guest_handle_its_setup(struct api_call_3* cmd);
103+
GUEST_CODE static void guest_handle_its_send_cmd(struct api_call_its_send_cmd* cmd);
104104

105105
typedef enum {
106106
UEXIT_END = (uint64)-1,

executor/common_kvm_syzos.h

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,11 @@
1212
// TODO(glider): once syz-env-old migrates to GCC>11 we can just use
1313
// __attribute__((no_stack_protector)).
1414
#if defined(__clang__)
15+
1516
// Clang supports the no_stack_protector attribute.
1617
#define __no_stack_protector __attribute__((no_stack_protector))
18+
#define __addrspace_guest __attribute__((address_space(10)))
19+
1720
#elif defined(__GNUC__)
1821
// The no_stack_protector attribute was introduced in GCC 11.1.
1922
#if __GNUC__ > 11
@@ -22,12 +25,15 @@
2225
// Fallback to the optimize attribute for older GCC versions.
2326
#define __no_stack_protector __attribute__((__optimize__("-fno-stack-protector")))
2427
#endif
28+
#define __addrspace_guest
29+
2530
#else
2631
#define __no_stack_protector
32+
#define __addrspace_guest
2733
#endif
2834

2935
// Host will map the code in this section into the guest address space.
30-
#define GUEST_CODE __attribute__((section("guest"))) __no_stack_protector
36+
#define GUEST_CODE __attribute__((section("guest"))) __no_stack_protector __addrspace_guest
3137

3238
// Start/end of the guest section.
3339
extern char *__start_guest, *__stop_guest;

0 commit comments

Comments
 (0)