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