1use std::{convert::TryInto, num::NonZeroU32};
3
4use thiserror::Error;
5
6use casper_types::{OpcodeCosts, WasmConfig};
7use casper_wasm::elements::{
8 self, External, Instruction, Internal, MemorySection, Module, Section, SignExtInstruction,
9 TableType, Type,
10};
11use casper_wasm_utils::{
12 self,
13 rules::{MemoryGrowCost, Rules},
14 stack_height,
15};
16
17use crate::execution::ExecError;
18
19const ATOMIC_OPCODE_PREFIX: u8 = 0xfe;
20const BULK_OPCODE_PREFIX: u8 = 0xfc;
21const SIMD_OPCODE_PREFIX: u8 = 0xfd;
22
23const DEFAULT_GAS_MODULE_NAME: &str = "env";
24const INTERNAL_GAS_FUNCTION_NAME: &str = "gas";
26
27pub const DEFAULT_MAX_TABLE_SIZE: u32 = 4096;
29pub const DEFAULT_BR_TABLE_MAX_SIZE: u32 = 256;
31pub const DEFAULT_MAX_GLOBALS: u32 = 256;
33pub const DEFAULT_MAX_PARAMETER_COUNT: u32 = 256;
35
36#[derive(Debug, Clone, Error)]
38#[non_exhaustive]
39pub enum WasmValidationError {
40 #[error("initial table size of {actual} exceeds allowed limit of {max}")]
42 InitialTableSizeExceeded {
43 max: u32,
45 actual: u32,
47 },
48 #[error("maximum table size of {actual} exceeds allowed limit of {max}")]
50 MaxTableSizeExceeded {
51 max: u32,
53 actual: u32,
55 },
56 #[error("the number of tables must be at most one")]
58 MoreThanOneTable,
59 #[error("maximum br_table size of {actual} exceeds allowed limit of {max}")]
61 BrTableSizeExceeded {
62 max: u32,
64 actual: usize,
66 },
67 #[error("declared number of globals ({actual}) exceeds allowed limit of {max}")]
69 TooManyGlobals {
70 max: u32,
72 actual: usize,
74 },
75 #[error("use of a function type with too many parameters (limit of {max} but function declares {actual})")]
77 TooManyParameters {
78 max: u32,
80 actual: usize,
82 },
83 #[error("module imports a non-existent function")]
85 MissingHostFunction,
86 #[error("opcode for a global access refers to non-existing global index {index}")]
88 IncorrectGlobalOperation {
89 index: u32,
91 },
92 #[error("missing function index {index}")]
94 MissingFunctionIndex {
95 index: u32,
97 },
98 #[error("missing type index {index}")]
100 MissingFunctionType {
101 index: u32,
103 },
104}
105
106#[derive(Debug, Clone, Error)]
108#[non_exhaustive]
109pub enum PreprocessingError {
110 #[error("Deserialization error: {0}")]
112 Deserialize(String),
113 #[error(
115 "Encountered operation forbidden by gas rules. Consult instruction -> metering config map"
116 )]
117 OperationForbiddenByGasRules,
118 #[error("Stack limiter error")]
120 StackLimiter,
121 #[error("Memory section should exist")]
123 MissingMemorySection,
124 #[error("Missing module")]
126 MissingModule,
127 #[error("Wasm validation error: {0}")]
129 WasmValidation(#[from] WasmValidationError),
130}
131
132impl From<elements::Error> for PreprocessingError {
133 fn from(error: elements::Error) -> Self {
134 PreprocessingError::Deserialize(error.to_string())
135 }
136}
137
138fn ensure_valid_access(module: &Module) -> Result<(), WasmValidationError> {
150 let function_types_count = module
151 .type_section()
152 .map(|ts| ts.types().len())
153 .unwrap_or_default();
154
155 let mut function_count = 0_u32;
156 if let Some(import_section) = module.import_section() {
157 for import_entry in import_section.entries() {
158 if let External::Function(function_type_index) = import_entry.external() {
159 if (*function_type_index as usize) < function_types_count {
160 function_count = function_count.saturating_add(1);
161 } else {
162 return Err(WasmValidationError::MissingFunctionType {
163 index: *function_type_index,
164 });
165 }
166 }
167 }
168 }
169 if let Some(function_section) = module.function_section() {
170 for function_entry in function_section.entries() {
171 let function_type_index = function_entry.type_ref();
172 if (function_type_index as usize) < function_types_count {
173 function_count = function_count.saturating_add(1);
174 } else {
175 return Err(WasmValidationError::MissingFunctionType {
176 index: function_type_index,
177 });
178 }
179 }
180 }
181
182 if let Some(function_index) = module.start_section() {
183 ensure_valid_function_index(function_index, function_count)?;
184 }
185 if let Some(export_section) = module.export_section() {
186 for export_entry in export_section.entries() {
187 if let Internal::Function(function_index) = export_entry.internal() {
188 ensure_valid_function_index(*function_index, function_count)?;
189 }
190 }
191 }
192
193 if let Some(code_section) = module.code_section() {
194 let global_len = module
195 .global_section()
196 .map(|global_section| global_section.entries().len())
197 .unwrap_or(0);
198
199 for instr in code_section
200 .bodies()
201 .iter()
202 .flat_map(|body| body.code().elements())
203 {
204 match instr {
205 Instruction::Call(idx) => {
206 ensure_valid_function_index(*idx, function_count)?;
207 }
208 Instruction::GetGlobal(idx) | Instruction::SetGlobal(idx)
209 if *idx as usize >= global_len =>
210 {
211 return Err(WasmValidationError::IncorrectGlobalOperation { index: *idx });
212 }
213 _ => {}
214 }
215 }
216 }
217
218 if let Some(element_section) = module.elements_section() {
219 for element_segment in element_section.entries() {
220 for idx in element_segment.members() {
221 ensure_valid_function_index(*idx, function_count)?;
222 }
223 }
224 }
225
226 Ok(())
227}
228
229fn ensure_valid_function_index(index: u32, function_count: u32) -> Result<(), WasmValidationError> {
230 if index >= function_count {
231 return Err(WasmValidationError::MissingFunctionIndex { index });
232 }
233 Ok(())
234}
235
236fn memory_section(module: &Module) -> Option<&MemorySection> {
238 for section in module.sections() {
239 if let Section::Memory(section) = section {
240 return if section.entries().is_empty() {
241 None
242 } else {
243 Some(section)
244 };
245 }
246 }
247 None
248}
249
250fn ensure_table_size_limit(mut module: Module, limit: u32) -> Result<Module, WasmValidationError> {
255 if let Some(sect) = module.table_section_mut() {
256 if sect.entries().len() > 1 {
258 return Err(WasmValidationError::MoreThanOneTable);
259 }
260
261 if let Some(table_entry) = sect.entries_mut().first_mut() {
262 let initial = table_entry.limits().initial();
263 if initial > limit {
264 return Err(WasmValidationError::InitialTableSizeExceeded {
265 max: limit,
266 actual: initial,
267 });
268 }
269
270 match table_entry.limits().maximum() {
271 Some(max) => {
272 if max > limit {
273 return Err(WasmValidationError::MaxTableSizeExceeded {
274 max: limit,
275 actual: max,
276 });
277 }
278 }
279 None => {
280 *table_entry = TableType::new(initial, Some(limit))
282 }
283 }
284 }
285 }
286
287 Ok(module)
288}
289
290fn ensure_br_table_size_limit(module: &Module, limit: u32) -> Result<(), WasmValidationError> {
292 let code_section = if let Some(type_section) = module.code_section() {
293 type_section
294 } else {
295 return Ok(());
296 };
297 for instr in code_section
298 .bodies()
299 .iter()
300 .flat_map(|body| body.code().elements())
301 {
302 if let Instruction::BrTable(br_table_data) = instr {
303 if br_table_data.table.len() > limit as usize {
304 return Err(WasmValidationError::BrTableSizeExceeded {
305 max: limit,
306 actual: br_table_data.table.len(),
307 });
308 }
309 }
310 }
311 Ok(())
312}
313
314fn ensure_global_variable_limit(module: &Module, limit: u32) -> Result<(), WasmValidationError> {
319 if let Some(global_section) = module.global_section() {
320 let actual = global_section.entries().len();
321 if actual > limit as usize {
322 return Err(WasmValidationError::TooManyGlobals { max: limit, actual });
323 }
324 }
325 Ok(())
326}
327
328fn ensure_parameter_limit(module: &Module, limit: u32) -> Result<(), WasmValidationError> {
337 let type_section = if let Some(type_section) = module.type_section() {
338 type_section
339 } else {
340 return Ok(());
341 };
342
343 for Type::Function(func) in type_section.types() {
344 let actual = func.params().len();
345 if actual > limit as usize {
346 return Err(WasmValidationError::TooManyParameters { max: limit, actual });
347 }
348 }
349
350 Ok(())
351}
352
353fn ensure_valid_imports(module: &Module) -> Result<(), WasmValidationError> {
355 let import_entries = module
356 .import_section()
357 .map(|is| is.entries())
358 .unwrap_or(&[]);
359
360 for import in import_entries {
365 if import.module() == DEFAULT_GAS_MODULE_NAME
366 && import.field() == INTERNAL_GAS_FUNCTION_NAME
367 {
368 return Err(WasmValidationError::MissingHostFunction);
369 }
370 }
371
372 Ok(())
373}
374
375pub fn preprocess(
387 wasm_config: WasmConfig,
388 module_bytes: &[u8],
389) -> Result<Module, PreprocessingError> {
390 let module = deserialize(module_bytes)?;
391
392 ensure_valid_access(&module)?;
393
394 if memory_section(&module).is_none() {
395 return Err(PreprocessingError::MissingMemorySection);
398 }
399
400 let module = ensure_table_size_limit(module, DEFAULT_MAX_TABLE_SIZE)?;
401 ensure_br_table_size_limit(&module, DEFAULT_BR_TABLE_MAX_SIZE)?;
402 ensure_global_variable_limit(&module, DEFAULT_MAX_GLOBALS)?;
403 ensure_parameter_limit(&module, DEFAULT_MAX_PARAMETER_COUNT)?;
404 ensure_valid_imports(&module)?;
405
406 let costs = RuledOpcodeCosts(wasm_config.v1().opcode_costs());
407 let module = casper_wasm_utils::externalize_mem(module, None, wasm_config.v1().max_memory());
408 let module = casper_wasm_utils::inject_gas_counter(module, &costs, DEFAULT_GAS_MODULE_NAME)
409 .map_err(|_| PreprocessingError::OperationForbiddenByGasRules)?;
410 let module = stack_height::inject_limiter(module, wasm_config.v1().max_stack_height())
411 .map_err(|_| PreprocessingError::StackLimiter)?;
412 Ok(module)
413}
414
415pub fn deserialize(module_bytes: &[u8]) -> Result<Module, PreprocessingError> {
417 casper_wasm::deserialize_buffer::<Module>(module_bytes).map_err(|deserialize_error| {
418 match deserialize_error {
419 casper_wasm::SerializationError::UnknownOpcode(BULK_OPCODE_PREFIX) => {
420 PreprocessingError::Deserialize(
421 "Bulk memory operations are not supported".to_string(),
422 )
423 }
424 casper_wasm::SerializationError::UnknownOpcode(SIMD_OPCODE_PREFIX) => {
425 PreprocessingError::Deserialize("SIMD operations are not supported".to_string())
426 }
427 casper_wasm::SerializationError::UnknownOpcode(ATOMIC_OPCODE_PREFIX) => {
428 PreprocessingError::Deserialize("Atomic operations are not supported".to_string())
429 }
430 casper_wasm::SerializationError::UnknownOpcode(_) => {
431 PreprocessingError::Deserialize("Encountered an unsupported operation".to_string())
432 }
433 casper_wasm::SerializationError::Other(
434 "Enable the multi_value feature to deserialize more than one function result",
435 ) => {
436 PreprocessingError::Deserialize(
443 "Multi value extension is not supported".to_string(),
444 )
445 }
446 _ => deserialize_error.into(),
447 }
448 })
449}
450
451pub fn get_module_from_entry_points(
453 entry_point_names: Vec<&str>,
454 mut module: Module,
455) -> Result<Vec<u8>, ExecError> {
456 let export_section = module
457 .export_section()
458 .ok_or_else(|| ExecError::FunctionNotFound(String::from("Missing Export Section")))?;
459
460 let maybe_missing_name: Option<String> = entry_point_names
461 .iter()
462 .find(|name| {
463 !export_section
464 .entries()
465 .iter()
466 .any(|export_entry| export_entry.field() == **name)
467 })
468 .map(|s| String::from(*s));
469
470 match maybe_missing_name {
471 Some(missing_name) => Err(ExecError::FunctionNotFound(missing_name)),
472 None => {
473 casper_wasm_utils::optimize(&mut module, entry_point_names)?;
474 casper_wasm::serialize(module).map_err(ExecError::ParityWasm)
475 }
476 }
477}
478
479pub fn cycles_for_instruction(instruction: &Instruction) -> u32 {
491 match instruction {
492 Instruction::Loop(_) => 1,
495 Instruction::Block(_) => 1,
496 Instruction::Else => 1,
497 Instruction::End => 1,
498
499 Instruction::Unreachable => 1,
500 Instruction::Nop => 1,
501
502 Instruction::If(_) => 3,
503
504 Instruction::Br(_) => 1,
507 Instruction::BrIf(_) => 3,
508 Instruction::BrTable(_) => 5,
509
510 Instruction::Return => 1,
511
512 Instruction::Call(_) => 22,
514 Instruction::CallIndirect(_, _) => 27,
515
516 Instruction::Drop => 1,
517
518 Instruction::Select => 11,
520
521 Instruction::GetLocal(_) | Instruction::SetLocal(_) | Instruction::TeeLocal(_) => 5,
522
523 Instruction::GetGlobal(_) => 7,
524 Instruction::SetGlobal(_) => 5,
525
526 Instruction::I64Load32S(_, _)
527 | Instruction::F32Load(_, _)
528 | Instruction::F64Load(_, _)
529 | Instruction::I32Load(_, _)
530 | Instruction::I64Load(_, _)
531 | Instruction::I32Load8S(_, _)
532 | Instruction::I64Load32U(_, _)
533 | Instruction::I64Load8U(_, _)
534 | Instruction::I64Load8S(_, _)
535 | Instruction::I32Load8U(_, _)
536 | Instruction::I64Load16U(_, _)
537 | Instruction::I32Load16U(_, _)
538 | Instruction::I64Load16S(_, _)
539 | Instruction::I32Load16S(_, _) => 8,
540
541 Instruction::I32Store(_, _)
542 | Instruction::I64Store(_, _)
543 | Instruction::F32Store(_, _)
544 | Instruction::F64Store(_, _)
545 | Instruction::I32Store8(_, _)
546 | Instruction::I32Store16(_, _)
547 | Instruction::I64Store8(_, _)
548 | Instruction::I64Store16(_, _)
549 | Instruction::I64Store32(_, _) => 4,
550
551 Instruction::CurrentMemory(_) => 5,
552 Instruction::GrowMemory(_) => 5,
553
554 Instruction::I32Const(_)
555 | Instruction::I64Const(_)
556 | Instruction::F32Const(_)
557 | Instruction::F64Const(_) => 5,
558
559 Instruction::I32Eqz
560 | Instruction::I32Eq
561 | Instruction::I32Ne
562 | Instruction::I32LtS
563 | Instruction::I32LtU
564 | Instruction::I32GtS
565 | Instruction::I32GtU
566 | Instruction::I32LeS
567 | Instruction::I32LeU
568 | Instruction::I32GeS
569 | Instruction::I32GeU
570 | Instruction::I64Eqz
571 | Instruction::I64Eq
572 | Instruction::I64Ne
573 | Instruction::I64LtS
574 | Instruction::I64LtU
575 | Instruction::I64GtS
576 | Instruction::I64GtU
577 | Instruction::I64LeS
578 | Instruction::I64LeU
579 | Instruction::I64GeS
580 | Instruction::I64GeU => 5,
581
582 Instruction::F32Eq
583 | Instruction::F32Ne
584 | Instruction::F32Lt
585 | Instruction::F32Gt
586 | Instruction::F32Le
587 | Instruction::F32Ge
588 | Instruction::F64Eq
589 | Instruction::F64Ne
590 | Instruction::F64Lt
591 | Instruction::F64Gt
592 | Instruction::F64Le
593 | Instruction::F64Ge => 5,
594
595 Instruction::I32Clz | Instruction::I32Ctz | Instruction::I32Popcnt => 5,
596
597 Instruction::I32Add | Instruction::I32Sub => 5,
598
599 Instruction::I32Mul => 5,
600
601 Instruction::I32DivS
602 | Instruction::I32DivU
603 | Instruction::I32RemS
604 | Instruction::I32RemU => 5,
605
606 Instruction::I32And
607 | Instruction::I32Or
608 | Instruction::I32Xor
609 | Instruction::I32Shl
610 | Instruction::I32ShrS
611 | Instruction::I32ShrU
612 | Instruction::I32Rotl
613 | Instruction::I32Rotr
614 | Instruction::I64Clz
615 | Instruction::I64Ctz
616 | Instruction::I64Popcnt => 5,
617
618 Instruction::I64Add | Instruction::I64Sub => 5,
619 Instruction::I64Mul => 5,
620
621 Instruction::I64DivS
622 | Instruction::I64DivU
623 | Instruction::I64RemS
624 | Instruction::I64RemU => 5,
625
626 Instruction::I64And
627 | Instruction::I64Or
628 | Instruction::I64Xor
629 | Instruction::I64Shl
630 | Instruction::I64ShrS
631 | Instruction::I64ShrU
632 | Instruction::I64Rotl
633 | Instruction::I64Rotr => 5,
634
635 Instruction::F32Abs
636 | Instruction::F32Neg
637 | Instruction::F32Ceil
638 | Instruction::F32Floor
639 | Instruction::F32Trunc
640 | Instruction::F32Nearest
641 | Instruction::F32Sqrt
642 | Instruction::F32Add
643 | Instruction::F32Sub
644 | Instruction::F32Mul
645 | Instruction::F32Div
646 | Instruction::F32Min
647 | Instruction::F32Max
648 | Instruction::F32Copysign
649 | Instruction::F64Abs
650 | Instruction::F64Neg
651 | Instruction::F64Ceil
652 | Instruction::F64Floor
653 | Instruction::F64Trunc
654 | Instruction::F64Nearest
655 | Instruction::F64Sqrt
656 | Instruction::F64Add
657 | Instruction::F64Sub
658 | Instruction::F64Mul
659 | Instruction::F64Div
660 | Instruction::F64Min
661 | Instruction::F64Max
662 | Instruction::F64Copysign => 5,
663
664 Instruction::I32WrapI64 | Instruction::I64ExtendSI32 | Instruction::I64ExtendUI32 => 5,
665
666 Instruction::F32ConvertSI32
667 | Instruction::F32ConvertUI32
668 | Instruction::F32ConvertSI64
669 | Instruction::F32ConvertUI64
670 | Instruction::F32DemoteF64
671 | Instruction::F64ConvertSI32
672 | Instruction::F64ConvertUI32
673 | Instruction::F64ConvertSI64
674 | Instruction::F64ConvertUI64
675 | Instruction::F64PromoteF32 => 5,
676
677 Instruction::I32ReinterpretF32
679 | Instruction::I64ReinterpretF64
680 | Instruction::F32ReinterpretI32
681 | Instruction::F64ReinterpretI64 => 5,
682
683 Instruction::SignExt(SignExtInstruction::I32Extend8S)
684 | Instruction::SignExt(SignExtInstruction::I32Extend16S)
685 | Instruction::SignExt(SignExtInstruction::I64Extend8S)
686 | Instruction::SignExt(SignExtInstruction::I64Extend16S)
687 | Instruction::SignExt(SignExtInstruction::I64Extend32S) => 5,
688
689 Instruction::I32TruncUF32 | Instruction::I64TruncSF32 => 40,
690
691 Instruction::I32TruncSF32 | Instruction::I64TruncUF32 => 42,
692
693 Instruction::I32TruncSF64
694 | Instruction::I32TruncUF64
695 | Instruction::I64TruncUF64
696 | Instruction::I64TruncSF64 => 195,
697 }
698}
699
700struct RuledOpcodeCosts(OpcodeCosts);
701
702impl RuledOpcodeCosts {
703 fn instruction_cost_multiplier(&self, instruction: &Instruction) -> Option<u32> {
705 let costs = self.0;
706
707 match instruction {
709 Instruction::Unreachable => Some(costs.unreachable),
710 Instruction::Nop => Some(costs.nop),
711
712 Instruction::Block(_) => Some(costs.control_flow.block),
714 Instruction::Loop(_) => Some(costs.control_flow.op_loop),
715 Instruction::If(_) => Some(costs.control_flow.op_if),
716 Instruction::Else => Some(costs.control_flow.op_else),
717 Instruction::End => Some(costs.control_flow.end),
718 Instruction::Br(_) => Some(costs.control_flow.br),
719 Instruction::BrIf(_) => Some(costs.control_flow.br_if),
720 Instruction::BrTable(br_table_data) => {
721 let br_table_size: u32 = br_table_data.table.len().try_into().ok()?;
725
726 let br_table_cost = costs.control_flow.br_table.cost;
727
728 let table_size_part =
729 br_table_size.checked_mul(costs.control_flow.br_table.size_multiplier)?;
730
731 let br_table_cost = br_table_cost.checked_add(table_size_part)?;
732 Some(br_table_cost)
733 }
734 Instruction::Return => Some(costs.control_flow.op_return),
735 Instruction::Call(_) => Some(costs.control_flow.call),
736 Instruction::CallIndirect(_, _) => Some(costs.control_flow.call_indirect),
737 Instruction::Drop => Some(costs.control_flow.drop),
738 Instruction::Select => Some(costs.control_flow.select),
739
740 Instruction::GetLocal(_) | Instruction::SetLocal(_) | Instruction::TeeLocal(_) => {
741 Some(costs.local)
742 }
743 Instruction::GetGlobal(_) | Instruction::SetGlobal(_) => Some(costs.global),
744
745 Instruction::I32Load(_, _)
746 | Instruction::I64Load(_, _)
747 | Instruction::F32Load(_, _)
748 | Instruction::F64Load(_, _)
749 | Instruction::I32Load8S(_, _)
750 | Instruction::I32Load8U(_, _)
751 | Instruction::I32Load16S(_, _)
752 | Instruction::I32Load16U(_, _)
753 | Instruction::I64Load8S(_, _)
754 | Instruction::I64Load8U(_, _)
755 | Instruction::I64Load16S(_, _)
756 | Instruction::I64Load16U(_, _)
757 | Instruction::I64Load32S(_, _)
758 | Instruction::I64Load32U(_, _) => Some(costs.load),
759
760 Instruction::I32Store(_, _)
761 | Instruction::I64Store(_, _)
762 | Instruction::F32Store(_, _)
763 | Instruction::F64Store(_, _)
764 | Instruction::I32Store8(_, _)
765 | Instruction::I32Store16(_, _)
766 | Instruction::I64Store8(_, _)
767 | Instruction::I64Store16(_, _)
768 | Instruction::I64Store32(_, _) => Some(costs.store),
769
770 Instruction::CurrentMemory(_) => Some(costs.current_memory),
771 Instruction::GrowMemory(_) => Some(costs.grow_memory),
772
773 Instruction::I32Const(_) | Instruction::I64Const(_) => Some(costs.op_const),
774
775 Instruction::F32Const(_) | Instruction::F64Const(_) => None, Instruction::I32Eqz
778 | Instruction::I32Eq
779 | Instruction::I32Ne
780 | Instruction::I32LtS
781 | Instruction::I32LtU
782 | Instruction::I32GtS
783 | Instruction::I32GtU
784 | Instruction::I32LeS
785 | Instruction::I32LeU
786 | Instruction::I32GeS
787 | Instruction::I32GeU
788 | Instruction::I64Eqz
789 | Instruction::I64Eq
790 | Instruction::I64Ne
791 | Instruction::I64LtS
792 | Instruction::I64LtU
793 | Instruction::I64GtS
794 | Instruction::I64GtU
795 | Instruction::I64LeS
796 | Instruction::I64LeU
797 | Instruction::I64GeS
798 | Instruction::I64GeU => Some(costs.integer_comparison),
799
800 Instruction::F32Eq
801 | Instruction::F32Ne
802 | Instruction::F32Lt
803 | Instruction::F32Gt
804 | Instruction::F32Le
805 | Instruction::F32Ge
806 | Instruction::F64Eq
807 | Instruction::F64Ne
808 | Instruction::F64Lt
809 | Instruction::F64Gt
810 | Instruction::F64Le
811 | Instruction::F64Ge => None, Instruction::I32Clz | Instruction::I32Ctz | Instruction::I32Popcnt => Some(costs.bit),
814
815 Instruction::I32Add | Instruction::I32Sub => Some(costs.add),
816
817 Instruction::I32Mul => Some(costs.mul),
818
819 Instruction::I32DivS
820 | Instruction::I32DivU
821 | Instruction::I32RemS
822 | Instruction::I32RemU => Some(costs.div),
823
824 Instruction::I32And
825 | Instruction::I32Or
826 | Instruction::I32Xor
827 | Instruction::I32Shl
828 | Instruction::I32ShrS
829 | Instruction::I32ShrU
830 | Instruction::I32Rotl
831 | Instruction::I32Rotr
832 | Instruction::I64Clz
833 | Instruction::I64Ctz
834 | Instruction::I64Popcnt => Some(costs.bit),
835
836 Instruction::I64Add | Instruction::I64Sub => Some(costs.add),
837 Instruction::I64Mul => Some(costs.mul),
838
839 Instruction::I64DivS
840 | Instruction::I64DivU
841 | Instruction::I64RemS
842 | Instruction::I64RemU => Some(costs.div),
843
844 Instruction::I64And
845 | Instruction::I64Or
846 | Instruction::I64Xor
847 | Instruction::I64Shl
848 | Instruction::I64ShrS
849 | Instruction::I64ShrU
850 | Instruction::I64Rotl
851 | Instruction::I64Rotr => Some(costs.bit),
852
853 Instruction::F32Abs
854 | Instruction::F32Neg
855 | Instruction::F32Ceil
856 | Instruction::F32Floor
857 | Instruction::F32Trunc
858 | Instruction::F32Nearest
859 | Instruction::F32Sqrt
860 | Instruction::F32Add
861 | Instruction::F32Sub
862 | Instruction::F32Mul
863 | Instruction::F32Div
864 | Instruction::F32Min
865 | Instruction::F32Max
866 | Instruction::F32Copysign
867 | Instruction::F64Abs
868 | Instruction::F64Neg
869 | Instruction::F64Ceil
870 | Instruction::F64Floor
871 | Instruction::F64Trunc
872 | Instruction::F64Nearest
873 | Instruction::F64Sqrt
874 | Instruction::F64Add
875 | Instruction::F64Sub
876 | Instruction::F64Mul
877 | Instruction::F64Div
878 | Instruction::F64Min
879 | Instruction::F64Max
880 | Instruction::F64Copysign => None, Instruction::I32WrapI64 | Instruction::I64ExtendSI32 | Instruction::I64ExtendUI32 => {
883 Some(costs.conversion)
884 }
885
886 Instruction::I32TruncSF32
887 | Instruction::I32TruncUF32
888 | Instruction::I32TruncSF64
889 | Instruction::I32TruncUF64
890 | Instruction::I64TruncSF32
891 | Instruction::I64TruncUF32
892 | Instruction::I64TruncSF64
893 | Instruction::I64TruncUF64
894 | Instruction::F32ConvertSI32
895 | Instruction::F32ConvertUI32
896 | Instruction::F32ConvertSI64
897 | Instruction::F32ConvertUI64
898 | Instruction::F32DemoteF64
899 | Instruction::F64ConvertSI32
900 | Instruction::F64ConvertUI32
901 | Instruction::F64ConvertSI64
902 | Instruction::F64ConvertUI64
903 | Instruction::F64PromoteF32 => None, Instruction::I32ReinterpretF32
907 | Instruction::I64ReinterpretF64
908 | Instruction::F32ReinterpretI32
909 | Instruction::F64ReinterpretI64 => None,
910
911 Instruction::SignExt(_) => Some(costs.sign),
912 }
913 }
914}
915
916impl Rules for RuledOpcodeCosts {
917 fn instruction_cost(&self, instruction: &Instruction) -> Option<u32> {
918 let cycles = cycles_for_instruction(instruction);
921
922 let multiplier = self.instruction_cost_multiplier(instruction)?;
924
925 cycles.checked_mul(multiplier)
926 }
927
928 fn memory_grow_cost(&self) -> Option<MemoryGrowCost> {
929 NonZeroU32::new(self.0.grow_memory).map(MemoryGrowCost::Linear)
930 }
931}
932
933#[cfg(test)]
934mod tests {
935 use casper_types::addressable_entity::DEFAULT_ENTRY_POINT_NAME;
936 use casper_wasm::{
937 builder,
938 elements::{CodeSection, Instructions},
939 };
940 use walrus::{FunctionBuilder, ModuleConfig, ValType};
941
942 use super::*;
943
944 #[test]
945 fn should_not_panic_on_empty_memory() {
946 const MODULE_BYTES_WITH_EMPTY_MEMORY: [u8; 61] = [
949 0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x01, 0x7f,
950 0x01, 0x7f, 0x60, 0x00, 0x00, 0x03, 0x03, 0x02, 0x00, 0x01, 0x05, 0x01, 0x00, 0x08,
951 0x01, 0x01, 0x0a, 0x1d, 0x02, 0x18, 0x00, 0x20, 0x00, 0x41, 0x80, 0x80, 0x82, 0x80,
952 0x78, 0x70, 0x41, 0x80, 0x82, 0x80, 0x80, 0x7e, 0x4f, 0x22, 0x00, 0x1a, 0x20, 0x00,
953 0x0f, 0x0b, 0x02, 0x00, 0x0b,
954 ];
955
956 match preprocess(WasmConfig::default(), &MODULE_BYTES_WITH_EMPTY_MEMORY).unwrap_err() {
957 PreprocessingError::MissingMemorySection => (),
958 error => panic!("expected MissingMemorySection, got {:?}", error),
959 }
960 }
961
962 #[test]
963 fn should_not_overflow_in_export_section() {
964 let module = builder::module()
965 .function()
966 .signature()
967 .build()
968 .body()
969 .with_instructions(Instructions::new(vec![Instruction::Nop, Instruction::End]))
970 .build()
971 .build()
972 .export()
973 .field(DEFAULT_ENTRY_POINT_NAME)
974 .internal()
975 .func(u32::MAX)
976 .build()
977 .memory()
979 .build()
980 .build();
981 let module_bytes = casper_wasm::serialize(module).expect("should serialize");
982 let error = preprocess(WasmConfig::default(), &module_bytes)
983 .expect_err("should fail with an error");
984 assert!(
985 matches!(
986 &error,
987 PreprocessingError::WasmValidation(WasmValidationError::MissingFunctionIndex { index: missing_index })
988 if *missing_index == u32::MAX
989 ),
990 "{:?}",
991 error,
992 );
993 }
994
995 #[test]
996 fn should_not_overflow_in_element_section() {
997 const CALL_FN_IDX: u32 = 0;
998
999 let module = builder::module()
1000 .function()
1001 .signature()
1002 .build()
1003 .body()
1004 .with_instructions(Instructions::new(vec![Instruction::Nop, Instruction::End]))
1005 .build()
1006 .build()
1007 .export()
1009 .field(DEFAULT_ENTRY_POINT_NAME)
1010 .internal()
1011 .func(CALL_FN_IDX)
1012 .build()
1013 .table()
1014 .with_element(u32::MAX, vec![u32::MAX])
1015 .build()
1016 .memory()
1018 .build()
1019 .build();
1020 let module_bytes = casper_wasm::serialize(module).expect("should serialize");
1021 let error = preprocess(WasmConfig::default(), &module_bytes)
1022 .expect_err("should fail with an error");
1023 assert!(
1024 matches!(
1025 &error,
1026 PreprocessingError::WasmValidation(WasmValidationError::MissingFunctionIndex { index: missing_index })
1027 if *missing_index == u32::MAX
1028 ),
1029 "{:?}",
1030 error,
1031 );
1032 }
1033
1034 #[test]
1035 fn should_not_overflow_in_call_opcode() {
1036 let module = builder::module()
1037 .function()
1038 .signature()
1039 .build()
1040 .body()
1041 .with_instructions(Instructions::new(vec![
1042 Instruction::Call(u32::MAX),
1043 Instruction::End,
1044 ]))
1045 .build()
1046 .build()
1047 .export()
1049 .field(DEFAULT_ENTRY_POINT_NAME)
1050 .build()
1051 .memory()
1054 .build()
1055 .build();
1056 let module_bytes = casper_wasm::serialize(module).expect("should serialize");
1057 let error = preprocess(WasmConfig::default(), &module_bytes)
1058 .expect_err("should fail with an error");
1059 assert!(
1060 matches!(
1061 &error,
1062 PreprocessingError::WasmValidation(WasmValidationError::MissingFunctionIndex { index: missing_index })
1063 if *missing_index == u32::MAX
1064 ),
1065 "{:?}",
1066 error,
1067 );
1068 }
1069
1070 #[test]
1071 fn should_not_overflow_in_start_section_without_code_section() {
1072 let module = builder::module()
1073 .with_section(Section::Start(u32::MAX))
1074 .memory()
1075 .build()
1076 .build();
1077 let module_bytes = casper_wasm::serialize(module).expect("should serialize");
1078
1079 let error = preprocess(WasmConfig::default(), &module_bytes)
1080 .expect_err("should fail with an error");
1081 assert!(
1082 matches!(
1083 &error,
1084 PreprocessingError::WasmValidation(WasmValidationError::MissingFunctionIndex { index: missing_index })
1085 if *missing_index == u32::MAX
1086 ),
1087 "{:?}",
1088 error,
1089 );
1090 }
1091
1092 #[test]
1093 fn should_not_overflow_in_start_section_with_code() {
1094 let module = builder::module()
1095 .with_section(Section::Start(u32::MAX))
1096 .with_section(Section::Code(CodeSection::with_bodies(Vec::new())))
1097 .memory()
1098 .build()
1099 .build();
1100 let module_bytes = casper_wasm::serialize(module).expect("should serialize");
1101 let error = preprocess(WasmConfig::default(), &module_bytes)
1102 .expect_err("should fail with an error");
1103 assert!(
1104 matches!(
1105 &error,
1106 PreprocessingError::WasmValidation(WasmValidationError::MissingFunctionIndex { index: missing_index })
1107 if *missing_index == u32::MAX
1108 ),
1109 "{:?}",
1110 error,
1111 );
1112 }
1113
1114 #[test]
1115 fn should_not_accept_multi_value_proposal_wasm() {
1116 let module_bytes = {
1117 let mut module = walrus::Module::with_config(ModuleConfig::new());
1118
1119 let _memory_id = module.memories.add_local(false, 11, None);
1120
1121 let mut func_with_locals =
1122 FunctionBuilder::new(&mut module.types, &[], &[ValType::I32, ValType::I64]);
1123
1124 func_with_locals.func_body().i64_const(0).i32_const(1);
1125
1126 let func_with_locals = func_with_locals.finish(vec![], &mut module.funcs);
1127
1128 let mut call_func = FunctionBuilder::new(&mut module.types, &[], &[]);
1129
1130 call_func.func_body().call(func_with_locals);
1131
1132 let call = call_func.finish(Vec::new(), &mut module.funcs);
1133
1134 module.exports.add(DEFAULT_ENTRY_POINT_NAME, call);
1135
1136 module.emit_wasm()
1137 };
1138 let error = preprocess(WasmConfig::default(), &module_bytes)
1139 .expect_err("should fail with an error");
1140 assert!(
1141 matches!(&error, PreprocessingError::Deserialize(msg)
1142 if msg == "Multi value extension is not supported"),
1143 "{:?}",
1144 error,
1145 );
1146 }
1147
1148 #[test]
1149 fn should_not_accept_atomics_proposal_wasm() {
1150 let module_bytes = {
1151 let mut module = walrus::Module::with_config(ModuleConfig::new());
1152
1153 let _memory_id = module.memories.add_local(false, 11, None);
1154
1155 let mut func_with_atomics = FunctionBuilder::new(&mut module.types, &[], &[]);
1156
1157 func_with_atomics.func_body().atomic_fence();
1158
1159 let func_with_atomics = func_with_atomics.finish(vec![], &mut module.funcs);
1160
1161 let mut call_func = FunctionBuilder::new(&mut module.types, &[], &[]);
1162
1163 call_func.func_body().call(func_with_atomics);
1164
1165 let call = call_func.finish(Vec::new(), &mut module.funcs);
1166
1167 module.exports.add(DEFAULT_ENTRY_POINT_NAME, call);
1168
1169 module.emit_wasm()
1170 };
1171 let error = preprocess(WasmConfig::default(), &module_bytes)
1172 .expect_err("should fail with an error");
1173 assert!(
1174 matches!(&error, PreprocessingError::Deserialize(msg)
1175 if msg == "Atomic operations are not supported"),
1176 "{:?}",
1177 error,
1178 );
1179 }
1180
1181 #[test]
1182 fn should_not_accept_bulk_proposal_wasm() {
1183 let module_bytes = {
1184 let mut module = walrus::Module::with_config(ModuleConfig::new());
1185
1186 let memory_id = module.memories.add_local(false, 11, None);
1187
1188 let mut func_with_bulk = FunctionBuilder::new(&mut module.types, &[], &[]);
1189
1190 func_with_bulk.func_body().memory_copy(memory_id, memory_id);
1191
1192 let func_with_bulk = func_with_bulk.finish(vec![], &mut module.funcs);
1193
1194 let mut call_func = FunctionBuilder::new(&mut module.types, &[], &[]);
1195
1196 call_func.func_body().call(func_with_bulk);
1197
1198 let call = call_func.finish(Vec::new(), &mut module.funcs);
1199
1200 module.exports.add(DEFAULT_ENTRY_POINT_NAME, call);
1201
1202 module.emit_wasm()
1203 };
1204 let error = preprocess(WasmConfig::default(), &module_bytes)
1205 .expect_err("should fail with an error");
1206 assert!(
1207 matches!(&error, PreprocessingError::Deserialize(msg)
1208 if msg == "Bulk memory operations are not supported"),
1209 "{:?}",
1210 error,
1211 );
1212 }
1213
1214 #[test]
1215 fn should_not_accept_simd_proposal_wasm() {
1216 let module_bytes = {
1217 let mut module = walrus::Module::with_config(ModuleConfig::new());
1218
1219 let _memory_id = module.memories.add_local(false, 11, None);
1220
1221 let mut func_with_simd = FunctionBuilder::new(&mut module.types, &[], &[]);
1222
1223 func_with_simd.func_body().v128_bitselect();
1224
1225 let func_with_simd = func_with_simd.finish(vec![], &mut module.funcs);
1226
1227 let mut call_func = FunctionBuilder::new(&mut module.types, &[], &[]);
1228
1229 call_func.func_body().call(func_with_simd);
1230
1231 let call = call_func.finish(Vec::new(), &mut module.funcs);
1232
1233 module.exports.add(DEFAULT_ENTRY_POINT_NAME, call);
1234
1235 module.emit_wasm()
1236 };
1237 let error = preprocess(WasmConfig::default(), &module_bytes)
1238 .expect_err("should fail with an error");
1239 assert!(
1240 matches!(&error, PreprocessingError::Deserialize(msg)
1241 if msg == "SIMD operations are not supported"),
1242 "{:?}",
1243 error,
1244 );
1245 }
1246}