wasmer_compiler_cranelift/
table.rs

1use cranelift_codegen::cursor::FuncCursor;
2use cranelift_codegen::ir::{self, InstBuilder, condcodes::IntCC, immediates::Imm64};
3use cranelift_frontend::FunctionBuilder;
4
5use crate::translator::{MemoryAliasRegion, set_memflags_alias_region};
6
7/// Size of a WebAssembly table, in elements.
8#[derive(Clone, Debug)]
9pub enum TableSize {
10    /// Non-resizable table.
11    Static {
12        /// Non-resizable tables have a constant size known at compile time.
13        bound: u32,
14    },
15    /// Resizable table.
16    Dynamic {
17        /// Resizable tables declare a Cranelift global value to load the
18        /// current size from.
19        bound_gv: ir::GlobalValue,
20    },
21}
22
23impl TableSize {
24    /// Get a CLIF value representing the current bounds of this table.
25    pub fn bound(&self, mut pos: FuncCursor, index_ty: ir::Type) -> ir::Value {
26        match *self {
27            Self::Static { bound } => pos.ins().iconst(index_ty, Imm64::new(i64::from(bound))),
28            Self::Dynamic { bound_gv } => pos.ins().global_value(index_ty, bound_gv),
29        }
30    }
31}
32
33/// An implementation of a WebAssembly table.
34#[derive(Clone, Debug)]
35pub struct TableData {
36    /// Global value giving the address of the start of the table.
37    pub base_gv: ir::GlobalValue,
38
39    /// Constant offset to apply to `base_gv` to get the start of the table.
40    pub base_offset: i32,
41
42    /// The size of the table, in elements.
43    pub bound: TableSize,
44
45    /// The size of a table element, in bytes.
46    pub element_size: u32,
47
48    /// Whether table entries are inline `VMCallerCheckedAnyfunc` values.
49    pub inline_anyfunc: bool,
50}
51
52impl TableData {
53    /// Return a CLIF value containing a native pointer to the beginning of the
54    /// given index within this table.
55    pub fn prepare_table_addr(
56        &self,
57        pos: &mut FunctionBuilder,
58        mut index: ir::Value,
59        addr_ty: ir::Type,
60        enable_table_access_spectre_mitigation: bool,
61    ) -> (ir::Value, ir::MemFlagsData) {
62        let index_ty = pos.func.dfg.value_type(index);
63
64        // Start with the bounds check. Trap if `index + 1 > bound`.
65        let bound = self.bound.bound(pos.cursor(), index_ty);
66
67        // `index > bound - 1` is the same as `index >= bound`.
68        let oob = pos
69            .ins()
70            .icmp(IntCC::UnsignedGreaterThanOrEqual, index, bound);
71
72        if !enable_table_access_spectre_mitigation {
73            pos.ins().trapnz(oob, crate::TRAP_TABLE_OUT_OF_BOUNDS);
74        }
75
76        // Convert `index` to `addr_ty`.
77        if index_ty != addr_ty {
78            index = pos.ins().uextend(addr_ty, index);
79        }
80
81        // Add the table base address base
82        let mut base = pos.ins().global_value(addr_ty, self.base_gv);
83        if self.base_offset != 0 {
84            base = pos.ins().iadd_imm(base, i64::from(self.base_offset));
85        }
86
87        let element_size = self.element_size;
88        let offset = if element_size == 1 {
89            index
90        } else if element_size.is_power_of_two() {
91            pos.ins()
92                .ishl_imm(index, i64::from(element_size.trailing_zeros()))
93        } else {
94            pos.ins().imul_imm(index, element_size as i64)
95        };
96
97        let element_addr = pos.ins().iadd(base, offset);
98
99        let mut base_flags = ir::MemFlagsData::new().with_aligned();
100        set_memflags_alias_region(pos.func, &mut base_flags, MemoryAliasRegion::Table);
101        if enable_table_access_spectre_mitigation {
102            // Short-circuit the computed table element address to a null pointer
103            // when out-of-bounds. The consumer of this address will trap when
104            // trying to access it.
105            let zero = pos.ins().iconst(addr_ty, 0);
106            (
107                pos.ins().select_spectre_guard(oob, zero, element_addr),
108                base_flags.with_trap_code(Some(crate::TRAP_TABLE_OUT_OF_BOUNDS)),
109            )
110        } else {
111            (element_addr, base_flags.with_trap_code(None))
112        }
113    }
114}