wit_validator/
lib.rs

1//! A validator for the wasm interface types binary format.
2//!
3//! This crate currently only provides the ability to validate a full in-memory
4//! wasm module. It does not currently expose the results of typechecking, only
5//! if a wasm binary typechecks or not.
6
7#![deny(missing_docs)]
8
9use anyhow::{anyhow, bail, Context, Result};
10use std::collections::HashSet;
11use std::mem;
12use wasmparser::{FuncType, ImportSectionEntryType, Payload, TypeDef};
13use wit_parser::*;
14
15/// Validates an entire WebAssembly module listed by `bytes`
16///
17/// The `bytes` given must be an entire WebAssembly module, not just the
18/// interface types custom section. This will validate only the wasm interface
19/// types custom section, it will only perform minimal validation of the rest of
20/// the module as needed. For example core wasm functions aren't typechecked
21/// here.
22pub fn validate(bytes: &[u8]) -> Result<()> {
23    let mut validator = Validator::default();
24    for payload in wasmparser::Parser::new(0).parse_all(bytes) {
25        match payload? {
26            Payload::TypeSection(s) => {
27                validator.validate_section(1, "type", s, |v, ty| {
28                    if let TypeDef::Func(ty) = ty {
29                        v.core_types.push(ty);
30                    }
31                    Ok(())
32                })?;
33            }
34            Payload::ImportSection(s) => {
35                validator.validate_section(2, "import", s, |v, ty| {
36                    match ty.ty {
37                        ImportSectionEntryType::Function(ty) => {
38                            v.validate_core_type_idx(ty)?;
39                            v.core_funcs.push((ty, CoreFunc::Import));
40                        }
41                        ImportSectionEntryType::Memory(_) => {
42                            v.memories += 1;
43                        }
44                        _ => {}
45                    }
46                    Ok(())
47                })?;
48            }
49            Payload::FunctionSection(s) => {
50                validator.validate_section(3, "function", s, |v, ty| {
51                    v.validate_core_type_idx(ty)?;
52                    v.core_funcs.push((ty, CoreFunc::Local));
53                    Ok(())
54                })?;
55            }
56            Payload::MemorySection(s) => {
57                validator.validate_section(4, "memory", s, |v, _| {
58                    v.memories += 1;
59                    Ok(())
60                })?;
61            }
62            Payload::CustomSection {
63                name: wit_schema_version::SECTION_NAME,
64                data,
65                data_offset,
66            } => {
67                validator
68                    .validate_wit_custom_section(data_offset, data)
69                    .context("failed to validate interface types section")?;
70            }
71            _ => {}
72        }
73    }
74    Ok(())
75}
76
77/// A validator for the wasm interface types section.
78///
79/// This structure is used to visit *just* the wasm interface types subsection,
80/// if it's already been parsed out.
81#[derive(Default)]
82struct Validator<'a> {
83    visited: bool,
84    last_order: u8,
85    memories: u32,
86    types: Vec<Type>,
87    func: Vec<u32>,
88    exports: HashSet<&'a str>,
89    core_types: Vec<FuncType>,
90    core_funcs: Vec<(u32, CoreFunc)>,
91    type_stack: Vec<ValType>,
92}
93
94enum CoreFunc {
95    Import,
96    Local,
97}
98
99impl<'a> Validator<'a> {
100    /// Validates the wasm interface types custom section given.
101    ///
102    /// The `offset` given is the offset within the file that `bytes` was found
103    /// at. This is purely used for error messages. The `bytes` given must be
104    /// the entire contents of the wasm interface types section.
105    fn validate_wit_custom_section(&mut self, offset: usize, bytes: &'a [u8]) -> Result<()> {
106        if self.visited {
107            bail!("found two `wasm-interface-types` custom sections");
108        }
109        self.visited = true;
110
111        let mut parser =
112            Parser::new(offset, bytes).context("failed to parse interface types header")?;
113
114        while !parser.is_empty() {
115            match parser.section().context("failed to read section header")? {
116                Section::Type(s) => {
117                    self.validate_section(100, "adapter type", s, Self::validate_type)?
118                }
119                Section::Import(s) => {
120                    self.validate_section(101, "adapter import", s, Self::validate_import)?
121                }
122                Section::Func(s) => {
123                    self.validate_section(102, "adapter func", s, Self::validate_func)?
124                }
125                Section::Export(s) => {
126                    self.validate_section(103, "adapter export", s, Self::validate_export)?
127                }
128                Section::Implement(s) => {
129                    self.validate_section(104, "adapter implement", s, Self::validate_implement)?
130                }
131            }
132        }
133        Ok(())
134    }
135
136    fn validate_section<S, T, E>(
137        &mut self,
138        id: u8,
139        name: &str,
140        iter: S,
141        mut validate: impl FnMut(&mut Self, T) -> Result<()>,
142    ) -> Result<()>
143    where
144        S: IntoIterator<Item = Result<T, E>>,
145        E: Into<anyhow::Error>,
146    {
147        if id <= self.last_order {
148            bail!("found `{}` section but was out of order", name);
149        }
150        self.last_order = id;
151        for (i, item) in iter.into_iter().enumerate() {
152            let item = item
153                .map_err(|e| e.into())
154                .with_context(|| format!("failed to parse {} {}", name, i))?;
155            validate(self, item).with_context(|| format!("failed to validate {} {}", name, i))?;
156        }
157        Ok(())
158    }
159
160    fn validate_type(&mut self, ty: Type) -> Result<()> {
161        self.types.push(ty);
162        Ok(())
163    }
164
165    fn validate_import(&mut self, import: Import<'a>) -> Result<()> {
166        self.validate_adapter_type_idx(import.ty)?;
167        self.func.push(import.ty);
168        Ok(())
169    }
170
171    fn validate_func(&mut self, func: Func<'a>) -> Result<()> {
172        let mut type_stack = mem::replace(&mut self.type_stack, Vec::new());
173        self.func.push(func.ty);
174        let ty = self.validate_adapter_type_idx(func.ty)?;
175
176        for instr in func.instrs() {
177            self.validate_instr(instr?, &ty.params, &mut type_stack)?;
178        }
179        for result in ty.results.iter() {
180            self.expect_interface(*result, &mut type_stack)?;
181        }
182        if !type_stack.is_empty() {
183            bail!("value stack isn't empty on function exit");
184        }
185        self.type_stack = type_stack;
186        return Ok(());
187    }
188
189    fn validate_instr(
190        &self,
191        instr: Instruction,
192        params: &[ValType],
193        stack: &mut Vec<ValType>,
194    ) -> Result<()> {
195        use Instruction::*;
196        match instr {
197            ArgGet(idx) => {
198                let ty = params
199                    .get(idx as usize)
200                    .ok_or_else(|| anyhow!("parameter index out of bounds: {}", idx))?;
201                stack.push(*ty);
202            }
203            CallCore(idx) => {
204                let ty = self.validate_core_func_idx(idx)?.0;
205                for param in ty.params.iter().rev() {
206                    self.expect_wasm(*param, stack)?;
207                }
208                for result in ty.returns.iter() {
209                    stack.push(wasm2adapter(*result)?);
210                }
211            }
212            MemoryToString(mem) => {
213                if mem >= self.memories {
214                    bail!("memory index out of bounds: {}", mem);
215                }
216                self.expect_wasm(wasmparser::Type::I32, stack)?;
217                self.expect_wasm(wasmparser::Type::I32, stack)?;
218                stack.push(ValType::String);
219            }
220            StringToMemory(args) => {
221                if args.mem >= self.memories {
222                    bail!("memory index out of bounds: {}", args.mem);
223                }
224                let ty = self.validate_core_func_idx(args.malloc)?.0;
225                if &*ty.params != [wasmparser::Type::I32] || &*ty.returns != [wasmparser::Type::I32]
226                {
227                    bail!(
228                        "malloc function {} does not have correct signature",
229                        args.malloc
230                    );
231                }
232                self.expect_interface(ValType::String, stack)?;
233                stack.push(ValType::I32);
234                stack.push(ValType::I32);
235            }
236            CallAdapter(idx) => {
237                let ty = self.validate_adapter_func_idx(idx)?;
238                for param in ty.params.iter().rev() {
239                    self.expect_interface(*param, stack)?;
240                }
241                for result in ty.results.iter() {
242                    stack.push(*result);
243                }
244            }
245            DeferCallCore(idx) => {
246                let ty = self.validate_core_func_idx(idx)?.0;
247                if ty.returns.len() > 0 {
248                    bail!("cannot have returned values in deferred calls");
249                }
250                // Make sure everything on the stack is right...
251                for param in ty.params.iter() {
252                    self.expect_wasm(*param, stack)?;
253                }
254                // ... but don't actually consume it.
255                for param in ty.params.iter().rev() {
256                    stack.push(wasm2adapter(*param)?);
257                }
258            }
259            End => bail!("extra `end` instruction found"),
260
261            I32ToS8 => {
262                self.expect_wasm(wasmparser::Type::I32, stack)?;
263                stack.push(ValType::S8);
264            }
265            I32ToS8X => {
266                self.expect_wasm(wasmparser::Type::I32, stack)?;
267                stack.push(ValType::S8);
268            }
269            I32ToU8 => {
270                self.expect_wasm(wasmparser::Type::I32, stack)?;
271                stack.push(ValType::U8);
272            }
273            I32ToS16 => {
274                self.expect_wasm(wasmparser::Type::I32, stack)?;
275                stack.push(ValType::S16);
276            }
277            I32ToS16X => {
278                self.expect_wasm(wasmparser::Type::I32, stack)?;
279                stack.push(ValType::S16);
280            }
281            I32ToU16 => {
282                self.expect_wasm(wasmparser::Type::I32, stack)?;
283                stack.push(ValType::U16);
284            }
285            I32ToS32 => {
286                self.expect_wasm(wasmparser::Type::I32, stack)?;
287                stack.push(ValType::S32);
288            }
289            I32ToU32 => {
290                self.expect_wasm(wasmparser::Type::I32, stack)?;
291                stack.push(ValType::U32);
292            }
293            I32ToS64 => {
294                self.expect_wasm(wasmparser::Type::I32, stack)?;
295                stack.push(ValType::S64);
296            }
297            I32ToU64 => {
298                self.expect_wasm(wasmparser::Type::I32, stack)?;
299                stack.push(ValType::U64);
300            }
301
302            I64ToS8 => {
303                self.expect_wasm(wasmparser::Type::I64, stack)?;
304                stack.push(ValType::S8);
305            }
306            I64ToS8X => {
307                self.expect_wasm(wasmparser::Type::I64, stack)?;
308                stack.push(ValType::S8);
309            }
310            I64ToU8 => {
311                self.expect_wasm(wasmparser::Type::I64, stack)?;
312                stack.push(ValType::U8);
313            }
314            I64ToS16 => {
315                self.expect_wasm(wasmparser::Type::I64, stack)?;
316                stack.push(ValType::S16);
317            }
318            I64ToS16X => {
319                self.expect_wasm(wasmparser::Type::I64, stack)?;
320                stack.push(ValType::S16);
321            }
322            I64ToU16 => {
323                self.expect_wasm(wasmparser::Type::I64, stack)?;
324                stack.push(ValType::U16);
325            }
326            I64ToS32 => {
327                self.expect_wasm(wasmparser::Type::I64, stack)?;
328                stack.push(ValType::S32);
329            }
330            I64ToS32X => {
331                self.expect_wasm(wasmparser::Type::I64, stack)?;
332                stack.push(ValType::S32);
333            }
334            I64ToU32 => {
335                self.expect_wasm(wasmparser::Type::I64, stack)?;
336                stack.push(ValType::U32);
337            }
338            I64ToS64 => {
339                self.expect_wasm(wasmparser::Type::I64, stack)?;
340                stack.push(ValType::S64);
341            }
342            I64ToU64 => {
343                self.expect_wasm(wasmparser::Type::I64, stack)?;
344                stack.push(ValType::U64);
345            }
346
347            S8ToI32 => {
348                self.expect_interface(ValType::S8, stack)?;
349                stack.push(wasm2adapter(wasmparser::Type::I32)?);
350            }
351            U8ToI32 => {
352                self.expect_interface(ValType::U8, stack)?;
353                stack.push(wasm2adapter(wasmparser::Type::I32)?);
354            }
355            S16ToI32 => {
356                self.expect_interface(ValType::S16, stack)?;
357                stack.push(wasm2adapter(wasmparser::Type::I32)?);
358            }
359            U16ToI32 => {
360                self.expect_interface(ValType::U16, stack)?;
361                stack.push(wasm2adapter(wasmparser::Type::I32)?);
362            }
363            S32ToI32 => {
364                self.expect_interface(ValType::S32, stack)?;
365                stack.push(wasm2adapter(wasmparser::Type::I32)?);
366            }
367            U32ToI32 => {
368                self.expect_interface(ValType::U32, stack)?;
369                stack.push(wasm2adapter(wasmparser::Type::I32)?);
370            }
371            S64ToI32 => {
372                self.expect_interface(ValType::S64, stack)?;
373                stack.push(wasm2adapter(wasmparser::Type::I32)?);
374            }
375            S64ToI32X => {
376                self.expect_interface(ValType::S64, stack)?;
377                stack.push(wasm2adapter(wasmparser::Type::I32)?);
378            }
379            U64ToI32 => {
380                self.expect_interface(ValType::U64, stack)?;
381                stack.push(wasm2adapter(wasmparser::Type::I32)?);
382            }
383            U64ToI32X => {
384                self.expect_interface(ValType::U64, stack)?;
385                stack.push(wasm2adapter(wasmparser::Type::I32)?);
386            }
387
388            S8ToI64 => {
389                self.expect_interface(ValType::S8, stack)?;
390                stack.push(wasm2adapter(wasmparser::Type::I64)?);
391            }
392            U8ToI64 => {
393                self.expect_interface(ValType::U8, stack)?;
394                stack.push(wasm2adapter(wasmparser::Type::I64)?);
395            }
396            S16ToI64 => {
397                self.expect_interface(ValType::S16, stack)?;
398                stack.push(wasm2adapter(wasmparser::Type::I64)?);
399            }
400            U16ToI64 => {
401                self.expect_interface(ValType::U16, stack)?;
402                stack.push(wasm2adapter(wasmparser::Type::I64)?);
403            }
404            S32ToI64 => {
405                self.expect_interface(ValType::S32, stack)?;
406                stack.push(wasm2adapter(wasmparser::Type::I64)?);
407            }
408            U32ToI64 => {
409                self.expect_interface(ValType::U32, stack)?;
410                stack.push(wasm2adapter(wasmparser::Type::I64)?);
411            }
412            S64ToI64 => {
413                self.expect_interface(ValType::S64, stack)?;
414                stack.push(wasm2adapter(wasmparser::Type::I64)?);
415            }
416            U64ToI64 => {
417                self.expect_interface(ValType::U64, stack)?;
418                stack.push(wasm2adapter(wasmparser::Type::I64)?);
419            }
420        }
421        Ok(())
422    }
423
424    fn expect_wasm(&self, expected: wasmparser::Type, stack: &mut Vec<ValType>) -> Result<()> {
425        let actual = match stack.pop() {
426            Some(t) => t,
427            None => bail!("expected {:?} on type stack, found nothing", expected),
428        };
429        if !tys_match(actual, expected) {
430            bail!("expected {:?} on type stack, found {:?}", expected, actual);
431        }
432        Ok(())
433    }
434
435    fn expect_interface(&self, expected: ValType, stack: &mut Vec<ValType>) -> Result<()> {
436        let actual = match stack.pop() {
437            Some(t) => t,
438            None => bail!("expected {:?} on type stack, found nothing", expected),
439        };
440        if expected != actual {
441            bail!("expected {:?} on type stack, found {:?}", expected, actual);
442        }
443        Ok(())
444    }
445
446    fn validate_export(&mut self, export: Export<'a>) -> Result<()> {
447        self.validate_adapter_func_idx(export.func)?;
448        if !self.exports.insert(export.name) {
449            bail!("found duplicate export `{}`", export.name);
450        }
451        Ok(())
452    }
453
454    fn validate_implement(&mut self, i: Implement) -> Result<()> {
455        let adapter_ty = self.validate_adapter_func_idx(i.adapter_func)?;
456        let (core_ty, kind) = self.validate_core_func_idx(i.core_func)?;
457        match kind {
458            CoreFunc::Import => {}
459            CoreFunc::Local => {
460                bail!(
461                    "implement directive must be connected to imported \
462                     function in the core module"
463                );
464            }
465        }
466
467        if adapter_ty.params.len() != core_ty.params.len()
468            || adapter_ty
469                .params
470                .iter()
471                .zip(core_ty.params.iter())
472                .any(|(a, b)| !tys_match(*a, *b))
473            || adapter_ty.results.len() != core_ty.returns.len()
474            || adapter_ty
475                .results
476                .iter()
477                .zip(core_ty.returns.iter())
478                .any(|(a, b)| !tys_match(*a, *b))
479        {
480            bail!(
481                "core function {} has a different type signature \
482                 than adapter function {}",
483                i.core_func,
484                i.adapter_func
485            );
486        }
487        Ok(())
488    }
489
490    fn validate_core_type_idx(&self, ty: u32) -> Result<&FuncType> {
491        self.core_types
492            .get(ty as usize)
493            .ok_or_else(|| anyhow!("type index too large: {}", ty))
494    }
495
496    fn validate_adapter_type_idx(&self, ty: u32) -> Result<&Type> {
497        self.types
498            .get(ty as usize)
499            .ok_or_else(|| anyhow!("adapter type index too large: {}", ty))
500    }
501
502    fn validate_adapter_func_idx(&self, ty: u32) -> Result<&Type> {
503        let ty = self
504            .func
505            .get(ty as usize)
506            .ok_or_else(|| anyhow!("adapter func index too large: {}", ty))?;
507        self.validate_adapter_type_idx(*ty)
508    }
509
510    fn validate_core_func_idx(&self, ty: u32) -> Result<(&FuncType, &CoreFunc)> {
511        let (ty, kind) = self
512            .core_funcs
513            .get(ty as usize)
514            .ok_or_else(|| anyhow!("func index too large: {}", ty))?;
515        Ok((self.validate_core_type_idx(*ty)?, kind))
516    }
517}
518
519fn tys_match(a: ValType, b: wasmparser::Type) -> bool {
520    match (a, b) {
521        (ValType::I32, wasmparser::Type::I32)
522        | (ValType::I64, wasmparser::Type::I64)
523        | (ValType::F32, wasmparser::Type::F32)
524        | (ValType::F64, wasmparser::Type::F64)
525        | (ValType::Externref, wasmparser::Type::ExternRef) => true,
526        _ => false,
527    }
528}
529
530fn wasm2adapter(a: wasmparser::Type) -> Result<ValType> {
531    Ok(match a {
532        wasmparser::Type::I32 => ValType::I32,
533        wasmparser::Type::I64 => ValType::I64,
534        wasmparser::Type::F32 => ValType::F32,
535        wasmparser::Type::F64 => ValType::F64,
536        wasmparser::Type::ExternRef => ValType::Externref,
537        _ => bail!("currently {:?} is not a valid wasm interface type", a),
538    })
539}