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, StorageKey,
20 TypeOption, 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^ {error}\x1b[0m")))
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::GetStorageKey(storage_key) => self.verify_get_storage_key(storage_key)?,
356 InstOp::IntToPtr(value, ty) => self.verify_int_to_ptr(value, ty)?,
357 InstOp::Load(ptr) => self.verify_load(ptr)?,
358 InstOp::MemCopyBytes {
359 dst_val_ptr,
360 src_val_ptr,
361 byte_len,
362 } => self.verify_mem_copy_bytes(dst_val_ptr, src_val_ptr, byte_len)?,
363 InstOp::MemCopyVal {
364 dst_val_ptr,
365 src_val_ptr,
366 } => self.verify_mem_copy_val(dst_val_ptr, src_val_ptr)?,
367 InstOp::MemClearVal { dst_val_ptr } => self.verify_mem_clear_val(dst_val_ptr)?,
368 InstOp::Nop => (),
369 InstOp::PtrToInt(val, ty) => self.verify_ptr_to_int(val, ty)?,
370 InstOp::Ret(val, ty) => self.verify_ret(val, ty)?,
371 InstOp::Store {
372 dst_val_ptr,
373 stored_val,
374 } => self.verify_store(&ins, dst_val_ptr, stored_val)?,
375 };
376
377 self.context.verify_metadata(value_content.metadata)?;
379 }
380
381 Ok(())
382 }
383
384 fn verify_bitcast(&self, value: &Value, ty: &Type) -> Result<(), IrError> {
385 let val_ty = value
389 .get_type(self.context)
390 .ok_or(IrError::VerifyBitcastUnknownSourceType)?;
391 if self.type_bit_size(&val_ty).is_some_and(|sz| sz > 64)
392 || self.type_bit_size(ty).is_some_and(|sz| sz > 64)
393 {
394 Err(IrError::VerifyBitcastBetweenInvalidTypes(
395 val_ty.as_string(self.context),
396 ty.as_string(self.context),
397 ))
398 } else {
399 Ok(())
400 }
401 }
402
403 fn verify_unary_op(&self, op: &UnaryOpKind, arg: &Value) -> Result<(), IrError> {
404 let arg_ty = arg
405 .get_type(self.context)
406 .ok_or(IrError::VerifyUnaryOpIncorrectArgType)?;
407 match op {
408 UnaryOpKind::Not => {
409 if !arg_ty.is_uint(self.context) && !arg_ty.is_b256(self.context) {
410 return Err(IrError::VerifyUnaryOpIncorrectArgType);
411 }
412 }
413 }
414
415 Ok(())
416 }
417
418 fn verify_wide_cmp(&self, _: &Predicate, arg1: &Value, arg2: &Value) -> Result<(), IrError> {
419 let arg1_ty = arg1
420 .get_type(self.context)
421 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
422 let arg2_ty = arg2
423 .get_type(self.context)
424 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
425
426 if arg1_ty.is_ptr(self.context) && arg2_ty.is_ptr(self.context) {
427 Ok(())
428 } else {
429 Err(IrError::VerifyBinaryOpIncorrectArgType)
430 }
431 }
432
433 fn verify_wide_modular_op(
434 &self,
435 _op: &BinaryOpKind,
436 result: &Value,
437 arg1: &Value,
438 arg2: &Value,
439 arg3: &Value,
440 ) -> Result<(), IrError> {
441 let result_ty = result
442 .get_type(self.context)
443 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
444 let arg1_ty = arg1
445 .get_type(self.context)
446 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
447 let arg2_ty = arg2
448 .get_type(self.context)
449 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
450 let arg3_ty = arg3
451 .get_type(self.context)
452 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
453
454 if !arg1_ty.is_ptr(self.context)
455 || !arg2_ty.is_ptr(self.context)
456 || !arg3_ty.is_ptr(self.context)
457 || !result_ty.is_ptr(self.context)
458 {
459 return Err(IrError::VerifyBinaryOpIncorrectArgType);
460 }
461
462 Ok(())
463 }
464
465 fn verify_wide_binary_op(
466 &self,
467 op: &BinaryOpKind,
468 result: &Value,
469 arg1: &Value,
470 arg2: &Value,
471 ) -> Result<(), IrError> {
472 let result_ty = result
473 .get_type(self.context)
474 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
475 let arg1_ty = arg1
476 .get_type(self.context)
477 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
478 let arg2_ty = arg2
479 .get_type(self.context)
480 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
481
482 match op {
483 BinaryOpKind::Lsh | BinaryOpKind::Rsh => {
485 if !arg1_ty.is_ptr(self.context)
486 || !arg2_ty.is_uint64(self.context)
487 || !result_ty.is_ptr(self.context)
488 {
489 return Err(IrError::VerifyBinaryOpIncorrectArgType);
490 }
491 }
492 BinaryOpKind::Add
493 | BinaryOpKind::Sub
494 | BinaryOpKind::Mul
495 | BinaryOpKind::Div
496 | BinaryOpKind::And
497 | BinaryOpKind::Or
498 | BinaryOpKind::Xor
499 | BinaryOpKind::Mod => {
500 if !arg1_ty.is_ptr(self.context)
501 || !arg2_ty.is_ptr(self.context)
502 || !result_ty.is_ptr(self.context)
503 {
504 return Err(IrError::VerifyBinaryOpIncorrectArgType);
505 }
506 }
507 }
508
509 Ok(())
510 }
511
512 fn verify_wide_unary_op(
513 &self,
514 _op: &UnaryOpKind,
515 result: &Value,
516 arg: &Value,
517 ) -> Result<(), IrError> {
518 let result_ty = result
519 .get_type(self.context)
520 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
521 let arg_ty = arg
522 .get_type(self.context)
523 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
524
525 if !arg_ty.is_ptr(self.context) || !result_ty.is_ptr(self.context) {
526 return Err(IrError::VerifyBinaryOpIncorrectArgType);
527 }
528
529 Ok(())
530 }
531
532 fn verify_binary_op(
533 &self,
534 op: &BinaryOpKind,
535 arg1: &Value,
536 arg2: &Value,
537 ) -> Result<(), IrError> {
538 let arg1_ty = arg1
539 .get_type(self.context)
540 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
541 let arg2_ty = arg2
542 .get_type(self.context)
543 .ok_or(IrError::VerifyBinaryOpIncorrectArgType)?;
544
545 match op {
546 BinaryOpKind::Lsh | BinaryOpKind::Rsh => {
548 let is_lhs_ok = arg1_ty.is_uint(self.context) || arg1_ty.is_b256(self.context);
549 if !is_lhs_ok || !arg2_ty.is_uint(self.context) {
550 return Err(IrError::VerifyBinaryOpIncorrectArgType);
551 }
552 }
553 BinaryOpKind::Add | BinaryOpKind::Sub => {
554 if !(arg1_ty.eq(self.context, &arg2_ty) && arg1_ty.is_uint(self.context)
555 || arg1_ty.is_ptr(self.context) && arg2_ty.is_uint64(self.context))
556 {
557 return Err(IrError::VerifyBinaryOpIncorrectArgType);
558 }
559 }
560 BinaryOpKind::Mul | BinaryOpKind::Div | BinaryOpKind::Mod => {
561 if !arg1_ty.eq(self.context, &arg2_ty) || !arg1_ty.is_uint(self.context) {
562 return Err(IrError::VerifyBinaryOpIncorrectArgType);
563 }
564 }
565 BinaryOpKind::And | BinaryOpKind::Or | BinaryOpKind::Xor => {
566 if !arg1_ty.eq(self.context, &arg2_ty)
567 || !(arg1_ty.is_uint(self.context) || arg1_ty.is_b256(self.context))
568 {
569 return Err(IrError::VerifyBinaryOpIncorrectArgType);
570 }
571 }
572 }
573
574 Ok(())
575 }
576
577 fn verify_br(&self, dest_block: &BranchToWithArgs) -> Result<(), IrError> {
578 if !self
579 .cur_function
580 .block_iter(self.context)
581 .contains(&dest_block.block)
582 {
583 Err(IrError::VerifyBranchToMissingBlock(
584 self.context.blocks[dest_block.block.0].label.clone(),
585 ))
586 } else {
587 self.verify_dest_args(dest_block)
588 }
589 }
590
591 fn verify_call(&self, callee: &Function, args: &[Value]) -> Result<(), IrError> {
592 let callee_content = &self.context.functions[callee.0];
593 if !self.cur_module.function_iter(self.context).contains(callee) {
594 return Err(IrError::VerifyCallToMissingFunction(
595 callee_content.name.clone(),
596 ));
597 }
598
599 let callee_arg_types = callee_content
600 .arguments
601 .iter()
602 .map(|(_, arg_val)| {
603 if let ValueDatum::Argument(BlockArgument { ty, .. }) =
604 &self.context.values[arg_val.0].value
605 {
606 Ok(*ty)
607 } else {
608 Err(IrError::VerifyArgumentValueIsNotArgument(
609 callee_content.name.clone(),
610 ))
611 }
612 })
613 .collect::<Result<Vec<Type>, IrError>>()?;
614
615 for (opt_caller_arg_type, callee_arg_type) in args
616 .iter()
617 .map(|val| val.get_type(self.context))
618 .zip(callee_arg_types.iter())
619 {
620 if opt_caller_arg_type.is_none() {
621 return Err(IrError::VerifyUntypedValuePassedToFunction);
622 }
623
624 let caller_arg_type = opt_caller_arg_type.as_ref().unwrap();
625 if !caller_arg_type.eq(self.context, callee_arg_type) {
626 return Err(IrError::VerifyCallArgTypeMismatch(
627 callee_content.name.clone(),
628 caller_arg_type.as_string(self.context),
629 callee_arg_type.as_string(self.context),
630 ));
631 }
632 }
633
634 Ok(())
635 }
636
637 fn verify_cast_ptr(&self, val: &Value, ty: &Type) -> Result<(), IrError> {
638 if !(val
639 .get_type(self.context)
640 .is_some_and(|ty| ty.is_ptr(self.context)))
641 {
642 let ty = val
643 .get_type(self.context)
644 .map(|ty| ty.as_string(self.context))
645 .unwrap_or("Unknown".into());
646 return Err(IrError::VerifyPtrCastFromNonPointer(ty));
647 }
648
649 if !ty.is_ptr(self.context) {
650 Err(IrError::VerifyPtrCastToNonPointer(
651 ty.as_string(self.context),
652 ))
653 } else {
654 Ok(())
655 }
656 }
657
658 fn verify_dest_args(&self, dest: &BranchToWithArgs) -> Result<(), IrError> {
659 if dest.block.num_args(self.context) != dest.args.len() {
660 return Err(IrError::VerifyBranchParamsMismatch);
661 }
662 for (arg_idx, dest_param) in dest.block.arg_iter(self.context).enumerate() {
663 match dest.args.get(arg_idx) {
664 Some(actual)
665 if dest_param
666 .get_type(self.context)
667 .unwrap()
668 .eq(self.context, &actual.get_type(self.context).unwrap()) => {}
669 _ =>
670 {
672 }
674 }
675 }
676 Ok(())
677 }
678
679 fn verify_cbr(
680 &self,
681 cond_val: &Value,
682 true_block: &BranchToWithArgs,
683 false_block: &BranchToWithArgs,
684 ) -> Result<(), IrError> {
685 if !cond_val
686 .get_type(self.context)
687 .is(Type::is_bool, self.context)
688 {
689 Err(IrError::VerifyConditionExprNotABool)
690 } else if !self
691 .cur_function
692 .block_iter(self.context)
693 .contains(&true_block.block)
694 {
695 Err(IrError::VerifyBranchToMissingBlock(
696 self.context.blocks[true_block.block.0].label.clone(),
697 ))
698 } else if !self
699 .cur_function
700 .block_iter(self.context)
701 .contains(&false_block.block)
702 {
703 Err(IrError::VerifyBranchToMissingBlock(
704 self.context.blocks[false_block.block.0].label.clone(),
705 ))
706 } else {
707 self.verify_dest_args(true_block)
708 .and_then(|()| self.verify_dest_args(false_block))
709 }
710 }
711
712 fn verify_cmp(
713 &self,
714 _pred: &Predicate,
715 lhs_value: &Value,
716 rhs_value: &Value,
717 ) -> Result<(), IrError> {
718 match (
720 lhs_value.get_type(self.context),
721 rhs_value.get_type(self.context),
722 ) {
723 (Some(lhs_ty), Some(rhs_ty)) => {
724 if !lhs_ty.eq(self.context, &rhs_ty) {
725 Err(IrError::VerifyCmpTypeMismatch(
726 lhs_ty.as_string(self.context),
727 rhs_ty.as_string(self.context),
728 ))
729 } else if lhs_ty.is_bool(self.context)
730 || lhs_ty.is_uint(self.context)
731 || lhs_ty.is_ptr(self.context)
732 || lhs_ty.is_b256(self.context)
733 {
734 Ok(())
735 } else {
736 Err(IrError::VerifyCmpBadTypes(
737 lhs_ty.as_string(self.context),
738 rhs_ty.as_string(self.context),
739 ))
740 }
741 }
742 _otherwise => Err(IrError::VerifyCmpUnknownTypes),
743 }
744 }
745
746 fn verify_contract_call(
747 &self,
748 params: &Value,
749 coins: &Value,
750 asset_id: &Value,
751 gas: &Value,
752 ) -> Result<(), IrError> {
753 if !self.context.experimental.new_encoding {
754 let fields = params
759 .get_type(self.context)
760 .and_then(|ty| ty.get_pointee_type(self.context))
761 .map_or_else(std::vec::Vec::new, |ty| ty.get_field_types(self.context));
762 if fields.len() != 3
763 || !fields[0].is_b256(self.context)
764 || !fields[1].is_uint64(self.context)
765 || !fields[2].is_uint64(self.context)
766 {
767 Err(IrError::VerifyContractCallBadTypes("params".to_owned()))
768 } else {
769 Ok(())
770 }
771 .and_then(|_| {
772 if coins
773 .get_type(self.context)
774 .is(Type::is_uint64, self.context)
775 {
776 Ok(())
777 } else {
778 Err(IrError::VerifyContractCallBadTypes("coins".to_owned()))
779 }
780 })
781 .and_then(|_| {
782 if asset_id
783 .get_type(self.context)
784 .and_then(|ty| ty.get_pointee_type(self.context))
785 .is(Type::is_b256, self.context)
786 {
787 Ok(())
788 } else {
789 Err(IrError::VerifyContractCallBadTypes("asset_id".to_owned()))
790 }
791 })
792 .and_then(|_| {
793 if gas.get_type(self.context).is(Type::is_uint64, self.context) {
794 Ok(())
795 } else {
796 Err(IrError::VerifyContractCallBadTypes("gas".to_owned()))
797 }
798 })
799 } else {
800 Ok(())
801 }
802 }
803
804 fn verify_get_elem_ptr(
805 &self,
806 ins: &Value,
807 base: &Value,
808 elem_ptr_ty: &Type,
809 indices: &[Value],
810 ) -> Result<(), IrError> {
811 let base_ty =
812 self.get_ptr_type(base, |s| IrError::VerifyGepFromNonPointer(s, Some(*ins)))?;
813 if !base_ty.is_aggregate(self.context) {
814 return Err(IrError::VerifyGepOnNonAggregate);
815 }
816
817 let Some(elem_inner_ty) = elem_ptr_ty.get_pointee_type(self.context) else {
818 return Err(IrError::VerifyGepElementTypeNonPointer);
819 };
820
821 if indices.is_empty() {
822 return Err(IrError::VerifyGepInconsistentTypes(
823 "Empty Indices".into(),
824 Some(*base),
825 ));
826 }
827
828 let index_ty = base_ty.get_value_indexed_type(self.context, indices);
829
830 if self.opt_ty_not_eq(&Some(elem_inner_ty), &index_ty) {
831 return Err(IrError::VerifyGepInconsistentTypes(
832 format!(
833 "Element type \"{}\" versus index type {:?}",
834 elem_inner_ty.as_string(self.context),
835 index_ty.map(|x| x.as_string(self.context))
836 ),
837 Some(*ins),
838 ));
839 }
840
841 Ok(())
842 }
843
844 fn verify_get_local(&self, local_var: &LocalVar) -> Result<(), IrError> {
845 if !self.context.functions[self.cur_function.0]
846 .local_storage
847 .values()
848 .any(|var| var == local_var)
849 {
850 Err(IrError::VerifyGetNonExistentLocalVarPointer)
851 } else {
852 Ok(())
853 }
854 }
855
856 fn verify_get_global(&self, global_var: &GlobalVar) -> Result<(), IrError> {
857 if !self.context.modules[self.cur_module.0]
858 .global_variables
859 .values()
860 .any(|var| var == global_var)
861 {
862 Err(IrError::VerifyGetNonExistentGlobalVarPointer)
863 } else {
864 Ok(())
865 }
866 }
867
868 fn verify_get_config(&self, module: Module, name: &str) -> Result<(), IrError> {
869 if !self.context.modules[module.0].configs.contains_key(name) {
870 Err(IrError::VerifyGetNonExistentConfigPointer)
871 } else {
872 Ok(())
873 }
874 }
875
876 fn verify_get_storage_key(&self, storage_key: &StorageKey) -> Result<(), IrError> {
877 if !self.context.modules[self.cur_module.0]
878 .storage_keys
879 .values()
880 .any(|key| key == storage_key)
881 {
882 Err(IrError::VerifyGetNonExistentStorageKeyPointer)
883 } else {
884 Ok(())
885 }
886 }
887
888 fn verify_gtf(&self, index: &Value, _tx_field_id: &u64) -> Result<(), IrError> {
889 if !index.get_type(self.context).is(Type::is_uint, self.context) {
891 Err(IrError::VerifyInvalidGtfIndexType)
892 } else {
893 Ok(())
894 }
895 }
896
897 fn verify_int_to_ptr(&self, value: &Value, ty: &Type) -> Result<(), IrError> {
898 let val_ty = value
900 .get_type(self.context)
901 .ok_or(IrError::VerifyIntToPtrUnknownSourceType)?;
902 if !val_ty.is_uint(self.context) {
903 return Err(IrError::VerifyIntToPtrFromNonIntegerType(
904 val_ty.as_string(self.context),
905 ));
906 }
907 if !ty.is_ptr(self.context) {
908 return Err(IrError::VerifyIntToPtrToNonPointer(
909 ty.as_string(self.context),
910 ));
911 }
912
913 Ok(())
914 }
915
916 fn verify_load(&self, src_val: &Value) -> Result<(), IrError> {
917 self.get_ptr_type(src_val, IrError::VerifyLoadFromNonPointer)
919 .map(|_| ())
920 }
921
922 fn verify_log(&self, log_val: &Value, log_ty: &Type, log_id: &Value) -> Result<(), IrError> {
923 if !log_id
924 .get_type(self.context)
925 .is(Type::is_uint64, self.context)
926 {
927 return Err(IrError::VerifyLogId);
928 }
929
930 if self.opt_ty_not_eq(&log_val.get_type(self.context), &Some(*log_ty)) {
931 return Err(IrError::VerifyLogMismatchedTypes);
932 }
933
934 Ok(())
935 }
936
937 fn verify_mem_copy_bytes(
938 &self,
939 dst_val_ptr: &Value,
940 src_val_ptr: &Value,
941 _byte_len: &u64,
942 ) -> Result<(), IrError> {
943 self.get_ptr_type(dst_val_ptr, IrError::VerifyMemcopyNonPointer)
945 .and_then(|_| self.get_ptr_type(src_val_ptr, IrError::VerifyMemcopyNonPointer))
946 .map(|_| ())
947 }
948
949 fn verify_mem_copy_val(&self, dst_val_ptr: &Value, src_val_ptr: &Value) -> Result<(), IrError> {
950 self.get_ptr_type(dst_val_ptr, IrError::VerifyMemcopyNonPointer)
952 .and_then(|dst_ty| {
953 self.get_ptr_type(src_val_ptr, IrError::VerifyMemcopyNonPointer)
954 .map(|src_ty| (dst_ty, src_ty))
955 })
956 .and_then(|(dst_ty, src_ty)| {
957 dst_ty
958 .eq(self.context, &src_ty)
959 .then_some(())
960 .ok_or_else(|| {
961 IrError::VerifyMemcopyMismatchedTypes(
962 dst_ty.as_string(self.context),
963 src_ty.as_string(self.context),
964 )
965 })
966 })
967 }
968
969 fn verify_mem_clear_val(&self, dst_val_ptr: &Value) -> Result<(), IrError> {
971 let _ = self.get_ptr_type(dst_val_ptr, IrError::VerifyMemClearValNonPointer)?;
972 Ok(())
973 }
974
975 fn verify_ptr_to_int(&self, val: &Value, ty: &Type) -> Result<(), IrError> {
976 if !(val
980 .get_type(self.context)
981 .is_some_and(|ty| ty.is_ptr(self.context)))
982 {
983 let ty = val
984 .get_type(self.context)
985 .map(|ty| ty.as_string(self.context))
986 .unwrap_or("Unknown".into());
987 return Err(IrError::VerifyPtrCastFromNonPointer(ty));
988 }
989 if !ty.is_uint(self.context) {
990 Err(IrError::VerifyPtrToIntToNonInteger(
991 ty.as_string(self.context),
992 ))
993 } else {
994 Ok(())
995 }
996 }
997
998 fn verify_ret(&self, val: &Value, ty: &Type) -> Result<(), IrError> {
999 if !self
1000 .cur_function
1001 .get_return_type(self.context)
1002 .eq(self.context, ty)
1003 || self.opt_ty_not_eq(&val.get_type(self.context), &Some(*ty))
1004 {
1005 Err(IrError::VerifyReturnMismatchedTypes(
1006 self.cur_function.get_name(self.context).to_string(),
1007 ))
1008 } else {
1009 Ok(())
1010 }
1011 }
1012
1013 fn verify_revert(&self, val: &Value) -> Result<(), IrError> {
1014 if !val.get_type(self.context).is(Type::is_uint64, self.context) {
1015 Err(IrError::VerifyRevertCodeBadType)
1016 } else {
1017 Ok(())
1018 }
1019 }
1020
1021 fn verify_smo(
1022 &self,
1023 recipient: &Value,
1024 message: &Value,
1025 message_size: &Value,
1026 coins: &Value,
1027 ) -> Result<(), IrError> {
1028 let recipient = self.get_ptr_type(recipient, IrError::VerifySmoRecipientNonPointer)?;
1030 if !recipient.is_b256(self.context) {
1031 return Err(IrError::VerifySmoRecipientBadType);
1032 }
1033
1034 let struct_ty = self.get_ptr_type(message, IrError::VerifySmoMessageNonPointer)?;
1036
1037 if !struct_ty.is_struct(self.context) {
1038 return Err(IrError::VerifySmoBadMessageType);
1039 }
1040 let fields = struct_ty.get_field_types(self.context);
1041 if fields.len() != 2 {
1042 return Err(IrError::VerifySmoBadMessageType);
1043 }
1044
1045 if !message_size
1047 .get_type(self.context)
1048 .is(Type::is_uint64, self.context)
1049 {
1050 return Err(IrError::VerifySmoMessageSize);
1051 }
1052
1053 if !coins
1055 .get_type(self.context)
1056 .is(Type::is_uint64, self.context)
1057 {
1058 return Err(IrError::VerifySmoCoins);
1059 }
1060
1061 Ok(())
1062 }
1063
1064 fn verify_state_clear(&self, key: &Value, number_of_slots: &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 if !number_of_slots
1069 .get_type(self.context)
1070 .is(Type::is_uint, self.context)
1071 {
1072 Err(IrError::VerifyStateAccessNumOfSlots)
1073 } else {
1074 Ok(())
1075 }
1076 }
1077
1078 fn verify_state_access_quad(
1079 &self,
1080 dst_val: &Value,
1081 key: &Value,
1082 number_of_slots: &Value,
1083 ) -> Result<(), IrError> {
1084 let dst_ty = self.get_ptr_type(dst_val, IrError::VerifyStateAccessQuadNonPointer)?;
1085 if !dst_ty.is_b256(self.context) {
1086 return Err(IrError::VerifyStateDestBadType(
1087 dst_ty.as_string(self.context),
1088 ));
1089 }
1090 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1091 if !key_type.is_b256(self.context) {
1092 return Err(IrError::VerifyStateKeyBadType);
1093 }
1094 if !number_of_slots
1095 .get_type(self.context)
1096 .is(Type::is_uint, self.context)
1097 {
1098 return Err(IrError::VerifyStateAccessNumOfSlots);
1099 }
1100 Ok(())
1101 }
1102
1103 fn verify_state_load_word(&self, key: &Value) -> Result<(), IrError> {
1104 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1105 if !key_type.is_b256(self.context) {
1106 Err(IrError::VerifyStateKeyBadType)
1107 } else {
1108 Ok(())
1109 }
1110 }
1111
1112 fn verify_state_store_word(&self, dst_val: &Value, key: &Value) -> Result<(), IrError> {
1113 let key_type = self.get_ptr_type(key, IrError::VerifyStateKeyNonPointer)?;
1114 if !key_type.is_b256(self.context) {
1115 Err(IrError::VerifyStateKeyBadType)
1116 } else if !dst_val
1117 .get_type(self.context)
1118 .is(Type::is_uint, self.context)
1119 {
1120 Err(IrError::VerifyStateDestBadType(
1121 Type::get_uint64(self.context).as_string(self.context),
1122 ))
1123 } else {
1124 Ok(())
1125 }
1126 }
1127
1128 fn verify_store(
1129 &self,
1130 ins: &Value,
1131 dst_val: &Value,
1132 stored_val: &Value,
1133 ) -> Result<(), IrError> {
1134 let dst_ty = self.get_ptr_type(dst_val, IrError::VerifyStoreToNonPointer)?;
1135 let stored_ty = stored_val.get_type(self.context);
1136 if self.opt_ty_not_eq(&Some(dst_ty), &stored_ty) {
1137 Err(IrError::VerifyStoreMismatchedTypes(Some(*ins)))
1138 } else {
1139 Ok(())
1140 }
1141 }
1142
1143 fn opt_ty_not_eq(&self, l_ty: &Option<Type>, r_ty: &Option<Type>) -> bool {
1148 l_ty.is_none() || r_ty.is_none() || !l_ty.unwrap().eq(self.context, r_ty.as_ref().unwrap())
1149 }
1150
1151 fn get_ptr_type<F: FnOnce(String) -> IrError>(
1152 &self,
1153 val: &Value,
1154 errfn: F,
1155 ) -> Result<Type, IrError> {
1156 val.get_type(self.context)
1157 .ok_or_else(|| "unknown".to_owned())
1158 .and_then(|ptr_ty| {
1159 ptr_ty
1160 .get_pointee_type(self.context)
1161 .ok_or_else(|| ptr_ty.as_string(self.context))
1162 })
1163 .map_err(errfn)
1164 }
1165
1166 fn type_bit_size(&self, ty: &Type) -> Option<usize> {
1168 if ty.is_unit(self.context) || ty.is_bool(self.context) {
1172 Some(1)
1173 } else if ty.is_uint(self.context) {
1174 Some(ty.get_uint_width(self.context).unwrap() as usize)
1175 } else if ty.is_b256(self.context) {
1176 Some(256)
1177 } else {
1178 None
1179 }
1180 }
1181}