|
1 | 1 | #![no_main] |
2 | 2 |
|
3 | | -use arbitrary::Arbitrary; |
4 | | -use capstone::arch::{BuildsCapstone, BuildsCapstoneSyntax}; |
5 | | -use cranelift_assembler_x64::{AsReg, Inst, Registers}; |
| 3 | +use cranelift_assembler_x64::{fuzz, Inst}; |
6 | 4 | use libfuzzer_sys::fuzz_target; |
7 | 5 |
|
8 | | -// Generate a random assembly instruction and check its encoding and |
9 | | -// pretty-printing against a known-good disassembler. |
10 | | -// |
11 | | -// # Panics |
12 | | -// |
13 | | -// This function panics to express failure as expected by the `arbitrary` |
14 | | -// fuzzer infrastructure. It may fail during assembly, disassembly, or when |
15 | | -// comparing the disassembled strings. |
16 | | -fuzz_target!(|inst: Inst<FuzzRegs>| { |
17 | | - // Check that we can actually assemble this instruction. |
18 | | - let assembled = assemble(&inst); |
19 | | - let expected = disassemble(&assembled); |
20 | | - |
21 | | - // Check that our pretty-printed output matches the known-good output. |
22 | | - let expected = expected.split_once(' ').unwrap().1; |
23 | | - let actual = inst.to_string(); |
24 | | - if expected != actual { |
25 | | - println!("> {inst}"); |
26 | | - println!(" debug: {inst:x?}"); |
27 | | - println!(" assembled: {}", pretty_print_hexadecimal(&assembled)); |
28 | | - assert_eq!(expected, &actual); |
29 | | - } |
| 6 | +fuzz_target!(|inst: Inst<fuzz::FuzzRegs>| { |
| 7 | + fuzz::roundtrip(&inst); |
30 | 8 | }); |
31 | | - |
32 | | -/// Use this assembler to emit machine code into a byte buffer. |
33 | | -/// |
34 | | -/// This will skip any traps or label registrations, but this is fine for the |
35 | | -/// single-instruction disassembly we're doing here. |
36 | | -fn assemble(insn: &Inst<FuzzRegs>) -> Vec<u8> { |
37 | | - let mut buffer = Vec::new(); |
38 | | - let offsets: Vec<i32> = Vec::new(); |
39 | | - insn.encode(&mut buffer, &offsets); |
40 | | - buffer |
41 | | -} |
42 | | - |
43 | | -/// Building a new `Capstone` each time is suboptimal (TODO). |
44 | | -fn disassemble(assembled: &[u8]) -> String { |
45 | | - let cs = capstone::Capstone::new() |
46 | | - .x86() |
47 | | - .mode(capstone::arch::x86::ArchMode::Mode64) |
48 | | - .syntax(capstone::arch::x86::ArchSyntax::Att) |
49 | | - .detail(true) |
50 | | - .build() |
51 | | - .expect("failed to create Capstone object"); |
52 | | - let insns = cs |
53 | | - .disasm_all(assembled, 0x0) |
54 | | - .expect("failed to disassemble"); |
55 | | - assert_eq!(insns.len(), 1, "not a single instruction: {assembled:x?}"); |
56 | | - let insn = insns.first().expect("at least one instruction"); |
57 | | - assert_eq!(assembled.len(), insn.len()); |
58 | | - insn.to_string() |
59 | | -} |
60 | | - |
61 | | -fn pretty_print_hexadecimal(hex: &[u8]) -> String { |
62 | | - use std::fmt::Write; |
63 | | - let mut s = String::with_capacity(hex.len() * 2); |
64 | | - for b in hex { |
65 | | - write!(&mut s, "{b:02X}").unwrap(); |
66 | | - } |
67 | | - s |
68 | | -} |
69 | | - |
70 | | -/// Fuzz-specific registers. |
71 | | -/// |
72 | | -/// For the fuzzer, we do not need any fancy register types; see [`FuzzReg`]. |
73 | | -#[derive(Arbitrary, Debug)] |
74 | | -pub struct FuzzRegs; |
75 | | - |
76 | | -impl Registers for FuzzRegs { |
77 | | - type ReadGpr = FuzzReg; |
78 | | - type ReadWriteGpr = FuzzReg; |
79 | | -} |
80 | | - |
81 | | -/// A simple `u8` register type for fuzzing only |
82 | | -#[derive(Clone, Copy, Debug)] |
83 | | -pub struct FuzzReg(u8); |
84 | | - |
85 | | -impl<'a> Arbitrary<'a> for FuzzReg { |
86 | | - fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> { |
87 | | - Ok(Self::new(u.int_in_range(0..=15)?)) |
88 | | - } |
89 | | -} |
90 | | - |
91 | | -impl AsReg for FuzzReg { |
92 | | - fn new(enc: u8) -> Self { |
93 | | - Self(enc) |
94 | | - } |
95 | | - fn enc(&self) -> u8 { |
96 | | - self.0 |
97 | | - } |
98 | | -} |
99 | | - |
100 | | -#[cfg(test)] |
101 | | -mod test { |
102 | | - use super::*; |
103 | | - use arbtest::arbtest; |
104 | | - use std::sync::atomic::{AtomicUsize, Ordering}; |
105 | | - |
106 | | - #[test] |
107 | | - fn smoke() { |
108 | | - let count = AtomicUsize::new(0); |
109 | | - arbtest(|u| { |
110 | | - let inst: Inst<FuzzRegs> = u.arbitrary()?; |
111 | | - roundtrip(&inst); |
112 | | - println!("#{}: {inst}", count.fetch_add(1, Ordering::SeqCst)); |
113 | | - Ok(()) |
114 | | - }) |
115 | | - .budget_ms(1_000); |
116 | | - |
117 | | - // This will run the `roundtrip` fuzzer for one second. To repeatably |
118 | | - // test a single input, append `.seed(0x<failing seed>)`. |
119 | | - } |
120 | | -} |
0 commit comments