chore: Harden RVM implementation (#537)

Signed-off-by: Anand Krishnamoorthi <anakrish@microsoft.com>
This commit is contained in:
Anand Krishnamoorthi
2026-01-14 06:07:31 +05:30
committed by GitHub
parent d626f75421
commit 740db8a0f5
16 changed files with 1685 additions and 1040 deletions
+242 -66
View File
@@ -1,6 +1,7 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
use super::execution_model::SuspendReason;
use crate::value::Value;
use alloc::string::String;
use alloc::vec::Vec;
@@ -9,112 +10,287 @@ use thiserror::Error;
/// VM execution errors
#[derive(Error, Debug, Clone, PartialEq)]
pub enum VmError {
#[error("Execution stopped: exceeded maximum instruction limit of {limit}")]
InstructionLimitExceeded { limit: usize },
#[error("Execution stopped: exceeded maximum instruction limit of {limit} after {executed} instructions (pc={pc})")]
InstructionLimitExceeded {
limit: usize,
executed: usize,
pc: usize,
},
#[error("Literal index {index} out of bounds")]
LiteralIndexOutOfBounds { index: usize },
#[error("Literal index {index} out of bounds (pc={pc})")]
LiteralIndexOutOfBounds { index: u16, pc: usize },
#[error("Register {register} does not contain an object")]
RegisterNotObject { register: u8 },
#[error("Register {register} does not contain an object (value={value:?}, pc={pc})")]
RegisterNotObject {
register: u8,
value: Value,
pc: usize,
},
#[error("ObjectCreate: template is not an object")]
ObjectCreateInvalidTemplate,
#[error("ObjectCreate: template is not an object (pc={pc}, template={template:?})")]
ObjectCreateInvalidTemplate { template: Value, pc: usize },
#[error("Register {register} does not contain an array")]
RegisterNotArray { register: u8 },
#[error("Register {register} does not contain an array (value={value:?}, pc={pc})")]
RegisterNotArray {
register: u8,
value: Value,
pc: usize,
},
#[error("Register {register} does not contain a set")]
RegisterNotSet { register: u8 },
#[error("Register {register} does not contain a set (value={value:?}, pc={pc})")]
RegisterNotSet {
register: u8,
value: Value,
pc: usize,
},
#[error("Rule index {index} out of bounds")]
RuleIndexOutOfBounds { index: u16 },
#[error("Register index {index} out of bounds (pc={pc}, register_count={register_count})")]
RegisterIndexOutOfBounds {
index: u8,
pc: usize,
register_count: usize,
},
#[error("Rule index {index} has no info")]
RuleInfoMissing { index: u16 },
#[error("Rule index {index} out of bounds (pc={pc}, available={available})")]
RuleIndexOutOfBounds {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid object create params index: {index}")]
InvalidObjectCreateParams { index: u16 },
#[error("Rule index {index} has no info (pc={pc}, available={available})")]
RuleInfoMissing {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid template literal index: {index}")]
InvalidTemplateLiteralIndex { index: u16 },
#[error("Invalid object create params index: {index} (pc={pc}, available={available})")]
InvalidObjectCreateParams {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid chained index params index: {index}")]
InvalidChainedIndexParams { index: u16 },
#[error("Invalid template literal index: {index} (pc={pc}, available={available})")]
InvalidTemplateLiteralIndex {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid array create params index: {index}")]
InvalidArrayCreateParams { index: u16 },
#[error("Invalid chained index params index: {index} (pc={pc}, available={available})")]
InvalidChainedIndexParams {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid set create params index: {index}")]
InvalidSetCreateParams { index: u16 },
#[error("Invalid array create params index: {index} (pc={pc}, available={available})")]
InvalidArrayCreateParams {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid virtual data document lookup params index: {index}")]
InvalidVirtualDataDocumentLookupParams { index: u16 },
#[error("Invalid set create params index: {index} (pc={pc}, available={available})")]
InvalidSetCreateParams {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid comprehension start params index: {index}")]
InvalidComprehensionBeginParams { index: u16 },
#[error("Invalid virtual data document lookup params index: {index} (pc={pc}, available={available})")]
InvalidVirtualDataDocumentLookupParams {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid rule index: {rule_index:?}")]
InvalidRuleIndex { rule_index: Value },
#[error("Invalid comprehension start params index: {index} (pc={pc}, available={available})")]
InvalidComprehensionBeginParams {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid rule tree entry: {value:?}")]
InvalidRuleTreeEntry { value: Value },
#[error("Invalid loop params index: {index} (pc={pc}, available={available})")]
InvalidLoopParams {
index: u16,
pc: usize,
available: usize,
},
#[error("Builtin function expects exactly {expected} arguments, got {actual}")]
BuiltinArgumentMismatch { expected: u16, actual: usize },
#[error("Invalid rule index: {rule_index:?} (pc={pc})")]
InvalidRuleIndex { rule_index: Value, pc: usize },
#[error("Builtin function not resolved: {name}")]
BuiltinNotResolved { name: String },
#[error("Invalid rule tree entry: {value:?} (pc={pc})")]
InvalidRuleTreeEntry { value: Value, pc: usize },
#[error("Cannot add {left:?} and {right:?}")]
InvalidAddition { left: Value, right: Value },
#[error("Builtin function expects exactly {expected} arguments, got {actual} (pc={pc})")]
BuiltinArgumentMismatch {
expected: u16,
actual: usize,
pc: usize,
},
#[error("Cannot subtract {left:?} and {right:?}")]
InvalidSubtraction { left: Value, right: Value },
#[error("Builtin function not resolved: {name} (pc={pc})")]
BuiltinNotResolved { name: String, pc: usize },
#[error("Cannot multiply {left:?} and {right:?}")]
InvalidMultiplication { left: Value, right: Value },
#[error("Cannot add {left:?} and {right:?} (pc={pc})")]
InvalidAddition {
left: Value,
right: Value,
pc: usize,
},
#[error("Cannot divide {left:?} and {right:?}")]
InvalidDivision { left: Value, right: Value },
#[error("Cannot subtract {left:?} and {right:?} (pc={pc})")]
InvalidSubtraction {
left: Value,
right: Value,
pc: usize,
},
#[error("modulo on floating-point number")]
ModuloOnFloat,
#[error("Cannot multiply {left:?} and {right:?} (pc={pc})")]
InvalidMultiplication {
left: Value,
right: Value,
pc: usize,
},
#[error("Cannot modulo {left:?} and {right:?}")]
InvalidModulo { left: Value, right: Value },
#[error("Cannot divide {left:?} and {right:?} (pc={pc})")]
InvalidDivision {
left: Value,
right: Value,
pc: usize,
},
#[error("Cannot iterate over {value:?}")]
InvalidIteration { value: Value },
#[error("modulo on floating-point number (left={left:?}, right={right:?}, pc={pc})")]
ModuloOnFloat {
left: Value,
right: Value,
pc: usize,
},
#[error("HostAwait executed but no response provided for destination register {dest} (id: {identifier:?})")]
HostAwaitResponseMissing { dest: u8, identifier: Value },
#[error("Cannot modulo {left:?} and {right:?} (pc={pc})")]
InvalidModulo {
left: Value,
right: Value,
pc: usize,
},
#[error("Assertion failed")]
AssertionFailed,
#[error("Cannot iterate over {value:?} (pc={pc})")]
InvalidIteration { value: Value, pc: usize },
#[error("Rule-data conflict: {0}")]
RuleDataConflict(String),
#[error("HostAwait executed but no response provided for destination register {dest} (id: {identifier:?}, pc={pc})")]
HostAwaitResponseMissing {
dest: u8,
identifier: Value,
pc: usize,
},
#[error("Arithmetic error: {0}")]
ArithmeticError(String),
#[error("Assertion failed (pc={pc})")]
AssertionFailed { pc: usize },
#[error("Entry point index {index} out of bounds (max: {max_index})")]
InvalidEntryPointIndex { index: usize, max_index: usize },
#[error("Rule-data conflict: {message} (pc={pc})")]
RuleDataConflict { message: String, pc: usize },
#[error("Entry point '{name}' not found. Available entry points: {available:?}")]
#[error("Arithmetic error: {message} (pc={pc})")]
ArithmeticError { message: String, pc: usize },
#[error("Entry point index {index} out of bounds (max: {max_index}, pc={pc})")]
InvalidEntryPointIndex {
index: usize,
max_index: usize,
pc: usize,
},
#[error("Entry point '{name}' not found (pc={pc}). Available entry points: {available:?}")]
EntryPointNotFound {
name: String,
available: Vec<String>,
pc: usize,
},
#[error("Internal VM error: {0}")]
Internal(String),
#[error("Entry point PC {pc} >= instruction count {instruction_count} for entry point '{entry_point}'")]
EntryPointPcOutOfBounds {
pc: usize,
instruction_count: usize,
entry_point: String,
},
#[error("Register count {register_count} below base count {base_count} (pc={pc})")]
RegisterCountBelowBase {
register_count: usize,
base_count: usize,
pc: usize,
},
#[error("Program counter {pc} out of bounds for instruction count {instruction_count}")]
ProgramCounterOutOfBounds { pc: usize, instruction_count: usize },
#[error("Rule cache size {cache_size} != rule info count {rule_info_count} (pc={pc})")]
RuleCacheSizeMismatch {
cache_size: usize,
rule_info_count: usize,
pc: usize,
},
#[error("Suspend reason {reason:?} is not supported in run-to-completion execution (pc={pc})")]
UnsupportedSuspendInRunToCompletion { reason: SuspendReason, pc: usize },
#[error("Cannot resume VM when execution state is {state} (pc={pc})")]
InvalidResumeState { state: String, pc: usize },
#[error("HostAwait suspension requires a resume value for reason {reason:?} (pc={pc})")]
MissingResumeValue { reason: SuspendReason, pc: usize },
#[error("Unexpected resume value supplied for reason {reason:?} (pc={pc})")]
UnexpectedResumeValue { reason: SuspendReason, pc: usize },
#[error("Missing execution frame: {context} (pc={pc})")]
MissingExecutionFrame { context: &'static str, pc: usize },
#[error("Unhandled instruction variant: {instruction} (pc={pc})")]
UnhandledInstruction { instruction: String, pc: usize },
#[error("Invalid function call params index: {index} (pc={pc}, available={available})")]
InvalidFunctionCallParamsIndex {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid builtin call params index: {index} (pc={pc}, available={available})")]
InvalidBuiltinCallParamsIndex {
index: u16,
pc: usize,
available: usize,
},
#[error("Invalid builtin info index: {index} (pc={pc}, available={available})")]
InvalidBuiltinInfoIndex {
index: u16,
pc: usize,
available: usize,
},
#[error("Rule frame has no initial PC (pc={pc})")]
RuleFrameMissingInitialPc { pc: usize },
#[error("Call rule stack underflow during rule finalization (pc={pc})")]
CallRuleStackUnderflow { pc: usize },
#[error("Internal VM error: {message} (pc={pc})")]
Internal { message: String, pc: usize },
}
impl From<anyhow::Error> for VmError {
fn from(err: anyhow::Error) -> Self {
VmError::ArithmeticError(alloc::format!("{}", err))
VmError::ArithmeticError {
message: alloc::format!("{}", err),
pc: 0,
}
}
}