radix_wasm_instrument/gas_metering/
mod.rs

1//! This module is used to instrument a Wasm module with the gas metering code.
2//!
3//! The primary public interface is the [`inject`] function which transforms a given
4//! module into one that charges gas for code to be executed. See function documentation for usage
5//! and details.
6
7mod backend;
8
9pub use backend::{host_function, mutable_global, Backend, GasMeter};
10
11#[cfg(test)]
12mod validation;
13
14#[cfg(not(feature = "ignore_custom_section"))]
15use crate::utils::transform::update_custom_section_function_indices;
16use crate::utils::{
17	module_info::{copy_locals, truncate_len_from_encoder, ModuleInfo},
18	translator::{ConstExprKind, DefaultTranslator, Translator},
19};
20use alloc::{string::String, vec, vec::Vec};
21use anyhow::{anyhow, Result};
22use core::{cmp::min, mem, num::NonZeroU32};
23use wasm_encoder::{
24	ElementMode, ElementSection, ElementSegment, Elements, ExportKind, ExportSection, Function,
25	Instruction, SectionId, StartSection,
26};
27use wasmparser::{
28	ElementItems, ElementKind, ExternalKind, FuncType, FunctionBody, GlobalType, Operator, Type,
29	ValType,
30};
31
32/// An interface that describes instruction costs.
33pub trait Rules {
34	/// Returns the cost for the passed `instruction`.
35	///
36	/// Returning `None` makes the gas instrumention end with an error. This is meant
37	/// as a way to have a partial rule set where any instruction that is not specifed
38	/// is considered as forbidden.
39	fn instruction_cost(&self, instruction: &Operator) -> Option<u32>;
40
41	/// Returns the costs for growing the memory using the `memory.grow` instruction.
42	///
43	/// Please note that these costs are in addition to the costs specified by `instruction_cost`
44	/// for the `memory.grow` instruction. Those are meant as dynamic costs which take the
45	/// amount of pages that the memory is grown by into consideration. This is not possible
46	/// using `instruction_cost` because those costs depend on the stack and must be injected as
47	/// code into the function calling `memory.grow`. Therefore returning anything but
48	/// [`MemoryGrowCost::Free`] introduces some overhead to the `memory.grow` instruction.
49	fn memory_grow_cost(&self) -> MemoryGrowCost;
50
51	/// A surcharge cost to calling a function that is added per local of that function.
52	fn call_per_local_cost(&self) -> u32;
53}
54
55/// Dynamic costs for memory growth.
56#[derive(Debug, PartialEq, Eq, Copy, Clone)]
57pub enum MemoryGrowCost {
58	/// Skip per page charge.
59	///
60	/// # Note
61	///
62	/// This makes sense when the amount of pages that a module is allowed to use is limited
63	/// to a rather small number by static validation. In that case it is viable to
64	/// benchmark the costs of `memory.grow` as the worst case (growing to to the maximum
65	/// number of pages).
66	Free,
67	/// Charge the specified amount for each page that the memory is grown by.
68	Linear(NonZeroU32),
69}
70
71impl MemoryGrowCost {
72	/// True iff memory growths code needs to be injected.
73	fn enabled(&self) -> bool {
74		match self {
75			Self::Free => false,
76			Self::Linear(_) => true,
77		}
78	}
79}
80
81/// A type that implements [`Rules`] so that every instruction costs the same.
82///
83/// This is a simplification that is mostly useful for development and testing.
84///
85/// # Note
86///
87/// In a production environment it usually makes no sense to assign every instruction
88/// the same cost. A proper implemention of [`Rules`] should be provided that is probably
89/// created by benchmarking.
90pub struct ConstantCostRules {
91	instruction_cost: u32,
92	memory_grow_cost: u32,
93	call_per_local_cost: u32,
94}
95
96impl ConstantCostRules {
97	/// Create a new [`ConstantCostRules`].
98	///
99	/// Uses `instruction_cost` for every instruction and `memory_grow_cost` to dynamically
100	/// meter the memory growth instruction.
101	pub fn new(instruction_cost: u32, memory_grow_cost: u32, call_per_local_cost: u32) -> Self {
102		Self { instruction_cost, memory_grow_cost, call_per_local_cost }
103	}
104}
105
106impl Default for ConstantCostRules {
107	/// Uses instruction cost of `1` and disables memory growth instrumentation.
108	fn default() -> Self {
109		Self { instruction_cost: 1, memory_grow_cost: 0, call_per_local_cost: 1 }
110	}
111}
112
113impl Rules for ConstantCostRules {
114	fn instruction_cost(&self, _: &Operator) -> Option<u32> {
115		Some(self.instruction_cost)
116	}
117
118	fn memory_grow_cost(&self) -> MemoryGrowCost {
119		NonZeroU32::new(self.memory_grow_cost).map_or(MemoryGrowCost::Free, MemoryGrowCost::Linear)
120	}
121
122	fn call_per_local_cost(&self) -> u32 {
123		self.call_per_local_cost
124	}
125}
126
127/// Transforms a given module into one that tracks the gas charged during its execution.
128///
129/// The output module uses the `gas` function to track the gas spent. The function could be either
130/// an imported or a local one modifying a mutable global. The argument is the amount of gas
131/// required to continue execution. The execution engine is meant to keep track of the total amount
132/// of gas used and trap or otherwise halt execution of the runtime if the gas usage exceeds some
133/// allowed limit.
134///
135/// The body of each function of the original module is divided into metered blocks, and the calls
136/// to charge gas are inserted at the beginning of every such block of code. A metered block is
137/// defined so that, unless there is a trap, either all of the instructions are executed or none
138/// are. These are similar to basic blocks in a control flow graph, except that in some cases
139/// multiple basic blocks can be merged into a single metered block. This is the case if any path
140/// through the control flow graph containing one basic block also contains another.
141///
142/// Charging gas at the beginning of each metered block ensures that 1) all instructions
143/// executed are already paid for, 2) instructions that will not be executed are not charged for
144/// unless execution traps, and 3) the number of calls to `gas` is minimized. The corollary is
145/// that modules instrumented with this metering code may charge gas for instructions not
146/// executed in the event of a trap.
147///
148/// Additionally, each `memory.grow` instruction found in the module is instrumented to first
149/// make a call to charge gas for the additional pages requested. This cannot be done as part of
150/// the block level gas charges as the gas cost is not static and depends on the stack argument
151/// to `memory.grow`.
152///
153/// The above transformations are performed for every function body defined in the module. This
154/// function also rewrites all function indices references by code, table elements, etc., since
155/// the addition of an imported functions changes the indices of module-defined functions. If
156/// the module has a `NameSection`, added by calling `parse_names`, the indices will also be
157/// updated.
158///
159/// Syncronizing the amount of gas charged with the execution engine can be done in two ways. The
160/// first way is by calling the imported `gas` host function, see [`host_function`] for details. The
161/// second way is by using a local `gas` function together with a mutable global, see
162/// [`mutable_global`] for details.
163///
164/// This routine runs in time linear in the size of the input module.
165///
166/// The function fails if the module contains any operation forbidden by gas rule set, returning
167/// the original module as an `Err`.
168pub fn inject<R: Rules, B: Backend>(
169	module_info: &mut ModuleInfo,
170	backend: B,
171	rules: &R,
172) -> Result<Vec<u8>> {
173	// Prepare module and return the gas function
174	let gas_meter = backend.gas_meter(module_info, rules);
175
176	let import_count = module_info.imported_functions_count;
177	let functions_count = module_info.num_functions();
178
179	// Calculate the indexes and gas function cost,
180	// for external gas function the cost is counted on the host side
181	let (
182		// Index of the gas function
183		gas_func_idx,
184		// Total number of functions (also the index of the next added function (if added)
185		grow_cnt_func_idx,
186		// Cost of the gas function execution
187		gas_fn_cost,
188	) = match gas_meter {
189		GasMeter::External { module: gas_module, function } => {
190			// Inject the import of the gas function
191			let ty = Type::Func(FuncType::new(vec![ValType::I64], vec![]));
192			module_info.add_import_func(gas_module, function, ty)?;
193
194			(
195				// import_count has become an index of the imported gas function
196				import_count,
197				functions_count + 1,
198				0,
199			)
200		},
201		GasMeter::Internal { module: _gas_module, global: global_name, ref func, cost } => {
202			let gas_global_idx = module_info.num_globals();
203
204			// Inject the gas counting global
205			module_info.add_global(
206				GlobalType { content_type: ValType::I64, mutable: true },
207				&wasm_encoder::ConstExpr::i64_const(0),
208			)?;
209
210			module_info.add_exports(&[(
211				String::from(global_name),
212				ExportKind::Global,
213				gas_global_idx,
214			)])?;
215
216			// Inject the local gas function
217			let ty = Type::Func(FuncType::new(vec![ValType::I64], vec![]));
218			module_info.add_functions(&[(ty, func.clone())])?;
219
220			(
221				// Don't inject counters to the local gas function, which is the last one as
222				// it's just added. Cost for its execution is added statically before each
223				// invocation (see `inject_counter()`).
224				functions_count,
225				functions_count + 1,
226				cost,
227			)
228		},
229	};
230
231	let mut need_grow_counter = false;
232	let mut error = false;
233
234	// Iterate over module sections and perform needed transformations.
235	// Indexes are needed to be fixed up in `GasMeter::External` case, as it adds an imported
236	// function, which goes to the beginning of the module's functions space.
237	if module_info.code_section_entry_count > 0 {
238		let mut code_section_builder = wasm_encoder::CodeSection::new();
239
240		for (func_body, is_last) in module_info
241			.code_section()?
242			.ok_or_else(|| anyhow!("no code section"))?
243			.into_iter()
244			.enumerate()
245			.map(|(index, item)| (item, index as u32 == module_info.code_section_entry_count - 1))
246		{
247			let current_locals = copy_locals(&func_body)?;
248
249			let locals_count = current_locals.iter().map(|(count, _)| count).sum();
250
251			let mut func_builder = wasm_encoder::Function::new(copy_locals(&func_body)?);
252
253			let operator_reader = func_body.get_operators_reader()?;
254			for op in operator_reader {
255				let op = op?;
256				let mut instruction: Option<Instruction> = None;
257				if let GasMeter::External { .. } = gas_meter {
258					if let Operator::Call { function_index } = op {
259						if function_index >= gas_func_idx {
260							instruction = Some(Instruction::Call(function_index + 1));
261						}
262					}
263				}
264				let instruction = match instruction {
265					Some(instruction) => instruction,
266					None => DefaultTranslator.translate_op(&op)?,
267				};
268				func_builder.instruction(&instruction);
269			}
270
271			if let GasMeter::Internal { .. } = gas_meter {
272				// If GasMeter::Internal then don't inject counters to the local gas function, which
273				// is the last one as it's just added. Cost for its execution is added statically
274				// before each invocation (see `inject_counter()`).
275				if is_last {
276					code_section_builder.function(&func_builder);
277					continue;
278				}
279			}
280
281			match inject_counter(
282				&FunctionBody::new(0, &truncate_len_from_encoder(&func_builder)?),
283				gas_fn_cost,
284				locals_count,
285				rules,
286				gas_func_idx,
287			) {
288				Ok(new_builder) => func_builder = new_builder,
289				Err(_) => {
290					error = true;
291					break;
292				},
293			}
294			if rules.memory_grow_cost().enabled() {
295				let counter;
296				(func_builder, counter) = inject_grow_counter(
297					&FunctionBody::new(0, &truncate_len_from_encoder(&func_builder)?),
298					grow_cnt_func_idx,
299				)?;
300				if counter > 0 {
301					need_grow_counter = true;
302				}
303			}
304			code_section_builder.function(&func_builder);
305		}
306		module_info.replace_section(SectionId::Code.into(), &code_section_builder)?;
307	}
308
309	if module_info.exports_count > 0 {
310		if let GasMeter::External { .. } = gas_meter {
311			let mut export_sec_builder = ExportSection::new();
312
313			for export in module_info.export_section()?.expect("no export section") {
314				let mut export_index = export.index;
315				if let ExternalKind::Func = export.kind {
316					if export_index >= gas_func_idx {
317						export_index += 1;
318					}
319				}
320				export_sec_builder.export(
321					export.name,
322					DefaultTranslator.translate_export_kind(export.kind)?,
323					export_index,
324				);
325			}
326			module_info.replace_section(SectionId::Export.into(), &export_sec_builder)?;
327		}
328	}
329
330	if module_info.elements_count > 0 {
331		// Note that we do not need to check the element type referenced because in the
332		// WebAssembly 1.0 spec, the only allowed element type is funcref.
333		if let GasMeter::External { .. } = gas_meter {
334			let mut ele_sec_builder = ElementSection::new();
335
336			// TODO rework updating Element section here and there to avoid code repetition
337			for elem in module_info.element_section()?.expect("no element_section section") {
338				let mut functions = vec![];
339				if let ElementItems::Functions(func_indexes) = elem.items {
340					for func_idx in func_indexes {
341						let mut func_idx = func_idx?;
342						if func_idx >= gas_func_idx {
343							func_idx += 1
344						}
345						functions.push(func_idx);
346					}
347				}
348
349				let offset;
350				let mode = match elem.kind {
351					ElementKind::Active { table_index, offset_expr } => {
352						offset = DefaultTranslator.translate_const_expr(
353							&offset_expr,
354							&ValType::I32,
355							ConstExprKind::ElementOffset,
356						)?;
357
358						ElementMode::Active { table: table_index, offset: &offset }
359					},
360					ElementKind::Passive => ElementMode::Passive,
361					ElementKind::Declared => ElementMode::Declared,
362				};
363
364				let element_type = DefaultTranslator.translate_ref_ty(&elem.ty)?;
365				let elements = Elements::Functions(&functions);
366
367				ele_sec_builder.segment(ElementSegment {
368					mode,
369					// The element type.
370					element_type,
371					// The element functions.
372					elements,
373				});
374			}
375			module_info.replace_section(SectionId::Element.into(), &ele_sec_builder)?;
376		}
377	}
378
379	if module_info.raw_sections.get_mut(&SectionId::Start.into()).is_some() {
380		if let GasMeter::External { .. } = gas_meter {
381			if let Some(func_idx) = module_info.start_function {
382				if func_idx >= gas_func_idx {
383					let start_section = StartSection { function_index: func_idx + 1 };
384					module_info.replace_section(SectionId::Start.into(), &start_section)?;
385				}
386			}
387		}
388	}
389
390	#[cfg(not(feature = "ignore_custom_section"))]
391	update_custom_section_function_indices(module_info, gas_func_idx)?;
392
393	if error {
394		return Err(anyhow!("inject fail"));
395	}
396
397	if need_grow_counter {
398		if let Some((func, grow_counter_func)) = generate_grow_counter(rules, gas_func_idx) {
399			module_info.add_functions(&[(func, grow_counter_func)])?;
400		}
401	}
402	Ok(module_info.bytes())
403}
404
405/// A control flow block is opened with the `block`, `loop`, and `if` instructions and is closed
406/// with `end`. Each block implicitly defines a new label. The control blocks form a stack during
407/// program execution.
408///
409/// An example of block:
410///
411/// ```ignore
412/// loop
413///   i32.const 1
414///   get_local 0
415///   i32.sub
416///   tee_local 0
417///   br_if 0
418/// end
419/// ```
420///
421/// The start of the block is `i32.const 1`.
422#[derive(Debug)]
423struct ControlBlock {
424	/// The lowest control stack index corresponding to a forward jump targeted by a br, br_if, or
425	/// br_table instruction within this control block. The index must refer to a control block
426	/// that is not a loop, meaning it is a forward jump. Given the way Wasm control flow is
427	/// structured, the lowest index on the stack represents the furthest forward branch target.
428	///
429	/// This value will always be at most the index of the block itself, even if there is no
430	/// explicit br instruction targeting this control block. This does not affect how the value is
431	/// used in the metering algorithm.
432	lowest_forward_br_target: usize,
433
434	/// The active metering block that new instructions contribute a gas cost towards.
435	active_metered_block: MeteredBlock,
436
437	/// Whether the control block is a loop. Loops have the distinguishing feature that branches to
438	/// them jump to the beginning of the block, not the end as with the other control blocks.
439	is_loop: bool,
440}
441
442/// A block of code that metering instructions will be inserted at the beginning of. Metered blocks
443/// are constructed with the property that, in the absence of any traps, either all instructions in
444/// the block are executed or none are.
445#[derive(Debug)]
446struct MeteredBlock {
447	/// Index of the first instruction (aka `Opcode`) in the block.
448	start_pos: usize,
449	/// Sum of costs of all instructions until end of the block.
450	cost: u64,
451}
452
453/// Counter is used to manage state during the gas metering algorithm implemented by
454/// `inject_counter`.
455struct Counter {
456	/// A stack of control blocks. This stack grows when new control blocks are opened with
457	/// `block`, `loop`, and `if` and shrinks when control blocks are closed with `end`. The first
458	/// block on the stack corresponds to the function body, not to any labelled block. Therefore
459	/// the actual Wasm label index associated with each control block is 1 less than its position
460	/// in this stack.
461	stack: Vec<ControlBlock>,
462
463	/// A list of metered blocks that have been finalized, meaning they will no longer change.
464	finalized_blocks: Vec<MeteredBlock>,
465}
466
467impl Counter {
468	fn new() -> Counter {
469		Counter { stack: Vec::new(), finalized_blocks: Vec::new() }
470	}
471
472	/// Open a new control block. The cursor is the position of the first instruction in the block.
473	fn begin_control_block(&mut self, cursor: usize, is_loop: bool) {
474		let index = self.stack.len();
475		self.stack.push(ControlBlock {
476			lowest_forward_br_target: index,
477			active_metered_block: MeteredBlock { start_pos: cursor, cost: 0 },
478			is_loop,
479		})
480	}
481
482	/// Close the last control block. The cursor is the position of the final (pseudo-)instruction
483	/// in the block.
484	fn finalize_control_block(&mut self, cursor: usize) -> Result<()> {
485		// This either finalizes the active metered block or merges its cost into the active
486		// metered block in the previous control block on the stack.
487		self.finalize_metered_block(cursor)?;
488
489		// Pop the control block stack.
490		let closing_control_block = self.stack.pop().ok_or_else(|| anyhow!("stack not found"))?;
491		let closing_control_index = self.stack.len();
492
493		if self.stack.is_empty() {
494			return Ok(());
495		}
496
497		// Update the lowest_forward_br_target for the control block now on top of the stack.
498		{
499			let control_block = self.stack.last_mut().ok_or_else(|| anyhow!("stack not found"))?;
500			control_block.lowest_forward_br_target = min(
501				control_block.lowest_forward_br_target,
502				closing_control_block.lowest_forward_br_target,
503			);
504		}
505
506		// If there may have been a branch to a lower index, then also finalize the active metered
507		// block for the previous control block. Otherwise, finalize it and begin a new one.
508		let may_br_out = closing_control_block.lowest_forward_br_target < closing_control_index;
509		if may_br_out {
510			self.finalize_metered_block(cursor)?;
511		}
512
513		Ok(())
514	}
515
516	/// Finalize the current active metered block.
517	///
518	/// Finalized blocks have final cost which will not change later.
519	fn finalize_metered_block(&mut self, cursor: usize) -> Result<()> {
520		let closing_metered_block = {
521			let control_block = self.stack.last_mut().ok_or_else(|| anyhow!("stack not found"))?;
522			mem::replace(
523				&mut control_block.active_metered_block,
524				MeteredBlock { start_pos: cursor + 1, cost: 0 },
525			)
526		};
527
528		// If the block was opened with a `block`, then its start position will be set to that of
529		// the active metered block in the control block one higher on the stack. This is because
530		// any instructions between a `block` and the first branch are part of the same basic block
531		// as the preceding instruction. In this case, instead of finalizing the block, merge its
532		// cost into the other active metered block to avoid injecting unnecessary instructions.
533		let last_index = self.stack.len() - 1;
534		if last_index > 0 {
535			let prev_control_block = self
536				.stack
537				.get_mut(last_index - 1)
538				.expect("last_index is greater than 0; last_index is stack size - 1; qed");
539			let prev_metered_block = &mut prev_control_block.active_metered_block;
540			if closing_metered_block.start_pos == prev_metered_block.start_pos {
541				prev_metered_block.cost = prev_metered_block
542					.cost
543					.checked_add(closing_metered_block.cost)
544					.ok_or_else(|| anyhow!("overflow occured"))?;
545				return Ok(());
546			}
547		}
548
549		if closing_metered_block.cost > 0 {
550			self.finalized_blocks.push(closing_metered_block);
551		}
552		Ok(())
553	}
554
555	/// Handle a branch instruction in the program. The cursor is the index of the branch
556	/// instruction in the program. The indices are the stack positions of the target control
557	/// blocks. Recall that the index is 0 for a `return` and relatively indexed from the top of
558	/// the stack by the label of `br`, `br_if`, and `br_table` instructions.
559	fn branch(&mut self, cursor: usize, indices: &[usize]) -> Result<()> {
560		self.finalize_metered_block(cursor)?;
561
562		// Update the lowest_forward_br_target of the current control block.
563		for &index in indices {
564			let target_is_loop = {
565				let target_block =
566					self.stack.get(index).ok_or_else(|| anyhow!("unable to find stack index"))?;
567				target_block.is_loop
568			};
569			if target_is_loop {
570				continue;
571			}
572
573			let control_block =
574				self.stack.last_mut().ok_or_else(|| anyhow!("stack does not exist"))?;
575			control_block.lowest_forward_br_target =
576				min(control_block.lowest_forward_br_target, index);
577		}
578
579		Ok(())
580	}
581
582	/// Returns the stack index of the active control block. Returns None if stack is empty.
583	fn active_control_block_index(&self) -> Option<usize> {
584		self.stack.len().checked_sub(1)
585	}
586
587	/// Get a reference to the currently active metered block.
588	fn active_metered_block(&mut self) -> Result<&mut MeteredBlock> {
589		let top_block = self.stack.last_mut().ok_or_else(|| anyhow!("stack does not exist"))?;
590		Ok(&mut top_block.active_metered_block)
591	}
592
593	/// Increment the cost of the current block by the specified value.
594	fn increment(&mut self, val: u32) -> Result<()> {
595		let top_block = self.active_metered_block()?;
596		top_block.cost = top_block
597			.cost
598			.checked_add(val.into())
599			.ok_or_else(|| anyhow!("add cost overflow"))?;
600		Ok(())
601	}
602}
603
604fn inject_grow_counter(
605	func_body: &FunctionBody,
606	grow_counter_func: u32,
607) -> Result<(Function, usize)> {
608	let mut counter = 0;
609	let mut new_func = Function::new(copy_locals(func_body)?);
610	let mut operator_reader = func_body.get_operators_reader()?;
611	while !operator_reader.eof() {
612		let op = operator_reader.read()?;
613		match op {
614			// TODO Bulk memories
615			Operator::MemoryGrow { .. } => {
616				new_func.instruction(&wasm_encoder::Instruction::Call(grow_counter_func));
617				counter += 1;
618			},
619			op => {
620				new_func.instruction(&DefaultTranslator.translate_op(&op)?);
621			},
622		}
623	}
624	Ok((new_func, counter))
625}
626
627fn generate_grow_counter<R: Rules>(rules: &R, gas_func: u32) -> Option<(Type, Function)> {
628	let cost = match rules.memory_grow_cost() {
629		MemoryGrowCost::Free => return None,
630		MemoryGrowCost::Linear(val) => val.get(),
631	};
632
633	let mut func = wasm_encoder::Function::new(None);
634	func.instruction(&wasm_encoder::Instruction::LocalGet(0));
635	func.instruction(&wasm_encoder::Instruction::LocalGet(0));
636	func.instruction(&wasm_encoder::Instruction::I64ExtendI32U);
637	func.instruction(&wasm_encoder::Instruction::I64Const(cost as i64));
638	func.instruction(&wasm_encoder::Instruction::I64Mul);
639	func.instruction(&wasm_encoder::Instruction::Call(gas_func));
640	func.instruction(&wasm_encoder::Instruction::MemoryGrow(0));
641	func.instruction(&wasm_encoder::Instruction::End);
642	Some((Type::Func(FuncType::new(vec![ValType::I32], vec![ValType::I32])), func))
643}
644
645fn determine_metered_blocks<R: Rules>(
646	func_body: &wasmparser::FunctionBody,
647	rules: &R,
648	locals_count: u32,
649) -> Result<Vec<MeteredBlock>> {
650	use wasmparser::Operator::*;
651
652	let mut counter = Counter::new();
653
654	// Begin an implicit function (i.e. `func...end`) block.
655	counter.begin_control_block(0, false);
656	// Add locals initialization cost to the function block.
657	let locals_init_cost = rules
658		.call_per_local_cost()
659		.checked_mul(locals_count)
660		.ok_or_else(|| anyhow!("overflow occured"))?;
661	counter.increment(locals_init_cost)?;
662
663	let operators = func_body
664		.get_operators_reader()?
665		.into_iter()
666		.collect::<wasmparser::Result<Vec<Operator>>>()?;
667
668	for (cursor, instruction) in operators.iter().enumerate() {
669		let instruction_cost = rules
670			.instruction_cost(instruction)
671			.ok_or_else(|| anyhow!("check gas rule fail"))?;
672		match instruction {
673			Block { blockty: _ } => {
674				counter.increment(instruction_cost)?;
675
676				// Begin new block. The cost of the following opcodes until `end` or `else` will
677				// be included into this block. The start position is set to that of the previous
678				// active metered block to signal that they should be merged in order to reduce
679				// unnecessary metering instructions.
680				let top_block_start_pos = counter.active_metered_block()?.start_pos;
681				counter.begin_control_block(top_block_start_pos, false);
682			},
683			If { blockty: _ } => {
684				counter.increment(instruction_cost)?;
685				counter.begin_control_block(cursor + 1, false);
686			},
687			Loop { blockty: _ } => {
688				counter.increment(instruction_cost)?;
689				counter.begin_control_block(cursor + 1, true);
690			},
691			End => {
692				counter.finalize_control_block(cursor)?;
693			},
694			Else => {
695				counter.finalize_metered_block(cursor)?;
696			},
697			Br { relative_depth } | BrIf { relative_depth } => {
698				counter.increment(instruction_cost)?;
699
700				// Label is a relative index into the control stack.
701				let active_index = counter
702					.active_control_block_index()
703					.ok_or_else(|| anyhow!("active control block not exit"))?;
704
705				let target_index = active_index
706					.checked_sub(*relative_depth as usize)
707					.ok_or_else(|| anyhow!("index not found"))?;
708
709				counter.branch(cursor, &[target_index])?;
710			},
711			BrTable { targets: br_table_data } => {
712				counter.increment(instruction_cost)?;
713
714				let active_index = counter
715					.active_control_block_index()
716					.ok_or_else(|| anyhow!("index not found"))?;
717				let r = br_table_data.targets().collect::<wasmparser::Result<Vec<u32>>>()?;
718				let target_indices = [br_table_data.default()]
719					.iter()
720					.chain(r.iter())
721					.map(|label| active_index.checked_sub(*label as usize))
722					.collect::<Option<Vec<_>>>()
723					.ok_or_else(|| anyhow!("to do check this error"))?;
724				counter.branch(cursor, &target_indices)?;
725			},
726			Return => {
727				counter.increment(instruction_cost)?;
728				counter.branch(cursor, &[0])?;
729			},
730			_ => {
731				// An ordinal non control flow instruction increments the cost of the current block.
732				counter.increment(instruction_cost)?;
733			},
734		}
735	}
736
737	counter.finalized_blocks.sort_unstable_by_key(|block| block.start_pos);
738	Ok(counter.finalized_blocks)
739}
740
741fn inject_counter<R: Rules>(
742	func_body: &FunctionBody,
743	gas_function_cost: u64,
744	locals_count: u32,
745	rules: &R,
746	gas_func: u32,
747) -> Result<wasm_encoder::Function> {
748	let blocks = determine_metered_blocks(func_body, rules, locals_count)?;
749	insert_metering_calls(func_body, gas_function_cost, blocks, gas_func)
750}
751
752// Then insert metering calls into a sequence of instructions given the block locations and costs.
753fn insert_metering_calls(
754	func_body: &FunctionBody,
755	gas_function_cost: u64,
756	blocks: Vec<MeteredBlock>,
757	gas_func: u32,
758) -> Result<wasm_encoder::Function> {
759	let mut new_func = wasm_encoder::Function::new(copy_locals(func_body)?);
760
761	// To do this in linear time, construct a new vector of instructions, copying over old
762	// instructions one by one and injecting new ones as required.
763	let mut block_iter = blocks.into_iter().peekable();
764	let operators = func_body
765		.get_operators_reader()?
766		.into_iter()
767		.collect::<wasmparser::Result<Vec<Operator>>>()?;
768	for (original_pos, instr) in operators.iter().enumerate() {
769		// If there the next block starts at this position, inject metering func_body.
770		let used_block = if let Some(block) = block_iter.peek() {
771			if block.start_pos == original_pos {
772				let cost = block
773					.cost
774					.checked_add(gas_function_cost)
775					.ok_or_else(|| anyhow!("block cost add overflow"))? as i64;
776				new_func.instruction(&wasm_encoder::Instruction::I64Const(cost));
777				new_func.instruction(&wasm_encoder::Instruction::Call(gas_func));
778				true
779			} else {
780				false
781			}
782		} else {
783			false
784		};
785
786		if used_block {
787			block_iter.next();
788		}
789
790		// Copy over the original instruction.
791		new_func.instruction(&DefaultTranslator.translate_op(instr)?);
792	}
793
794	if block_iter.next().is_some() {
795		return Err(anyhow!("block should be consume all"));
796	}
797	Ok(new_func)
798}
799
800#[cfg(test)]
801mod tests {
802	use super::*;
803	use wasm_encoder::{BlockType, Encode, Instruction::*};
804
805	fn check_expect_function_body(
806		raw_wasm: &[u8],
807		index: usize,
808		ops2: &[wasm_encoder::Instruction],
809	) -> bool {
810		let mut body_raw = vec![];
811		ops2.iter().for_each(|v| v.encode(&mut body_raw));
812		get_function_body(raw_wasm, index).eq(&body_raw)
813	}
814
815	fn get_function_body(raw_wasm: &[u8], index: usize) -> Vec<u8> {
816		let module = ModuleInfo::new(raw_wasm).unwrap();
817		let func_sec = module.raw_sections.get(&SectionId::Code.into()).unwrap();
818		let func_bodies = module.code_section().unwrap().expect("no code section");
819
820		let func_body = func_bodies
821			.get(index)
822			.unwrap_or_else(|| panic!("module doesn't have function {} body", index));
823
824		let start = func_body.get_operators_reader().unwrap().original_position();
825		func_sec.data[start..func_body.range().end].to_vec()
826	}
827
828	fn get_function_operators(raw_wasm: &[u8], index: usize) -> Vec<Instruction> {
829		let module = ModuleInfo::new(raw_wasm).unwrap();
830		let func_bodies = module.code_section().unwrap().expect("no code section");
831
832		let func_body = func_bodies
833			.get(index)
834			.unwrap_or_else(|| panic!("module doesn't have function {} body", index));
835
836		let operators = func_body
837			.get_operators_reader()
838			.unwrap()
839			.into_iter()
840			.map(|op| DefaultTranslator.translate_op(&op.unwrap()).unwrap())
841			.collect::<Vec<Instruction>>();
842
843		operators
844	}
845
846	fn parse_wat(source: &str) -> ModuleInfo {
847		let module_bytes = wat::parse_str(source).unwrap();
848		ModuleInfo::new(&module_bytes).unwrap()
849	}
850
851	#[test]
852	fn simple_grow_host_fn() {
853		let mut module = parse_wat(
854			r#"(module
855			(func (result i32)
856			  global.get 0
857			  memory.grow)
858			(global i32 (i32.const 42))
859			(memory 0 1)
860			)"#,
861		);
862
863		let backend = host_function::Injector::new("env", "gas");
864		let injected_raw_wasm =
865			super::inject(&mut module, backend, &ConstantCostRules::new(1, 10_000, 1)).unwrap();
866
867		// main function
868		assert!(check_expect_function_body(
869			&injected_raw_wasm,
870			0,
871			&[I64Const(2), Call(0), GlobalGet(0), Call(2), End,]
872		));
873		// grow counter
874		assert!(check_expect_function_body(
875			&injected_raw_wasm,
876			1,
877			&[
878				LocalGet(0),
879				LocalGet(0),
880				I64ExtendI32U,
881				I64Const(10000),
882				I64Mul,
883				Call(0),
884				MemoryGrow(0),
885				End,
886			]
887		));
888
889		wasmparser::validate(&injected_raw_wasm).unwrap();
890	}
891
892	#[test]
893	fn simple_grow_mut_global() {
894		let mut module = parse_wat(
895			r#"(module
896			(func (result i32)
897			  global.get 0
898			  memory.grow)
899			(global i32 (i32.const 42))
900			(memory 0 1)
901			)"#,
902		);
903
904		let backend = mutable_global::Injector::new("env", "gas_left");
905		let injected_raw_wasm =
906			super::inject(&mut module, backend, &ConstantCostRules::new(1, 10_000, 1)).unwrap();
907
908		// gas_counter
909		assert!(check_expect_function_body(
910			&injected_raw_wasm,
911			1,
912			&[
913				GlobalGet(1),
914				LocalGet(0),
915				I64GeU,
916				If(BlockType::Empty),
917				GlobalGet(1),
918				LocalGet(0),
919				I64Sub,
920				GlobalSet(1),
921				Else,
922				I64Const(-1i64),
923				GlobalSet(1),
924				Unreachable,
925				End,
926				End
927			]
928		));
929
930		// grow_counter
931		assert!(check_expect_function_body(
932			&injected_raw_wasm,
933			2,
934			&[
935				LocalGet(0),
936				LocalGet(0),
937				I64ExtendI32U,
938				I64Const(10000i64),
939				I64Mul,
940				Call(1),
941				MemoryGrow(0),
942				End
943			]
944		));
945
946		wasmparser::validate(&injected_raw_wasm).unwrap();
947	}
948
949	#[test]
950	fn grow_no_gas_no_track_host_fn() {
951		let mut module = parse_wat(
952			r"(module
953			(func (result i32)
954			  global.get 0
955			  memory.grow)
956			(global i32 (i32.const 42))
957			(memory 0 1)
958			)",
959		);
960
961		let backend = host_function::Injector::new("env", "gas");
962		let injected_raw_wasm =
963			super::inject(&mut module, backend, &ConstantCostRules::default()).unwrap();
964
965		// main function
966		assert!(check_expect_function_body(
967			&injected_raw_wasm,
968			0,
969			&[I64Const(2), Call(0), GlobalGet(0), MemoryGrow(0), End,]
970		));
971
972		// Sum of local ('main') and imported functions ('gas') shall be 2
973		assert_eq!(module.num_functions(), 2);
974
975		wasmparser::validate(&injected_raw_wasm).unwrap();
976	}
977	#[test]
978	fn grow_no_gas_no_track_mut_global() {
979		let mut module = parse_wat(
980			r"(module
981			(func (result i32)
982			  global.get 0
983			  memory.grow)
984			(global i32 (i32.const 42))
985			(memory 0 1)
986			)",
987		);
988
989		let backend = host_function::Injector::new("env", "gas");
990		let injected_raw_wasm =
991			super::inject(&mut module, backend, &ConstantCostRules::default()).unwrap();
992
993		// main function
994		assert!(check_expect_function_body(
995			&injected_raw_wasm,
996			0,
997			&[I64Const(2), Call(0), GlobalGet(0), MemoryGrow(0), End,]
998		));
999
1000		// Sum of local ('main') and imported functions ('gas') shall be 2
1001		assert_eq!(module.num_functions(), 2);
1002
1003		wasmparser::validate(&injected_raw_wasm).unwrap();
1004	}
1005	#[test]
1006	fn call_index_host_fn() {
1007		let mut module = parse_wat(
1008			r"(module
1009			(type (;0;) (func (result i32)))
1010			(func (;0;) (type 0) (result i32))
1011			(func (;1;) (type 0) (result i32)
1012				call 0
1013				if  ;; label = @1
1014				  call 0
1015				  call 0
1016				  call 0
1017				else
1018				  call 0
1019				  call 0
1020				end
1021				call 0
1022			)
1023			(global (;0;) i32 (i32.const 0))
1024			)",
1025		);
1026
1027		let backend = host_function::Injector::new("env", "gas");
1028		let injected_raw_wasm =
1029			super::inject(&mut module, backend, &ConstantCostRules::default()).unwrap();
1030
1031		// main function
1032		assert!(check_expect_function_body(
1033			&injected_raw_wasm,
1034			1,
1035			&vec![
1036				I64Const(3),
1037				Call(0),
1038				Call(1),
1039				If(BlockType::Empty),
1040				I64Const(3),
1041				Call(0),
1042				Call(1),
1043				Call(1),
1044				Call(1),
1045				Else,
1046				I64Const(2),
1047				Call(0),
1048				Call(1),
1049				Call(1),
1050				End,
1051				Call(1),
1052				End
1053			]
1054		));
1055	}
1056
1057	#[test]
1058	fn call_index_mut_global() {
1059		let mut module = parse_wat(
1060			r"(module
1061			(type (;0;) (func (result i32)))
1062			(func (;0;) (type 0) (result i32))
1063			(func (;1;) (type 0) (result i32)
1064				call 0
1065				if  ;; label = @1
1066				  call 0
1067				  call 0
1068				  call 0
1069				else
1070				  call 0
1071				  call 0
1072				end
1073				call 0
1074			)
1075			(global (;0;) i32 (i32.const 0))
1076			)",
1077		);
1078
1079		let backend = mutable_global::Injector::new("env", "gas_left");
1080		let injected_raw_wasm =
1081			super::inject(&mut module, backend, &ConstantCostRules::default()).unwrap();
1082
1083		// main function
1084		assert!(check_expect_function_body(
1085			&injected_raw_wasm,
1086			1,
1087			&vec![
1088				I64Const(14),
1089				Call(2),
1090				Call(0),
1091				If(BlockType::Empty),
1092				I64Const(14),
1093				Call(2),
1094				Call(0),
1095				Call(0),
1096				Call(0),
1097				Else,
1098				I64Const(13),
1099				Call(2),
1100				Call(0),
1101				Call(0),
1102				End,
1103				Call(0),
1104				End
1105			]
1106		));
1107	}
1108
1109	macro_rules! test_gas_counter_injection {
1110		(names = ($name1:ident, $name2:ident); input = $input:expr; expected = $expected:expr) => {
1111			#[test]
1112			fn $name1() {
1113				let mut module = parse_wat($input);
1114				let expected_module = parse_wat($expected);
1115				let injected_wasm = super::inject(
1116					&mut module,
1117					host_function::Injector::new("env", "gas"),
1118					&ConstantCostRules::default(),
1119				)
1120				.expect("inject_gas_counter call failed");
1121
1122				let actual_func_body = get_function_body(&injected_wasm, 0);
1123				let expected_func_body = get_function_body(&expected_module.bytes(), 0);
1124
1125				assert_eq!(actual_func_body, expected_func_body);
1126			}
1127
1128			#[test]
1129			fn $name2() {
1130				let mut module = parse_wat($input);
1131				let draft_module = parse_wat($expected);
1132				let gas_fun_cost = match mutable_global::Injector::new("env", "gas_left")
1133					.gas_meter(&mut module, &ConstantCostRules::default())
1134				{
1135					GasMeter::Internal { cost, .. } => cost as i64,
1136					_ => 0i64,
1137				};
1138
1139				let injected_wasm = super::inject(
1140					&mut module,
1141					mutable_global::Injector::new("env", "gas_left"),
1142					&ConstantCostRules::default(),
1143				)
1144				.expect("inject_gas_counter call failed");
1145
1146				let actual_func_body = get_function_body(&injected_wasm, 0);
1147
1148				let expected_module_bytes = draft_module.bytes();
1149				let mut expected_func_operators = get_function_operators(&expected_module_bytes, 0);
1150
1151				// modify expected instructions set for gas_metering::mutable_global
1152				let mut iter = expected_func_operators.iter_mut();
1153				while let Some(ins) = iter.next() {
1154					if let I64Const(cost) = ins {
1155						if let Some(ins_next) = iter.next() {
1156							if let Call(0) = ins_next {
1157								*cost += gas_fun_cost;
1158								*ins_next = Call(1);
1159							}
1160						}
1161					}
1162				}
1163				let mut expected_func_body = vec![];
1164				expected_func_operators.iter().for_each(|v| v.encode(&mut expected_func_body));
1165
1166				assert_eq!(actual_func_body, expected_func_body);
1167			}
1168		};
1169	}
1170
1171	test_gas_counter_injection! {
1172		names = (simple_host_fn, simple_mut_global);
1173		input = r#"
1174		(module
1175			(func (result i32)
1176				(get_global 0)))
1177		"#;
1178		expected = r#"
1179		(module
1180			(func (result i32)
1181				(call 0 (i64.const 1))
1182				(get_global 0)))
1183		"#
1184	}
1185
1186	test_gas_counter_injection! {
1187		names = (nested_host_fn, nested_mut_global);
1188		input = r#"
1189		(module
1190			(func (result i32)
1191				(get_global 0)
1192				(block
1193					(get_global 0)
1194					(get_global 0)
1195					(get_global 0))
1196				(get_global 0)))
1197		"#;
1198		expected = r#"
1199		(module
1200			(func (result i32)
1201				(call 0 (i64.const 6))
1202				(get_global 0)
1203				(block
1204					(get_global 0)
1205					(get_global 0)
1206					(get_global 0))
1207				(get_global 0)))
1208		"#
1209	}
1210
1211	test_gas_counter_injection! {
1212		names = (ifelse_host_fn, ifelse_mut_global);
1213		input = r#"
1214		(module
1215			(func (result i32)
1216				(get_global 0)
1217				(if
1218					(then
1219						(get_global 0)
1220						(get_global 0)
1221						(get_global 0))
1222					(else
1223						(get_global 0)
1224						(get_global 0)))
1225				(get_global 0)))
1226		"#;
1227		expected = r#"
1228		(module
1229			(func (result i32)
1230				(call 0 (i64.const 3))
1231				(get_global 0)
1232				(if
1233					(then
1234						(call 0 (i64.const 3))
1235						(get_global 0)
1236						(get_global 0)
1237						(get_global 0))
1238					(else
1239						(call 0 (i64.const 2))
1240						(get_global 0)
1241						(get_global 0)))
1242				(get_global 0)))
1243		"#
1244	}
1245
1246	test_gas_counter_injection! {
1247		names = (branch_innermost_host_fn, branch_innermost_mut_global);
1248		input = r#"
1249		(module
1250			(func (result i32)
1251				(get_global 0)
1252				(block
1253					(get_global 0)
1254					(drop)
1255					(br 0)
1256					(get_global 0)
1257					(drop))
1258				(get_global 0)))
1259		"#;
1260		expected = r#"
1261		(module
1262			(func (result i32)
1263				(call 0 (i64.const 6))
1264				(get_global 0)
1265				(block
1266					(get_global 0)
1267					(drop)
1268					(br 0)
1269					(call 0 (i64.const 2))
1270					(get_global 0)
1271					(drop))
1272				(get_global 0)))
1273		"#
1274	}
1275
1276	test_gas_counter_injection! {
1277		names = (branch_outer_block_host_fn, branch_outer_block_mut_global);
1278		input = r#"
1279		(module
1280			(func (result i32)
1281				(get_global 0)
1282				(block
1283					(get_global 0)
1284					(if
1285						(then
1286							(get_global 0)
1287							(get_global 0)
1288							(drop)
1289							(br_if 1)))
1290					(get_global 0)
1291					(drop))
1292				(get_global 0)))
1293		"#;
1294		expected = r#"
1295		(module
1296			(func (result i32)
1297				(call 0 (i64.const 5))
1298				(get_global 0)
1299				(block
1300					(get_global 0)
1301					(if
1302						(then
1303							(call 0 (i64.const 4))
1304							(get_global 0)
1305							(get_global 0)
1306							(drop)
1307							(br_if 1)))
1308					(call 0 (i64.const 2))
1309					(get_global 0)
1310					(drop))
1311				(get_global 0)))
1312		"#
1313	}
1314
1315	test_gas_counter_injection! {
1316		names = (branch_outer_loop_host_fn, branch_outer_loop_mut_global);
1317		input = r#"
1318		(module
1319			(func (result i32)
1320				(get_global 0)
1321				(loop
1322					(get_global 0)
1323					(if
1324						(then
1325							(get_global 0)
1326							(br_if 0))
1327						(else
1328							(get_global 0)
1329							(get_global 0)
1330							(drop)
1331							(br_if 1)))
1332					(get_global 0)
1333					(drop))
1334				(get_global 0)))
1335		"#;
1336		expected = r#"
1337		(module
1338			(func (result i32)
1339				(call 0 (i64.const 3))
1340				(get_global 0)
1341				(loop
1342					(call 0 (i64.const 4))
1343					(get_global 0)
1344					(if
1345						(then
1346							(call 0 (i64.const 2))
1347							(get_global 0)
1348							(br_if 0))
1349						(else
1350							(call 0 (i64.const 4))
1351							(get_global 0)
1352							(get_global 0)
1353							(drop)
1354							(br_if 1)))
1355					(get_global 0)
1356					(drop))
1357				(get_global 0)))
1358		"#
1359	}
1360
1361	test_gas_counter_injection! {
1362		names = (return_from_func_host_fn, return_from_func_mut_global);
1363		input = r#"
1364		(module
1365			(func (result i32)
1366				(get_global 0)
1367				(if
1368					(then
1369						(return)))
1370				(get_global 0)))
1371		"#;
1372		expected = r#"
1373		(module
1374			(func (result i32)
1375				(call 0 (i64.const 2))
1376				(get_global 0)
1377				(if
1378					(then
1379						(call 0 (i64.const 1))
1380						(return)))
1381				(call 0 (i64.const 1))
1382				(get_global 0)))
1383		"#
1384	}
1385
1386	test_gas_counter_injection! {
1387		names = (branch_from_if_not_else_host_fn, branch_from_if_not_else_mut_global);
1388		input = r#"
1389		(module
1390			(func (result i32)
1391				(get_global 0)
1392				(block
1393					(get_global 0)
1394					(if
1395						(then (br 1))
1396						(else (br 0)))
1397					(get_global 0)
1398					(drop))
1399				(get_global 0)))
1400		"#;
1401		expected = r#"
1402		(module
1403			(func (result i32)
1404				(call 0 (i64.const 5))
1405				(get_global 0)
1406				(block
1407					(get_global 0)
1408					(if
1409						(then
1410							(call 0 (i64.const 1))
1411							(br 1))
1412						(else
1413							(call 0 (i64.const 1))
1414							(br 0)))
1415					(call 0 (i64.const 2))
1416					(get_global 0)
1417					(drop))
1418				(get_global 0)))
1419		"#
1420	}
1421
1422	test_gas_counter_injection! {
1423		names = (empty_loop_host_fn, empty_loop_mut_global);
1424		input = r#"
1425		(module
1426			(func
1427				(loop
1428					(br 0)
1429				)
1430				unreachable
1431			)
1432		)
1433		"#;
1434		expected = r#"
1435		(module
1436			(func
1437				(call 0 (i64.const 2))
1438				(loop
1439					(call 0 (i64.const 1))
1440					(br 0)
1441				)
1442				unreachable
1443			)
1444		)
1445		"#
1446	}
1447}