Skip to content

Commit 31c0c6a

Browse files
docs: add syzos.md
Initial commit of the SYZOS technical documentation
1 parent c97f8d0 commit 31c0c6a

File tree

1 file changed

+214
-0
lines changed

1 file changed

+214
-0
lines changed

docs/syzos.md

Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
# SYZOS Technical Documentation
2+
3+
## 1. System Overview
4+
5+
### Concept
6+
SYZOS is not a traditional operating system but an **immutable C library** designed to run as a Guest (L1) within a KVM virtual machine. Its primary purpose is to expose an easy-to-fuzz API to the Host (L0) fuzzer (`syzkaller`), allowing for state-aware interactions that are difficult to achieve with raw instruction fuzzing.
7+
8+
In this architecture, the **Host (syz-executor)** acts as the orchestrator, while the **Guest (SYZOS)** acts as the execution engine for a pre-defined sequence of commands.
9+
10+
### Execution Flow
11+
SYZOS leverages syzkaller's standard execution model, where the fuzzer generates a sequence of syscalls (a `syzlang` program) to be executed by the host executor. For SYZOS, this program constructs the VM and defines the guest's internal logic via pseudo-syscalls.
12+
13+
1. **VM Creation:** The fuzzer calls standard KVM ioctls (e.g., `openat`, `KVM_CREATE_VM`) to create the VM container.
14+
2. **Environment Setup (`syz_kvm_setup_syzos_vm`):** This pseudo-syscall automates the complex setup of Guest memory, ensuring the VM has valid code and stack regions.
15+
3. **VCPU & Program Loading (`syz_kvm_add_vcpu`, see 3.2):**
16+
* Instead of a bare `KVM_CREATE_VCPU`, the fuzzer calls `syz_kvm_add_vcpu` that creates a new VCPU in the VM and initializes its state.
17+
* This call takes the **entire sequence of SYZOS commands** as an argument. This sequence effectively becomes the "program" the guest will execute.
18+
* **Concurrency:** SYZOS supports up to 4 separate VCPUs sharing the same address space, allowing the fuzzer to schedule concurrent guest operations.
19+
4. **Execution (`KVM_RUN`):** The fuzzer triggers execution via standard `KVM_RUN` calls. The Guest executes its pre-loaded commands step-by-step.
20+
* **Yielding:** When the Guest needs to perform an action that requires Host intervention (e.g., a transition during Nested Virtualization), it yields to L0 via `UEXIT`.
21+
* **Resumption:** If the program contains multiple `KVM_RUN` calls, they are used to resume the Guest until the pre-loaded program completes.
22+
23+
### Design Philosophy
24+
* **Logical Mutation:** Instead of fuzzing raw assembly bytes, SYZOS exposes high-level primitives to the fuzzer. The fuzzer mutates the arguments of the SYZOS commands.
25+
* **State Validity:** By implementing setup sequences in C, SYZOS ensures that complex structures like IRQ tables or Page Tables are valid enough to reach deep kernel code paths.
26+
27+
---
28+
29+
## 2. Memory Layout & ABI (Communication Interface)
30+
31+
The Host and Guest communicate via a shared memory protocol. The Host writes commands and arguments into specific physical memory addresses, which the Guest maps and reads.
32+
33+
### Communication Interface
34+
* **Command Channel:** A dedicated memory region where the Host writes the commands and their arguments.
35+
* **Result Channel:** Mechanism for the Guest to report status back to the Host, piggybacked on the `UEXIT` mechanism.
36+
* **Scratch Space:** Mutable memory used by the Guest to generate dynamic code blobs or store temporary data needed for operations like `MSR` writes.
37+
38+
### ARM64 Memory Map
39+
The ARM64 implementation relies on a static physical memory layout to ensure the Host knows exactly where to place data.
40+
41+
| Physical Address | Description | Usage |
42+
| :--- | :--- | :--- |
43+
| `0x08000000` | GIC v3 Distributor | Interaction with Generic Interrupt Controller |
44+
| `0x080a0000` | GIC v3 Redistributor | Per-CPU Interrupt Controller interface |
45+
| `0xdddd0000` - `0xeeee0000` | Read-only / Command Page | Host writes SYZOS commands here. Also used to trigger page faults for `UEXIT` |
46+
| `0xeeee8000` - `0xeeef0000` | Code / Scratch Space | Where SYZOS resides. Also used for generated code (e.g., MSR trampolines) |
47+
| `0xffff1000` | EL1 Stack | Stack space for the SYZOS Guest execution |
48+
49+
### x86 Memory Map
50+
While the exact addresses may vary by implementation, the x86 layout follows similar principles:
51+
* **Guest Code:** Allocated via `KVM_SET_USER_MEMORY_REGION` (typically 1 page).
52+
* **Page Tables:** Setup by the Host to allow virtual-to-physical translation required for long mode.
53+
* **IDT (Interrupt Descriptor Table):** Setup by the Host to handle exceptions within the Guest.
54+
55+
---
56+
57+
## 3. Host-Side Implementation (`syz-executor`)
58+
59+
The Host side is responsible for the heavy lifting of VM initialization. This is achieved through "pseudo-syscalls" - functions implemented in `syz-executor` that look like syscalls to the fuzzer but perform complex setup logic.
60+
61+
### 3.1 VM Initialization: `syz_kvm_setup_syzos_vm()`
62+
This pseudo-syscall creates the VM and prepares the environment.
63+
* **Memory Allocation:** Calls `ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &memreg)` multiple times to map the Guest physical memory slots (Code, Stack, MMIO).
64+
* **Image Loading:** Copies the compiled SYZOS C library binary into the allocated Guest Code region.
65+
66+
### 3.2 VCPU Initialization: `syz_kvm_add_vcpu()`
67+
This function adds a virtual CPU to the VM and configures its initial state to jump into the SYZOS entry point.
68+
* **Program Loading:** It parses the `syzlang`-generated argument structure, which contains the sequence of SYZOS commands, and copies them into the Guest's Command Page.
69+
* **Context Setup:**
70+
* **x86:** Sets up SREGS (Segments, Page Tables) and IDT.
71+
* **ARM64:** Sets PC to the entry point and SP to the stack.
72+
73+
### 3.3 The Handshake Mechanism (`UEXIT`)
74+
The core synchronization primitive is the `UEXIT`.
75+
* **Trigger:** The Guest reads from a specific unmapped or read-only address (e.g., inside `0xdddd0000` on ARM) or executes a specific instruction sequence.
76+
* **Detection:** `KVM_RUN` returns on the Host. The Host may check `kvm_run->exit_reason`.
77+
* **Handling:**
78+
* If the exit indicates a Page Fault (EPT violation) at the specific `UEXIT` address, the Host treats this as a voluntary yield.
79+
* The Host reads the exit qualification or register state to retrieve the "return argument" passed by the Guest.
80+
81+
---
82+
83+
## 4. Guest-Side Implementation (SYZOS Library)
84+
85+
### Source Organization & `GUEST_CODE`
86+
SYZOS guest handlers are defined directly in architecture-specific executor headers (e.g., `executor/common_kvm_amd64_syzos.h`).
87+
* **The `GUEST_CODE` Macro:** Functions intended to run inside the guest are marked with `GUEST_CODE` (e.g., `GUEST_CODE static void guest_handle_...`). This instructs the compiler/linker to place these functions in a specific section that `syz-executor` copies into the Guest's physical memory.
88+
* **Header-Based Implementation:** The entire SYZOS codebase is contained within header files included by the executor. This architecture is necessitated by `syz-prog2c`, a tool that converts `syzlang` reproducers into standalone C programs. By concatenating these headers (via `#include` expansion), `syz-prog2c` can produce a single, build-system-independent C source file that compiles anywhere without external dependencies.
89+
90+
### The Dispatch Loop (`guest_main`)
91+
The entry point `guest_main` iterates through the command buffer that was populated by `syz_kvm_add_vcpu`.
92+
* **Command Routing (If/Else Chain):** The routing is strictly implemented as a series of `if/else if` statements rather than a `switch`. The reason for this is that a `switch` statement can be optimized by compilers into a jump table stored in the executable's `.rodata` section. Since the global data sections are not mapped into the Guest address space, accessing a jump table would cause an immediate Page Fault.
93+
* **Argument Parsing:** Commands are cast to specific structures (e.g., `struct api_call_5*`) to access arguments safely.
94+
* **Execution:** The handler performs the logic and the loop advances to the next command in the buffer.
95+
96+
### Core Primitives
97+
* **`SYZOS_API_UEXIT`:** - Triggers a specific exception that the Host recognizes as a "yield". It passes a return value (1 argument) back to the Host to signal success/failure or data.
98+
* **`SYZOS_API_CODE`:** - Executes a raw blob of machine code supplied by the Host. This can be used to emit exact instruction sequences not covered by high-level APIs.
99+
100+
---
101+
102+
## 5. Platform Specifics
103+
104+
### x86 (Intel & AMD)
105+
106+
#### Privileged Operations
107+
SYZOS exposes specific APIs to fuzz privileged x86 instructions:
108+
* **`SYZOS_API_CPUID`:** Executes the `CPUID` instruction.
109+
* **`SYZOS_API_WRMSR` / `SYZOS_API_RDMSR`:** Reads/Writes Model Specific Registers.
110+
* **`SYZOS_API_WR_CRN` / `SYZOS_API_WR_DRN`:** Writes to Control Registers and Debug Registers.
111+
* **`SYZOS_API_IN_DX` / `SYZOS_API_OUT_DX`:** Executes I/O port operations.
112+
113+
#### Nested Virtualization (NV) Engine
114+
SYZOS acts as a lightweight L1 hypervisor to fuzz L2 guests, abstracting the architectural differences between Intel VMX and AMD SVM. It provides a uniform API for the VM lifecycle while offering architecture-specific commands for state mutation.
115+
116+
##### VM Lifecycle & Execution
117+
The following primitives control the nested guest's existence and execution flow:
118+
119+
* **`SYZOS_API_ENABLE_NESTED`:** Enables the virtualization extensions (VMXON on Intel, EFER.SVME on AMD).
120+
* **`SYZOS_API_NESTED_CREATE_VM`:** Initializes the necessary control structures (VMCS for Intel, VMCB for AMD) and sets up Nested Page Tables.
121+
* **`SYZOS_API_NESTED_LOAD_CODE`:** Injects a sequence of instructions into the L2 guest's memory, defining what code the nested machine will execute.
122+
* **`SYZOS_API_NESTED_VMLAUNCH`:** Performs the initial VM Entry, transferring control to the L2 guest.
123+
* **`SYZOS_API_NESTED_VMRESUME`:** Resumes execution of the L2 guest after it has exited back to L1.
124+
125+
##### State Mutation (Architecture Specific)
126+
To stress the host's handling of invalid or edge-case states, SYZOS allows direct mutation of the hardware control structures. This is done by applying the "set/unset/flip" mask logic: `new_val = (old_val & ~unset_mask) | set_mask ^ flip_mask`.
127+
The SYZOS commands are **`SYZOS_API_NESTED_INTEL_VMWRITE_MASK`** (mutates the VMCS fields on Intel) and **`SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK`** (VMCB on AMD).
128+
129+
---
130+
131+
### ARM64
132+
133+
#### Device Emulation
134+
A significant portion of ARM64 KVM code is device emulation. SYZOS provides specialized APIs to fuzz these complex interactions.
135+
* **GICv3 & ITS:**
136+
* **`SYZOS_API_IRQ_SETUP`:** Sets up the VGICv3 distributor and installs the guest IRQ table.
137+
* **`SYZOS_API_ITS_SETUP`:** Allocates translation tables and configures the Interrupt Translation Service (ITS) base.
138+
* **`SYZOS_API_ITS_SEND_CMD`:** Injects structured GIC commands (e.g., `MAPD`, `MOVI`) into the command queue.
139+
140+
#### Hypervisor Interface
141+
SYZOS targets the boundary between the Guest and EL2/Firmware.
142+
* **Hypercalls:**
143+
* **`SYZOS_API_HVC`:** Executes `hvc #0` with fuzzer-controlled parameters in registers `x0-x5`.
144+
* **`SYZOS_API_SMC`:** Executes `smc #0` (Secure Monitor Call) with parameters in `x0-x5`.
145+
146+
---
147+
148+
## 7. Developer Guide: How to Add a New Command
149+
150+
This guide details the process of adding a new SYZOS command, using `SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK` as a reference case.
151+
152+
### Step 1: Define API ID and Handler Prototype
153+
Modify the architecture-specific executor header (e.g., `executor/common_kvm_amd64_syzos.h`) to register the new command.
154+
155+
1. **Add the Enum ID:** Add a new entry to the `syzos_api_id` enum.
156+
```c
157+
typedef enum {
158+
// ...
159+
SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK = 380, // New ID
160+
SYZOS_API_STOP,
161+
} syzos_api_id;
162+
```
163+
2. **Declare the Handler:** Add a forward declaration using the `GUEST_CODE` macro.
164+
```c
165+
GUEST_CODE static void guest_handle_nested_amd_vmcb_write_mask(struct api_call_5* cmd, uint64 cpu_id);
166+
```
167+
168+
### Step 2: Implement Guest Logic and Dispatch
169+
In the same file (or corresponding source), implement the guest logic.
170+
171+
1. **Add Dispatch Case:** Update `guest_main`.
172+
```c
173+
} else if (call == SYZOS_API_NESTED_AMD_VMCB_WRITE_MASK) {
174+
guest_handle_nested_amd_vmcb_write_mask((struct api_call_5*)cmd, cpu);
175+
}
176+
```
177+
2. **Implement Handler:** Write the function logic. Strict guest-safe code restrictions apply.
178+
```c
179+
GUEST_CODE static noinline void
180+
guest_handle_nested_amd_vmcb_write_mask(struct api_call_5* cmd, uint64 cpu_id)
181+
{
182+
if (get_cpu_vendor() != CPU_VENDOR_AMD) return;
183+
// ... parse args and perform logic ...
184+
vmcb_write64(vmcb_addr, offset, new_value);
185+
}
186+
```
187+
188+
### Step 3: Define syzlang Description
189+
Expose the new command to `syzkaller` in the description file (e.g., `sys/linux/dev_kvm_amd64.txt`).
190+
191+
1. **Define Structures:** Define any necessary constants or structures.
192+
2. **Map Command ID:** Add the command to the `syzos_api_call` union. **Crucial:** The ID (e.g., `380`) must match the enum in the C header.
193+
```
194+
syzos_api_call$x86 [
195+
nested_amd_vmcb_write_mask syzos_api$x86[380, syzos_api_nested_amd_vmcb_write_mask]
196+
] [varlen]
197+
```
198+
199+
---
200+
201+
## 8. Validation & Regression Testing
202+
203+
The system includes a regression testing framework located in `sys/linux/test/`. New commands must include a test case to verify they trigger the expected Hypervisor behavior.
204+
205+
### Test File Structure
206+
Tests are `syzlang` programs with special assertions.
207+
* **Header:** Requires metadata, e.g., `# requires: arch=amd64 -threaded`.
208+
* **Setup:** Standard boilerplate creates a VM and enters SYZOS.
209+
* **Logic:** The test configures the guest to perform a specific action (e.g., executing `HLT` in a nested L2 guest).
210+
211+
### Assertions
212+
Tests use specialized pseudo-syscalls to assert the VM's exit state:
213+
* **`syz_kvm_assert_syzos_uexit$x86(fd, code)`:** Asserts that the guest voluntarily yielded with a specific `UEXIT` code (e.g., `0xe2e20001`).
214+
* **`syz_kvm_assert_syzos_kvm_exit$x86(fd, exit_reason)`:** Asserts that the guest triggered a standard KVM exit (e.g., `0x5` for `KVM_EXIT_HLT`) that was trapped by L0.

0 commit comments

Comments
 (0)