/src/cloud-hypervisor/fuzz/fuzz_targets/x86emul.rs
Line | Count | Source (jump to first uncovered line) |
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.82M | fn write_memory(&mut self, _gva: u64, _data: &[u8]) -> Result<(), PlatformError> { |
28 | 4.82M | // Discard writes |
29 | 4.82M | Ok(()) |
30 | 4.82M | } |
31 | | |
32 | 1.91k | fn cpu_state(&self, _cpu_id: usize) -> Result<Self::CpuState, PlatformError> { |
33 | 1.91k | Ok(self.state.clone()) |
34 | 1.91k | } |
35 | | |
36 | 0 | fn set_cpu_state(&self, _cpu_id: usize, _state: Self::CpuState) -> Result<(), PlatformError> { |
37 | 0 | // Ignore |
38 | 0 | Ok(()) |
39 | 0 | } |
40 | | |
41 | 0 | fn fetch(&self, _ip: u64, _data: &mut [u8]) -> Result<(), PlatformError> { |
42 | 0 | // 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 | 15.2k | fn generate_segment_register( |
65 | 15.2k | u: &mut arbitrary::Unstructured<'_>, |
66 | 15.2k | ) -> arbitrary::Result<SegmentRegister> { |
67 | 15.2k | Ok(SegmentRegister { |
68 | 15.2k | base: u.arbitrary()?, |
69 | 15.2k | limit: u.arbitrary()?, |
70 | 15.2k | selector: u.arbitrary()?, |
71 | 15.2k | avl: u.arbitrary()?, |
72 | 15.2k | dpl: u.arbitrary()?, |
73 | 15.2k | db: u.arbitrary()?, |
74 | 15.2k | g: u.arbitrary()?, |
75 | 15.2k | l: u.arbitrary()?, |
76 | 15.2k | present: u.arbitrary()?, |
77 | 15.2k | s: u.arbitrary()?, |
78 | 15.2k | type_: u.arbitrary()?, |
79 | 15.2k | unusable: u.arbitrary()?, |
80 | | }) |
81 | 15.2k | } |
82 | | |
83 | 3.82k | fn generate_descriptor_table( |
84 | 3.82k | u: &mut arbitrary::Unstructured<'_>, |
85 | 3.82k | ) -> arbitrary::Result<DescriptorTable> { |
86 | 3.82k | Ok(DescriptorTable { |
87 | 3.82k | base: u.arbitrary()?, |
88 | 3.82k | limit: u.arbitrary()?, |
89 | | }) |
90 | 3.82k | } |
91 | | |
92 | 1.91k | fn generate_context_and_instruction( |
93 | 1.91k | bytes: &[u8], |
94 | 1.91k | ) -> arbitrary::Result<(EmulatorContext, [u8; 16])> { |
95 | 1.91k | let mut u = arbitrary::Unstructured::new(bytes); |
96 | | |
97 | 1.91k | let mut regs = mshv_bindings::StandardRegisters { |
98 | 1.91k | rax: u.arbitrary()?, |
99 | 1.91k | rbx: u.arbitrary()?, |
100 | 1.91k | rcx: u.arbitrary()?, |
101 | 1.91k | rdx: u.arbitrary()?, |
102 | 1.91k | rsi: u.arbitrary()?, |
103 | 1.91k | rdi: u.arbitrary()?, |
104 | 1.91k | rsp: u.arbitrary()?, |
105 | 1.91k | rbp: u.arbitrary()?, |
106 | 1.91k | r8: u.arbitrary()?, |
107 | 1.91k | r9: u.arbitrary()?, |
108 | 1.91k | r10: u.arbitrary()?, |
109 | 1.91k | r11: u.arbitrary()?, |
110 | 1.91k | r12: u.arbitrary()?, |
111 | 1.91k | r13: u.arbitrary()?, |
112 | 1.91k | r14: u.arbitrary()?, |
113 | 1.91k | r15: u.arbitrary()?, |
114 | 1.91k | rip: u.arbitrary()?, |
115 | 1.91k | rflags: u.arbitrary()?, |
116 | | }; |
117 | | |
118 | | // Cap RCX to avoid looping for too long for reps instructions. |
119 | 1.91k | regs.rcx &= 0xFFFFu64; |
120 | 1.91k | |
121 | 1.91k | let regs = StandardRegisters::Mshv(regs); |
122 | | |
123 | 1.91k | let sregs = SpecialRegisters { |
124 | 1.91k | cs: generate_segment_register(&mut u)?, |
125 | 1.91k | ds: generate_segment_register(&mut u)?, |
126 | 1.91k | es: generate_segment_register(&mut u)?, |
127 | 1.91k | fs: generate_segment_register(&mut u)?, |
128 | 1.91k | gs: generate_segment_register(&mut u)?, |
129 | 1.91k | ss: generate_segment_register(&mut u)?, |
130 | 1.91k | tr: generate_segment_register(&mut u)?, |
131 | 1.91k | ldt: generate_segment_register(&mut u)?, |
132 | 1.91k | gdt: generate_descriptor_table(&mut u)?, |
133 | 1.91k | idt: generate_descriptor_table(&mut u)?, |
134 | 1.91k | cr0: u.arbitrary()?, |
135 | 1.91k | cr2: u.arbitrary()?, |
136 | 1.91k | cr3: u.arbitrary()?, |
137 | 1.91k | cr4: u.arbitrary()?, |
138 | 1.91k | cr8: u.arbitrary()?, |
139 | 1.91k | efer: u.arbitrary()?, |
140 | 1.91k | apic_base: u.arbitrary()?, |
141 | 1.91k | interrupt_bitmap: u.arbitrary()?, |
142 | | }; |
143 | | |
144 | 1.91k | let memory = u.arbitrary::<[u8; 8]>()?; |
145 | 1.91k | let insn = u.arbitrary::<[u8; 16]>()?; |
146 | | |
147 | 1.91k | let ctx = EmulatorContext { |
148 | 1.91k | state: EmulatorCpuState { regs, sregs }, |
149 | 1.91k | memory, |
150 | 1.91k | }; |
151 | 1.91k | |
152 | 1.91k | Ok((ctx, insn)) |
153 | 1.91k | } |