From 671f5d66a0e4d771f4d93be980605343e3d0188e Mon Sep 17 00:00:00 2001 From: lazymio Date: Wed, 24 Dec 2025 09:08:25 +0000 Subject: [PATCH] various fixes --- .gitignore | 3 +- Cargo.lock | 56 +++++++++- crates/movy-fuzz/Cargo.toml | 2 +- crates/movy-fuzz/src/executor.rs | 4 +- crates/movy-fuzz/src/operations/sui_fuzz.rs | 2 +- .../src/oracles/sui/bool_judgement.rs | 4 +- .../src/oracles/sui/infinite_loop.rs | 4 +- crates/movy-fuzz/src/oracles/sui/overflow.rs | 2 +- .../src/oracles/sui/precision_loss.rs | 2 +- crates/movy-fuzz/src/oracles/sui/proceeds.rs | 2 +- .../src/oracles/sui/type_conversion.rs | 2 +- crates/movy-fuzz/src/oracles/sui/typed_bug.rs | 2 +- crates/movy-replay/src/tracer/fuzz.rs | 6 +- crates/movy-replay/src/tracer/oracle.rs | 41 ++++--- crates/movy-sui/src/database/cache.rs | 101 ++++++++++++++---- crates/movy-sui/src/database/file.rs | 8 +- crates/movy-sui/src/rpc/graphql.rs | 4 +- 17 files changed, 181 insertions(+), 64 deletions(-) diff --git a/.gitignore b/.gitignore index 41f6f6e..fbf4ec1 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ target/ build .vscode log -*.log \ No newline at end of file +*.log +flamegraph.svg \ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock index e84904c..d54155c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -95,6 +95,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", + "getrandom 0.3.4", "once_cell", "version_check", "zerocopy", @@ -3979,6 +3980,24 @@ dependencies = [ "serde_core", ] +[[package]] +name = "inferno" +version = "0.11.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "232929e1d75fe899576a3d5c7416ad0d88dbfbb3c3d6aa00873a7408a50ddb88" +dependencies = [ + "ahash", + "indexmap 2.12.1", + "is-terminal", + "itoa", + "log", + "num-format", + "once_cell", + "quick-xml 0.26.0", + "rgb", + "str_stack", +] + [[package]] name = "inline_colorization" version = "0.1.6" @@ -6250,6 +6269,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-format" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a652d9771a63711fd3c3deb670acfbe5c30a4072e664d7a3bf5a9e1056ac72c3" +dependencies = [ + "arrayvec", + "itoa", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -6406,7 +6435,7 @@ dependencies = [ "md-5", "parking_lot 0.12.5", "percent-encoding", - "quick-xml", + "quick-xml 0.37.5", "rand 0.8.5", "reqwest", "ring", @@ -7053,6 +7082,7 @@ dependencies = [ "backtrace", "cfg-if", "findshlibs", + "inferno", "libc", "log", "nix 0.26.4", @@ -7352,6 +7382,15 @@ version = "1.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d01941d82fa2ab50be1e79e6714289dd7cde78eba4c074bc5a4374f650dfe0" +[[package]] +name = "quick-xml" +version = "0.26.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f50b1c63b38611e7d4d7f68b82d3ad0cc71a2ad2e7f61fc10f1328d917c93cd" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.37.5" @@ -7791,6 +7830,15 @@ dependencies = [ "subtle", ] +[[package]] +name = "rgb" +version = "0.8.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce" +dependencies = [ + "bytemuck", +] + [[package]] name = "ring" version = "0.17.14" @@ -8992,6 +9040,12 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "str_stack" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9091b6114800a5f2141aee1d1b9d6ca3592ac062dc5decb3764ec5895a47b4eb" + [[package]] name = "string_cache" version = "0.8.9" diff --git a/crates/movy-fuzz/Cargo.toml b/crates/movy-fuzz/Cargo.toml index b67600c..0fc2158 100644 --- a/crates/movy-fuzz/Cargo.toml +++ b/crates/movy-fuzz/Cargo.toml @@ -42,4 +42,4 @@ move-vm-stack = {workspace = true} move-vm-types = {workspace = true} sui-types = {workspace = true} sui-json-rpc-types = {workspace = true} -pprof = {workspace = true, optional = true} +pprof = {workspace = true, optional = true, features = ["flamegraph"] } diff --git a/crates/movy-fuzz/src/executor.rs b/crates/movy-fuzz/src/executor.rs index bc74422..5632af1 100644 --- a/crates/movy-fuzz/src/executor.rs +++ b/crates/movy-fuzz/src/executor.rs @@ -1,4 +1,4 @@ -use std::{borrow::Cow, collections::BTreeMap, fmt::Display, marker::PhantomData}; +use std::{borrow::Cow, collections::BTreeMap, fmt::Display, marker::PhantomData, ops::AddAssign}; use libafl::{ HasMetadata, @@ -126,7 +126,7 @@ where self.oracles.pre_execution(&db, state, input.sequence())?; trace!("Executing input: {}", input.sequence()); - + state.executions_mut().add_assign(1); let gas_id = state.fuzz_state().gas_id; let tracer = SuiFuzzTracer::new(&mut self.ob, state, &mut self.oracles, CODE_OBSERVER_NAME); diff --git a/crates/movy-fuzz/src/operations/sui_fuzz.rs b/crates/movy-fuzz/src/operations/sui_fuzz.rs index d0d9a07..464bc7a 100644 --- a/crates/movy-fuzz/src/operations/sui_fuzz.rs +++ b/crates/movy-fuzz/src/operations/sui_fuzz.rs @@ -124,7 +124,7 @@ where executor: executor_inner, ob: tuple_list!(code_observer), attacker, - oracles: oracles(false), + oracles: oracles(true), disable_oracles: false, epoch: state.fuzz_state().epoch, epoch_ms: state.fuzz_state().epoch_ms, diff --git a/crates/movy-fuzz/src/oracles/sui/bool_judgement.rs b/crates/movy-fuzz/src/oracles/sui/bool_judgement.rs index 3937ea0..88f37d3 100644 --- a/crates/movy-fuzz/src/oracles/sui/bool_judgement.rs +++ b/crates/movy-fuzz/src/oracles/sui/bool_judgement.rs @@ -38,7 +38,7 @@ impl SuiGeneralOracle for BoolJudgementOracle { event: &TraceEvent, _stack: Option<&Stack>, symbol_stack: &ConcolicState, - current_function: Option, + current_function: Option<&movy_types::input::FunctionIdent>, _state: &mut S, ) -> Result, MovyError> { match event { @@ -46,7 +46,7 @@ impl SuiGeneralOracle for BoolJudgementOracle { pc, instruction, .. } => { let stack_syms = &symbol_stack.stack; - let current = current_function.as_ref().and_then(to_module_func); + let current = current_function.and_then(to_module_func); let loss = match instruction { Bytecode::Eq | Bytecode::Neq diff --git a/crates/movy-fuzz/src/oracles/sui/infinite_loop.rs b/crates/movy-fuzz/src/oracles/sui/infinite_loop.rs index 92e9138..d8f0a33 100644 --- a/crates/movy-fuzz/src/oracles/sui/infinite_loop.rs +++ b/crates/movy-fuzz/src/oracles/sui/infinite_loop.rs @@ -40,7 +40,7 @@ impl SuiGeneralOracle for InfiniteLoopOracle { event: &TraceEvent, _stack: Option<&Stack>, symbol_stack: &ConcolicState, - current_function: Option, + current_function: Option<&movy_types::input::FunctionIdent>, _state: &mut S, ) -> Result, MovyError> { match event { @@ -54,7 +54,7 @@ impl SuiGeneralOracle for InfiniteLoopOracle { } => { match instruction { Bytecode::BrFalse(_) | Bytecode::BrTrue(_) => { - let Some(func) = current_function.as_ref().and_then(to_module_func) else { + let Some(func) = current_function.and_then(to_module_func) else { return Ok(vec![]); }; if symbol_stack.stack.is_empty() { diff --git a/crates/movy-fuzz/src/oracles/sui/overflow.rs b/crates/movy-fuzz/src/oracles/sui/overflow.rs index 549f052..f692e50 100644 --- a/crates/movy-fuzz/src/oracles/sui/overflow.rs +++ b/crates/movy-fuzz/src/oracles/sui/overflow.rs @@ -43,7 +43,7 @@ impl SuiGeneralOracle for OverflowOracle { event: &TraceEvent, stack: Option<&Stack>, _symbol_stack: &ConcolicState, - current_function: Option, + current_function: Option<&movy_types::input::FunctionIdent>, _state: &mut S, ) -> Result, MovyError> { match event { diff --git a/crates/movy-fuzz/src/oracles/sui/precision_loss.rs b/crates/movy-fuzz/src/oracles/sui/precision_loss.rs index 4eeca67..9f50ccc 100644 --- a/crates/movy-fuzz/src/oracles/sui/precision_loss.rs +++ b/crates/movy-fuzz/src/oracles/sui/precision_loss.rs @@ -28,7 +28,7 @@ impl SuiGeneralOracle for PrecisionLossOracle { event: &TraceEvent, _stack: Option<&Stack>, symbol_stack: &ConcolicState, - current_function: Option, + current_function: Option<&movy_types::input::FunctionIdent>, _state: &mut S, ) -> Result, MovyError> { match event { diff --git a/crates/movy-fuzz/src/oracles/sui/proceeds.rs b/crates/movy-fuzz/src/oracles/sui/proceeds.rs index 33b6b2b..9ee6c5d 100644 --- a/crates/movy-fuzz/src/oracles/sui/proceeds.rs +++ b/crates/movy-fuzz/src/oracles/sui/proceeds.rs @@ -218,7 +218,7 @@ where _event: &TraceEvent, _stack: Option<&Stack>, _symbol_stack: &ConcolicState, - _current_function: Option, + _current_function: Option<&movy_types::input::FunctionIdent>, _state: &mut S, ) -> Result, MovyError> { Ok(vec![]) diff --git a/crates/movy-fuzz/src/oracles/sui/type_conversion.rs b/crates/movy-fuzz/src/oracles/sui/type_conversion.rs index 090de0f..ba0247c 100644 --- a/crates/movy-fuzz/src/oracles/sui/type_conversion.rs +++ b/crates/movy-fuzz/src/oracles/sui/type_conversion.rs @@ -28,7 +28,7 @@ impl SuiGeneralOracle for TypeConversionOracle { event: &TraceEvent, stack: Option<&Stack>, _symbol_stack: &ConcolicState, - current_function: Option, + current_function: Option<&movy_types::input::FunctionIdent>, _state: &mut S, ) -> Result, MovyError> { match event { diff --git a/crates/movy-fuzz/src/oracles/sui/typed_bug.rs b/crates/movy-fuzz/src/oracles/sui/typed_bug.rs index 192f0e1..759cd6b 100644 --- a/crates/movy-fuzz/src/oracles/sui/typed_bug.rs +++ b/crates/movy-fuzz/src/oracles/sui/typed_bug.rs @@ -34,7 +34,7 @@ where _event: &TraceEvent, _stack: Option<&Stack>, _symbol_stack: &ConcolicState, - _current_function: Option, + _current_function: Option<&movy_types::input::FunctionIdent>, _state: &mut S, ) -> Result, MovyError> { Ok(vec![]) diff --git a/crates/movy-replay/src/tracer/fuzz.rs b/crates/movy-replay/src/tracer/fuzz.rs index e2bb9e1..d780c5a 100644 --- a/crates/movy-replay/src/tracer/fuzz.rs +++ b/crates/movy-replay/src/tracer/fuzz.rs @@ -164,14 +164,14 @@ where let rhs = stack .value .get(stack_len - 1) - .ok_or_eyre(eyre!("stack less than 2?!"))? + .ok_or_else(|| eyre!("stack less than 2?!"))? .copy_value()? .value_as::()? .into(); let lhs = stack .value .get(stack_len - 2) - .ok_or_eyre(eyre!("stack less than 2?!"))? + .ok_or_else(|| eyre!("stack less than 2?!"))? .copy_value()? .value_as::()? .into(); @@ -190,7 +190,7 @@ where event, stack, &self.outcome.concolic, - self.current_functions.last().cloned(), + self.current_functions.last(), self.state, )?; if !oracle_vulns.is_empty() { diff --git a/crates/movy-replay/src/tracer/oracle.rs b/crates/movy-replay/src/tracer/oracle.rs index f9b69c8..29fc3c0 100644 --- a/crates/movy-replay/src/tracer/oracle.rs +++ b/crates/movy-replay/src/tracer/oracle.rs @@ -22,7 +22,7 @@ pub trait SuiGeneralOracle { event: &TraceEvent, stack: Option<&Stack>, symbol_stack: &ConcolicState, - current_function: Option, + current_function: Option<&FunctionIdent>, state: &mut S, ) -> Result, MovyError>; @@ -49,7 +49,7 @@ impl SuiGeneralOracle for () { _event: &TraceEvent, _stack: Option<&Stack>, _symbol_stack: &ConcolicState, - _current_function: Option, + _current_function: Option<&movy_types::input::FunctionIdent>, _state: &mut S, ) -> Result, MovyError> { Ok(vec![]) @@ -85,22 +85,19 @@ where event: &TraceEvent, stack: Option<&Stack>, symbol_stack: &ConcolicState, - current_function: Option, + current_function: Option<&movy_types::input::FunctionIdent>, state: &mut S, ) -> Result, MovyError> { - let mut findings = vec![]; - findings.extend(self.0.event( - event, - stack, - symbol_stack, - current_function.clone(), - state, - )?); - findings.extend( - self.1 - .event(event, stack, symbol_stack, current_function, state)?, - ); - Ok(findings) + Ok(self + .0 + .event(event, stack, symbol_stack, current_function.clone(), state)? + .into_iter() + .chain( + self.1 + .event(event, stack, symbol_stack, current_function, state)? + .into_iter(), + ) + .collect()) } fn done_execution( @@ -109,10 +106,12 @@ where state: &mut S, effects: &TransactionEffects, ) -> Result, MovyError> { - let mut findings = vec![]; - findings.extend(self.0.done_execution(db, state, effects)?); - findings.extend(self.1.done_execution(db, state, effects)?); - Ok(findings) + Ok(self + .0 + .done_execution(db, state, effects)? + .into_iter() + .chain(self.1.done_execution(db, state, effects)?.into_iter()) + .collect()) } } @@ -148,7 +147,7 @@ where event: &TraceEvent, stack: Option<&Stack>, symbol_stack: &ConcolicState, - current_function: Option, + current_function: Option<&FunctionIdent>, state: &mut S, ) -> Result, MovyError> { if self.disabled { diff --git a/crates/movy-sui/src/database/cache.rs b/crates/movy-sui/src/database/cache.rs index e0ad313..de7a7d0 100644 --- a/crates/movy-sui/src/database/cache.rs +++ b/crates/movy-sui/src/database/cache.rs @@ -28,7 +28,23 @@ pub trait ObjectSuiStoreCommit { #[derive(Debug, Default, Clone, Serialize, Deserialize)] pub struct CachedSnapshot { - pub objects: BTreeMap>, + pub objects: BTreeMap>>, +} + +enum ResolvedResult { + Unbound, + KnownNotExisting, + Existing(Object), +} + +impl ResolvedResult { + pub fn into_object(self) -> Option { + match self { + Self::Unbound => None, + Self::KnownNotExisting => None, + Self::Existing(obj) => Some(obj), + } + } } impl CachedSnapshot { @@ -36,6 +52,7 @@ impl CachedSnapshot { self.objects .get(object) .and_then(|t| t.get(&version).cloned()) + .flatten() } pub fn object_id_cached(&self, object: &ObjectID) -> Option { @@ -43,18 +60,37 @@ impl CachedSnapshot { } pub fn object_resolved_upperbound(&self, object: &ObjectID, upbound: u64) -> Option { + self.object_resolve_upperbound_details(object, upbound) + .into_object() + } + + fn object_resolve_upperbound_details(&self, object: &ObjectID, upbound: u64) -> ResolvedResult { self.objects .get(object) - .and_then(|vmap| vmap.range(0..=upbound).max_by_key(|v| v.0)) - .map(|v| v.1.clone()) + .map(|vmap| { + let resolved = vmap.range(0..=upbound).max_by_key(|v| v.0); + if let Some(resolved) = resolved { + if let Some(object) = resolved.1.as_ref() { + ResolvedResult::Existing(object.clone()) + } else { + ResolvedResult::KnownNotExisting + } + } else { + ResolvedResult::Unbound + } + }) + .unwrap_or(ResolvedResult::Unbound) } pub fn cache_object_only(&mut self, object: Object) { - let object_id = object.id(); + // Shall we kept the old versions? Seems it does not violate any invariants + self.cache_query(object.id(), object.version().into(), Some(object)); + } + pub fn cache_query(&mut self, object_id: ObjectID, version: u64, object: Option) { self.objects .entry(object_id) .or_default() - .insert(object.version().into(), object); + .insert(version, object); } } @@ -221,29 +257,45 @@ impl ChildObjectResolver for CachedStore { let hit = { let guard = self.inner.borrow_mut(); - guard.object_resolved_upperbound(child, child_version_upper_bound.into()) + guard.object_resolve_upperbound_details(child, child_version_upper_bound.into()) }; - if let Some(hit) = hit { - if hit.version() == child_version_upper_bound { + match hit { + ResolvedResult::KnownNotExisting => { debug!( - "[CachedStore] read_child_object perfect hit for {}:{}, digest {}", - child, - child_version_upper_bound, - hit.digest() + "[CachedStore] read_child_object not existing for {}:{}", + child, child_version_upper_bound, ); + return Ok(None); + } + ResolvedResult::Existing(hit) => { + if hit.version() == child_version_upper_bound { + debug!( + "[CachedStore] read_child_object perfect hit for {}:{}, digest {}", + child, + child_version_upper_bound, + hit.digest() + ); - return Ok(Some(hit)); - } else { + return Ok(Some(hit)); + } else { + debug!( + "[CachedStore] read_child_object hit version {} but not the ideal version for {}:{}, digest {}", + hit.version(), + child, + child_version_upper_bound, + hit.digest() + ); + } + } + _ => { debug!( - "[CachedStore] read_child_object hit version {} but not the ideal version for {}:{}, digest {}", - hit.version(), - child, - child_version_upper_bound, - hit.digest() + "[CachedStore] read_child_object unbound miss for {}:{}", + child, child_version_upper_bound, ); } } + debug!( "[CachedStore] read_child_object miss for {}:{}", child, child_version_upper_bound, @@ -255,6 +307,9 @@ impl ChildObjectResolver for CachedStore { self.inner.borrow_mut().cache_object_only(hit.clone()); Ok(Some(hit)) } else { + self.inner + .borrow_mut() + .cache_query(*child, child_version_upper_bound.into(), None); Ok(None) } } @@ -293,7 +348,11 @@ impl ObjectSuiStoreCommit for CachedStore { "[CachedStore] Removing deleted/transferred consensus objects {}:{}", id, version ); - guard.objects.entry(id).or_default().remove(&version.into()); + guard + .objects + .entry(id) + .or_default() + .insert(version.into(), None); } let smeared_version = store.lamport_version; @@ -316,7 +375,7 @@ impl ObjectSuiStoreCommit for CachedStore { .objects .entry(id) .or_default() - .remove(&smeared_version.into()); + .insert(smeared_version.into(), None); } // Optionally prune history objects? diff --git a/crates/movy-sui/src/database/file.rs b/crates/movy-sui/src/database/file.rs index eb969a8..4bb5333 100644 --- a/crates/movy-sui/src/database/file.rs +++ b/crates/movy-sui/src/database/file.rs @@ -124,7 +124,7 @@ impl MDBXCachedStore { .entry(key.id.into()) .or_default() .entry(key.version) - .or_insert(value.object); + .or_insert(Some(value.object)); } Ok(snap) } @@ -137,7 +137,11 @@ impl MDBXCachedStore { let tx = self.env.begin_rw_txn().await?; for (_obj, obj_map) in snap.objects { for (_version, object) in obj_map { - self.may_cache_object_only(&tx, object).await?; + if let Some(object) = object { + self.may_cache_object_only(&tx, object).await?; + } else { + // TODO: ! + } } } Ok(()) diff --git a/crates/movy-sui/src/rpc/graphql.rs b/crates/movy-sui/src/rpc/graphql.rs index 4e31928..c639ce1 100644 --- a/crates/movy-sui/src/rpc/graphql.rs +++ b/crates/movy-sui/src/rpc/graphql.rs @@ -747,7 +747,7 @@ impl GraphQlClientInner { )?; out.push(object); } else { - log::debug!("Got a none in tx resp, probably {:?}", keys.get(idx)); + log::debug!("Got a none in objects resp, probably {:?}", keys.get(idx)); } } @@ -795,7 +795,7 @@ impl GraphQlClientInner { }; out.push(data); } else { - log::debug!("Got a none in tx resp, probably {:?}", keys.get(idx)); + log::debug!("Got a none in epoch resp, probably {:?}", keys.get(idx)); } }