sui_verifier_v0/
id_leak_verifier.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4//! Objects whose struct type has key ability represent Sui objects.
5//! They have unique IDs that should never be reused. This verifier makes
6//! sure that the id field of Sui objects never get leaked.
7//! Unpack is the only bytecode that could extract the id field out of
8//! a Sui object. From there, we track the flow of the value and make
9//! sure it can never get leaked outside of the function. There are four
10//! ways it can happen:
11//! 1. Returned
12//! 2. Written into a mutable reference
13//! 3. Added to a vector
14//! 4. Passed to a function cal::;
15use 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// Note: the authenticator/randomness objects should never exist when v0 execution is being used.
87// However, object_deletion_tests.rs forcibly sets the execution version to 0, so we need
88// to handle this case. Since that test only runs in the simulator we can special case it with
89// cfg(msim) so that we don't risk breaking release builds.
90#[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                // Handle verifificaiton timeout specially
161                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    /// create a new abstract state
187    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    /// attempts to join state to self and returns the result
204    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        // invariant: the stack should be empty at the end of the block
296        // If it is not, something is wrong with the implementation, so throw an invariant
297        // violation
298        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    // When packing, an object whose struct type has key ability must have the first field as
351    // "id". That fields must come from one of the functions that creates a new UID.
352    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    // TODO: Better diagnostics with location
393    match bytecode {
394        Bytecode::Pop => {
395            verifier.stack.pop().unwrap();
396        }
397        Bytecode::CopyLoc(_local) => {
398            // cannot copy a UID
399            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        // Reference won't be ID.
411        Bytecode::FreezeRef
412        // ID doesn't have copy ability, hence ReadRef won't produce an ID.
413        | Bytecode::ReadRef
414        // Following are unary operators that don't apply to ID.
415        | 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        // These bytecodes don't operate on any value.
429        Bytecode::Branch(_)
430        | Bytecode::Nop => {}
431
432        // These binary operators cannot produce ID as result.
433        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        // These bytecodes produce references, and hence cannot be ID.
463        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        // These bytecodes are not allowed, and will be
475        // flagged as error in a different verifier.
476        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        // These bytecodes produce constants, and hence cannot be ID.
508        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}