use super::Reachability;
use crate::{
func_environ::FuncEnvironment,
heap::{HeapData, HeapStyle},
translator::materialize_global_value,
};
use Reachability::*;
use cranelift_codegen::{
cursor::{Cursor, FuncCursor},
ir::{self, InstBuilder, RelSourceLoc, condcodes::IntCC},
};
use cranelift_frontend::FunctionBuilder;
use wasmer_types::WasmResult;
pub fn bounds_check_and_compute_addr(
builder: &mut FunctionBuilder,
env: &mut FuncEnvironment<'_>,
heap: &HeapData,
index: ir::Value,
offset: u32,
access_size: u8,
) -> WasmResult<Reachability<ir::Value>> {
let index = cast_index_to_pointer_ty(
index,
heap.index_type,
env.pointer_type(),
&mut builder.cursor(),
);
let offset_and_size = offset_plus_size(offset, access_size);
let spectre_mitigations_enabled = env.heap_access_spectre_mitigation();
let host_page_size_log2 = env.target_config().page_size_align_log2;
let can_use_virtual_memory = heap.page_size_log2 >= host_page_size_log2;
let make_compare =
|builder: &mut FunctionBuilder, compare_kind: IntCC, lhs: ir::Value, rhs: ir::Value| {
builder.ins().icmp(compare_kind, lhs, rhs)
};
Ok(match heap.style {
HeapStyle::Dynamic { .. } if offset_and_size == 1 => {
let bound = get_dynamic_heap_bound(builder, env, heap);
let oob = make_compare(builder, IntCC::UnsignedGreaterThanOrEqual, index, bound);
Reachable(explicit_check_oob_condition_and_compute_addr(
&mut builder.cursor(),
heap,
env.pointer_type(),
index,
offset,
spectre_mitigations_enabled,
oob,
))
}
HeapStyle::Dynamic { .. }
if can_use_virtual_memory && offset_and_size <= heap.offset_guard_size =>
{
let bound = get_dynamic_heap_bound(builder, env, heap);
let oob = make_compare(builder, IntCC::UnsignedGreaterThan, index, bound);
Reachable(explicit_check_oob_condition_and_compute_addr(
&mut builder.cursor(),
heap,
env.pointer_type(),
index,
offset,
spectre_mitigations_enabled,
oob,
))
}
HeapStyle::Dynamic { .. } if offset_and_size <= heap.min_size => {
let bound = get_dynamic_heap_bound(builder, env, heap);
let adjustment = offset_and_size as i64;
let adjustment_value = builder.ins().iconst(env.pointer_type(), adjustment);
let adjusted_bound = builder.ins().isub(bound, adjustment_value);
let oob = make_compare(builder, IntCC::UnsignedGreaterThan, index, adjusted_bound);
Reachable(explicit_check_oob_condition_and_compute_addr(
&mut builder.cursor(),
heap,
env.pointer_type(),
index,
offset,
spectre_mitigations_enabled,
oob,
))
}
HeapStyle::Dynamic { .. } => {
let access_size_val = builder
.ins()
.iconst(env.pointer_type(), offset_and_size as i64);
let adjusted_index = builder.ins().uadd_overflow_trap(
index,
access_size_val,
ir::TrapCode::HEAP_OUT_OF_BOUNDS,
);
let bound = get_dynamic_heap_bound(builder, env, heap);
let oob = make_compare(builder, IntCC::UnsignedGreaterThan, adjusted_index, bound);
Reachable(explicit_check_oob_condition_and_compute_addr(
&mut builder.cursor(),
heap,
env.pointer_type(),
index,
offset,
spectre_mitigations_enabled,
oob,
))
}
HeapStyle::Static => {
assert!(
can_use_virtual_memory,
"static memories require the ability to use virtual memory"
);
Reachable(compute_addr(
&mut builder.cursor(),
heap,
env.pointer_type(),
index,
offset,
))
}
})
}
fn get_dynamic_heap_bound(
builder: &mut FunctionBuilder,
env: &mut FuncEnvironment<'_>,
heap: &HeapData,
) -> ir::Value {
match (heap.max_size, &heap.style) {
(Some(max_size), HeapStyle::Dynamic { .. }) if heap.min_size == max_size => {
builder.ins().iconst(env.pointer_type(), max_size as i64)
}
(_, HeapStyle::Dynamic { bound_gv }) => {
materialize_global_value(&mut builder.cursor(), env.pointer_type(), *bound_gv)
}
(_, HeapStyle::Static) => unreachable!("not a dynamic heap"),
}
}
fn cast_index_to_pointer_ty(
index: ir::Value,
index_ty: ir::Type,
pointer_ty: ir::Type,
pos: &mut FuncCursor,
) -> ir::Value {
if index_ty == pointer_ty {
return index;
}
assert!(index_ty.bits() < pointer_ty.bits());
let extended_index = pos.ins().uextend(pointer_ty, index);
let loc = pos.srcloc();
let loc = RelSourceLoc::from_base_offset(pos.func.params.base_srcloc(), loc);
pos.func
.stencil
.dfg
.add_value_label_alias(extended_index, loc, index);
extended_index
}
#[allow(clippy::too_many_arguments)]
fn explicit_check_oob_condition_and_compute_addr(
pos: &mut FuncCursor,
heap: &HeapData,
addr_ty: ir::Type,
index: ir::Value,
offset: u32,
spectre_mitigations_enabled: bool,
oob_condition: ir::Value,
) -> ir::Value {
if !spectre_mitigations_enabled {
pos.ins()
.trapnz(oob_condition, ir::TrapCode::HEAP_OUT_OF_BOUNDS);
}
let mut addr = compute_addr(pos, heap, addr_ty, index, offset);
if spectre_mitigations_enabled {
let null = pos.ins().iconst(addr_ty, 0);
addr = pos.ins().select_spectre_guard(oob_condition, null, addr);
}
addr
}
fn compute_addr(
pos: &mut FuncCursor,
heap: &HeapData,
addr_ty: ir::Type,
index: ir::Value,
offset: u32,
) -> ir::Value {
debug_assert_eq!(pos.func.dfg.value_type(index), addr_ty);
let heap_base = materialize_global_value(pos, addr_ty, heap.base);
let base_and_index = pos.ins().iadd(heap_base, index);
if offset == 0 {
base_and_index
} else {
let offset_val = pos.ins().iconst(addr_ty, i64::from(offset));
pos.ins().iadd(base_and_index, offset_val)
}
}
#[inline]
fn offset_plus_size(offset: u32, size: u8) -> u64 {
offset as u64 + size as u64
}