Coverage Report

Created: 2026-07-03 16:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/build/source/src/validation/error.rs
Line
Count
Source
1
use core::fmt::{Display, Formatter};
2
3
use crate::{
4
    core::structure::{modules::indices::FuncIdx, types::ValType},
5
    validation::validation_stack::ValidationStackEntry,
6
    DecodingError, RefType,
7
};
8
9
#[derive(Debug, PartialEq, Eq, Clone)]
10
pub enum ValidationError {
11
    Decoding(DecodingError),
12
    /// An index for a type is invalid.
13
    InvalidTypeIdx(u32),
14
    /// An index for a function is invalid.
15
    InvalidFuncIdx(u32),
16
    /// An index for a table is invalid.
17
    InvalidTableIdx(u32),
18
    /// An index for a memory is invalid.
19
    InvalidMemIdx(u32),
20
    /// An index for a global is invalid.
21
    InvalidGlobalIdx(u32),
22
    /// An index for an element segment is invalid.
23
    InvalidElemIdx(u32),
24
    /// An index for a data segment is invalid.
25
    InvalidDataIdx(u32),
26
    /// An index for a local is invalid.
27
    InvalidLocalIdx(u32),
28
    /// An index for a label is invalid.
29
    InvalidLabelIdx(u32),
30
    /// An index for a lane of some vector type is invalid.
31
    InvalidLaneIdx(u8),
32
33
    ExprMissingEnd,
34
    InvalidInstr(u8),
35
    InvalidMultiByteInstr(u8, u32),
36
    EndInvalidValueStack,
37
    InvalidValidationStackValType(Option<ValType>),
38
    InvalidValidationStackType(ValidationStackEntry),
39
    ExpectedAnOperand,
40
    /// An attempt has been made to mutate a const global
41
    MutationOfConstGlobal,
42
    /// An alignment of some memory instruction is invalid
43
    ErroneousAlignment {
44
        alignment: u32,
45
        minimum_required_alignment: u32,
46
    },
47
    /// The validation control stack is empty, even though an entry was expected.
48
    // TODO Reconsider if we want to expose this error. It should probably never happen and thus also never bubble up to the user.
49
    ValidationCtrlStackEmpty,
50
    /// An `else` instruction was found while not inside an `if` block.
51
    ElseWithoutMatchingIf,
52
    /// An `end` for a matching `if` instruction was found, but there was no `else` instruction in between.
53
    IfWithoutMatchingElse,
54
    /// A `table.init` instruction specified a table and an element segment that store different reference types.
55
    MismatchedRefTypesDuringTableInit {
56
        table_ty: RefType,
57
        elem_ty: RefType,
58
    },
59
    /// A `table.copy` instruction referenced two tables that store different reference types.
60
    MismatchedRefTypesDuringTableCopy {
61
        source_table_ty: RefType,
62
        destination_table_ty: RefType,
63
    },
64
    /// An expected reference type did not match the actual reference type on the validation stack.
65
    MismatchedRefTypesOnValidationStack {
66
        expected: RefType,
67
        actual: RefType,
68
    },
69
    /// An indirect call to a table with does not store function references was made.
70
    IndirectCallToNonFuncRefTable(RefType),
71
    /// A reference type was expected to be on the stack, but a value type was found.
72
    ExpectedReferenceTypeOnStack(ValType),
73
    /// When a is referenced in the code section it must be contained in `C.refs`, which was not the case
74
    ReferencingAnUnreferencedFunction(FuncIdx),
75
    /// The select instructions may work with multiple values in the future. However, as of now its vector may only have one element.
76
    InvalidSelectTypeVectorLength(usize),
77
    /// Multiple exports share the same name
78
    DuplicateExportName,
79
    /// Multiple memories are not yet allowed without the proposal.
80
    UnsupportedMultipleMemoriesProposal,
81
    /// An expr in the code section has trailing instructions following its `end` instruction.
82
    CodeExprHasTrailingInstructions,
83
    /// The lengths of the function and code sections must match.
84
    FunctionAndCodeSectionsHaveDifferentLengths,
85
    /// The data count specified in the data count section and the length of the data section must match.
86
    DataCountAndDataSectionsLengthAreDifferent,
87
    InvalidImportType,
88
    /// The function signature of the start function is invalid. It must not specify any parameters or return values.
89
    InvalidStartFunctionSignature,
90
    /// An active element segment's type and its table's type are different.
91
    ActiveElementSegmentTypeMismatch,
92
    /// The data count section is required, if there are instructions that use
93
    /// data indices.
94
    MissingDataCountSection,
95
    /// The mode of a data segment was invalid. Only values in the range 0..=2
96
    /// are allowed.
97
    InvalidDataSegmentMode(u32),
98
    /// The mode of an element was invalid. Only values in the range 0..=7 are
99
    /// allowed.
100
    InvalidElementMode(u32),
101
    /// The module contains too many functions, i.e. imported or locally-defined
102
    /// functions. The maximum number of functions is [`u32::MAX`].
103
    TooManyFunctions,
104
    /// The module contains too many tables, i.e. imported or locally-defined
105
    /// tables. The maximum number of tables is [`u32::MAX`].
106
    TooManyTables,
107
    /// The module contains too many memories, i.e. imported or locally-defined
108
    /// memories. The maximum number of memories is [`u32::MAX`].
109
    TooManyMemories,
110
    /// The module contains too many globals, i.e. imported or locally-defined
111
    /// globals. The maximum number of memories is [`u32::MAX`].
112
    TooManyGlobals,
113
    /// The min field of a limits type is larger than the max field.
114
    LimitsMinLargerThanMax {
115
        min: u32,
116
        max: u32,
117
    },
118
    /// The min or max field of a limits type is not within the expected range.
119
    LimitsNotWithinRange(u32),
120
    /// A mutable global was referenced in some global.get instruction in a constant expression.
121
    MutGlobalInConstGlobalGet,
122
}
123
124
impl core::error::Error for ValidationError {}
125
126
impl Display for ValidationError {
127
0
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
128
0
        match self {
129
0
            ValidationError::Decoding(err) => write!(f, "Decoding failed: {err}"),
130
131
0
            ValidationError::InvalidTypeIdx(idx) => write!(f, "The type index {idx} is invalid"),
132
0
            ValidationError::InvalidFuncIdx(idx) => write!(f, "The function index {idx} is invalid"),
133
0
            ValidationError::InvalidTableIdx(idx) => write!(f, "The table index {idx} is invalid"),
134
0
            ValidationError::InvalidMemIdx(idx) => write!(f, "The memory index {idx} is invalid"),
135
0
            ValidationError::InvalidGlobalIdx(idx) => write!(f, "The global index {idx} is invalid"),
136
0
            ValidationError::InvalidElemIdx(idx) => write!(f, "The element segment index {idx} is invalid"),
137
0
            ValidationError::InvalidDataIdx(idx) => write!(f, "The data segment index {idx} is invalid"),
138
0
            ValidationError::InvalidLocalIdx(idx) => write!(f, "The local index {idx} is invalid"),
139
0
            ValidationError::InvalidLabelIdx(idx) => write!(f, "The label index {idx} is invalid"),
140
0
            ValidationError::InvalidLaneIdx(idx) => write!(f, "The lane index {idx} is invalid"),
141
142
0
            ValidationError::ExprMissingEnd => write!(f, "An expr type is missing an end byte"),
143
0
            ValidationError::InvalidInstr(byte) => write!(f, "The instruction {byte:#x} is invalid"),
144
0
            ValidationError::InvalidMultiByteInstr(first_byte, second_instr) => write!(f, "The multi-byte instruction {first_byte:#x} {second_instr} is invalid"),
145
0
            ValidationError::ActiveElementSegmentTypeMismatch => write!(f, "an element segment's type and its table's type are different"),
146
0
            ValidationError::EndInvalidValueStack => write!(f, "Different value stack types were expected at the end of a block/function"),
147
0
            ValidationError::InvalidValidationStackValType(ty) => write!(f, "An unexpected type `{ty:?}` was found on the stack when trying to pop another"),
148
0
            ValidationError::InvalidValidationStackType(ty) => write!(f, "An unexpected type `{ty:?}` was found on the stack"),
149
0
            ValidationError::ExpectedAnOperand => write!(f, "Expected a value type operand on the stack"),
150
0
            ValidationError::MutationOfConstGlobal => write!(f, "An attempt has been made to mutate a const global"),
151
0
            ValidationError::ErroneousAlignment {alignment , minimum_required_alignment} => write!(f, "The alignment 2^{alignment} is not less or equal to the required alignment 2^{minimum_required_alignment}"),
152
0
            ValidationError::ValidationCtrlStackEmpty => write!(f, "Failed to retrieve last ctrl block because validation ctrl stack is empty"),
153
0
            ValidationError::ElseWithoutMatchingIf => write!(f, "Found `else` without a previous matching `if` instruction"),
154
0
            ValidationError::IfWithoutMatchingElse => write!(f, "Found `end` without a previous matching `else` to an `if` instruction"),
155
0
            ValidationError::MismatchedRefTypesDuringTableInit { table_ty, elem_ty } => write!(f, "Mismatch of table type `{table_ty:?}` and element segment type `{elem_ty:?}` for `table.init` instruction"),
156
0
            ValidationError::MismatchedRefTypesDuringTableCopy { source_table_ty, destination_table_ty } => write!(f, "Mismatch of source table type `{source_table_ty:?}` and destination table type `{destination_table_ty:?}` for `table.copy` instruction"),
157
0
            ValidationError::MismatchedRefTypesOnValidationStack { expected, actual } => write!(f, "Mismatch of reference types on the value stack: Expected `{expected:?}` but got `{actual:?}`"),
158
0
            ValidationError::IndirectCallToNonFuncRefTable(table_ty) => write!(f, "An indirect call to a table which does not store function references but instead `{table_ty:?}` was made"),
159
0
            ValidationError::ExpectedReferenceTypeOnStack(found_valtype) => write!(f, "Expected a reference type but instead found a `{found_valtype:?}` on the stack"),
160
0
            ValidationError::ReferencingAnUnreferencedFunction(func_idx) => write!(f, "Referenced a function with index {func_idx} that was not referenced in prior validation"),
161
0
            ValidationError::InvalidSelectTypeVectorLength(len) => write!(f, "The type vector of a `select` instruction must be of length 1 as of now but it is of length {len} instead"),
162
0
            ValidationError::DuplicateExportName => write!(f,"Multiple exports share the same name"),
163
0
            ValidationError::UnsupportedMultipleMemoriesProposal => write!(f,"A memory index other than 1 was used, but the proposal for multiple memories is not yet supported"),
164
0
            ValidationError::CodeExprHasTrailingInstructions => write!(f,"A code expression has invalid trailing instructions following its `end` instruction"),
165
0
            ValidationError::FunctionAndCodeSectionsHaveDifferentLengths => write!(f,"The function and code sections have different lengths"),
166
0
            ValidationError::DataCountAndDataSectionsLengthAreDifferent => write!(f,"The data count section specifies a different length than there are data segments in the data section"),
167
0
            ValidationError::InvalidImportType => f.write_str("Invalid import type"),
168
0
            ValidationError::InvalidStartFunctionSignature => write!(f,"The start function has parameters or return types which it is not allowed to have"),
169
0
            ValidationError::MissingDataCountSection => f.write_str("Some instructions could not be validated because the data count section is missing"),
170
0
            ValidationError::InvalidDataSegmentMode(mode) => write!(f, "The mode of a data segment was invalid (only 0..=2 is allowed): {mode}"),
171
0
            ValidationError::InvalidElementMode(mode) => write!(f, "The mode of an element was invalid (only 0..=7 is allowed): {mode}"),
172
0
            ValidationError::TooManyFunctions => f.write_str("The module contains too many functions. The maximum number of functions (either imported or locally-defined) is 2^32 - 1"),
173
0
            ValidationError::TooManyTables => f.write_str("The module contains too many tables. The maximum number of tables (either imported or locally-defined) is 2^32 - 1"),
174
0
            ValidationError::TooManyMemories => f.write_str("The module contains too many memories. The maximum number of memories (either imported or locally-defined) is 2^32 - 1"),
175
0
            ValidationError::TooManyGlobals => f.write_str("The module contains too many globals. The maximum number of globals (either imported or locally-defined) is 2^32 - 1"),
176
0
            ValidationError::LimitsMinLargerThanMax { min, max } => write!(f, "Limits are invalid because min={min} is larger than max={max}"),
177
0
            ValidationError::LimitsNotWithinRange(range) => write!(f, "The min or max field of a limits type is not within the expected range of {range}"),
178
0
            ValidationError::MutGlobalInConstGlobalGet => f.write_str("A mutable global was referenced in some global.get instruction in a constant expression"),
179
        }
180
0
    }
181
}
182
183
impl ValidationError {
184
    /// Convert this error to a message that is compatible with the error messages used by the official Wasm testsuite.
185
0
    pub fn to_message(&self) -> &'static str {
186
0
        todo!("convert validation error to testsuite message");
187
    }
188
}
189
190
impl From<DecodingError> for ValidationError {
191
681
    fn from(error: DecodingError) -> Self {
192
681
        Self::Decoding(error)
193
681
    }
194
}
195
196
#[cfg(test)]
197
mod test {
198
    use alloc::string::ToString;
199
200
    use crate::core::decoding::error::DecodingError;
201
202
    #[test]
203
1
    fn fmt_invalid_magic() {
204
1
        assert!(DecodingError::InvalidMagic
205
1
            .to_string()
206
1
            .contains("magic number"));
207
1
    }
208
209
    #[test]
210
1
    fn fmt_invalid_version() {
211
1
        assert!(DecodingError::InvalidBinaryFormatVersion
212
1
            .to_string()
213
1
            .contains("version"));
214
1
    }
215
}