1use itertools::Itertools;
7
8use crate::{
9 context::Context,
10 error::IrError,
11 function::Function,
12 instruction::{FuelVmInstruction, InstOp, Predicate},
13 irtype::Type,
14 metadata::{MetadataIndex, Metadatum},
15 printer,
16 value::{Value, ValueDatum},
17 variable::LocalVar,
18 AnalysisResult, AnalysisResultT, AnalysisResults, BinaryOpKind, Block, BlockArgument,
19 BranchToWithArgs, Doc, GlobalVar, Module, Pass, PassMutability, ScopedPass, TypeOption,
20 UnaryOpKind,
21};
22
23pub struct ModuleVerifierResult;
24impl AnalysisResultT for ModuleVerifierResult {}
25
26pub fn module_verifier(
28 context: &Context,
29 _analyses: &AnalysisResults,
30 module: Module,
31) -> Result<AnalysisResult, IrError> {
32 context.verify_module(module)?;
33 Ok(Box::new(ModuleVerifierResult))
34}
35
36pub const MODULE_VERIFIER_NAME: &str = "module-verifier";
37
38pub fn create_module_verifier_pass() -> Pass {
39 Pass {
40 name: MODULE_VERIFIER_NAME,
41 descr: "Verify module",
42 deps: vec![],
43 runner: ScopedPass::ModulePass(PassMutability::Analysis(module_verifier)),
44 }
45}
46
47impl Context<'_> {
48 pub fn verify(self) -> Result<Self, IrError> {
50 for (module, _) in &self.modules {
51 let module = Module(module);
52 self.verify_module(module)?;
53 }
54 Ok(self)
55 }
56
57 fn verify_module(&self, module: Module) -> Result<(), IrError> {
58 for function in module.function_iter(self) {
59 self.verify_function(module, function)?;
60 }
61
62 for global in &self.modules[module.0].global_variables {
64 if !global.1.is_mutable(self) && global.1.get_initializer(self).is_none() {
65 let global_name = module.lookup_global_variable_name(self, global.1);
66 return Err(IrError::VerifyGlobalMissingInitializer(
67 global_name.unwrap_or_else(|| "<unknown>".to_owned()),
68 ));
69 }
70 }
71 Ok(())
72 }
73
74 fn verify_function(&self, cur_module: Module, function: Function) -> Result<(), IrError> {
75 if function.get_module(self) != cur_module {
76 return Err(IrError::InconsistentParent(
77 function.get_name(self).into(),
78 format!("Module_Index_{:?}", cur_module.0),
79 format!("Module_Index_{:?}", function.get_module(self).0),
80 ));
81 }
82
83 let entry_block = function.get_entry_block(self);
84
85 if entry_block.num_predecessors(self) != 0 {
86 return Err(IrError::VerifyEntryBlockHasPredecessors(
87 function.get_name(self).to_string(),
88 entry_block
89 .pred_iter(self)
90 .map(|block| block.get_label(self))
91 .collect(),
92 ));
93 }
94
95 if function.num_args(self) != entry_block.num_args(self) {
97 return Err(IrError::VerifyBlockArgMalformed);
98 }
99 for ((_, func_arg), block_arg) in function.args_iter(self).zip(entry_block.arg_iter(self)) {
100 if func_arg != block_arg {
101 return Err(IrError::VerifyBlockArgMalformed);
102 }
103 }
104
105 for block in function.block_iter(self) {
118 self.verify_block(cur_module, function, block)?;
119 }
120 self.verify_metadata(function.get_metadata(self))?;
121 Ok(())
122 }
123
124 fn verify_block(
125 &self,
126 cur_module: Module,
127 cur_function: Function,
128 cur_block: Block,
129 ) -> Result<(), IrError> {
130 if cur_block.get_function(self) != cur_function {
131 return Err(IrError::InconsistentParent(
132 cur_block.get_label(self),
133 cur_function.get_name(self).into(),
134 cur_block.get_function(self).get_name(self).into(),
135 ));
136 }
137
138 if cur_block.num_instructions(self) <= 1 && cur_block.num_predecessors(self) == 0 {
139 return Ok(());
141 }
142
143 for (arg_idx, arg_val) in cur_block.arg_iter(self).enumerate() {
144 match self.values[arg_val.0].value {
145 ValueDatum::Argument(BlockArgument { idx, .. }) if idx == arg_idx => (),
146 _ => return Err(IrError::VerifyBlockArgMalformed),
147 }
148 }
149
150 let r = InstructionVerifier {
151 context: self,
152 cur_module,
153 cur_function,
154 cur_block,
155 }
156 .verify_instructions();
157
158 if let Err(error) = &r {
162 println!(
163 "Verification failed at {}::{}",
164 cur_function.get_name(self),
165 cur_block.get_label(self)
166 );
167
168 let block = if let Some(problematic_value) = error.get_problematic_value() {
169 printer::context_print(self, &|current_value: &Value, doc: Doc| {
170 if *current_value == *problematic_value {
171 doc.append(Doc::text_line(format!("\x1b[0;31m^ {}\x1b[0m", error)))
172 } else {
173 doc
174 }
175 })
176 } else {
177 printer::block_print(self, cur_function, cur_block, &|_, doc| doc)
178 };
179
180 println!("{}", block);
181 }
182
183 r?;
184
185 let (last_is_term, num_terms) =
186 cur_block
187 .instruction_iter(self)
188 .fold((false, 0), |(_, n), ins| {
189 if ins.is_terminator(self) {
190 (true, n + 1)
191 } else {
192 (false, n)
193 }
194 });
195 if !last_is_term {
196 Err(IrError::MissingTerminator(
197 cur_block.get_label(self).clone(),
198 ))
199 } else if num_terms != 1 {
200 Err(IrError::MisplacedTerminator(
201 cur_block.get_label(self).clone(),
202 ))
203 } else {
204 Ok(())
205 }
206 }
207
208 fn verify_metadata(&self, md_idx: Option<MetadataIndex>) -> Result<(), IrError> {
209 if let Some(md_idx) = md_idx {
211 match &self.metadata[md_idx.0] {
212 Metadatum::List(md_idcs) => {
213 for md_idx in md_idcs {
214 self.verify_metadata(Some(*md_idx))?;
215 }
216 }
217 Metadatum::Struct(tag, ..) => {
218 if tag.is_empty() {
221 return Err(IrError::InvalidMetadatum(
222 "Struct has empty tag.".to_owned(),
223 ));
224 }
225 let mut chs = tag.chars();
226 let ch0 = chs.next().unwrap();
227 if !(ch0.is_ascii_alphabetic() || ch0 == '_')
228 || chs.any(|ch| !(ch.is_ascii_alphanumeric() || ch == '_'))
229 {
230 return Err(IrError::InvalidMetadatum(format!(
231 "Invalid struct tag: '{tag}'."
232 )));
233 }
234 }
235 _otherwise => (),
236 }
237 }
238 Ok(())
239 }
240}
241
242struct InstructionVerifier<'a, 'eng> {
243 context: &'a Context<'eng>,
244 cur_module: Module,
245 cur_function: Function,
246 cur_block: Block,
247}
248
249impl InstructionVerifier<'_, '_> {
250 fn verify_instructions(&self) -> Result<(), IrError> {
251 for ins in self.cur_block.instruction_iter(self.context) {
252 let value_content = &self.context.values[ins.0];
253 let ValueDatum::Instruction(instruction) = &value_content.value else {
254 unreachable!("The value must be an instruction, because it is retrieved via block instruction iterator.")
255 };
256
257 if instruction.parent != self.cur_block {
258 return Err(IrError::InconsistentParent(
259 format!("Instr_{:?}", ins.0),
260 self.cur_block.get_label(self.context),
261 instruction.parent.get_label(self.context),
262 ));
263 }
264
265 match &instruction.op {
266 InstOp::AsmBlock(..) => (),
267 InstOp::BitCast(value, ty) => self.verify_bitcast(value, ty)?,
268 InstOp::UnaryOp { op, arg } => self.verify_unary_op(op, arg)?,
269 InstOp::BinaryOp { op, arg1, arg2 } => self.verify_binary_op(op, arg1, arg2)?,
270 InstOp::Branch(block) => self.verify_br(block)?,
271 InstOp::Call(func, args) => self.verify_call(func, args)?,
272 InstOp::CastPtr(val, ty) => self.verify_cast_ptr(val, ty)?,
273 InstOp::Cmp(pred, lhs_value, rhs_value) => {
274 self.verify_cmp(pred, lhs_value, rhs_value)?
275 }
276 InstOp::ConditionalBranch {
277 cond_value,
278 true_block,
279 false_block,
280 } => self.verify_cbr(cond_value, true_block, false_block)?,
281 InstOp::ContractCall {
282 params,
283 coins,
284 asset_id,
285 gas,
286 ..
287 } => self.verify_contract_call(params, coins, asset_id, gas)?,
288 InstOp::FuelVm(fuel_vm_instr) => match fuel_vm_instr {
290 FuelVmInstruction::Gtf { index, tx_field_id } => {
291 self.verify_gtf(index, tx_field_id)?
292 }
293 FuelVmInstruction::Log {
294 log_val,
295 log_ty,
296 log_id,
297 } => self.verify_log(log_val, log_ty, log_id)?,
298 FuelVmInstruction::ReadRegister(_) => (),
299 FuelVmInstruction::JmpMem => (),
300 FuelVmInstruction::Revert(val) => self.verify_revert(val)?,
301 FuelVmInstruction::Smo {
302 recipient,
303 message,
304 message_size,
305 coins,
306 } => self.verify_smo(recipient, message, message_size, coins)?,
307 FuelVmInstruction::StateClear {
308 key,
309 number_of_slots,
310 } => self.verify_state_clear(key, number_of_slots)?,
311 FuelVmInstruction::StateLoadWord(key) => self.verify_state_load_word(key)?,
312 FuelVmInstruction::StateLoadQuadWord {
313 load_val: dst_val,
314 key,
315 number_of_slots,
316 }
317 | FuelVmInstruction::StateStoreQuadWord {
318 stored_val: dst_val,
319 key,
320 number_of_slots,
321 } => self.verify_state_access_quad(dst_val, key, number_of_slots)?,
322 FuelVmInstruction::StateStoreWord {
323 stored_val: dst_val,
324 key,
325 } => self.verify_state_store_word(dst_val, key)?,
326 FuelVmInstruction::WideUnaryOp { op, result, arg } => {
327 self.verify_wide_unary_op(op, result, arg)?
328 }
329 FuelVmInstruction::WideBinaryOp {
330 op,
331 result,
332 arg1,
333 arg2,
334 } => self.verify_wide_binary_op(op, result, arg1, arg2)?,
335 FuelVmInstruction::WideModularOp {
336 op,
337 result,
338 arg1,
339 arg2,
340 arg3,
341 } => self.verify_wide_modular_op(op, result, arg1, arg2, arg3)?,
342 FuelVmInstruction::WideCmpOp { op, arg1, arg2 } => {
343 self.verify_wide_cmp(op, arg1, arg2)?
344 }
345 FuelVmInstruction::Retd { .. } => (),
346 },
347 InstOp::GetElemPtr {
348 base,
349 elem_ptr_ty,
350 indices,
351 } => self.verify_get_elem_ptr(&ins, base, elem_ptr_ty, indices)?,
352 InstOp::GetLocal(local_var) => self.verify_get_local(local_var)?,
353 InstOp::GetGlobal(global_var) => self.verify_get_global(global_var)?,
354 InstOp::GetConfig(_, name) => self.verify_get_config(self.cur_module, name)?,
355 InstOp::IntToPtr(value, ty) => self.verify_int_to_ptr(value, ty)?,
356 InstOp::Load(ptr) => self.verify_load(ptr)?,
357 InstOp::MemCopyBytes {
358 dst_val_ptr,
359 src_val_ptr,
360 byte_len,
361 } => self.verify_mem_copy_bytes(dst_val_ptr, src_val_ptr, byte_len)?,
362 InstOp::MemCopyVal {
363 dst_val_ptr,
364 src_val_ptr,
365 } => self.verify_mem_copy_val(dst_val_ptr, src_val_ptr)?,
366 InstOp::Nop => (),
367 InstOp::PtrToInt(val, ty) => self.verify_ptr_to_int(val, ty)?,
368 InstOp::Ret(val, ty) => self.verify_ret(val, ty)?,
369 InstOp::Store {
370 dst_val_ptr,
371 stored_val,
372 } => self.verify_store(&ins, dst_val_ptr, stored_val)?,
373 };
374
375 self.context.verify_metadata(value_content.metadata)?;
377 }
378
379 Ok(())
380 }
381
382 fn verify_bitcast(&self, value: &Value, ty: &Type) -> Result<(), IrError> {
383 let val_ty = value
387 .get_type(self.context)
388 .ok_or(IrError::VerifyBitcastUnknownSourceType)?;
389 if self.type_bit_size(&val_ty).is_some_and(|sz| sz > 64)
390 || self.type_bit_size(ty).is_some_and(|sz| sz > 64)
391 {
392 Err(IrError::VerifyBitcastBetweenInvalidTypes(
393 val_ty.as_string(self.context),
394 ty.as_string(self.context),
395 ))
396 } else {
397 Ok(())
398 }
399 }
400
401 fn verify_unary_op(&self, op: &UnaryOpKind, arg: &Value) -> Result<(), IrError> {
402 let arg_ty = arg
403 .get_type(self.context)
404 .ok_or(IrError::VerifyUnaryOpIncorrectArgType)?;
405 match op {
406 UnaryOpKind::Not => {
407 if !arg_ty.is_uint(self.context) && !arg_ty.is_b256(self.context) {
408 return Err(IrError::VerifyUnaryOpIncorrectArgType);
409 }
410 }
411 }
412
413 Ok(())
414 }
415
416 fn verify_wide_cmp(&self, _: &Predicate, arg1: &Value, arg2: &Value) -> Result<(), IrError> {
417 let arg1_ty = arg1
418 .get_type(self.context)
419 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
420 let arg2_ty = arg2
421 .get_type(self.context)
422 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
423
424 if arg1_ty.is_ptr(self.context) && arg2_ty.is_ptr(self.context) {
425 Ok(())
426 } else {
427 Err(IrError::VerifyBinaryOpIncorrectArgType)
428 }
429 }
430
431 fn verify_wide_modular_op(
432 &self,
433 _op: &BinaryOpKind,
434 result: &Value,
435 arg1: &Value,
436 arg2: &Value,
437 arg3: &Value,
438 ) -> Result<(), IrError> {
439 let result_ty = result
440 .get_type(self.context)
441 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
442 let arg1_ty = arg1
443 .get_type(self.context)
444 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
445 let arg2_ty = arg2
446 .get_type(self.context)
447 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
448 let arg3_ty = arg3
449 .get_type(self.context)
450 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
451
452 if !arg1_ty.is_ptr(self.context)
453 || !arg2_ty.is_ptr(self.context)
454 || !arg3_ty.is_ptr(self.context)
455 || !result_ty.is_ptr(self.context)
456 {
457 return Err(IrError::VerifyBinaryOpIncorrectArgType);
458 }
459
460 Ok(())
461 }
462
463 fn verify_wide_binary_op(
464 &self,
465 op: &BinaryOpKind,
466 result: &Value,
467 arg1: &Value,
468 arg2: &Value,
469 ) -> Result<(), IrError> {
470 let result_ty = result
471 .get_type(self.context)
472 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
473 let arg1_ty = arg1
474 .get_type(self.context)
475 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
476 let arg2_ty = arg2
477 .get_type(self.context)
478 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
479
480 match op {
481 BinaryOpKind::Lsh | BinaryOpKind::Rsh => {
483 if !arg1_ty.is_ptr(self.context)
484 || !arg2_ty.is_uint64(self.context)
485 || !result_ty.is_ptr(self.context)
486 {
487 return Err(IrError::VerifyBinaryOpIncorrectArgType);
488 }
489 }
490 BinaryOpKind::Add
491 | BinaryOpKind::Sub
492 | BinaryOpKind::Mul
493 | BinaryOpKind::Div
494 | BinaryOpKind::And
495 | BinaryOpKind::Or
496 | BinaryOpKind::Xor
497 | BinaryOpKind::Mod => {
498 if !arg1_ty.is_ptr(self.context)
499 || !arg2_ty.is_ptr(self.context)
500 || !result_ty.is_ptr(self.context)
501 {
502 return Err(IrError::VerifyBinaryOpIncorrectArgType);
503 }
504 }
505 }
506
507 Ok(())
508 }
509
510 fn verify_wide_unary_op(
511 &self,
512 _op: &UnaryOpKind,
513 result: &Value,
514 arg: &Value,
515 ) -> Result<(), IrError> {
516 let result_ty = result
517 .get_type(self.context)
518 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
519 let arg_ty = arg
520 .get_type(self.context)
521 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
522
523 if !arg_ty.is_ptr(self.context) || !result_ty.is_ptr(self.context) {
524 return Err(IrError::VerifyBinaryOpIncorrectArgType);
525 }
526
527 Ok(())
528 }
529
530 fn verify_binary_op(
531 &self,
532 op: &BinaryOpKind,
533 arg1: &Value,
534 arg2: &Value,
535 ) -> Result<(), IrError> {
536 let arg1_ty = arg1
537 .get_type(self.context)
538 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
539 let arg2_ty = arg2
540 .get_type(self.context)
541 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
542
543 match op {
544 BinaryOpKind::Lsh | BinaryOpKind::Rsh => {
546 let is_lhs_ok = arg1_ty.is_uint(self.context) || arg1_ty.is_b256(self.context);
547 if !is_lhs_ok || !arg2_ty.is_uint(self.context) {
548 return Err(IrError::VerifyBinaryOpIncorrectArgType);
549 }
550 }
551 BinaryOpKind::Add => {
552 if !(arg1_ty.eq(self.context, &arg2_ty) && arg1_ty.is_uint(self.context)
553 || arg1_ty.is_ptr(self.context) && arg2_ty.is_uint64(self.context))
554 {
555 return Err(IrError::VerifyBinaryOpIncorrectArgType);
556 }
557 }
558 BinaryOpKind::Sub | BinaryOpKind::Mul | BinaryOpKind::Div | BinaryOpKind::Mod => {
559 if !arg1_ty.eq(self.context, &arg2_ty) || !arg1_ty.is_uint(self.context) {
560 return Err(IrError::VerifyBinaryOpIncorrectArgType);
561 }
562 }
563 BinaryOpKind::And | BinaryOpKind::Or | BinaryOpKind::Xor => {
564 if !arg1_ty.eq(self.context, &arg2_ty)
565 || !(arg1_ty.is_uint(self.context) || arg1_ty.is_b256(self.context))
566 {
567 return Err(IrError::VerifyBinaryOpIncorrectArgType);
568 }
569 }
570 }
571
572 Ok(())
573 }
574
575 fn verify_br(&self, dest_block: &BranchToWithArgs) -> Result<(), IrError> {
576 if !self
577 .cur_function
578 .block_iter(self.context)
579 .contains(&dest_block.block)
580 {
581 Err(IrError::VerifyBranchToMissingBlock(
582 self.context.blocks[dest_block.block.0].label.clone(),
583 ))
584 } else {
585 self.verify_dest_args(dest_block)
586 }
587 }
588
589 fn verify_call(&self, callee: &Function, args: &[Value]) -> Result<(), IrError> {
590 let callee_content = &self.context.functions[callee.0];
591 if !self.cur_module.function_iter(self.context).contains(callee) {
592 return Err(IrError::VerifyCallToMissingFunction(
593 callee_content.name.clone(),
594 ));
595 }
596
597 let callee_arg_types = callee_content
598 .arguments
599 .iter()
600 .map(|(_, arg_val)| {
601 if let ValueDatum::Argument(BlockArgument { ty, .. }) =
602 &self.context.values[arg_val.0].value
603 {
604 Ok(*ty)
605 } else {
606 Err(IrError::VerifyArgumentValueIsNotArgument(
607 callee_content.name.clone(),
608 ))
609 }
610 })
611 .collect::<Result<Vec<Type>, IrError>>()?;
612
613 for (opt_caller_arg_type, callee_arg_type) in args
614 .iter()
615 .map(|val| val.get_type(self.context))
616 .zip(callee_arg_types.iter())
617 {
618 if opt_caller_arg_type.is_none() {
619 return Err(IrError::VerifyUntypedValuePassedToFunction);
620 }
621
622 let caller_arg_type = opt_caller_arg_type.as_ref().unwrap();
623 if !caller_arg_type.eq(self.context, callee_arg_type) {
624 return Err(IrError::VerifyCallArgTypeMismatch(
625 callee_content.name.clone(),
626 caller_arg_type.as_string(self.context),
627 callee_arg_type.as_string(self.context),
628 ));
629 }
630 }
631
632 Ok(())
633 }
634
635 fn verify_cast_ptr(&self, val: &Value, ty: &Type) -> Result<(), IrError> {
636 let _ = self.get_ptr_type(val, IrError::VerifyPtrCastFromNonPointer)?;
637 if !ty.is_ptr(self.context) {
638 Err(IrError::VerifyPtrCastToNonPointer(
639 ty.as_string(self.context),
640 ))
641 } else {
642 Ok(())
643 }
644 }
645
646 fn verify_dest_args(&self, dest: &BranchToWithArgs) -> Result<(), IrError> {
647 if dest.block.num_args(self.context) != dest.args.len() {
648 return Err(IrError::VerifyBranchParamsMismatch);
649 }
650 for (arg_idx, dest_param) in dest.block.arg_iter(self.context).enumerate() {
651 match dest.args.get(arg_idx) {
652 Some(actual)
653 if dest_param
654 .get_type(self.context)
655 .unwrap()
656 .eq(self.context, &actual.get_type(self.context).unwrap()) => {}
657 _ =>
658 {
660 }
662 }
663 }
664 Ok(())
665 }
666
667 fn verify_cbr(
668 &self,
669 cond_val: &Value,
670 true_block: &BranchToWithArgs,
671 false_block: &BranchToWithArgs,
672 ) -> Result<(), IrError> {
673 if !cond_val
674 .get_type(self.context)
675 .is(Type::is_bool, self.context)
676 {
677 Err(IrError::VerifyConditionExprNotABool)
678 } else if !self
679 .cur_function
680 .block_iter(self.context)
681 .contains(&true_block.block)
682 {
683 Err(IrError::VerifyBranchToMissingBlock(
684 self.context.blocks[true_block.block.0].label.clone(),
685 ))
686 } else if !self
687 .cur_function
688 .block_iter(self.context)
689 .contains(&false_block.block)
690 {
691 Err(IrError::VerifyBranchToMissingBlock(
692 self.context.blocks[false_block.block.0].label.clone(),
693 ))
694 } else {
695 self.verify_dest_args(true_block)
696 .and_then(|()| self.verify_dest_args(false_block))
697 }
698 }
699
700 fn verify_cmp(
701 &self,
702 _pred: &Predicate,
703 lhs_value: &Value,
704 rhs_value: &Value,
705 ) -> Result<(), IrError> {
706 match (
708 lhs_value.get_type(self.context),
709 rhs_value.get_type(self.context),
710 ) {
711 (Some(lhs_ty), Some(rhs_ty)) => {
712 if !lhs_ty.eq(self.context, &rhs_ty) {
713 Err(IrError::VerifyCmpTypeMismatch(
714 lhs_ty.as_string(self.context),
715 rhs_ty.as_string(self.context),
716 ))
717 } else if lhs_ty.is_bool(self.context)
718 || lhs_ty.is_uint(self.context)
719 || lhs_ty.is_b256(self.context)
720 {
721 Ok(())
722 } else {
723 Err(IrError::VerifyCmpBadTypes(
724 lhs_ty.as_string(self.context),
725 rhs_ty.as_string(self.context),
726 ))
727 }
728 }
729 _otherwise => Err(IrError::VerifyCmpUnknownTypes),
730 }
731 }
732
733 fn verify_contract_call(
734 &self,
735 params: &Value,
736 coins: &Value,
737 asset_id: &Value,
738 gas: &Value,
739 ) -> Result<(), IrError> {
740 if !self.context.experimental.new_encoding {
741 let fields = params
746 .get_type(self.context)
747 .and_then(|ty| ty.get_pointee_type(self.context))
748 .map_or_else(std::vec::Vec::new, |ty| ty.get_field_types(self.context));
749 if fields.len() != 3
750 || !fields[0].is_b256(self.context)
751 || !fields[1].is_uint64(self.context)
752 || !fields[2].is_uint64(self.context)
753 {
754 Err(IrError::VerifyContractCallBadTypes("params".to_owned()))
755 } else {
756 Ok(())
757 }
758 .and_then(|_| {
759 if coins
760 .get_type(self.context)
761 .is(Type::is_uint64, self.context)
762 {
763 Ok(())
764 } else {
765 Err(IrError::VerifyContractCallBadTypes("coins".to_owned()))
766 }
767 })
768 .and_then(|_| {
769 if asset_id
770 .get_type(self.context)
771 .and_then(|ty| ty.get_pointee_type(self.context))
772 .is(Type::is_b256, self.context)
773 {
774 Ok(())
775 } else {
776 Err(IrError::VerifyContractCallBadTypes("asset_id".to_owned()))
777 }
778 })
779 .and_then(|_| {
780 if gas.get_type(self.context).is(Type::is_uint64, self.context) {
781 Ok(())
782 } else {
783 Err(IrError::VerifyContractCallBadTypes("gas".to_owned()))
784 }
785 })
786 } else {
787 Ok(())
788 }
789 }
790
791 fn verify_get_elem_ptr(
792 &self,
793 ins: &Value,
794 base: &Value,
795 elem_ptr_ty: &Type,
796 indices: &[Value],
797 ) -> Result<(), IrError> {
798 let base_ty =
799 self.get_ptr_type(base, |s| IrError::VerifyGepFromNonPointer(s, Some(*ins)))?;
800 if !base_ty.is_aggregate(self.context) {
801 return Err(IrError::VerifyGepOnNonAggregate);
802 }
803
804 let Some(elem_inner_ty) = elem_ptr_ty.get_pointee_type(self.context) else {
805 return Err(IrError::VerifyGepElementTypeNonPointer);
806 };
807
808 if indices.is_empty() {
809 return Err(IrError::VerifyGepInconsistentTypes(
810 "Empty Indices".into(),
811 Some(*base),
812 ));
813 }
814
815 let index_ty = base_ty.get_value_indexed_type(self.context, indices);
816
817 if self.opt_ty_not_eq(&Some(elem_inner_ty), &index_ty) {
818 return Err(IrError::VerifyGepInconsistentTypes(
819 format!(
820 "Element type \"{}\" versus index type {:?}",
821 elem_inner_ty.as_string(self.context),
822 index_ty.map(|x| x.as_string(self.context))
823 ),
824 Some(*ins),
825 ));
826 }
827
828 Ok(())
829 }
830
831 fn verify_get_local(&self, local_var: &LocalVar) -> Result<(), IrError> {
832 if !self.context.functions[self.cur_function.0]
833 .local_storage
834 .values()
835 .any(|var| var == local_var)
836 {
837 Err(IrError::VerifyGetNonExistentPointer)
838 } else {
839 Ok(())
840 }
841 }
842
843 fn verify_get_global(&self, global_var: &GlobalVar) -> Result<(), IrError> {
844 if !self.context.modules[self.cur_module.0]
845 .global_variables
846 .values()
847 .any(|var| var == global_var)
848 {
849 Err(IrError::VerifyGetNonExistentPointer)
850 } else {
851 Ok(())
852 }
853 }
854
855 fn verify_get_config(&self, module: Module, name: &str) -> Result<(), IrError> {
856 if !self.context.modules[module.0].configs.contains_key(name) {
857 Err(IrError::VerifyGetNonExistentPointer)
858 } else {
859 Ok(())
860 }
861 }
862
863 fn verify_gtf(&self, index: &Value, _tx_field_id: &u64) -> Result<(), IrError> {
864 if !index.get_type(self.context).is(Type::is_uint, self.context) {
866 Err(IrError::VerifyInvalidGtfIndexType)
867 } else {
868 Ok(())
869 }
870 }
871
872 fn verify_int_to_ptr(&self, value: &Value, ty: &Type) -> Result<(), IrError> {
873 let val_ty = value
875 .get_type(self.context)
876 .ok_or(IrError::VerifyIntToPtrUnknownSourceType)?;
877 if !val_ty.is_uint(self.context) {
878 return Err(IrError::VerifyIntToPtrFromNonIntegerType(
879 val_ty.as_string(self.context),
880 ));
881 }
882 if !ty.is_ptr(self.context) {
883 return Err(IrError::VerifyIntToPtrToNonPointer(
884 ty.as_string(self.context),
885 ));
886 }
887
888 Ok(())
889 }
890
891 fn verify_load(&self, src_val: &Value) -> Result<(), IrError> {
892 self.get_ptr_type(src_val, IrError::VerifyLoadFromNonPointer)
894 .map(|_| ())
895 }
896
897 fn verify_log(&self, log_val: &Value, log_ty: &Type, log_id: &Value) -> Result<(), IrError> {
898 if !log_id
899 .get_type(self.context)
900 .is(Type::is_uint64, self.context)
901 {
902 return Err(IrError::VerifyLogId);
903 }
904
905 if self.opt_ty_not_eq(&log_val.get_type(self.context), &Some(*log_ty)) {
906 return Err(IrError::VerifyLogMismatchedTypes);
907 }
908
909 Ok(())
910 }
911
912 fn verify_mem_copy_bytes(
913 &self,
914 dst_val_ptr: &Value,
915 src_val_ptr: &Value,
916 _byte_len: &u64,
917 ) -> Result<(), IrError> {
918 self.get_ptr_type(dst_val_ptr, IrError::VerifyMemcopyNonPointer)
920 .and_then(|_| self.get_ptr_type(src_val_ptr, IrError::VerifyMemcopyNonPointer))
921 .map(|_| ())
922 }
923
924 fn verify_mem_copy_val(&self, dst_val_ptr: &Value, src_val_ptr: &Value) -> Result<(), IrError> {
925 self.get_ptr_type(dst_val_ptr, IrError::VerifyMemcopyNonPointer)
927 .and_then(|dst_ty| {
928 self.get_ptr_type(src_val_ptr, IrError::VerifyMemcopyNonPointer)
929 .map(|src_ty| (dst_ty, src_ty))
930 })
931 .and_then(|(dst_ty, src_ty)| {
932 dst_ty
933 .eq(self.context, &src_ty)
934 .then_some(())
935 .ok_or_else(|| {
936 IrError::VerifyMemcopyMismatchedTypes(
937 dst_ty.as_string(self.context),
938 src_ty.as_string(self.context),
939 )
940 })
941 })
942 }
943
944 fn verify_ptr_to_int(&self, _val: &Value, ty: &Type) -> Result<(), IrError> {
945 if !ty.is_uint(self.context) {
951 Err(IrError::VerifyPtrToIntToNonInteger(
952 ty.as_string(self.context),
953 ))
954 } else {
955 Ok(())
956 }
957 }
958
959 fn verify_ret(&self, val: &Value, ty: &Type) -> Result<(), IrError> {
960 if !self
961 .cur_function
962 .get_return_type(self.context)
963 .eq(self.context, ty)
964 || self.opt_ty_not_eq(&val.get_type(self.context), &Some(*ty))
965 {
966 Err(IrError::VerifyReturnMismatchedTypes(
967 self.cur_function.get_name(self.context).to_string(),
968 ))
969 } else {
970 Ok(())
971 }
972 }
973
974 fn verify_revert(&self, val: &Value) -> Result<(), IrError> {
975 if !val.get_type(self.context).is(Type::is_uint64, self.context) {
976 Err(IrError::VerifyRevertCodeBadType)
977 } else {
978 Ok(())
979 }
980 }
981
982 fn verify_smo(
983 &self,
984 recipient: &Value,
985 message: &Value,
986 message_size: &Value,
987 coins: &Value,
988 ) -> Result<(), IrError> {
989 let recipient = self.get_ptr_type(recipient, IrError::VerifySmoRecipientNonPointer)?;
991 if !recipient.is_b256(self.context) {
992 return Err(IrError::VerifySmoRecipientBadType);
993 }
994
995 let struct_ty = self.get_ptr_type(message, IrError::VerifySmoMessageNonPointer)?;
997
998 if !struct_ty.is_struct(self.context) {
999 return Err(IrError::VerifySmoBadMessageType);
1000 }
1001 let fields = struct_ty.get_field_types(self.context);
1002 if fields.len() != 2 {
1003 return Err(IrError::VerifySmoBadMessageType);
1004 }
1005
1006 if !message_size
1008 .get_type(self.context)
1009 .is(Type::is_uint64, self.context)
1010 {
1011 return Err(IrError::VerifySmoMessageSize);
1012 }
1013
1014 if !coins
1016 .get_type(self.context)
1017 .is(Type::is_uint64, self.context)
1018 {
1019 return Err(IrError::VerifySmoCoins);
1020 }
1021
1022 Ok(())
1023 }
1024
1025 fn verify_state_clear(&self, key: &Value, number_of_slots: &Value) -> Result<(), IrError> {
1026 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1027 if !key_type.is_b256(self.context) {
1028 Err(IrError::VerifyStateKeyBadType)
1029 } else if !number_of_slots
1030 .get_type(self.context)
1031 .is(Type::is_uint, self.context)
1032 {
1033 Err(IrError::VerifyStateAccessNumOfSlots)
1034 } else {
1035 Ok(())
1036 }
1037 }
1038
1039 fn verify_state_access_quad(
1040 &self,
1041 dst_val: &Value,
1042 key: &Value,
1043 number_of_slots: &Value,
1044 ) -> Result<(), IrError> {
1045 let dst_ty = self.get_ptr_type(dst_val, IrError::VerifyStateAccessQuadNonPointer)?;
1046 if !dst_ty.is_b256(self.context) {
1047 return Err(IrError::VerifyStateDestBadType(
1048 dst_ty.as_string(self.context),
1049 ));
1050 }
1051 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1052 if !key_type.is_b256(self.context) {
1053 return Err(IrError::VerifyStateKeyBadType);
1054 }
1055 if !number_of_slots
1056 .get_type(self.context)
1057 .is(Type::is_uint, self.context)
1058 {
1059 return Err(IrError::VerifyStateAccessNumOfSlots);
1060 }
1061 Ok(())
1062 }
1063
1064 fn verify_state_load_word(&self, key: &Value) -> Result<(), IrError> {
1065 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1066 if !key_type.is_b256(self.context) {
1067 Err(IrError::VerifyStateKeyBadType)
1068 } else {
1069 Ok(())
1070 }
1071 }
1072
1073 fn verify_state_store_word(&self, dst_val: &Value, key: &Value) -> Result<(), IrError> {
1074 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1075 if !key_type.is_b256(self.context) {
1076 Err(IrError::VerifyStateKeyBadType)
1077 } else if !dst_val
1078 .get_type(self.context)
1079 .is(Type::is_uint, self.context)
1080 {
1081 Err(IrError::VerifyStateDestBadType(
1082 Type::get_uint64(self.context).as_string(self.context),
1083 ))
1084 } else {
1085 Ok(())
1086 }
1087 }
1088
1089 fn verify_store(
1090 &self,
1091 ins: &Value,
1092 dst_val: &Value,
1093 stored_val: &Value,
1094 ) -> Result<(), IrError> {
1095 let dst_ty = self.get_ptr_type(dst_val, IrError::VerifyStoreToNonPointer)?;
1096 let stored_ty = stored_val.get_type(self.context);
1097 if self.opt_ty_not_eq(&Some(dst_ty), &stored_ty) {
1098 Err(IrError::VerifyStoreMismatchedTypes(Some(*ins)))
1099 } else {
1100 Ok(())
1101 }
1102 }
1103
1104 fn opt_ty_not_eq(&self, l_ty: &Option<Type>, r_ty: &Option<Type>) -> bool {
1109 l_ty.is_none() || r_ty.is_none() || !l_ty.unwrap().eq(self.context, r_ty.as_ref().unwrap())
1110 }
1111
1112 fn get_ptr_type<F: FnOnce(String) -> IrError>(
1113 &self,
1114 val: &Value,
1115 errfn: F,
1116 ) -> Result<Type, IrError> {
1117 val.get_type(self.context)
1118 .ok_or_else(|| "unknown".to_owned())
1119 .and_then(|ptr_ty| {
1120 ptr_ty
1121 .get_pointee_type(self.context)
1122 .ok_or_else(|| ptr_ty.as_string(self.context))
1123 })
1124 .map_err(errfn)
1125 }
1126
1127 fn type_bit_size(&self, ty: &Type) -> Option<usize> {
1129 if ty.is_unit(self.context) || ty.is_bool(self.context) {
1133 Some(1)
1134 } else if ty.is_uint(self.context) {
1135 Some(ty.get_uint_width(self.context).unwrap() as usize)
1136 } else if ty.is_b256(self.context) {
1137 Some(256)
1138 } else {
1139 None
1140 }
1141 }
1142}