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