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