diff options
author | iximeow <me@iximeow.net> | 2025-04-13 19:34:39 -0700 |
---|---|---|
committer | iximeow <me@iximeow.net> | 2025-04-13 19:34:39 -0700 |
commit | 6f10ec12b4c81e4d040b933b1e3ee01da5ac9a0c (patch) | |
tree | d7e94de37b909b2a6ca29085d3e3c11902df1c30 /src | |
parent | 2a7d0f4dd1b7ec13fa402cf7c18dc9f62e8c4b55 (diff) |
fuzz cases: only 64 system registers, display should never panic
Diffstat (limited to 'src')
-rw-r--r-- | src/display.rs | 123 | ||||
-rw-r--r-- | src/lib.rs | 47 |
2 files changed, 100 insertions, 70 deletions
diff --git a/src/display.rs b/src/display.rs index 6827108..90dfca4 100644 --- a/src/display.rs +++ b/src/display.rs @@ -40,20 +40,29 @@ fn assign_mode_label(mode: Option<AssignMode>) -> &'static str { } } +fn display_or_partial<T: fmt::Display + ?Sized>(t: Option<&T>) -> String { + match t { + Some(t) => { t.to_string() }, + None => { "<invalid>".to_string() } + } +} + impl fmt::Display for Instruction { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { if special_display_rules(&self.opcode) { match self.opcode { Opcode::AndAnd => { - return write!(f, "{} = and({}, and({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = and({}, and({}, {}))", + display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); } Opcode::AndOr => { - return write!(f, "{} = and({}, or({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = and({}, or({}, {}))", + display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); } Opcode::OrAnd => { - return write!(f, "{} = or({}, and({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = or({}, and({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); } Opcode::AndNot => { @@ -65,125 +74,125 @@ impl fmt::Display for Instruction { assign_mode_label(self.flags.assign_mode), self.sources[0], self.sources[1]); } else { - return write!(f, "{} {} and({}, ~{})", self.dest.as_ref().unwrap(), + return write!(f, "{} {} and({}, ~{})", display_or_partial(self.dest.as_ref()), assign_mode_label(self.flags.assign_mode), self.sources[0], self.sources[1]); } } Opcode::OrOr => { - return write!(f, "{} = or({}, or({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = or({}, or({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); } Opcode::AndAndNot => { - return write!(f, "{} = and({}, and({}, !{}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = and({}, and({}, !{}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); } Opcode::AndOrNot => { - return write!(f, "{} = and({}, or({}, !{}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = and({}, or({}, !{}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); } Opcode::OrAndNot => { - return write!(f, "{} = or({}, and({}, !{}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = or({}, and({}, !{}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); } Opcode::OrNot => { - return write!(f, "{} {} or({}, !{})", self.dest.as_ref().unwrap(), + return write!(f, "{} {} or({}, !{})", display_or_partial(self.dest.as_ref()), assign_mode_label(self.flags.assign_mode), self.sources[0], self.sources[1]); } Opcode::OrOrNot => { - return write!(f, "{} = or({}, or({}, !{}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = or({}, or({}, !{}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); } Opcode::AndLsr => { - return write!(f, "{} = and({}, lsr({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = and({}, lsr({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::OrLsr => { - return write!(f, "{} = or({}, lsr({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = or({}, lsr({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::AddLsr => { - return write!(f, "{} = add({}, lsr({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = add({}, lsr({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::SubLsr => { - return write!(f, "{} = sub({}, lsr({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = sub({}, lsr({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::AddLsl => { - return write!(f, "{} = add({}, lsl({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = add({}, lsl({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::AddAsl => { - return write!(f, "{} = add({}, asl({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = add({}, asl({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::SubAsl => { - return write!(f, "{} = sub({}, asl({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = sub({}, asl({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::AndAsl => { - return write!(f, "{} = and({}, asl({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = and({}, asl({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::OrAsl => { - return write!(f, "{} = or({}, asl({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = or({}, asl({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::AddAdd => { - return write!(f, "{} = add({}, add({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = add({}, add({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::AddSub => { - return write!(f, "{} = add({}, sub({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = add({}, sub({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::AddMpyi => { - return write!(f, "{} = add({}, mpyi({}, {}))", self.dest.as_ref().unwrap(), + return write!(f, "{} = add({}, mpyi({}, {}))", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1], self.sources[2]); }, Opcode::MpyiPos => { - return write!(f, "{} = +mpyi({}, {})", self.dest.as_ref().unwrap(), + return write!(f, "{} = +mpyi({}, {})", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1]); }, Opcode::MpyiNeg => { - return write!(f, "{} = -mpyi({}, {})", self.dest.as_ref().unwrap(), + return write!(f, "{} = -mpyi({}, {})", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1]); }, Opcode::AddClb => { - return write!(f, "{} = add(clb({}), {})", self.dest.as_ref().unwrap(), + return write!(f, "{} = add(clb({}), {})", display_or_partial(self.dest.as_ref()), self.sources[0], self.sources[1]); } Opcode::Vacsh => { return write!(f, "{}, {} = {}({}, {})", - self.dest.as_ref().unwrap(), self.alt_dest.as_ref().unwrap(), + display_or_partial(self.dest.as_ref()), display_or_partial(self.alt_dest.as_ref()), self.opcode, self.sources[0], self.sources[1]); } Opcode::Vminub => { if let Some(alt_dest) = self.alt_dest.as_ref() { return write!(f, "{}, {} = {}({}, {})", - self.dest.as_ref().unwrap(), alt_dest, + display_or_partial(self.dest.as_ref()), alt_dest, self.opcode, self.sources[0], self.sources[1]); } else { return write!(f, "{} = {}({}, {})", - self.dest.as_ref().unwrap(), + display_or_partial(self.dest.as_ref()), self.opcode, self.sources[0], self.sources[1]); } } Opcode::SfRecipa => { return write!(f, "{}, {} = {}({}, {})", - self.dest.as_ref().unwrap(), self.alt_dest.as_ref().unwrap(), + display_or_partial(self.dest.as_ref()), display_or_partial(self.alt_dest.as_ref()), self.opcode, self.sources[0], self.sources[1]); } Opcode::SfInvsqrta => { return write!(f, "{}, {} = {}({})", - self.dest.as_ref().unwrap(), self.alt_dest.as_ref().unwrap(), + display_or_partial(self.dest.as_ref()), display_or_partial(self.alt_dest.as_ref()), self.opcode, self.sources[0]); } Opcode::Any8VcmpbEq => { return write!(f, "{} = {}any8(vcmpb.eq({}, {}))", - self.dest.as_ref().unwrap(), + display_or_partial(self.dest.as_ref()), if self.flags.negated { "!" } else { "" }, self.sources[0], self.sources[1]); } @@ -211,7 +220,7 @@ impl fmt::Display for Instruction { Some(BranchHint::NotTaken) => { ":nt" }, None => { "" }, }, - self.dest.as_ref().unwrap() + display_or_partial(self.dest.as_ref()) ); } // handle cmp+jump first; this includes elements that would be misformatted below (like @@ -221,25 +230,31 @@ impl fmt::Display for Instruction { Opcode::CmpGtuJump, Opcode::TestClrJump, ]; if COMPARE_JUMPS.contains(&self.opcode) { - let predicate = self.flags.predicate.as_ref().unwrap(); - let preg = Operand::pred(predicate.num()); + if let Some(predicate) = self.flags.predicate.as_ref() { + let preg = Operand::pred(predicate.num()); - let hint_label = match self.flags.branch_hint.unwrap() { - BranchHint::Taken => { "t" }, - BranchHint::NotTaken => { "nt" }, - }; + let hint_label = match self.flags.branch_hint { + Some(BranchHint::Taken) => { "t" }, + Some(BranchHint::NotTaken) => { "nt" }, + None => { + "<invalid decode>" + } + }; - write!(f, "{} = {}({}, {}); if ({}{}.new) jump:{} {}", - preg, - self.opcode.cmp_str().unwrap(), - self.sources[0], - self.sources[1], - if predicate.negated() { "!" } else { "" }, - preg, - hint_label, - self.dest.as_ref().unwrap(), - )?; - return Ok(()); + write!(f, "{} = {}({}, {}); if ({}{}.new) jump:{} {}", + preg, + display_or_partial(self.opcode.cmp_str()), + self.sources[0], + self.sources[1], + if predicate.negated() { "!" } else { "" }, + preg, + hint_label, + display_or_partial(self.dest.as_ref()), + )?; + return Ok(()); + } else { + return write!(f, "<invalid decode>"); + } } if let Some(predication) = self.flags.predicate { @@ -309,9 +324,9 @@ impl fmt::Display for Instruction { // TransferRegisterJump and TransferImmediateJump also have special display rules... if self.opcode == Opcode::TransferRegisterJump || self.opcode == Opcode::TransferImmediateJump { write!(f, "{} = {}; jump {}", - self.alt_dest.as_ref().unwrap(), + display_or_partial(self.alt_dest.as_ref()), self.sources[0], - self.dest.as_ref().unwrap(), + display_or_partial(self.dest.as_ref()), )?; return Ok(()); } @@ -327,11 +342,11 @@ impl fmt::Display for Instruction { None => { "" }, }; write!(f, "if ({}({}, {})) jump{} {}", - self.opcode.cmp_str().unwrap(), // (obvious, but) decoder bug if this fails + display_or_partial(self.opcode.cmp_str()), // (obvious, but) decoder bug if this fails self.sources[0], self.sources[1], hint_label, - self.dest.unwrap(), + display_or_partial(self.dest.as_ref()), )?; return Ok(()); } @@ -347,7 +362,7 @@ impl fmt::Display for Instruction { Some(BranchHint::NotTaken) => ":nt", None => "", }, - self.dest.unwrap())?; + display_or_partial(self.dest.as_ref()))?; return Ok(()); } @@ -112,7 +112,7 @@ impl Predicate { } } -#[derive(Debug, Copy, Clone, Default)] +#[derive(Debug, Copy, Clone, Default, PartialEq)] pub struct LoopEnd { loops_ended: u8 } @@ -163,6 +163,16 @@ pub struct InstructionPacket { loop_effect: LoopEnd, } +impl PartialEq for InstructionPacket { + fn eq(&self, other: &Self) -> bool { + let instructions = self.instruction_count as usize; + self.instruction_count == other.instruction_count && + self.word_count == other.word_count && + self.loop_effect == other.loop_effect && + self.instructions[..instructions] == other.instructions[..instructions] + } +} + /// a decoded `hexagon` instruction. this is only one of potentially several instructions in an /// [`InstructionPacket`]. /// @@ -1204,8 +1214,8 @@ trait DecodeHandler<T: Reader<<Hexagon as Arch>::Address, <Hexagon as Arch>::Wor fn read_inst_word(&mut self, words: &mut T) -> Result<u32, <Hexagon as Arch>::DecodeError>; fn on_decode_start(&mut self) {} fn on_decode_end(&mut self) {} - fn start_instruction(&mut self); - fn end_instruction(&mut self); + fn start_instruction(&mut self) -> Result<(), <Hexagon as Arch>::DecodeError> { Ok(()) } + fn end_instruction(&mut self) -> Result<(), <Hexagon as Arch>::DecodeError> { Ok(()) } fn on_loop_end(&mut self, loop_num: u8); fn on_opcode_decoded(&mut self, _opcode: Opcode) -> Result<(), <Hexagon as Arch>::DecodeError> { Ok(()) } fn on_source_decoded(&mut self, _operand: Operand) -> Result<(), <Hexagon as Arch>::DecodeError> { Ok(()) } @@ -1368,9 +1378,13 @@ impl<T: yaxpeax_arch::Reader<<Hexagon as Arch>::Address, <Hexagon as Arch>::Word self.read_u32(words) } fn on_word_read(&mut self, _word: <Hexagon as Arch>::Word) { } - fn start_instruction(&mut self) { } - fn end_instruction(&mut self) { + fn start_instruction(&mut self) -> Result<(), <Hexagon as Arch>::DecodeError> { + opcode_check!(self.instruction_count < self.instructions.len() as u8); + Ok(()) + } + fn end_instruction(&mut self) -> Result<(), <Hexagon as Arch>::DecodeError> { self.instruction_count += 1; + Ok(()) } } @@ -1981,12 +1995,12 @@ fn decode_packet< parse_lo: fn(&mut H, u16, &mut Option<u32>) -> Result<(), <Hexagon as Arch>::DecodeError>, parse_hi: fn(&mut H, u16, &mut Option<u32>) -> Result<(), <Hexagon as Arch>::DecodeError>, ) -> Result<(), yaxpeax_arch::StandardDecodeError> { - self.handler.start_instruction(); + self.handler.start_instruction()?; parse_lo(self.handler, self.subinstr_low, self.extender)?; - self.handler.end_instruction(); - self.handler.start_instruction(); + self.handler.end_instruction()?; + self.handler.start_instruction()?; parse_hi(self.handler, self.subinstr_high, self.extender)?; - self.handler.end_instruction(); + self.handler.end_instruction()?; Ok(()) } } @@ -2080,7 +2094,7 @@ fn decode_packet< if iclass == 0b0000 { extender = Some((inst & 0x3fff) | ((inst >> 2) & 0x3ffc000)); } else { - handler.start_instruction(); + handler.start_instruction()?; decode_instruction(decoder, handler, inst, &mut extender)?; // V73 section 10.9: // > The constant extender serves as a prefix for an instruction: it does not execute @@ -2095,7 +2109,7 @@ fn decode_packet< // if the extender was not consumed, the instruction (packet) is invalid return Err(DecodeError::InvalidOpcode); } - handler.end_instruction(); + handler.end_instruction()?; } current_word += 1; @@ -3113,9 +3127,9 @@ fn decode_instruction< 0b0111000 => { // not in V73! store to supervisor register? let sssss = reg_b16(inst); - let ddddddd = inst & 0b111_1111; + let dddddd = inst & 0b11_1111; handler.on_opcode_decoded(Opcode::TransferRegister)?; - handler.on_dest_decoded(Operand::sr(ddddddd as u8))?; + handler.on_dest_decoded(Operand::sr(dddddd as u8))?; handler.on_source_decoded(Operand::gpr(sssss))?; } // TODO: 1001000 loop0 goes here @@ -3152,9 +3166,9 @@ fn decode_instruction< 0b1110110 | 0b1110111 => { // not in V73! load supervisor register? let sssss = reg_b0(inst); - let ddddddd = (inst >> 16) & 0b111_1111; + let dddddd = (inst >> 16) & 0b11_1111; handler.on_opcode_decoded(Opcode::TransferRegister)?; - handler.on_source_decoded(Operand::sr(ddddddd as u8))?; + handler.on_source_decoded(Operand::sr(dddddd as u8))?; handler.on_dest_decoded(Operand::gpr(sssss))?; } 0b1111000 | 0b1111001 | @@ -3763,7 +3777,8 @@ fn decode_instruction< handler.on_opcode_decoded(Opcode::Nop)?; } _ => { - unreachable!("impossible pattern"); + // anything left unhandled is invalid.. + opcode_check!(false); } } } |