|
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