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 | BinaryOpKind::Sub
553 | BinaryOpKind::Mul
554 | BinaryOpKind::Div
555 | BinaryOpKind::Mod => {
556 if !arg1_ty.eq(self.context, &arg2_ty) || !arg1_ty.is_uint(self.context) {
557 return Err(IrError::VerifyBinaryOpIncorrectArgType);
558 }
559 }
560 BinaryOpKind::And | BinaryOpKind::Or | BinaryOpKind::Xor => {
561 if !arg1_ty.eq(self.context, &arg2_ty)
562 || !(arg1_ty.is_uint(self.context) || arg1_ty.is_b256(self.context))
563 {
564 return Err(IrError::VerifyBinaryOpIncorrectArgType);
565 }
566 }
567 }
568
569 Ok(())
570 }
571
572 fn verify_br(&self, dest_block: &BranchToWithArgs) -> Result<(), IrError> {
573 if !self
574 .cur_function
575 .block_iter(self.context)
576 .contains(&dest_block.block)
577 {
578 Err(IrError::VerifyBranchToMissingBlock(
579 self.context.blocks[dest_block.block.0].label.clone(),
580 ))
581 } else {
582 self.verify_dest_args(dest_block)
583 }
584 }
585
586 fn verify_call(&self, callee: &Function, args: &[Value]) -> Result<(), IrError> {
587 let callee_content = &self.context.functions[callee.0];
588 if !self.cur_module.function_iter(self.context).contains(callee) {
589 return Err(IrError::VerifyCallToMissingFunction(
590 callee_content.name.clone(),
591 ));
592 }
593
594 let callee_arg_types = callee_content
595 .arguments
596 .iter()
597 .map(|(_, arg_val)| {
598 if let ValueDatum::Argument(BlockArgument { ty, .. }) =
599 &self.context.values[arg_val.0].value
600 {
601 Ok(*ty)
602 } else {
603 Err(IrError::VerifyArgumentValueIsNotArgument(
604 callee_content.name.clone(),
605 ))
606 }
607 })
608 .collect::<Result<Vec<Type>, IrError>>()?;
609
610 for (opt_caller_arg_type, callee_arg_type) in args
611 .iter()
612 .map(|val| val.get_type(self.context))
613 .zip(callee_arg_types.iter())
614 {
615 if opt_caller_arg_type.is_none() {
616 return Err(IrError::VerifyUntypedValuePassedToFunction);
617 }
618
619 let caller_arg_type = opt_caller_arg_type.as_ref().unwrap();
620 if !caller_arg_type.eq(self.context, callee_arg_type) {
621 return Err(IrError::VerifyCallArgTypeMismatch(
622 callee_content.name.clone(),
623 caller_arg_type.as_string(self.context),
624 callee_arg_type.as_string(self.context),
625 ));
626 }
627 }
628
629 Ok(())
630 }
631
632 fn verify_cast_ptr(&self, val: &Value, ty: &Type) -> Result<(), IrError> {
633 let _ = self.get_ptr_type(val, IrError::VerifyPtrCastFromNonPointer)?;
634 if !ty.is_ptr(self.context) {
635 Err(IrError::VerifyPtrCastToNonPointer(
636 ty.as_string(self.context),
637 ))
638 } else {
639 Ok(())
640 }
641 }
642
643 fn verify_dest_args(&self, dest: &BranchToWithArgs) -> Result<(), IrError> {
644 if dest.block.num_args(self.context) != dest.args.len() {
645 return Err(IrError::VerifyBranchParamsMismatch);
646 }
647 for (arg_idx, dest_param) in dest.block.arg_iter(self.context).enumerate() {
648 match dest.args.get(arg_idx) {
649 Some(actual)
650 if dest_param
651 .get_type(self.context)
652 .unwrap()
653 .eq(self.context, &actual.get_type(self.context).unwrap()) => {}
654 _ =>
655 {
657 }
659 }
660 }
661 Ok(())
662 }
663
664 fn verify_cbr(
665 &self,
666 cond_val: &Value,
667 true_block: &BranchToWithArgs,
668 false_block: &BranchToWithArgs,
669 ) -> Result<(), IrError> {
670 if !cond_val
671 .get_type(self.context)
672 .is(Type::is_bool, self.context)
673 {
674 Err(IrError::VerifyConditionExprNotABool)
675 } else if !self
676 .cur_function
677 .block_iter(self.context)
678 .contains(&true_block.block)
679 {
680 Err(IrError::VerifyBranchToMissingBlock(
681 self.context.blocks[true_block.block.0].label.clone(),
682 ))
683 } else if !self
684 .cur_function
685 .block_iter(self.context)
686 .contains(&false_block.block)
687 {
688 Err(IrError::VerifyBranchToMissingBlock(
689 self.context.blocks[false_block.block.0].label.clone(),
690 ))
691 } else {
692 self.verify_dest_args(true_block)
693 .and_then(|()| self.verify_dest_args(false_block))
694 }
695 }
696
697 fn verify_cmp(
698 &self,
699 _pred: &Predicate,
700 lhs_value: &Value,
701 rhs_value: &Value,
702 ) -> Result<(), IrError> {
703 match (
705 lhs_value.get_type(self.context),
706 rhs_value.get_type(self.context),
707 ) {
708 (Some(lhs_ty), Some(rhs_ty)) => {
709 if !lhs_ty.eq(self.context, &rhs_ty) {
710 Err(IrError::VerifyCmpTypeMismatch(
711 lhs_ty.as_string(self.context),
712 rhs_ty.as_string(self.context),
713 ))
714 } else if lhs_ty.is_bool(self.context)
715 || lhs_ty.is_uint(self.context)
716 || lhs_ty.is_b256(self.context)
717 {
718 Ok(())
719 } else {
720 Err(IrError::VerifyCmpBadTypes(
721 lhs_ty.as_string(self.context),
722 rhs_ty.as_string(self.context),
723 ))
724 }
725 }
726 _otherwise => Err(IrError::VerifyCmpUnknownTypes),
727 }
728 }
729
730 fn verify_contract_call(
731 &self,
732 params: &Value,
733 coins: &Value,
734 asset_id: &Value,
735 gas: &Value,
736 ) -> Result<(), IrError> {
737 if !self.context.experimental.new_encoding {
738 let fields = params
743 .get_type(self.context)
744 .and_then(|ty| ty.get_pointee_type(self.context))
745 .map_or_else(std::vec::Vec::new, |ty| ty.get_field_types(self.context));
746 if fields.len() != 3
747 || !fields[0].is_b256(self.context)
748 || !fields[1].is_uint64(self.context)
749 || !fields[2].is_uint64(self.context)
750 {
751 Err(IrError::VerifyContractCallBadTypes("params".to_owned()))
752 } else {
753 Ok(())
754 }
755 .and_then(|_| {
756 if coins
757 .get_type(self.context)
758 .is(Type::is_uint64, self.context)
759 {
760 Ok(())
761 } else {
762 Err(IrError::VerifyContractCallBadTypes("coins".to_owned()))
763 }
764 })
765 .and_then(|_| {
766 if asset_id
767 .get_type(self.context)
768 .and_then(|ty| ty.get_pointee_type(self.context))
769 .is(Type::is_b256, self.context)
770 {
771 Ok(())
772 } else {
773 Err(IrError::VerifyContractCallBadTypes("asset_id".to_owned()))
774 }
775 })
776 .and_then(|_| {
777 if gas.get_type(self.context).is(Type::is_uint64, self.context) {
778 Ok(())
779 } else {
780 Err(IrError::VerifyContractCallBadTypes("gas".to_owned()))
781 }
782 })
783 } else {
784 Ok(())
785 }
786 }
787
788 fn verify_get_elem_ptr(
789 &self,
790 ins: &Value,
791 base: &Value,
792 elem_ptr_ty: &Type,
793 indices: &[Value],
794 ) -> Result<(), IrError> {
795 use crate::constant::ConstantValue;
796
797 let base_ty =
798 self.get_ptr_type(base, |s| IrError::VerifyGepFromNonPointer(s, Some(*ins)))?;
799 if !base_ty.is_aggregate(self.context) {
800 return Err(IrError::VerifyGepOnNonAggregate);
801 }
802
803 let Some(elem_inner_ty) = elem_ptr_ty.get_pointee_type(self.context) else {
804 return Err(IrError::VerifyGepElementTypeNonPointer);
805 };
806
807 if indices.is_empty() {
808 return Err(IrError::VerifyGepInconsistentTypes(
809 "Empty Indices".into(),
810 Some(*base),
811 ));
812 }
813
814 let index_ty = indices.iter().try_fold(base_ty, |ty, idx_val| {
819 idx_val
820 .get_constant(self.context)
821 .and_then(|const_ref| {
822 if let ConstantValue::Uint(n) = const_ref.get_content(self.context).value {
823 Some(n)
824 } else {
825 None
826 }
827 })
828 .and_then(|idx| ty.get_field_type(self.context, idx))
829 .or_else(|| ty.get_array_elem_type(self.context))
830 });
831
832 if self.opt_ty_not_eq(&Some(elem_inner_ty), &index_ty) {
833 return Err(IrError::VerifyGepInconsistentTypes(
834 format!(
835 "Element type \"{}\" versus index type {:?}",
836 elem_inner_ty.as_string(self.context),
837 index_ty.map(|x| x.as_string(self.context))
838 ),
839 Some(*ins),
840 ));
841 }
842
843 Ok(())
844 }
845
846 fn verify_get_local(&self, local_var: &LocalVar) -> Result<(), IrError> {
847 if !self.context.functions[self.cur_function.0]
848 .local_storage
849 .values()
850 .any(|var| var == local_var)
851 {
852 Err(IrError::VerifyGetNonExistentPointer)
853 } else {
854 Ok(())
855 }
856 }
857
858 fn verify_get_global(&self, global_var: &GlobalVar) -> Result<(), IrError> {
859 if !self.context.modules[self.cur_module.0]
860 .global_variables
861 .values()
862 .any(|var| var == global_var)
863 {
864 Err(IrError::VerifyGetNonExistentPointer)
865 } else {
866 Ok(())
867 }
868 }
869
870 fn verify_get_config(&self, module: Module, name: &str) -> Result<(), IrError> {
871 if !self.context.modules[module.0].configs.contains_key(name) {
872 Err(IrError::VerifyGetNonExistentPointer)
873 } else {
874 Ok(())
875 }
876 }
877
878 fn verify_gtf(&self, index: &Value, _tx_field_id: &u64) -> Result<(), IrError> {
879 if !index.get_type(self.context).is(Type::is_uint, self.context) {
881 Err(IrError::VerifyInvalidGtfIndexType)
882 } else {
883 Ok(())
884 }
885 }
886
887 fn verify_int_to_ptr(&self, value: &Value, ty: &Type) -> Result<(), IrError> {
888 let val_ty = value
890 .get_type(self.context)
891 .ok_or(IrError::VerifyIntToPtrUnknownSourceType)?;
892 if !val_ty.is_uint(self.context) {
893 return Err(IrError::VerifyIntToPtrFromNonIntegerType(
894 val_ty.as_string(self.context),
895 ));
896 }
897 if !ty.is_ptr(self.context) {
898 return Err(IrError::VerifyIntToPtrToNonPointer(
899 ty.as_string(self.context),
900 ));
901 }
902
903 Ok(())
904 }
905
906 fn verify_load(&self, src_val: &Value) -> Result<(), IrError> {
907 self.get_ptr_type(src_val, IrError::VerifyLoadFromNonPointer)
909 .map(|_| ())
910 }
911
912 fn verify_log(&self, log_val: &Value, log_ty: &Type, log_id: &Value) -> Result<(), IrError> {
913 if !log_id
914 .get_type(self.context)
915 .is(Type::is_uint64, self.context)
916 {
917 return Err(IrError::VerifyLogId);
918 }
919
920 if self.opt_ty_not_eq(&log_val.get_type(self.context), &Some(*log_ty)) {
921 return Err(IrError::VerifyLogMismatchedTypes);
922 }
923
924 Ok(())
925 }
926
927 fn verify_mem_copy_bytes(
928 &self,
929 dst_val_ptr: &Value,
930 src_val_ptr: &Value,
931 _byte_len: &u64,
932 ) -> Result<(), IrError> {
933 self.get_ptr_type(dst_val_ptr, IrError::VerifyMemcopyNonPointer)
935 .and_then(|_| self.get_ptr_type(src_val_ptr, IrError::VerifyMemcopyNonPointer))
936 .map(|_| ())
937 }
938
939 fn verify_mem_copy_val(&self, dst_val_ptr: &Value, src_val_ptr: &Value) -> Result<(), IrError> {
940 self.get_ptr_type(dst_val_ptr, IrError::VerifyMemcopyNonPointer)
942 .and_then(|dst_ty| {
943 self.get_ptr_type(src_val_ptr, IrError::VerifyMemcopyNonPointer)
944 .map(|src_ty| (dst_ty, src_ty))
945 })
946 .and_then(|(dst_ty, src_ty)| {
947 dst_ty
948 .eq(self.context, &src_ty)
949 .then_some(())
950 .ok_or_else(|| {
951 IrError::VerifyMemcopyMismatchedTypes(
952 dst_ty.as_string(self.context),
953 src_ty.as_string(self.context),
954 )
955 })
956 })
957 }
958
959 fn verify_ptr_to_int(&self, _val: &Value, ty: &Type) -> Result<(), IrError> {
960 if !ty.is_uint(self.context) {
966 Err(IrError::VerifyPtrToIntToNonInteger(
967 ty.as_string(self.context),
968 ))
969 } else {
970 Ok(())
971 }
972 }
973
974 fn verify_ret(&self, val: &Value, ty: &Type) -> Result<(), IrError> {
975 if !self
976 .cur_function
977 .get_return_type(self.context)
978 .eq(self.context, ty)
979 || self.opt_ty_not_eq(&val.get_type(self.context), &Some(*ty))
980 {
981 Err(IrError::VerifyReturnMismatchedTypes(
982 self.cur_function.get_name(self.context).to_string(),
983 ))
984 } else {
985 Ok(())
986 }
987 }
988
989 fn verify_revert(&self, val: &Value) -> Result<(), IrError> {
990 if !val.get_type(self.context).is(Type::is_uint64, self.context) {
991 Err(IrError::VerifyRevertCodeBadType)
992 } else {
993 Ok(())
994 }
995 }
996
997 fn verify_smo(
998 &self,
999 recipient: &Value,
1000 message: &Value,
1001 message_size: &Value,
1002 coins: &Value,
1003 ) -> Result<(), IrError> {
1004 let recipient = self.get_ptr_type(recipient, IrError::VerifySmoRecipientNonPointer)?;
1006 if !recipient.is_b256(self.context) {
1007 return Err(IrError::VerifySmoRecipientBadType);
1008 }
1009
1010 let struct_ty = self.get_ptr_type(message, IrError::VerifySmoMessageNonPointer)?;
1012
1013 if !struct_ty.is_struct(self.context) {
1014 return Err(IrError::VerifySmoBadMessageType);
1015 }
1016 let fields = struct_ty.get_field_types(self.context);
1017 if fields.len() != 2 {
1018 return Err(IrError::VerifySmoBadMessageType);
1019 }
1020
1021 if !message_size
1023 .get_type(self.context)
1024 .is(Type::is_uint64, self.context)
1025 {
1026 return Err(IrError::VerifySmoMessageSize);
1027 }
1028
1029 if !coins
1031 .get_type(self.context)
1032 .is(Type::is_uint64, self.context)
1033 {
1034 return Err(IrError::VerifySmoCoins);
1035 }
1036
1037 Ok(())
1038 }
1039
1040 fn verify_state_clear(&self, key: &Value, number_of_slots: &Value) -> Result<(), IrError> {
1041 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1042 if !key_type.is_b256(self.context) {
1043 Err(IrError::VerifyStateKeyBadType)
1044 } else if !number_of_slots
1045 .get_type(self.context)
1046 .is(Type::is_uint, self.context)
1047 {
1048 Err(IrError::VerifyStateAccessNumOfSlots)
1049 } else {
1050 Ok(())
1051 }
1052 }
1053
1054 fn verify_state_access_quad(
1055 &self,
1056 dst_val: &Value,
1057 key: &Value,
1058 number_of_slots: &Value,
1059 ) -> Result<(), IrError> {
1060 let dst_ty = self.get_ptr_type(dst_val, IrError::VerifyStateAccessQuadNonPointer)?;
1061 if !dst_ty.is_b256(self.context) {
1062 return Err(IrError::VerifyStateDestBadType(
1063 dst_ty.as_string(self.context),
1064 ));
1065 }
1066 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1067 if !key_type.is_b256(self.context) {
1068 return Err(IrError::VerifyStateKeyBadType);
1069 }
1070 if !number_of_slots
1071 .get_type(self.context)
1072 .is(Type::is_uint, self.context)
1073 {
1074 return Err(IrError::VerifyStateAccessNumOfSlots);
1075 }
1076 Ok(())
1077 }
1078
1079 fn verify_state_load_word(&self, key: &Value) -> Result<(), IrError> {
1080 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1081 if !key_type.is_b256(self.context) {
1082 Err(IrError::VerifyStateKeyBadType)
1083 } else {
1084 Ok(())
1085 }
1086 }
1087
1088 fn verify_state_store_word(&self, dst_val: &Value, key: &Value) -> Result<(), IrError> {
1089 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1090 if !key_type.is_b256(self.context) {
1091 Err(IrError::VerifyStateKeyBadType)
1092 } else if !dst_val
1093 .get_type(self.context)
1094 .is(Type::is_uint, self.context)
1095 {
1096 Err(IrError::VerifyStateDestBadType(
1097 Type::get_uint64(self.context).as_string(self.context),
1098 ))
1099 } else {
1100 Ok(())
1101 }
1102 }
1103
1104 fn verify_store(
1105 &self,
1106 ins: &Value,
1107 dst_val: &Value,
1108 stored_val: &Value,
1109 ) -> Result<(), IrError> {
1110 let dst_ty = self.get_ptr_type(dst_val, IrError::VerifyStoreToNonPointer)?;
1111 let stored_ty = stored_val.get_type(self.context);
1112 if self.opt_ty_not_eq(&Some(dst_ty), &stored_ty) {
1113 Err(IrError::VerifyStoreMismatchedTypes(Some(*ins)))
1114 } else {
1115 Ok(())
1116 }
1117 }
1118
1119 fn opt_ty_not_eq(&self, l_ty: &Option<Type>, r_ty: &Option<Type>) -> bool {
1124 l_ty.is_none() || r_ty.is_none() || !l_ty.unwrap().eq(self.context, r_ty.as_ref().unwrap())
1125 }
1126
1127 fn get_ptr_type<F: FnOnce(String) -> IrError>(
1128 &self,
1129 val: &Value,
1130 errfn: F,
1131 ) -> Result<Type, IrError> {
1132 val.get_type(self.context)
1133 .ok_or_else(|| "unknown".to_owned())
1134 .and_then(|ptr_ty| {
1135 ptr_ty
1136 .get_pointee_type(self.context)
1137 .ok_or_else(|| ptr_ty.as_string(self.context))
1138 })
1139 .map_err(errfn)
1140 }
1141
1142 fn type_bit_size(&self, ty: &Type) -> Option<usize> {
1144 if ty.is_unit(self.context) || ty.is_bool(self.context) {
1148 Some(1)
1149 } else if ty.is_uint(self.context) {
1150 Some(ty.get_uint_width(self.context).unwrap() as usize)
1151 } else if ty.is_b256(self.context) {
1152 Some(256)
1153 } else {
1154 None
1155 }
1156 }
1157}