/src/cloud-hypervisor/fuzz/fuzz_targets/x86emul.rs
Line | Count | Source |
1 | | // Copyright © 2025 Microsoft Corporation |
2 | | // |
3 | | // SPDX-License-Identifier: Apache-2.0 |
4 | | |
5 | | #![no_main] |
6 | | |
7 | | use hypervisor::arch::emulator::{PlatformEmulator, PlatformError}; |
8 | | use hypervisor::arch::x86::emulator::{Emulator, EmulatorCpuState}; |
9 | | use hypervisor::arch::x86::{DescriptorTable, SegmentRegister, SpecialRegisters}; |
10 | | use hypervisor::StandardRegisters; |
11 | | use libfuzzer_sys::{fuzz_target, Corpus}; |
12 | | |
13 | | #[derive(Debug)] |
14 | | struct EmulatorContext { |
15 | | state: EmulatorCpuState, |
16 | | memory: [u8; 8], |
17 | | } |
18 | | |
19 | | impl PlatformEmulator for EmulatorContext { |
20 | | type CpuState = EmulatorCpuState; |
21 | | |
22 | 2.23M | fn read_memory(&self, _gva: u64, data: &mut [u8]) -> Result<(), PlatformError> { |
23 | 2.23M | data.copy_from_slice(&self.memory[..data.len()]); |
24 | 2.23M | Ok(()) |
25 | 2.23M | } |
26 | | |
27 | 4.93M | fn write_memory(&mut self, _gva: u64, _data: &[u8]) -> Result<(), PlatformError> { |
28 | | // Discard writes |
29 | 4.93M | Ok(()) |
30 | 4.93M | } |
31 | | |
32 | 2.12k | fn cpu_state(&self, _cpu_id: usize) -> Result<Self::CpuState, PlatformError> { |
33 | 2.12k | Ok(self.state.clone()) |
34 | 2.12k | } |
35 | | |
36 | 0 | fn set_cpu_state(&self, _cpu_id: usize, _state: Self::CpuState) -> Result<(), PlatformError> { |
37 | | // Ignore |
38 | 0 | Ok(()) |
39 | 0 | } |
40 | | |
41 | 0 | fn fetch(&self, _ip: u64, _data: &mut [u8]) -> Result<(), PlatformError> { |
42 | | // The fuzzer already provides 16 bytes of data, we don't need to fetch anything |
43 | 0 | panic!("fetch should not be called"); |
44 | | } |
45 | | } |
46 | | |
47 | | fuzz_target!(|bytes: &[u8]| -> Corpus { |
48 | | let (mut ctx, insn) = match generate_context_and_instruction(bytes) { |
49 | | Ok((ctx, insn)) => (ctx, insn), |
50 | | Err(_) => return Corpus::Reject, |
51 | | }; |
52 | | |
53 | | let mut e = Emulator::new(&mut ctx); |
54 | | |
55 | | if e.emulate_first_insn(0, &insn).is_err() { |
56 | | return Corpus::Reject; |
57 | | } |
58 | | |
59 | | Corpus::Keep |
60 | | }); |
61 | | |
62 | | // Helper functions to generate structures from fuzzer input below |
63 | | |
64 | 17.0k | fn generate_segment_register( |
65 | 17.0k | u: &mut arbitrary::Unstructured<'_>, |
66 | 17.0k | ) -> arbitrary::Result<SegmentRegister> { |
67 | | Ok(SegmentRegister { |
68 | 17.0k | base: u.arbitrary()?, |
69 | 17.0k | limit: u.arbitrary()?, |
70 | 17.0k | selector: u.arbitrary()?, |
71 | 17.0k | avl: u.arbitrary()?, |
72 | 17.0k | dpl: u.arbitrary()?, |
73 | 17.0k | db: u.arbitrary()?, |
74 | 17.0k | g: u.arbitrary()?, |
75 | 17.0k | l: u.arbitrary()?, |
76 | 17.0k | present: u.arbitrary()?, |
77 | 17.0k | s: u.arbitrary()?, |
78 | 17.0k | type_: u.arbitrary()?, |
79 | 17.0k | unusable: u.arbitrary()?, |
80 | | }) |
81 | 17.0k | } |
82 | | |
83 | 4.25k | fn generate_descriptor_table( |
84 | 4.25k | u: &mut arbitrary::Unstructured<'_>, |
85 | 4.25k | ) -> arbitrary::Result<DescriptorTable> { |
86 | | Ok(DescriptorTable { |
87 | 4.25k | base: u.arbitrary()?, |
88 | 4.25k | limit: u.arbitrary()?, |
89 | | }) |
90 | 4.25k | } |
91 | | |
92 | 2.12k | fn generate_context_and_instruction( |
93 | 2.12k | bytes: &[u8], |
94 | 2.12k | ) -> arbitrary::Result<(EmulatorContext, [u8; 16])> { |
95 | 2.12k | let mut u = arbitrary::Unstructured::new(bytes); |
96 | | |
97 | 2.12k | let mut regs = mshv_bindings::StandardRegisters { |
98 | 2.12k | rax: u.arbitrary()?, |
99 | 2.12k | rbx: u.arbitrary()?, |
100 | 2.12k | rcx: u.arbitrary()?, |
101 | 2.12k | rdx: u.arbitrary()?, |
102 | 2.12k | rsi: u.arbitrary()?, |
103 | 2.12k | rdi: u.arbitrary()?, |
104 | 2.12k | rsp: u.arbitrary()?, |
105 | 2.12k | rbp: u.arbitrary()?, |
106 | 2.12k | r8: u.arbitrary()?, |
107 | 2.12k | r9: u.arbitrary()?, |
108 | 2.12k | r10: u.arbitrary()?, |
109 | 2.12k | r11: u.arbitrary()?, |
110 | 2.12k | r12: u.arbitrary()?, |
111 | 2.12k | r13: u.arbitrary()?, |
112 | 2.12k | r14: u.arbitrary()?, |
113 | 2.12k | r15: u.arbitrary()?, |
114 | 2.12k | rip: u.arbitrary()?, |
115 | 2.12k | rflags: u.arbitrary()?, |
116 | | }; |
117 | | |
118 | | // Cap RCX to avoid looping for too long for reps instructions. |
119 | 2.12k | regs.rcx &= 0xFFFFu64; |
120 | | |
121 | 2.12k | let regs = StandardRegisters::Mshv(regs); |
122 | | |
123 | 2.12k | let sregs = SpecialRegisters { |
124 | 2.12k | cs: generate_segment_register(&mut u)?, |
125 | 2.12k | ds: generate_segment_register(&mut u)?, |
126 | 2.12k | es: generate_segment_register(&mut u)?, |
127 | 2.12k | fs: generate_segment_register(&mut u)?, |
128 | 2.12k | gs: generate_segment_register(&mut u)?, |
129 | 2.12k | ss: generate_segment_register(&mut u)?, |
130 | 2.12k | tr: generate_segment_register(&mut u)?, |
131 | 2.12k | ldt: generate_segment_register(&mut u)?, |
132 | 2.12k | gdt: generate_descriptor_table(&mut u)?, |
133 | 2.12k | idt: generate_descriptor_table(&mut u)?, |
134 | 2.12k | cr0: u.arbitrary()?, |
135 | 2.12k | cr2: u.arbitrary()?, |
136 | 2.12k | cr3: u.arbitrary()?, |
137 | 2.12k | cr4: u.arbitrary()?, |
138 | 2.12k | cr8: u.arbitrary()?, |
139 | 2.12k | efer: u.arbitrary()?, |
140 | 2.12k | apic_base: u.arbitrary()?, |
141 | 2.12k | interrupt_bitmap: u.arbitrary()?, |
142 | | }; |
143 | | |
144 | 2.12k | let memory = u.arbitrary::<[u8; 8]>()?; |
145 | 2.12k | let insn = u.arbitrary::<[u8; 16]>()?; |
146 | | |
147 | 2.12k | let ctx = EmulatorContext { |
148 | 2.12k | state: EmulatorCpuState { regs, sregs }, |
149 | 2.12k | memory, |
150 | 2.12k | }; |
151 | | |
152 | 2.12k | Ok((ctx, insn)) |
153 | 2.12k | } |