From dd8bd5ce0772b08c271205508e48e98ef1c58ea8 Mon Sep 17 00:00:00 2001 From: iximeow Date: Mon, 24 Jun 2024 14:06:22 -0700 Subject: justify the current max instruction length this is also checked by a new fuzz target --- fuzz/Cargo.toml | 6 +++ .../instruction_text_buffer_size_ok.rs | 51 ++++++++++++++++++++++ src/lib.rs | 36 +++++++++++++++ src/long_mode/display.rs | 4 +- src/protected_mode/display.rs | 4 +- src/real_mode/display.rs | 4 +- 6 files changed, 96 insertions(+), 9 deletions(-) create mode 100644 fuzz/fuzz_targets/instruction_text_buffer_size_ok.rs diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index 2203dc3..a1f871e 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -36,6 +36,12 @@ test = false doc = false [[bin]] +name = "instruction_text_buffer_size_ok" +path = "fuzz_targets/instruction_text_buffer_size_ok.rs" +test = false +doc = false + +[[bin]] name = "display_c_does_not_panic" path = "fuzz_targets/display_c_does_not_panic.rs" test = false diff --git a/fuzz/fuzz_targets/instruction_text_buffer_size_ok.rs b/fuzz/fuzz_targets/instruction_text_buffer_size_ok.rs new file mode 100644 index 0000000..2c88424 --- /dev/null +++ b/fuzz/fuzz_targets/instruction_text_buffer_size_ok.rs @@ -0,0 +1,51 @@ +#![no_main] +#[macro_use] extern crate libfuzzer_sys; +extern crate yaxpeax_x86; +extern crate yaxpeax_arch; + +use std::fmt::Write; + +fuzz_target!(|data: &[u8]| { + let x86_64_decoder = yaxpeax_x86::long_mode::InstDecoder::default(); + let x86_32_decoder = yaxpeax_x86::protected_mode::InstDecoder::default(); + let x86_16_decoder = yaxpeax_x86::real_mode::InstDecoder::default(); + + if let Ok(inst) = x86_64_decoder.decode_slice(data) { + use yaxpeax_x86::long_mode::DisplayStyle; + + let mut s = String::new(); + write!(s, "{}", inst.display_with(DisplayStyle::Intel)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + s.clear(); + write!(s, "{}", inst.display_with(DisplayStyle::C)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + }; + + if let Ok(inst) = x86_32_decoder.decode_slice(data) { + use yaxpeax_x86::protected_mode::DisplayStyle; + + let mut s = String::new(); + write!(s, "{}", inst.display_with(DisplayStyle::Intel)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + s.clear(); + write!(s, "{}", inst.display_with(DisplayStyle::C)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + }; + + if let Ok(inst) = x86_16_decoder.decode_slice(data) { + use yaxpeax_x86::real_mode::DisplayStyle; + + let mut s = String::new(); + write!(s, "{}", inst.display_with(DisplayStyle::Intel)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + s.clear(); + write!(s, "{}", inst.display_with(DisplayStyle::C)).expect("can write"); + // MAX_INSTRUCTION_LEN is not a public crate item yet... + assert!(s.len() < 512); + }; +}); diff --git a/src/lib.rs b/src/lib.rs index 7ab6cb8..93274f9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -138,6 +138,42 @@ pub use protected_mode::Arch as x86_32; pub mod real_mode; pub use real_mode::Arch as x86_16; +// this exists to size `InstructionTextBuffer`'s buffer. it ideally would come from an `Arch` +// impl, or something related to `Arch`, but i'm not yet sure how to wire that up into +// yaxpeax-arch. so instead calculate an appropriate max size for all of 16-bit/32-bit/64-bit +// instruction printing that `InstructionTextBuffer` can be used for. +// +// `InstructionTextBuffer` prints an `InstructionDisplayer`, which means either intel syntax or +// pseudo-C. in the future, at&t probably, as well. +// +// the pseudo-C syntax's max length would be something like: +// ``` +// xacquire xrelease lock { repnz qword if /* signed */ greater_or_equal(rflags) then jmp gs:[xmm31 + +// xmm31 * 8 + 0x12345678]{k7}{z}{rne-sae} } +// ``` +// (which is nonsensical) or for an unknown opcode, +// ``` +// xacquire xrelease lock { op0 = op(op0, op1, op2, op3) } +// ``` +// where `opN` is an operand. the longest operand, same as above, would be something like +// ``` +// gs:[xmm31 + xmm31 * 8 + 0x12345678]{k7}{z}{rne-sae} +// ``` +// for a length like 262 bytes of operand, 55 bytes of prefixes and syntax, and another up-to-20 +// bytes of opcode. +// +// the longest contextualize_c might write is around 337 bytes. round up to 512 because it's.. not +// much extra. +// +// the same reasoning for intel syntax yields a smaller instruction: +// ``` +// xacquire xrelease lock op op1, op2, op3, op4 +// ``` +// where the longest operands are the same as above. this comes out to closer to 307 bytes. 512 +// bytes is still the longest of the two options. +#[allow(dead_code)] // can be an unused constant in some library configurations +const MAX_INSTRUCTION_LEN: usize = 512; + const MEM_SIZE_STRINGS: [&'static str; 65] = [ "BUG", "byte", "word", "BUG", "dword", "ptr", "far", "BUG", "qword", diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs index 1193f35..89d952b 100644 --- a/src/long_mode/display.rs +++ b/src/long_mode/display.rs @@ -4410,9 +4410,7 @@ mod buffer_sink { /// settings format instructions identically to their corresponding `fmt::Display`. pub fn new() -> Self { let mut buf = alloc::string::String::new(); - // TODO: move 512 out to a MAX_INSTRUCTION_LEN const and appropriate justification (and - // fuzzing and ..) - buf.reserve(512); + buf.reserve(crate::MAX_INSTRUCTION_LEN); Self { content: buf, } diff --git a/src/protected_mode/display.rs b/src/protected_mode/display.rs index db12878..321b5b5 100644 --- a/src/protected_mode/display.rs +++ b/src/protected_mode/display.rs @@ -2955,9 +2955,7 @@ mod buffer_sink { /// settings format instructions identically to their corresponding `fmt::Display`. pub fn new() -> Self { let mut buf = alloc::string::String::new(); - // TODO: move 512 out to a MAX_INSTRUCTION_LEN const and appropriate justification (and - // fuzzing and ..) - buf.reserve(512); + buf.reserve(crate::MAX_INSTRUCTION_LEN); Self { content: buf, } diff --git a/src/real_mode/display.rs b/src/real_mode/display.rs index e8bd191..669b8d7 100644 --- a/src/real_mode/display.rs +++ b/src/real_mode/display.rs @@ -2957,9 +2957,7 @@ mod buffer_sink { /// settings format instructions identically to their corresponding `fmt::Display`. pub fn new() -> Self { let mut buf = alloc::string::String::new(); - // TODO: move 512 out to a MAX_INSTRUCTION_LEN const and appropriate justification (and - // fuzzing and ..) - buf.reserve(512); + buf.reserve(crate::MAX_INSTRUCTION_LEN); Self { content: buf, } -- cgit v1.1