2023-06-24 17:18:31 -05:00
|
|
|
//! Validate if program is sound to execute
|
|
|
|
|
2023-06-24 17:16:14 -05:00
|
|
|
/// Program validation error kind
|
2023-06-20 19:07:48 -05:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
pub enum ErrorKind {
|
2023-06-24 17:16:14 -05:00
|
|
|
/// Unknown opcode
|
2023-06-20 19:07:48 -05:00
|
|
|
InvalidInstruction,
|
2023-06-24 17:16:14 -05:00
|
|
|
/// VM doesn't implement this valid opcode
|
2023-06-20 19:07:48 -05:00
|
|
|
Unimplemented,
|
2023-06-24 17:16:14 -05:00
|
|
|
/// Attempted to copy over register boundary
|
2023-06-20 19:07:48 -05:00
|
|
|
RegisterArrayOverflow,
|
2023-07-13 04:05:41 -05:00
|
|
|
/// Program is not validly terminated
|
|
|
|
InvalidEnd,
|
2023-06-20 19:07:48 -05:00
|
|
|
}
|
|
|
|
|
2023-06-24 17:16:14 -05:00
|
|
|
/// Error
|
2023-06-20 19:07:48 -05:00
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
|
|
pub struct Error {
|
2023-06-24 17:16:14 -05:00
|
|
|
/// Kind
|
2023-07-07 08:22:03 -05:00
|
|
|
pub kind: ErrorKind,
|
2023-06-24 17:16:14 -05:00
|
|
|
/// Location in bytecode
|
2023-06-20 19:07:48 -05:00
|
|
|
pub index: usize,
|
|
|
|
}
|
|
|
|
|
2023-06-24 17:16:14 -05:00
|
|
|
/// Perform bytecode validation. If it passes, the program should be
|
|
|
|
/// sound to execute.
|
2023-06-20 19:07:48 -05:00
|
|
|
pub fn validate(mut program: &[u8]) -> Result<(), Error> {
|
|
|
|
use hbbytecode::opcode::*;
|
|
|
|
|
2023-07-13 04:05:41 -05:00
|
|
|
if program.len() < 12 {
|
|
|
|
return Err(Error {
|
|
|
|
kind: ErrorKind::InvalidEnd,
|
|
|
|
index: 0,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
for (index, item) in program.iter().enumerate().skip(program.len() - 12) {
|
|
|
|
if *item != 0 {
|
|
|
|
return Err(Error {
|
|
|
|
kind: ErrorKind::InvalidEnd,
|
|
|
|
index,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-06-20 19:07:48 -05:00
|
|
|
let start = program;
|
|
|
|
loop {
|
2023-06-24 17:16:14 -05:00
|
|
|
// Match on instruction types and perform necessary checks
|
2023-06-20 19:07:48 -05:00
|
|
|
program = match program {
|
|
|
|
[] => return Ok(()),
|
|
|
|
[LD..=ST, reg, _, _, _, _, _, _, _, _, _, count, ..]
|
|
|
|
if usize::from(*reg) * 8 + usize::from(*count) > 2048 =>
|
|
|
|
{
|
|
|
|
return Err(Error {
|
2023-07-07 08:22:03 -05:00
|
|
|
kind: ErrorKind::RegisterArrayOverflow,
|
2023-06-20 19:07:48 -05:00
|
|
|
index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
[BRC, src, dst, count, ..]
|
|
|
|
if src.checked_add(*count).is_none() || dst.checked_add(*count).is_none() =>
|
|
|
|
{
|
|
|
|
return Err(Error {
|
2023-07-07 08:22:03 -05:00
|
|
|
kind: ErrorKind::RegisterArrayOverflow,
|
2023-06-20 19:07:48 -05:00
|
|
|
index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
|
|
|
|
})
|
|
|
|
}
|
2023-07-13 04:05:41 -05:00
|
|
|
[UN | NOP | ECALL, rest @ ..]
|
2023-06-20 19:07:48 -05:00
|
|
|
| [DIR | DIRF, _, _, _, _, rest @ ..]
|
|
|
|
| [ADD..=CMPU | BRC | ADDF..=MULF, _, _, _, rest @ ..]
|
2023-07-07 08:22:03 -05:00
|
|
|
| [NEG..=NOT | CP..=SWA | NEGF..=FTI, _, _, rest @ ..]
|
2023-07-12 05:45:50 -05:00
|
|
|
| [LI, _, _, _, _, _, _, _, _, _, rest @ ..]
|
2023-07-24 11:48:42 -05:00
|
|
|
| [ADDI..=XORI | CMPI..=CMPUI | BMC | JAL..=JGTU | ADDFI..=MULFI, _, _, _, _, _, _, _, _, _, _, rest @ ..]
|
|
|
|
| [SLI..=SRSI, _, _, _, _, rest @ ..]
|
2023-06-20 19:07:48 -05:00
|
|
|
| [LD..=ST, _, _, _, _, _, _, _, _, _, _, _, _, rest @ ..] => rest,
|
|
|
|
_ => {
|
|
|
|
return Err(Error {
|
2023-07-07 08:22:03 -05:00
|
|
|
kind: ErrorKind::InvalidInstruction,
|
2023-06-20 19:07:48 -05:00
|
|
|
index: (program.as_ptr() as usize) - (start.as_ptr() as usize),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|