1use std::{collections::HashMap, sync::RwLock};
2
3use wasmer_compiler::wasmparser::{BlockType, Operator, ValType};
7use wasmer_compiler::{FunctionMiddleware, MiddlewareReaderState, ModuleMiddleware};
8use wasmer_types::{
9 ExportIndex, FunctionIndex, FunctionType, GlobalIndex, GlobalInit, ImportIndex,
10 LocalFunctionIndex, MiddlewareError, ModuleInfo, SignatureIndex, Type,
11};
12
13use crate::meter::{STYLUS_ENTRY_POINT, STYLUS_INK_LEFT, STYLUS_INK_STATUS, STYLUS_STACK_LEFT};
14
15const SCRATCH_GLOBAL: &str = "stylus_scratch_global";
16
17fn mw_err(msg: impl Into<String>) -> MiddlewareError {
18 MiddlewareError::new("stylus", msg.into())
19}
20
21const STYLUS_START: &str = "stylus_start";
28
29#[derive(Debug)]
30pub struct StartMover {
31 debug: bool,
32}
33
34impl StartMover {
35 pub fn new(debug: bool) -> Self {
36 Self { debug }
37 }
38}
39
40impl ModuleMiddleware for StartMover {
41 fn transform_module_info(&self, info: &mut ModuleInfo) -> Result<(), MiddlewareError> {
42 let exports_before = info.exports.len();
43
44 let had_start = if let Some(start) = info.start_function.take() {
45 if info.exports.contains_key(STYLUS_START) {
46 return Err(mw_err(format!("function {STYLUS_START} already exists")));
47 }
48 info.exports
49 .insert(STYLUS_START.to_owned(), ExportIndex::Function(start));
50 info.function_names.insert(start, STYLUS_START.to_owned());
51 true
52 } else {
53 false
54 };
55
56 if had_start && !self.debug {
57 return Err(mw_err("start functions not allowed"));
58 }
59
60 if !self.debug {
61 info.exports.retain(|name, export| match name.as_str() {
63 STYLUS_ENTRY_POINT => matches!(export, ExportIndex::Function(_)),
64 STYLUS_START => matches!(export, ExportIndex::Function(_)),
65 "memory" => matches!(export, ExportIndex::Memory(_)),
66 _ => false,
67 });
68 info.function_names.clear();
69 }
70 tracing::debug!(target: "stylus",
71 had_start, exports_before, exports_after = info.exports.len(),
72 "StartMover applied");
73 Ok(())
74 }
75
76 fn generate_function_middleware<'a>(
77 &self,
78 _: LocalFunctionIndex,
79 ) -> Box<dyn FunctionMiddleware<'a> + 'a> {
80 Box::new(NoopFunctionMiddleware)
81 }
82}
83
84#[derive(Debug)]
85struct NoopFunctionMiddleware;
86
87impl<'a> FunctionMiddleware<'a> for NoopFunctionMiddleware {
88 fn feed(
89 &mut self,
90 op: Operator<'a>,
91 state: &mut MiddlewareReaderState<'a>,
92 ) -> Result<(), MiddlewareError> {
93 state.push_operator(op);
94 Ok(())
95 }
96}
97
98#[derive(Debug)]
101pub struct InkMeter {
102 header_cost: u64,
103 globals: RwLock<Option<[GlobalIndex; 2]>>,
104 sigs: RwLock<HashMap<u32, usize>>,
105}
106
107impl InkMeter {
108 pub fn new(header_cost: u64) -> Self {
109 Self {
110 header_cost,
111 globals: RwLock::new(None),
112 sigs: RwLock::new(HashMap::new()),
113 }
114 }
115
116 fn globals(&self) -> [GlobalIndex; 2] {
117 self.globals
118 .read()
119 .expect("ink globals lock poisoned")
120 .expect("missing ink globals")
121 }
122}
123
124impl ModuleMiddleware for InkMeter {
125 fn transform_module_info(&self, info: &mut ModuleInfo) -> Result<(), MiddlewareError> {
126 let ink_ty = wasmer_types::GlobalType::new(Type::I64, wasmer_types::Mutability::Var);
127 let status_ty = wasmer_types::GlobalType::new(Type::I32, wasmer_types::Mutability::Var);
128
129 let ink_idx = info.globals.push(ink_ty);
130 let status_idx = info.globals.push(status_ty);
131 info.global_initializers.push(GlobalInit::I64Const(0));
132 info.global_initializers.push(GlobalInit::I32Const(0));
133
134 info.exports.insert(
135 STYLUS_INK_LEFT.to_string(),
136 wasmer_types::ExportIndex::Global(ink_idx),
137 );
138 info.exports.insert(
139 STYLUS_INK_STATUS.to_string(),
140 wasmer_types::ExportIndex::Global(status_idx),
141 );
142
143 let mut sig_map = self.sigs.write().expect("ink sigs lock poisoned");
144 for (sig_idx, sig) in info.signatures.iter() {
145 sig_map.insert(sig_idx.as_u32(), sig.params().len());
146 }
147
148 *self.globals.write().expect("ink globals lock poisoned") = Some([ink_idx, status_idx]);
149 Ok(())
150 }
151
152 fn generate_function_middleware<'a>(
153 &self,
154 _: LocalFunctionIndex,
155 ) -> Box<dyn FunctionMiddleware<'a> + 'a> {
156 let [ink, status] = self.globals();
157 let sigs = self.sigs.read().expect("ink sigs lock poisoned").clone();
158 Box::new(InkMeterFn {
159 ink_global: ink,
160 status_global: status,
161 block: vec![],
162 block_cost: 0,
163 header_cost: self.header_cost,
164 sigs,
165 })
166 }
167}
168
169#[derive(Debug)]
170struct InkMeterFn {
171 ink_global: GlobalIndex,
172 status_global: GlobalIndex,
173 block: Vec<Operator<'static>>,
174 block_cost: u64,
175 header_cost: u64,
176 sigs: HashMap<u32, usize>,
177}
178
179fn ends_basic_block(op: &Operator) -> bool {
180 use Operator::*;
181 matches!(
182 op,
183 End | Else
184 | Return
185 | Loop { .. }
186 | Br { .. }
187 | BrTable { .. }
188 | BrIf { .. }
189 | If { .. }
190 | Call { .. }
191 | CallIndirect { .. }
192 )
193}
194
195impl<'a> FunctionMiddleware<'a> for InkMeterFn {
196 fn feed(
197 &mut self,
198 op: Operator<'a>,
199 state: &mut MiddlewareReaderState<'a>,
200 ) -> Result<(), MiddlewareError> {
201 let end = ends_basic_block(&op);
202 let op_cost = opcode_ink_cost(&op, &self.sigs);
203 let mut cost = self.block_cost.saturating_add(op_cost);
204 self.block_cost = cost;
205
206 let op_static = unsafe { std::mem::transmute::<Operator<'a>, Operator<'static>>(op) };
214 self.block.push(op_static);
215
216 if end {
217 let ink = self.ink_global.as_u32();
218 let status = self.status_global.as_u32();
219 cost = cost.saturating_add(self.header_cost);
220
221 state.push_operator(Operator::GlobalGet { global_index: ink });
222 state.push_operator(Operator::I64Const { value: cost as i64 });
223 state.push_operator(Operator::I64LtU);
224 state.push_operator(Operator::If {
225 blockty: BlockType::Empty,
226 });
227 state.push_operator(Operator::I32Const { value: 1 });
228 state.push_operator(Operator::GlobalSet {
229 global_index: status,
230 });
231 state.push_operator(Operator::Unreachable);
232 state.push_operator(Operator::End);
233
234 state.push_operator(Operator::GlobalGet { global_index: ink });
235 state.push_operator(Operator::I64Const { value: cost as i64 });
236 state.push_operator(Operator::I64Sub);
237 state.push_operator(Operator::GlobalSet { global_index: ink });
238
239 for buffered in self.block.drain(..) {
240 let op_a =
244 unsafe { std::mem::transmute::<Operator<'static>, Operator<'a>>(buffered) };
245 state.push_operator(op_a);
246 }
247 self.block_cost = 0;
248 }
249 Ok(())
250 }
251}
252
253#[derive(Debug)]
256pub struct DynamicMeter {
257 memory_fill_ink: u64,
258 memory_copy_ink: u64,
259 globals: RwLock<Option<[GlobalIndex; 3]>>,
260}
261
262impl DynamicMeter {
263 pub fn new(memory_fill_ink: u64, memory_copy_ink: u64) -> Self {
264 Self {
265 memory_fill_ink,
266 memory_copy_ink,
267 globals: RwLock::new(None),
268 }
269 }
270}
271
272impl ModuleMiddleware for DynamicMeter {
273 fn transform_module_info(&self, info: &mut ModuleInfo) -> Result<(), MiddlewareError> {
274 let ink_idx = info
275 .exports
276 .get(STYLUS_INK_LEFT)
277 .and_then(|e| match e {
278 wasmer_types::ExportIndex::Global(g) => Some(*g),
279 _ => None,
280 })
281 .ok_or_else(|| mw_err("ink global not found"))?;
282
283 let status_idx = info
284 .exports
285 .get(STYLUS_INK_STATUS)
286 .and_then(|e| match e {
287 wasmer_types::ExportIndex::Global(g) => Some(*g),
288 _ => None,
289 })
290 .ok_or_else(|| mw_err("ink status global not found"))?;
291
292 let scratch_ty = wasmer_types::GlobalType::new(Type::I32, wasmer_types::Mutability::Var);
293 let scratch_idx = info.globals.push(scratch_ty);
294 info.global_initializers.push(GlobalInit::I32Const(0));
295 info.exports.insert(
296 SCRATCH_GLOBAL.to_string(),
297 wasmer_types::ExportIndex::Global(scratch_idx),
298 );
299
300 *self.globals.write().expect("dynamic meter lock poisoned") =
301 Some([ink_idx, status_idx, scratch_idx]);
302 Ok(())
303 }
304
305 fn generate_function_middleware<'a>(
306 &self,
307 _: LocalFunctionIndex,
308 ) -> Box<dyn FunctionMiddleware<'a> + 'a> {
309 let globals = self
310 .globals
311 .read()
312 .expect("dynamic meter lock poisoned")
313 .expect("missing dynamic globals");
314 Box::new(DynamicMeterFn {
315 memory_fill_ink: self.memory_fill_ink,
316 memory_copy_ink: self.memory_copy_ink,
317 globals,
318 })
319 }
320}
321
322#[derive(Debug)]
323struct DynamicMeterFn {
324 memory_fill_ink: u64,
325 memory_copy_ink: u64,
326 globals: [GlobalIndex; 3],
327}
328
329impl<'a> FunctionMiddleware<'a> for DynamicMeterFn {
330 fn feed(
331 &mut self,
332 op: Operator<'a>,
333 state: &mut MiddlewareReaderState<'a>,
334 ) -> Result<(), MiddlewareError> {
335 use Operator::*;
336
337 let [ink, status, scratch] = self.globals.map(|x| x.as_u32());
338 let blockty = BlockType::Empty;
339
340 let coefficient = match &op {
341 MemoryFill { .. } => Some(self.memory_fill_ink as i64),
342 MemoryCopy { .. } => Some(self.memory_copy_ink as i64),
343 _ => None,
344 };
345
346 if let Some(coeff) = coefficient {
347 state.extend([
350 GlobalSet {
351 global_index: scratch,
352 },
353 GlobalGet { global_index: ink },
354 GlobalGet { global_index: ink },
355 GlobalGet {
356 global_index: scratch,
357 },
358 I64ExtendI32U,
359 I64Const { value: coeff },
360 I64Mul,
361 I64Sub,
362 GlobalSet { global_index: ink },
363 GlobalGet { global_index: ink },
364 I64LtU,
365 If { blockty },
366 I32Const { value: 1 },
367 GlobalSet {
368 global_index: status,
369 },
370 Unreachable,
371 End,
372 GlobalGet {
373 global_index: scratch,
374 },
375 ]);
376 }
377
378 state.push_operator(op);
379 Ok(())
380 }
381}
382
383type FuncMap = HashMap<FunctionIndex, FunctionType>;
386type SigMap = HashMap<SignatureIndex, FunctionType>;
387
388#[derive(Debug)]
389pub struct DepthChecker {
390 frame_limit: u32,
391 frame_contention: u16,
392 global: RwLock<Option<GlobalIndex>>,
393 funcs: RwLock<Option<FuncMap>>,
394 sigs: RwLock<Option<SigMap>>,
395}
396
397impl DepthChecker {
398 pub fn new(frame_limit: u32, frame_contention: u16) -> Self {
399 Self {
400 frame_limit,
401 frame_contention,
402 global: RwLock::new(None),
403 funcs: RwLock::new(None),
404 sigs: RwLock::new(None),
405 }
406 }
407}
408
409impl ModuleMiddleware for DepthChecker {
410 fn transform_module_info(&self, info: &mut ModuleInfo) -> Result<(), MiddlewareError> {
411 let ty = wasmer_types::GlobalType::new(Type::I32, wasmer_types::Mutability::Var);
412 let idx = info.globals.push(ty);
413 info.global_initializers.push(GlobalInit::I32Const(0));
414 info.exports.insert(
415 STYLUS_STACK_LEFT.to_string(),
416 wasmer_types::ExportIndex::Global(idx),
417 );
418
419 let mut funcs = HashMap::new();
420 for (func_idx, sig_idx) in info.functions.iter() {
421 if let Some(sig) = info.signatures.get(*sig_idx) {
422 funcs.insert(func_idx, sig.clone());
423 }
424 }
425 let mut sigs = HashMap::new();
426 for (sig_idx, sig) in info.signatures.iter() {
427 sigs.insert(sig_idx, sig.clone());
428 }
429
430 *self.global.write().expect("depth checker lock poisoned") = Some(idx);
431 *self.funcs.write().expect("depth checker lock poisoned") = Some(funcs);
432 *self.sigs.write().expect("depth checker lock poisoned") = Some(sigs);
433 Ok(())
434 }
435
436 fn generate_function_middleware<'a>(
437 &self,
438 _: LocalFunctionIndex,
439 ) -> Box<dyn FunctionMiddleware<'a> + 'a> {
440 let g = self
441 .global
442 .read()
443 .expect("depth checker lock poisoned")
444 .expect("missing depth global");
445 let funcs = self
446 .funcs
447 .read()
448 .expect("depth checker lock poisoned")
449 .clone()
450 .expect("missing funcs");
451 let sigs = self
452 .sigs
453 .read()
454 .expect("depth checker lock poisoned")
455 .clone()
456 .expect("missing sigs");
457 Box::new(DepthCheckerFn {
458 global: g,
459 funcs,
460 sigs,
461 locals: None,
462 frame_limit: self.frame_limit,
463 frame_contention: self.frame_contention,
464 scopes: 1,
465 code: vec![],
466 done: false,
467 })
468 }
469}
470
471#[derive(Debug)]
472struct DepthCheckerFn {
473 global: GlobalIndex,
474 funcs: FuncMap,
475 sigs: SigMap,
476 locals: Option<usize>,
477 frame_limit: u32,
478 frame_contention: u16,
479 scopes: isize,
480 code: Vec<Operator<'static>>,
481 done: bool,
482}
483
484impl DepthCheckerFn {
485 #[rustfmt::skip]
486 fn worst_case_depth(&self) -> Result<u32, MiddlewareError> {
487 use Operator::*;
488
489 let mut worst: u32 = 0;
490 let mut stack: u32 = 0;
491
492 macro_rules! push {
493 ($count:expr_2021) => {{ stack += $count; worst = worst.max(stack); }};
494 () => { push!(1) };
495 }
496 macro_rules! pop {
497 ($count:expr_2021) => {{ stack = stack.saturating_sub($count); }};
498 () => { pop!(1) };
499 }
500 macro_rules! ins_and_outs {
501 ($ty:expr_2021) => {{
502 let ins = $ty.params().len() as u32;
503 let outs = $ty.results().len() as u32;
504 push!(outs);
505 pop!(ins);
506 }};
507 }
508 macro_rules! op {
509 ($first:ident $(,$opcode:ident)* $(,)?) => {
510 $first $(| $opcode)*
511 };
512 }
513 macro_rules! dot {
514 ($first:ident $(,$opcode:ident)* $(,)?) => {
515 $first { .. } $(| $opcode { .. })*
516 };
517 }
518 macro_rules! block_type {
519 ($ty:expr_2021) => {{
520 match $ty {
521 BlockType::Empty => {}
522 BlockType::Type(_) => push!(1),
523 BlockType::FuncType(id) => {
524 let index = SignatureIndex::from_u32(*id);
525 let Some(ty) = self.sigs.get(&index) else {
526 return Err(mw_err(format!("missing type for func {id}")));
527 };
528 ins_and_outs!(ty);
529 }
530 }
531 }};
532 }
533
534 let mut scopes = vec![stack];
535
536 for op in &self.code {
537 match op {
538 Block { blockty } => {
539 block_type!(blockty);
540 scopes.push(stack);
541 }
542 Loop { blockty } => {
543 block_type!(blockty);
544 scopes.push(stack);
545 }
546 If { blockty } => {
547 pop!();
548 block_type!(blockty);
549 scopes.push(stack);
550 }
551 Else => {
552 stack = match scopes.last() {
553 Some(scope) => *scope,
554 None => return Err(mw_err("malformed if-else scope")),
555 };
556 }
557 End => {
558 stack = match scopes.pop() {
559 Some(stack) => stack,
560 None => return Err(mw_err("malformed scoping at end of block")),
561 };
562 }
563
564 Call { function_index } => {
565 let index = FunctionIndex::from_u32(*function_index);
566 let Some(ty) = self.funcs.get(&index) else {
567 return Err(mw_err(format!("missing type for func {function_index}")));
568 };
569 ins_and_outs!(ty)
570 }
571 CallIndirect { type_index, .. } => {
572 let index = SignatureIndex::from_u32(*type_index);
573 let Some(ty) = self.sigs.get(&index) else {
574 return Err(mw_err(format!("missing type for signature {type_index}")));
575 };
576 ins_and_outs!(ty);
577 pop!() }
579
580 MemoryFill { .. } | MemoryCopy { .. } => pop!(3), op!(
583 Nop, Unreachable,
584 I32Eqz, I64Eqz, I32Clz, I32Ctz, I32Popcnt, I64Clz, I64Ctz, I64Popcnt,
585 )
586 | dot!(
587 Br, Return,
588 LocalTee, MemoryGrow,
589 I32Load, I64Load, F32Load, F64Load,
590 I32Load8S, I32Load8U, I32Load16S, I32Load16U, I64Load8S, I64Load8U,
591 I64Load16S, I64Load16U, I64Load32S, I64Load32U,
592 I32WrapI64, I64ExtendI32S, I64ExtendI32U,
593 I32Extend8S, I32Extend16S, I64Extend8S, I64Extend16S, I64Extend32S,
594 F32Abs, F32Neg, F32Ceil, F32Floor, F32Trunc, F32Nearest, F32Sqrt,
595 F64Abs, F64Neg, F64Ceil, F64Floor, F64Trunc, F64Nearest, F64Sqrt,
596 I32TruncF32S, I32TruncF32U, I32TruncF64S, I32TruncF64U,
597 I64TruncF32S, I64TruncF32U, I64TruncF64S, I64TruncF64U,
598 F32ConvertI32S, F32ConvertI32U, F32ConvertI64S, F32ConvertI64U, F32DemoteF64,
599 F64ConvertI32S, F64ConvertI32U, F64ConvertI64S, F64ConvertI64U, F64PromoteF32,
600 I32ReinterpretF32, I64ReinterpretF64, F32ReinterpretI32, F64ReinterpretI64,
601 I32TruncSatF32S, I32TruncSatF32U, I32TruncSatF64S, I32TruncSatF64U,
602 I64TruncSatF32S, I64TruncSatF32U, I64TruncSatF64S, I64TruncSatF64U,
603 ) => {}
604
605 dot!(
606 LocalGet, GlobalGet, MemorySize,
607 I32Const, I64Const, F32Const, F64Const,
608 ) => push!(),
609
610 op!(
611 Drop,
612 I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU,
613 I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU,
614 F32Eq, F32Ne, F32Lt, F32Gt, F32Le, F32Ge,
615 F64Eq, F64Ne, F64Lt, F64Gt, F64Le, F64Ge,
616 I32Add, I32Sub, I32Mul, I32DivS, I32DivU, I32RemS, I32RemU,
617 I64Add, I64Sub, I64Mul, I64DivS, I64DivU, I64RemS, I64RemU,
618 I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr,
619 I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr,
620 F32Add, F32Sub, F32Mul, F32Div, F32Min, F32Max, F32Copysign,
621 F64Add, F64Sub, F64Mul, F64Div, F64Min, F64Max, F64Copysign,
622 )
623 | dot!(BrIf, BrTable, LocalSet, GlobalSet) => pop!(),
624
625 dot!(
626 Select,
627 I32Store, I64Store, F32Store, F64Store,
628 I32Store8, I32Store16, I64Store8, I64Store16, I64Store32,
629 ) => pop!(2),
630
631 unsupported @ dot!(Try, Catch, Throw, Rethrow, ThrowRef, TryTable) => {
632 return Err(mw_err(format!("exception-handling not supported {unsupported:?}")));
633 }
634 unsupported @ dot!(ReturnCall, ReturnCallIndirect) => {
635 return Err(mw_err(format!("tail-call not supported {unsupported:?}")));
636 }
637 unsupported @ dot!(CallRef, ReturnCallRef) => {
638 return Err(mw_err(format!("typed function references not supported {unsupported:?}")));
639 }
640 unsupported @ (dot!(Delegate) | op!(CatchAll)) => {
641 return Err(mw_err(format!("exception-handling not supported {unsupported:?}")));
642 }
643 unsupported @ (op!(RefIsNull) | dot!(TypedSelect, RefNull, RefFunc, RefEq)) => {
644 return Err(mw_err(format!("reference-types not supported {unsupported:?}")));
645 }
646 unsupported @ dot!(RefAsNonNull, BrOnNull, BrOnNonNull) => {
647 return Err(mw_err(format!("typed function references not supported {unsupported:?}")));
648 }
649 unsupported @ dot!(
650 MemoryInit, DataDrop, TableInit, ElemDrop,
651 TableCopy, TableFill, TableGet, TableSet, TableGrow, TableSize
652 ) => {
653 return Err(mw_err(format!("bulk-memory not fully supported {unsupported:?}")));
654 }
655 unsupported @ dot!(MemoryDiscard) => {
656 return Err(mw_err(format!("memory discard not supported {unsupported:?}")));
657 }
658 unsupported @ dot!(
659 StructNew, StructNewDefault, StructGet, StructGetS, StructGetU, StructSet,
660 ArrayNew, ArrayNewDefault, ArrayNewFixed, ArrayNewData, ArrayNewElem,
661 ArrayGet, ArrayGetS, ArrayGetU, ArraySet, ArrayLen, ArrayFill, ArrayCopy,
662 ArrayInitData, ArrayInitElem,
663 RefTestNonNull, RefTestNullable, RefCastNonNull, RefCastNullable,
664 BrOnCast, BrOnCastFail, AnyConvertExtern, ExternConvertAny,
665 RefI31, I31GetS, I31GetU
666 ) => {
667 return Err(mw_err(format!("GC extension not supported {unsupported:?}")));
668 }
669 unsupported @ dot!(
670 MemoryAtomicNotify, MemoryAtomicWait32, MemoryAtomicWait64, AtomicFence,
671 I32AtomicLoad, I64AtomicLoad, I32AtomicLoad8U, I32AtomicLoad16U,
672 I64AtomicLoad8U, I64AtomicLoad16U, I64AtomicLoad32U,
673 I32AtomicStore, I64AtomicStore, I32AtomicStore8, I32AtomicStore16,
674 I64AtomicStore8, I64AtomicStore16, I64AtomicStore32,
675 I32AtomicRmwAdd, I64AtomicRmwAdd, I32AtomicRmw8AddU, I32AtomicRmw16AddU,
676 I64AtomicRmw8AddU, I64AtomicRmw16AddU, I64AtomicRmw32AddU,
677 I32AtomicRmwSub, I64AtomicRmwSub, I32AtomicRmw8SubU, I32AtomicRmw16SubU,
678 I64AtomicRmw8SubU, I64AtomicRmw16SubU, I64AtomicRmw32SubU,
679 I32AtomicRmwAnd, I64AtomicRmwAnd, I32AtomicRmw8AndU, I32AtomicRmw16AndU,
680 I64AtomicRmw8AndU, I64AtomicRmw16AndU, I64AtomicRmw32AndU,
681 I32AtomicRmwOr, I64AtomicRmwOr, I32AtomicRmw8OrU, I32AtomicRmw16OrU,
682 I64AtomicRmw8OrU, I64AtomicRmw16OrU, I64AtomicRmw32OrU,
683 I32AtomicRmwXor, I64AtomicRmwXor, I32AtomicRmw8XorU, I32AtomicRmw16XorU,
684 I64AtomicRmw8XorU, I64AtomicRmw16XorU, I64AtomicRmw32XorU,
685 I32AtomicRmwXchg, I64AtomicRmwXchg, I32AtomicRmw8XchgU, I32AtomicRmw16XchgU,
686 I64AtomicRmw8XchgU, I64AtomicRmw16XchgU, I64AtomicRmw32XchgU,
687 I32AtomicRmwCmpxchg, I64AtomicRmwCmpxchg, I32AtomicRmw8CmpxchgU,
688 I32AtomicRmw16CmpxchgU, I64AtomicRmw8CmpxchgU, I64AtomicRmw16CmpxchgU,
689 I64AtomicRmw32CmpxchgU
690 ) => {
691 return Err(mw_err(format!("threads extension not supported {unsupported:?}")));
692 }
693 unsupported @ dot!(
694 V128Load, V128Load8x8S, V128Load8x8U, V128Load16x4S, V128Load16x4U,
695 V128Load32x2S, V128Load8Splat, V128Load16Splat, V128Load32Splat,
696 V128Load64Splat, V128Load32Zero, V128Load64Zero, V128Load32x2U,
697 V128Store, V128Load8Lane, V128Load16Lane, V128Load32Lane, V128Load64Lane,
698 V128Store8Lane, V128Store16Lane, V128Store32Lane, V128Store64Lane, V128Const,
699 I8x16Shuffle, I8x16ExtractLaneS, I8x16ExtractLaneU, I8x16ReplaceLane,
700 I16x8ExtractLaneS, I16x8ExtractLaneU, I16x8ReplaceLane,
701 I32x4ExtractLane, I32x4ReplaceLane, I64x2ExtractLane, I64x2ReplaceLane,
702 F32x4ExtractLane, F32x4ReplaceLane, F64x2ExtractLane, F64x2ReplaceLane,
703 I8x16Swizzle, I8x16Splat, I16x8Splat, I32x4Splat, I64x2Splat,
704 F32x4Splat, F64x2Splat,
705 I8x16Eq, I8x16Ne, I8x16LtS, I8x16LtU, I8x16GtS, I8x16GtU,
706 I8x16LeS, I8x16LeU, I8x16GeS, I8x16GeU,
707 I16x8Eq, I16x8Ne, I16x8LtS, I16x8LtU, I16x8GtS, I16x8GtU,
708 I16x8LeS, I16x8LeU, I16x8GeS, I16x8GeU,
709 I32x4Eq, I32x4Ne, I32x4LtS, I32x4LtU, I32x4GtS, I32x4GtU,
710 I32x4LeS, I32x4LeU, I32x4GeS, I32x4GeU,
711 I64x2Eq, I64x2Ne, I64x2LtS, I64x2GtS, I64x2LeS, I64x2GeS,
712 F32x4Eq, F32x4Ne, F32x4Lt, F32x4Gt, F32x4Le, F32x4Ge,
713 F64x2Eq, F64x2Ne, F64x2Lt, F64x2Gt, F64x2Le, F64x2Ge,
714 V128Not, V128And, V128AndNot, V128Or, V128Xor, V128Bitselect, V128AnyTrue,
715 I8x16Abs, I8x16Neg, I8x16Popcnt, I8x16AllTrue, I8x16Bitmask,
716 I8x16NarrowI16x8S, I8x16NarrowI16x8U,
717 I8x16Shl, I8x16ShrS, I8x16ShrU, I8x16Add, I8x16AddSatS, I8x16AddSatU,
718 I8x16Sub, I8x16SubSatS, I8x16SubSatU, I8x16MinS, I8x16MinU,
719 I8x16MaxS, I8x16MaxU, I8x16AvgrU,
720 I16x8ExtAddPairwiseI8x16S, I16x8ExtAddPairwiseI8x16U, I16x8Abs, I16x8Neg,
721 I16x8Q15MulrSatS, I16x8AllTrue, I16x8Bitmask,
722 I16x8NarrowI32x4S, I16x8NarrowI32x4U,
723 I16x8ExtendLowI8x16S, I16x8ExtendHighI8x16S,
724 I16x8ExtendLowI8x16U, I16x8ExtendHighI8x16U,
725 I16x8Shl, I16x8ShrS, I16x8ShrU, I16x8Add, I16x8AddSatS, I16x8AddSatU,
726 I16x8Sub, I16x8SubSatS, I16x8SubSatU, I16x8Mul,
727 I16x8MinS, I16x8MinU, I16x8MaxS, I16x8MaxU, I16x8AvgrU,
728 I16x8ExtMulLowI8x16S, I16x8ExtMulHighI8x16S,
729 I16x8ExtMulLowI8x16U, I16x8ExtMulHighI8x16U,
730 I32x4ExtAddPairwiseI16x8U, I32x4Abs, I32x4Neg, I32x4AllTrue, I32x4Bitmask,
731 I32x4ExtAddPairwiseI16x8S,
732 I32x4ExtendLowI16x8S, I32x4ExtendHighI16x8S,
733 I32x4ExtendLowI16x8U, I32x4ExtendHighI16x8U,
734 I32x4Shl, I32x4ShrS, I32x4ShrU, I32x4Add, I32x4Sub, I32x4Mul,
735 I32x4MinS, I32x4MinU, I32x4MaxS, I32x4MaxU, I32x4DotI16x8S,
736 I32x4ExtMulLowI16x8S, I32x4ExtMulHighI16x8S,
737 I32x4ExtMulLowI16x8U, I32x4ExtMulHighI16x8U,
738 I64x2Abs, I64x2Neg, I64x2AllTrue, I64x2Bitmask,
739 I64x2ExtendLowI32x4S, I64x2ExtendHighI32x4S,
740 I64x2ExtendLowI32x4U, I64x2ExtendHighI32x4U,
741 I64x2Shl, I64x2ShrS, I64x2ShrU, I64x2Add, I64x2Sub, I64x2Mul,
742 I64x2ExtMulLowI32x4S, I64x2ExtMulHighI32x4S,
743 I64x2ExtMulLowI32x4U, I64x2ExtMulHighI32x4U,
744 F32x4Ceil, F32x4Floor, F32x4Trunc, F32x4Nearest,
745 F32x4Abs, F32x4Neg, F32x4Sqrt, F32x4Add, F32x4Sub, F32x4Mul, F32x4Div,
746 F32x4Min, F32x4Max, F32x4PMin, F32x4PMax,
747 F64x2Ceil, F64x2Floor, F64x2Trunc, F64x2Nearest,
748 F64x2Abs, F64x2Neg, F64x2Sqrt, F64x2Add, F64x2Sub, F64x2Mul, F64x2Div,
749 F64x2Min, F64x2Max, F64x2PMin, F64x2PMax,
750 I32x4TruncSatF32x4S, I32x4TruncSatF32x4U,
751 F32x4ConvertI32x4S, F32x4ConvertI32x4U,
752 I32x4TruncSatF64x2SZero, I32x4TruncSatF64x2UZero,
753 F64x2ConvertLowI32x4S, F64x2ConvertLowI32x4U,
754 F32x4DemoteF64x2Zero, F64x2PromoteLowF32x4,
755 I8x16RelaxedSwizzle,
756 I32x4RelaxedTruncF32x4S, I32x4RelaxedTruncF32x4U,
757 I32x4RelaxedTruncF64x2SZero, I32x4RelaxedTruncF64x2UZero,
758 F32x4RelaxedMadd, F32x4RelaxedNmadd, F64x2RelaxedMadd, F64x2RelaxedNmadd,
759 I8x16RelaxedLaneselect, I16x8RelaxedLaneselect,
760 I32x4RelaxedLaneselect, I64x2RelaxedLaneselect,
761 F32x4RelaxedMin, F32x4RelaxedMax, F64x2RelaxedMin, F64x2RelaxedMax,
762 I16x8RelaxedQ15mulrS, I16x8RelaxedDotI8x16I7x16S,
763 I32x4RelaxedDotI8x16I7x16AddS
764 ) => {
765 return Err(mw_err(format!("SIMD extension not supported {unsupported:?}")));
766 }
767 unsupported => {
771 return Err(mw_err(format!("unsupported opcode {unsupported:?}")));
772 }
773 };
774 }
775
776 if self.locals.is_none() {
777 return Err(mw_err("missing locals info"));
778 }
779
780 let contention = worst;
781 if contention > self.frame_contention.into() {
782 return Err(mw_err(format!(
783 "too many values on the stack at once: {contention} > {}",
784 self.frame_contention
785 )));
786 }
787
788 let locals = self.locals.unwrap_or_default();
789 Ok(worst + locals as u32 + 4)
790 }
791}
792
793impl<'a> FunctionMiddleware<'a> for DepthCheckerFn {
794 fn locals_info(&mut self, locals: &[ValType]) {
795 self.locals = Some(locals.len());
796 }
797
798 fn feed(
799 &mut self,
800 op: Operator<'a>,
801 state: &mut MiddlewareReaderState<'a>,
802 ) -> Result<(), MiddlewareError> {
803 if self.done {
804 return Err(mw_err("depth checker: feed called after finalization"));
805 }
806
807 match op {
808 Operator::Block { .. } | Operator::Loop { .. } | Operator::If { .. } => {
809 self.scopes += 1;
810 }
811 Operator::End => {
812 self.scopes -= 1;
813 }
814 _ => {}
815 }
816 if self.scopes < 0 {
817 return Err(mw_err("malformed scoping detected"));
818 }
819
820 let last = self.scopes == 0 && matches!(op, Operator::End);
821
822 let op_static = unsafe { std::mem::transmute::<Operator<'a>, Operator<'static>>(op) };
828 self.code.push(op_static);
829
830 if !last {
831 return Ok(());
832 }
833
834 let size = self.worst_case_depth()?;
835 let g = self.global.as_u32();
836
837 if size > self.frame_limit {
838 return Err(mw_err(format!(
839 "frame too large: {size} > {}-word limit",
840 self.frame_limit
841 )));
842 }
843
844 let mut code = std::mem::take(&mut self.code);
846 let Some(final_end) = code.pop() else {
847 return Err(mw_err("depth checker: missing final End operator"));
848 };
849
850 state.extend([
852 Operator::GlobalGet { global_index: g },
853 Operator::I32Const { value: size as i32 },
854 Operator::I32LeU,
855 Operator::If {
856 blockty: BlockType::Empty,
857 },
858 Operator::I32Const { value: 0 },
859 Operator::GlobalSet { global_index: g },
860 Operator::Unreachable,
861 Operator::End,
862 Operator::GlobalGet { global_index: g },
863 Operator::I32Const { value: size as i32 },
864 Operator::I32Sub,
865 Operator::GlobalSet { global_index: g },
866 ]);
867
868 code.push(Operator::Return);
869 code.push(final_end);
870
871 for op_s in code {
872 let is_return = matches!(op_s, Operator::Return);
873 if is_return {
874 state.extend([
875 Operator::GlobalGet { global_index: g },
876 Operator::I32Const { value: size as i32 },
877 Operator::I32Add,
878 Operator::GlobalSet { global_index: g },
879 ]);
880 }
881 let op_a = unsafe { std::mem::transmute::<Operator<'static>, Operator<'a>>(op_s) };
884 state.push_operator(op_a);
885 }
886
887 self.done = true;
888 Ok(())
889 }
890}
891
892#[derive(Debug)]
895pub struct HeapBound {
896 globals: RwLock<Option<(GlobalIndex, Option<FunctionIndex>)>>,
897}
898
899impl HeapBound {
900 pub fn new() -> Self {
901 Self {
902 globals: RwLock::new(None),
903 }
904 }
905}
906
907impl ModuleMiddleware for HeapBound {
908 fn transform_module_info(&self, info: &mut ModuleInfo) -> Result<(), MiddlewareError> {
909 let scratch_idx = info
910 .exports
911 .get(SCRATCH_GLOBAL)
912 .and_then(|e| match e {
913 wasmer_types::ExportIndex::Global(g) => Some(*g),
914 _ => None,
915 })
916 .ok_or_else(|| mw_err("scratch global not found"))?;
917
918 let pay_func = info.imports.iter().find_map(|(key, idx)| {
919 if key.field == "pay_for_memory_grow"
920 && let ImportIndex::Function(f) = idx
921 {
922 return Some(*f);
923 }
924 None
925 });
926
927 *self.globals.write().expect("heap bound lock poisoned") = Some((scratch_idx, pay_func));
928 Ok(())
929 }
930
931 fn generate_function_middleware<'a>(
932 &self,
933 _: LocalFunctionIndex,
934 ) -> Box<dyn FunctionMiddleware<'a> + 'a> {
935 let (scratch, pay_func) = self
936 .globals
937 .read()
938 .expect("heap bound lock poisoned")
939 .expect("missing heap globals");
940 Box::new(HeapBoundFn { scratch, pay_func })
941 }
942}
943
944#[derive(Debug)]
945struct HeapBoundFn {
946 scratch: GlobalIndex,
947 pay_func: Option<FunctionIndex>,
948}
949
950impl<'a> FunctionMiddleware<'a> for HeapBoundFn {
951 fn feed(
952 &mut self,
953 op: Operator<'a>,
954 state: &mut MiddlewareReaderState<'a>,
955 ) -> Result<(), MiddlewareError> {
956 if let (Operator::MemoryGrow { .. }, Some(pay)) = (&op, self.pay_func) {
957 let g = self.scratch.as_u32();
958 let f = pay.as_u32();
959 state.extend([
960 Operator::GlobalSet { global_index: g },
961 Operator::GlobalGet { global_index: g },
962 Operator::GlobalGet { global_index: g },
963 Operator::Call { function_index: f },
964 ]);
965 }
966 state.push_operator(op);
967 Ok(())
968 }
969}
970
971#[rustfmt::skip]
975pub fn opcode_ink_cost(op: &Operator, sigs: &HashMap<u32, usize>) -> u64 {
976 use Operator::*;
977
978 macro_rules! op {
979 ($first:ident $(,$opcode:ident)*) => { $first $(| $opcode)* };
980 }
981 macro_rules! dot {
982 ($first:ident $(,$opcode:ident)*) => { $first { .. } $(| $opcode { .. })* };
983 }
984
985 match op {
986 op!(Unreachable, Return) => 1,
987 op!(Nop) | dot!(I32Const, I64Const) => 1,
988 op!(Drop) => 9,
989
990 dot!(Block, Loop) | op!(Else, End) => 1,
991 dot!(Br, BrIf, If) => 765,
992 dot!(Select) => 1250,
993 dot!(Call) => 3800,
994 dot!(LocalGet, LocalTee) => 75,
995 dot!(LocalSet) => 210,
996 dot!(GlobalGet) => 225,
997 dot!(GlobalSet) => 575,
998 dot!(I32Load, I32Load8S, I32Load8U, I32Load16S, I32Load16U) => 670,
999 dot!(I64Load, I64Load8S, I64Load8U, I64Load16S, I64Load16U, I64Load32S, I64Load32U) => 680,
1000 dot!(I32Store, I32Store8, I32Store16) => 825,
1001 dot!(I64Store, I64Store8, I64Store16, I64Store32) => 950,
1002 dot!(MemorySize) => 3000,
1003 dot!(MemoryGrow) => 8050,
1004
1005 op!(I32Eqz, I32Eq, I32Ne, I32LtS, I32LtU, I32GtS, I32GtU, I32LeS, I32LeU, I32GeS, I32GeU) => 170,
1006 op!(I64Eqz, I64Eq, I64Ne, I64LtS, I64LtU, I64GtS, I64GtU, I64LeS, I64LeU, I64GeS, I64GeU) => 225,
1007
1008 op!(I32Clz, I32Ctz) => 210,
1009 op!(I32Add, I32Sub) => 70,
1010 op!(I32Mul) => 160,
1011 op!(I32DivS, I32DivU, I32RemS, I32RemU) => 1120,
1012 op!(I32And, I32Or, I32Xor, I32Shl, I32ShrS, I32ShrU, I32Rotl, I32Rotr) => 70,
1013
1014 op!(I64Clz, I64Ctz) => 210,
1015 op!(I64Add, I64Sub) => 100,
1016 op!(I64Mul) => 160,
1017 op!(I64DivS, I64DivU, I64RemS, I64RemU) => 1270,
1018 op!(I64And, I64Or, I64Xor, I64Shl, I64ShrS, I64ShrU, I64Rotl, I64Rotr) => 100,
1019
1020 op!(I32Popcnt) => 2650,
1021 op!(I64Popcnt) => 6000,
1022
1023 op!(I32WrapI64, I64ExtendI32S, I64ExtendI32U) => 100,
1024 op!(I32Extend8S, I32Extend16S, I64Extend8S, I64Extend16S, I64Extend32S) => 100,
1025 dot!(MemoryCopy) => 950,
1026 dot!(MemoryFill) => 950,
1027
1028 BrTable { targets } => 2400 + 325 * targets.len() as u64,
1029 CallIndirect { type_index, .. } => {
1030 let params = sigs.get(type_index).copied().unwrap_or(0);
1031 13610 + 650 * params as u64
1032 },
1033
1034 _ => u64::MAX,
1035 }
1036}