1use move_abstract_stack::AbstractStack;
16use move_binary_format::{
17 errors::PartialVMError,
18 file_format::{
19 Bytecode, CodeOffset, CompiledModule, FunctionDefinitionIndex, FunctionHandle, LocalIndex,
20 StructDefinition, StructFieldInformation,
21 },
22};
23use move_bytecode_verifier::absint::{
24 AbstractDomain, FunctionContext, JoinResult, TransferFunctions, analyze_function,
25};
26use move_bytecode_verifier_meter::{Meter, Scope};
27use move_core_types::{ident_str, vm_status::StatusCode};
28use std::{collections::BTreeMap, error::Error, num::NonZeroU64};
29use sui_types::bridge::BRIDGE_MODULE_NAME;
30use sui_types::deny_list_v1::{DENY_LIST_CREATE_FUNC, DENY_LIST_MODULE};
31use sui_types::{
32 BRIDGE_ADDRESS, SUI_FRAMEWORK_ADDRESS, SUI_SYSTEM_ADDRESS,
33 accumulator_event::ACCUMULATOR_MODULE_NAME,
34 authenticator_state::AUTHENTICATOR_STATE_MODULE_NAME,
35 clock::CLOCK_MODULE_NAME,
36 error::{ExecutionError, VMMVerifierErrorSubStatusCode},
37 id::OBJECT_MODULE_NAME,
38 randomness_state::RANDOMNESS_MODULE_NAME,
39 sui_system_state::SUI_SYSTEM_MODULE_NAME,
40};
41
42use crate::{
43 FunctionIdent, TEST_SCENARIO_MODULE_NAME, check_for_verifier_timeout,
44 to_verification_timeout_error, verification_failure,
45};
46pub(crate) const JOIN_BASE_COST: u128 = 10;
47pub(crate) const JOIN_PER_LOCAL_COST: u128 = 5;
48pub(crate) const STEP_BASE_COST: u128 = 15;
49
50#[derive(Clone, Copy, Debug, Eq, PartialEq)]
51enum AbstractValue {
52 Fresh,
53 Other,
54}
55
56const OBJECT_NEW: FunctionIdent = (SUI_FRAMEWORK_ADDRESS, OBJECT_MODULE_NAME, ident_str!("new"));
57const OBJECT_NEW_UID_FROM_HASH: FunctionIdent = (
58 SUI_FRAMEWORK_ADDRESS,
59 OBJECT_MODULE_NAME,
60 ident_str!("new_uid_from_hash"),
61);
62const OBJECT_NEW_DERIVED: FunctionIdent = (
63 SUI_FRAMEWORK_ADDRESS,
64 ident_str!("derived_object"),
65 ident_str!("claim"),
66);
67const TS_NEW_OBJECT: FunctionIdent = (
68 SUI_FRAMEWORK_ADDRESS,
69 ident_str!(TEST_SCENARIO_MODULE_NAME),
70 ident_str!("new_object"),
71);
72const SUI_SYSTEM_CREATE: FunctionIdent = (
73 SUI_SYSTEM_ADDRESS,
74 SUI_SYSTEM_MODULE_NAME,
75 ident_str!("create"),
76);
77const SUI_CLOCK_CREATE: FunctionIdent = (
78 SUI_FRAMEWORK_ADDRESS,
79 CLOCK_MODULE_NAME,
80 ident_str!("create"),
81);
82const SUI_AUTHENTICATOR_STATE_CREATE: FunctionIdent = (
83 SUI_FRAMEWORK_ADDRESS,
84 AUTHENTICATOR_STATE_MODULE_NAME,
85 ident_str!("create"),
86);
87const SUI_RANDOMNESS_STATE_CREATE: FunctionIdent = (
88 SUI_FRAMEWORK_ADDRESS,
89 RANDOMNESS_MODULE_NAME,
90 ident_str!("create"),
91);
92const SUI_DENY_LIST_CREATE: FunctionIdent = (
93 SUI_FRAMEWORK_ADDRESS,
94 DENY_LIST_MODULE,
95 DENY_LIST_CREATE_FUNC,
96);
97
98const SUI_BRIDGE_CREATE: FunctionIdent = (BRIDGE_ADDRESS, BRIDGE_MODULE_NAME, ident_str!("create"));
99const SUI_ACCUMULATOR_CREATE: FunctionIdent = (
100 SUI_FRAMEWORK_ADDRESS,
101 ACCUMULATOR_MODULE_NAME,
102 ident_str!("create"),
103);
104const SUI_COIN_REGISTRY_CREATE: FunctionIdent = (
105 SUI_FRAMEWORK_ADDRESS,
106 ident_str!("coin_registry"),
107 ident_str!("create"),
108);
109const SUI_DISPLAY_REGISTRY_CREATE: FunctionIdent = (
110 SUI_FRAMEWORK_ADDRESS,
111 ident_str!("display_registry"),
112 ident_str!("create"),
113);
114const SUI_ALIAS_CREATE: FunctionIdent = (
115 SUI_FRAMEWORK_ADDRESS,
116 ident_str!("address_alias"),
117 ident_str!("create"),
118);
119const FRESH_ID_FUNCTIONS: &[FunctionIdent] = &[
120 OBJECT_NEW,
121 OBJECT_NEW_UID_FROM_HASH,
122 OBJECT_NEW_DERIVED,
123 TS_NEW_OBJECT,
124];
125const FUNCTIONS_TO_SKIP: &[FunctionIdent] = &[
126 SUI_SYSTEM_CREATE,
127 SUI_CLOCK_CREATE,
128 SUI_AUTHENTICATOR_STATE_CREATE,
129 SUI_RANDOMNESS_STATE_CREATE,
130 SUI_DENY_LIST_CREATE,
131 SUI_BRIDGE_CREATE,
132 SUI_ACCUMULATOR_CREATE,
133 SUI_COIN_REGISTRY_CREATE,
134 SUI_DISPLAY_REGISTRY_CREATE,
135 SUI_ALIAS_CREATE,
136];
137
138impl AbstractValue {
139 pub fn join(&self, value: &AbstractValue) -> AbstractValue {
140 if self == value {
141 *value
142 } else {
143 AbstractValue::Other
144 }
145 }
146}
147
148pub fn verify_module(
149 module: &CompiledModule,
150 meter: &mut (impl Meter + ?Sized),
151) -> Result<(), ExecutionError> {
152 verify_id_leak(module, meter)
153}
154
155fn verify_id_leak(
156 module: &CompiledModule,
157 meter: &mut (impl Meter + ?Sized),
158) -> Result<(), ExecutionError> {
159 for (index, func_def) in module.function_defs.iter().enumerate() {
160 let code = match func_def.code.as_ref() {
161 Some(code) => code,
162 None => continue,
163 };
164 let handle = module.function_handle_at(func_def.function);
165 let function_context =
166 FunctionContext::new(module, FunctionDefinitionIndex(index as u16), code, handle);
167 let initial_state = AbstractState::new(&function_context);
168 let mut verifier = IDLeakAnalysis::new(module, &function_context);
169 let function_to_verify = verifier.cur_function();
170 if FUNCTIONS_TO_SKIP.contains(&function_to_verify) {
171 continue;
172 }
173 analyze_function(&function_context, meter, &mut verifier, initial_state).map_err(
174 |err| {
175 if check_for_verifier_timeout(&err.major_status()) {
177 to_verification_timeout_error(err.to_string())
178 } else if let Some(message) = err.source().as_ref() {
179 let function_name =
180 module.identifier_at(module.function_handle_at(func_def.function).name);
181 let module_name = module.self_id();
182 verification_failure(format!(
183 "{} Found in {module_name}::{function_name}",
184 message
185 ))
186 } else {
187 verification_failure(err.to_string())
188 }
189 },
190 )?;
191 }
192
193 Ok(())
194}
195
196#[derive(Clone, Debug, PartialEq, Eq)]
197pub(crate) struct AbstractState {
198 locals: BTreeMap<LocalIndex, AbstractValue>,
199}
200
201impl AbstractState {
202 pub fn new(function_context: &FunctionContext) -> Self {
204 let mut state = AbstractState {
205 locals: BTreeMap::new(),
206 };
207
208 for param_idx in 0..function_context.parameters().len() {
209 state
210 .locals
211 .insert(param_idx as LocalIndex, AbstractValue::Other);
212 }
213
214 state
215 }
216}
217
218impl AbstractDomain for AbstractState {
219 fn join(
221 &mut self,
222 state: &AbstractState,
223 meter: &mut (impl Meter + ?Sized),
224 ) -> Result<JoinResult, PartialVMError> {
225 meter.add(Scope::Function, JOIN_BASE_COST)?;
226 meter.add_items(Scope::Function, JOIN_PER_LOCAL_COST, state.locals.len())?;
227 let mut changed = false;
228 for (local, value) in &state.locals {
229 let old_value = *self.locals.get(local).unwrap_or(&AbstractValue::Other);
230 let new_value = value.join(&old_value);
231 changed |= new_value != old_value;
232 self.locals.insert(*local, new_value);
233 }
234 if changed {
235 Ok(JoinResult::Changed)
236 } else {
237 Ok(JoinResult::Unchanged)
238 }
239 }
240}
241
242struct IDLeakAnalysis<'a> {
243 binary_view: &'a CompiledModule,
244 function_context: &'a FunctionContext<'a>,
245 stack: AbstractStack<AbstractValue>,
246}
247
248impl<'a> IDLeakAnalysis<'a> {
249 fn new(binary_view: &'a CompiledModule, function_context: &'a FunctionContext<'a>) -> Self {
250 Self {
251 binary_view,
252 function_context,
253 stack: AbstractStack::new(),
254 }
255 }
256
257 fn stack_popn(&mut self, n: u64) -> Result<(), PartialVMError> {
258 let Some(n) = NonZeroU64::new(n) else {
259 return Ok(());
260 };
261 self.stack.pop_any_n(n).map_err(|e| {
262 PartialVMError::new(StatusCode::VERIFIER_INVARIANT_VIOLATION)
263 .with_message(format!("Unexpected stack error on pop_n: {e}"))
264 })
265 }
266
267 fn stack_push(&mut self, val: AbstractValue) -> Result<(), PartialVMError> {
268 self.stack.push(val).map_err(|e| {
269 PartialVMError::new(StatusCode::VERIFIER_INVARIANT_VIOLATION)
270 .with_message(format!("Unexpected stack error on push: {e}"))
271 })
272 }
273
274 fn stack_pushn(&mut self, n: u64, val: AbstractValue) -> Result<(), PartialVMError> {
275 self.stack.push_n(val, n).map_err(|e| {
276 PartialVMError::new(StatusCode::VERIFIER_INVARIANT_VIOLATION)
277 .with_message(format!("Unexpected stack error on push_n: {e}"))
278 })
279 }
280
281 fn resolve_function(&self, function_handle: &FunctionHandle) -> FunctionIdent<'a> {
282 let m = self.binary_view.module_handle_at(function_handle.module);
283 let address = *self.binary_view.address_identifier_at(m.address);
284 let module = self.binary_view.identifier_at(m.name);
285 let function = self.binary_view.identifier_at(function_handle.name);
286 (address, module, function)
287 }
288
289 fn cur_function(&self) -> FunctionIdent<'a> {
290 let fdef = self
291 .binary_view
292 .function_def_at(self.function_context.index().unwrap());
293 let handle = self.binary_view.function_handle_at(fdef.function);
294 self.resolve_function(handle)
295 }
296}
297
298impl TransferFunctions for IDLeakAnalysis<'_> {
299 type State = AbstractState;
300
301 fn execute(
302 &mut self,
303 state: &mut Self::State,
304 bytecode: &Bytecode,
305 index: CodeOffset,
306 (_first_index, last_index): (u16, u16),
307 meter: &mut (impl Meter + ?Sized),
308 ) -> Result<(), PartialVMError> {
309 execute_inner(self, state, bytecode, index, meter)?;
310 if index == last_index && !self.stack.is_empty() {
314 let msg = "Invalid stack transitions. Non-zero stack size at the end of the block"
315 .to_string();
316 debug_assert!(false, "{msg}",);
317 return Err(
318 PartialVMError::new(StatusCode::VERIFIER_INVARIANT_VIOLATION).with_message(msg),
319 );
320 }
321 Ok(())
322 }
323}
324
325fn call(
326 verifier: &mut IDLeakAnalysis,
327 function_handle: &FunctionHandle,
328) -> Result<(), PartialVMError> {
329 let parameters = verifier
330 .binary_view
331 .signature_at(function_handle.parameters);
332 verifier.stack_popn(parameters.len() as u64)?;
333
334 let return_ = verifier.binary_view.signature_at(function_handle.return_);
335 let function = verifier.resolve_function(function_handle);
336 if FRESH_ID_FUNCTIONS.contains(&function) {
337 if return_.0.len() != 1 {
338 debug_assert!(false, "{:?} should have a single return value", function);
339 return Err(PartialVMError::new(StatusCode::UNKNOWN_VERIFICATION_ERROR)
340 .with_message("Should have a single return value".to_string())
341 .with_sub_status(
342 VMMVerifierErrorSubStatusCode::MULTIPLE_RETURN_VALUES_NOT_ALLOWED as u64,
343 ));
344 }
345 verifier.stack_push(AbstractValue::Fresh)?;
346 } else {
347 verifier.stack_pushn(return_.0.len() as u64, AbstractValue::Other)?;
348 }
349 Ok(())
350}
351
352fn num_fields(struct_def: &StructDefinition) -> u64 {
353 match &struct_def.field_information {
354 StructFieldInformation::Native => 0,
355 StructFieldInformation::Declared(fields) => fields.len() as u64,
356 }
357}
358
359fn pack(
360 verifier: &mut IDLeakAnalysis,
361 struct_def: &StructDefinition,
362) -> Result<(), PartialVMError> {
363 let handle = verifier
366 .binary_view
367 .datatype_handle_at(struct_def.struct_handle);
368 let num_fields = num_fields(struct_def);
369 verifier.stack_popn(num_fields - 1)?;
370 let last_value = verifier.stack.pop().unwrap();
371 if handle.abilities.has_key() && last_value != AbstractValue::Fresh {
372 let (cur_package, cur_module, cur_function) = verifier.cur_function();
373 let msg = format!(
374 "Invalid object creation in {cur_package}::{cur_module}::{cur_function}. \
375 Object created without a newly created UID. \
376 The UID must come directly from `sui::{}::{}`, or `sui::{}::{}`. \
377 For tests, it can also come from `sui::{}::{}`",
378 OBJECT_NEW.1,
379 OBJECT_NEW.2,
380 OBJECT_NEW_DERIVED.1,
381 OBJECT_NEW_DERIVED.2,
382 TS_NEW_OBJECT.1,
383 TS_NEW_OBJECT.2
384 );
385
386 return Err(PartialVMError::new(StatusCode::UNKNOWN_VERIFICATION_ERROR)
387 .with_message(msg)
388 .with_sub_status(VMMVerifierErrorSubStatusCode::INVALID_OBJECT_CREATION as u64));
389 }
390 verifier.stack_push(AbstractValue::Other)?;
391 Ok(())
392}
393
394fn unpack(
395 verifier: &mut IDLeakAnalysis,
396 struct_def: &StructDefinition,
397) -> Result<(), PartialVMError> {
398 verifier.stack.pop().unwrap();
399 verifier.stack_pushn(num_fields(struct_def), AbstractValue::Other)
400}
401
402fn execute_inner(
403 verifier: &mut IDLeakAnalysis,
404 state: &mut AbstractState,
405 bytecode: &Bytecode,
406 _: CodeOffset,
407 meter: &mut (impl Meter + ?Sized),
408) -> Result<(), PartialVMError> {
409 meter.add(Scope::Function, STEP_BASE_COST)?;
410 match bytecode {
412 Bytecode::Pop => {
413 verifier.stack.pop().unwrap();
414 }
415 Bytecode::CopyLoc(_local) => {
416 verifier.stack_push(AbstractValue::Other)?;
418 }
419 Bytecode::MoveLoc(local) => {
420 let value = state.locals.remove(local).unwrap();
421 verifier.stack_push(value)?;
422 }
423 Bytecode::StLoc(local) => {
424 let value = verifier.stack.pop().unwrap();
425 state.locals.insert(*local, value);
426 }
427
428 Bytecode::FreezeRef
430 | Bytecode::ReadRef
432 | Bytecode::CastU8
434 | Bytecode::CastU16
435 | Bytecode::CastU32
436 | Bytecode::CastU64
437 | Bytecode::CastU128
438 | Bytecode::CastU256
439 | Bytecode::Not
440 | Bytecode::VecLen(_)
441 | Bytecode::VecPopBack(_) => {
442 verifier.stack.pop().unwrap();
443 verifier.stack_push(AbstractValue::Other)?;
444 }
445
446 Bytecode::Branch(_)
448 | Bytecode::Nop => {}
449
450 Bytecode::Eq
452 | Bytecode::Neq
453 | Bytecode::Add
454 | Bytecode::Sub
455 | Bytecode::Mul
456 | Bytecode::Mod
457 | Bytecode::Div
458 | Bytecode::BitOr
459 | Bytecode::BitAnd
460 | Bytecode::Xor
461 | Bytecode::Shl
462 | Bytecode::Shr
463 | Bytecode::Or
464 | Bytecode::And
465 | Bytecode::Lt
466 | Bytecode::Gt
467 | Bytecode::Le
468 | Bytecode::Ge
469 | Bytecode::VecImmBorrow(_)
470 | Bytecode::VecMutBorrow(_) => {
471 verifier.stack.pop().unwrap();
472 verifier.stack.pop().unwrap();
473 verifier.stack_push(AbstractValue::Other)?;
474 }
475 Bytecode::WriteRef => {
476 verifier.stack.pop().unwrap();
477 verifier.stack.pop().unwrap();
478 }
479
480 Bytecode::MutBorrowLoc(_)
482 | Bytecode::ImmBorrowLoc(_) => verifier.stack_push(AbstractValue::Other)?,
483
484 | Bytecode::MutBorrowField(_)
485 | Bytecode::MutBorrowFieldGeneric(_)
486 | Bytecode::ImmBorrowField(_)
487 | Bytecode::ImmBorrowFieldGeneric(_) => {
488 verifier.stack.pop().unwrap();
489 verifier.stack_push(AbstractValue::Other)?;
490 }
491
492 Bytecode::MoveFromDeprecated(_)
495 | Bytecode::MoveFromGenericDeprecated(_)
496 | Bytecode::MoveToDeprecated(_)
497 | Bytecode::MoveToGenericDeprecated(_)
498 | Bytecode::ImmBorrowGlobalDeprecated(_)
499 | Bytecode::MutBorrowGlobalDeprecated(_)
500 | Bytecode::ImmBorrowGlobalGenericDeprecated(_)
501 | Bytecode::MutBorrowGlobalGenericDeprecated(_)
502 | Bytecode::ExistsDeprecated(_)
503 | Bytecode::ExistsGenericDeprecated(_) => {
504 panic!("Should have been checked by global_storage_access_verifier.");
505 }
506
507 Bytecode::Call(idx) => {
508 let function_handle = verifier.binary_view.function_handle_at(*idx);
509 call(verifier, function_handle)?;
510 }
511 Bytecode::CallGeneric(idx) => {
512 let func_inst = verifier.binary_view.function_instantiation_at(*idx);
513 let function_handle = verifier.binary_view.function_handle_at(func_inst.handle);
514 call(verifier, function_handle)?;
515 }
516
517 Bytecode::Ret => {
518 verifier.stack_popn(verifier.function_context.return_().len() as u64)?
519 }
520
521 Bytecode::BrTrue(_) | Bytecode::BrFalse(_) | Bytecode::Abort => {
522 verifier.stack.pop().unwrap();
523 }
524
525 Bytecode::LdTrue | Bytecode::LdFalse | Bytecode::LdU8(_) | Bytecode::LdU16(_)| Bytecode::LdU32(_) | Bytecode::LdU64(_) | Bytecode::LdU128(_)| Bytecode::LdU256(_) | Bytecode::LdConst(_) => {
527 verifier.stack_push(AbstractValue::Other)?;
528 }
529
530 Bytecode::Pack(idx) => {
531 let struct_def = verifier.binary_view.struct_def_at(*idx);
532 pack(verifier, struct_def)?;
533 }
534 Bytecode::PackGeneric(idx) => {
535 let struct_inst = verifier.binary_view.struct_instantiation_at(*idx);
536 let struct_def = verifier.binary_view.struct_def_at(struct_inst.def);
537 pack(verifier, struct_def)?;
538 }
539 Bytecode::Unpack(idx) => {
540 let struct_def = verifier.binary_view.struct_def_at(*idx);
541 unpack(verifier, struct_def)?;
542 }
543 Bytecode::UnpackGeneric(idx) => {
544 let struct_inst = verifier.binary_view.struct_instantiation_at(*idx);
545 let struct_def = verifier.binary_view.struct_def_at(struct_inst.def);
546 unpack(verifier, struct_def)?;
547 }
548
549 Bytecode::VecPack(_, num) => {
550 verifier.stack_popn(*num )?;
551 verifier.stack_push(AbstractValue::Other)?;
552 }
553
554 Bytecode::VecPushBack(_) => {
555 verifier.stack.pop().unwrap();
556 verifier.stack.pop().unwrap();
557 }
558
559 Bytecode::VecUnpack(_, num) => {
560 verifier.stack.pop().unwrap();
561 verifier.stack_pushn(*num, AbstractValue::Other)?;
562 }
563
564 Bytecode::VecSwap(_) => {
565 verifier.stack.pop().unwrap();
566 verifier.stack.pop().unwrap();
567 verifier.stack.pop().unwrap();
568 }
569 Bytecode::PackVariant(vidx) => {
570 let handle = verifier.binary_view.variant_handle_at(*vidx);
571 let variant = verifier.binary_view.variant_def_at(handle.enum_def, handle.variant);
572 let num_fields = variant.fields.len();
573 verifier.stack_popn(num_fields as u64)?;
574 verifier.stack_push(AbstractValue::Other)?;
575 }
576 Bytecode::PackVariantGeneric(vidx) => {
577 let handle = verifier.binary_view.variant_instantiation_handle_at(*vidx);
578 let enum_inst = verifier.binary_view.enum_instantiation_at(handle.enum_def);
579 let variant = verifier.binary_view.variant_def_at(enum_inst.def, handle.variant);
580 let num_fields = variant.fields.len();
581 verifier.stack_popn(num_fields as u64)?;
582 verifier.stack_push(AbstractValue::Other)?;
583 }
584 Bytecode::UnpackVariant(vidx)
585 | Bytecode::UnpackVariantImmRef(vidx)
586 | Bytecode::UnpackVariantMutRef(vidx) => {
587 let handle = verifier.binary_view.variant_handle_at(*vidx);
588 let variant = verifier.binary_view.variant_def_at(handle.enum_def, handle.variant);
589 let num_fields = variant.fields.len();
590 verifier.stack.pop().unwrap();
591 verifier.stack_pushn(num_fields as u64, AbstractValue::Other)?;
592 }
593 Bytecode::UnpackVariantGeneric(vidx)
594 | Bytecode::UnpackVariantGenericImmRef(vidx)
595 | Bytecode::UnpackVariantGenericMutRef(vidx) => {
596 let handle = verifier.binary_view.variant_instantiation_handle_at(*vidx);
597 let enum_inst = verifier.binary_view.enum_instantiation_at(handle.enum_def);
598 let variant = verifier.binary_view.variant_def_at(enum_inst.def, handle.variant);
599 let num_fields = variant.fields.len();
600 verifier.stack.pop().unwrap();
601 verifier.stack_pushn(num_fields as u64, AbstractValue::Other)?;
602 }
603 Bytecode::VariantSwitch(_) => {
604 verifier.stack.pop().unwrap();
605 }
606 };
607 Ok(())
608}