From 8da90951eb1b35fc609a70445c5d64d8de95bd9d Mon Sep 17 00:00:00 2001 From: YsielX <332494219@qq.com> Date: Wed, 31 Dec 2025 04:35:47 +0000 Subject: [PATCH 1/4] fixed stack unbalance --- crates/movy-replay/src/tracer/concolic.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/crates/movy-replay/src/tracer/concolic.rs b/crates/movy-replay/src/tracer/concolic.rs index c3d7ebc..ba92064 100644 --- a/crates/movy-replay/src/tracer/concolic.rs +++ b/crates/movy-replay/src/tracer/concolic.rs @@ -316,7 +316,7 @@ impl ConcolicState { self.stack.pop(); } if self.stack.len() != s.value.len() { - debug!( + warn!( "stack: {:?}, stack from trace: {:?}, event: {:?}, disabling concolic execution", self.stack, s.value, event ); @@ -423,6 +423,13 @@ impl ConcolicState { instruction, extra, } => { + trace!( + "Before instruction at pc {}: {:?}, extra: {:?}. Current stack: {:?}", + pc, + instruction, + extra, + stack.map(|s| &s.value) + ); match instruction { Bytecode::Pop | Bytecode::BrTrue(_) @@ -1003,11 +1010,14 @@ impl ConcolicState { _ => unreachable!(), } } + Bytecode::VariantSwitch(_) => { + self.stack.pop(); + } _ => {} } } _ => { - trace!("Unsupported event: {:?}", event); + // trace!("Unsupported event: {:?}", event); } } None From 4f4502adb5a7d973319c250b7ac3fda7914d5235 Mon Sep 17 00:00:00 2001 From: YsielX <332494219@qq.com> Date: Wed, 31 Dec 2025 04:49:31 +0000 Subject: [PATCH 2/4] exclude non-public function in type graph --- crates/movy-analysis/src/type_graph.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/movy-analysis/src/type_graph.rs b/crates/movy-analysis/src/type_graph.rs index e5bbfa2..6c52936 100644 --- a/crates/movy-analysis/src/type_graph.rs +++ b/crates/movy-analysis/src/type_graph.rs @@ -4,7 +4,8 @@ use std::{ }; use movy_types::abi::{ - MoveAbiSignatureToken, MoveFunctionAbi, MoveModuleAbi, MoveModuleId, MovePackageAbi, + MoveAbiSignatureToken, MoveFunctionAbi, MoveFunctionVisibility, MoveModuleAbi, MoveModuleId, + MovePackageAbi, }; use petgraph::{graph::NodeIndex, visit::EdgeRef}; use serde::{Deserialize, Serialize}; @@ -113,6 +114,9 @@ impl MoveTypeGraph { log::trace!("Analyze module {}", &module.module_id); self.modules.insert(module.module_id.clone()); for func in module.functions.iter() { + if func.visibility != MoveFunctionVisibility::Public { + continue; + } self.add_function(func, &module.module_id); } } From b73e36a49d59bf4315a16fefd07b9758c6105cbb Mon Sep 17 00:00:00 2001 From: YsielX <332494219@qq.com> Date: Wed, 31 Dec 2025 05:40:52 +0000 Subject: [PATCH 3/4] processed T as hot potato --- crates/movy-fuzz/src/executor.rs | 12 +++++++++++- crates/movy-fuzz/src/mutators/object_data.rs | 19 +++++++++++-------- crates/movy-fuzz/src/mutators/utils.rs | 6 +++++- 3 files changed, 27 insertions(+), 10 deletions(-) diff --git a/crates/movy-fuzz/src/executor.rs b/crates/movy-fuzz/src/executor.rs index 65ac69f..47faa3a 100644 --- a/crates/movy-fuzz/src/executor.rs +++ b/crates/movy-fuzz/src/executor.rs @@ -148,8 +148,18 @@ where .expect("tracer should be present when tracing is enabled") .outcome(); + trace!("Execution finished with status: {:?}", effects.status()); + let (stage_idx, success) = match effects.status() { - ExecutionStatus::Failure { command, .. } => (command.map(|c| c), false), + ExecutionStatus::Failure { command, .. } => ( + // command index may be out of bound when meeting non-aborted error + if command.is_some_and(|c| c < input.sequence().commands.len()) { + command.clone() + } else { + None + }, + false, + ), _ => (None, true), }; if effects.status().is_err() { diff --git a/crates/movy-fuzz/src/mutators/object_data.rs b/crates/movy-fuzz/src/mutators/object_data.rs index 485d597..ba533a9 100644 --- a/crates/movy-fuzz/src/mutators/object_data.rs +++ b/crates/movy-fuzz/src/mutators/object_data.rs @@ -184,6 +184,7 @@ impl ObjectData { // continue; // Skip parameters that have the Copy ability // } let instantiated_param = param.subst(ty_args_map).unwrap(); + let partial_instantiation = param.partial_subst(ty_args_map); existing_objects .get_mut(&instantiated_param) .unwrap_or_else(|| { @@ -203,25 +204,26 @@ impl ObjectData { if matches!(arg, SequenceArgument::Input(_)) { continue; // Skip input arguments for key store and hot potatoes } - if param.is_balance() { + if partial_instantiation.is_balance() { balances.retain(|a| *a != *arg); // Remove from balances if it is a balance object } - if param.is_key_store() { + if partial_instantiation.is_key_store() { key_store_objects.retain(|a| *a != *arg); // Remove from key_store_objects if it is a key store object } - if param.is_hot_potato() { + if partial_instantiation.is_hot_potato() { hot_potatoes.remove( hot_potatoes .iter() .position(|x| x == &instantiated_param) .unwrap_or_else(|| { panic!( - "Expected hot potato object for type {:?}, input {}", + "Expected hot potato object for type {}, input {}, hot potatoes {:?}", instantiated_param, MoveFuzzInput { sequence: ptb.clone(), ..Default::default() - } + }, + hot_potatoes ) }), ); @@ -265,6 +267,7 @@ impl ObjectData { continue; } let instantiated_ret_ty = ret_ty.subst(ty_args_map).unwrap(); + let partial_instantiation = ret_ty.partial_subst(ty_args_map); if !matches!(instantiated_ret_ty, MoveTypeTag::Struct(_)) { continue; // Only process struct return types } @@ -277,13 +280,13 @@ impl ObjectData { .entry(instantiated_ret_ty.clone()) .or_default() .push((res_arg, Gate::Owned)); - if ret_ty.is_balance() { + if partial_instantiation.is_balance() { balances.push(res_arg); } - if ret_ty.is_key_store() { + if partial_instantiation.is_key_store() { key_store_objects.push(res_arg); } - if ret_ty.is_hot_potato() { + if partial_instantiation.is_hot_potato() { hot_potatoes.push(instantiated_ret_ty); } } diff --git a/crates/movy-fuzz/src/mutators/utils.rs b/crates/movy-fuzz/src/mutators/utils.rs index f42edbf..2f1801b 100644 --- a/crates/movy-fuzz/src/mutators/utils.rs +++ b/crates/movy-fuzz/src/mutators/utils.rs @@ -83,6 +83,10 @@ impl StageReplay { self.reset_progress(); return StageReplayAction::Fresh; }; + debug!( + "StageReplay decide: current stage idx {}, recorded stage idx {:?}, attempts {}", + stage_idx, self.stage_idx, self.attempts + ); let prev_stage = self.stage_idx; self.stage_idx = match self.stage_idx { @@ -266,7 +270,7 @@ where let mut ty_args = if let Some(stage_idx) = stage_idx { let Some(cmd) = ptb.commands.get_mut(*stage_idx) else { warn!( - "Stage idx {} out of bounds for commands: {:?}", + "Stage idx {} out of bounds for commands mutating ty args: {:?}", stage_idx, ptb.commands ); return MutationResult::Skipped; From d08982c60cc24fe39c8128b96f012f3138c21d18 Mon Sep 17 00:00:00 2001 From: YsielX <332494219@qq.com> Date: Wed, 31 Dec 2025 07:02:38 +0000 Subject: [PATCH 4/4] find public consumer/producer --- crates/movy-analysis/src/type_graph.rs | 11 ++++++++--- crates/movy-fuzz/src/meta.rs | 2 +- crates/movy-fuzz/src/mutators/object_data.rs | 2 +- crates/movy-fuzz/src/mutators/sequence/append.rs | 5 ++++- 4 files changed, 14 insertions(+), 6 deletions(-) diff --git a/crates/movy-analysis/src/type_graph.rs b/crates/movy-analysis/src/type_graph.rs index 6c52936..70e00ca 100644 --- a/crates/movy-analysis/src/type_graph.rs +++ b/crates/movy-analysis/src/type_graph.rs @@ -114,9 +114,6 @@ impl MoveTypeGraph { log::trace!("Analyze module {}", &module.module_id); self.modules.insert(module.module_id.clone()); for func in module.functions.iter() { - if func.visibility != MoveFunctionVisibility::Public { - continue; - } self.add_function(func, &module.module_id); } } @@ -130,6 +127,7 @@ impl MoveTypeGraph { pub fn find_consumers( &self, ty: &MoveAbiSignatureToken, + public_only: bool, ) -> Vec<(&MoveModuleId, &MoveFunctionAbi)> { let mut consumers = vec![]; for (graph_ty, node) in self.tys.iter() { @@ -141,6 +139,9 @@ impl MoveTypeGraph { .edges_directed(*node, petgraph::Direction::Outgoing) { if let TypeGraphNode::Function(m, f) = &self.graph[edge.target()] { + if public_only && f.visibility != MoveFunctionVisibility::Public { + continue; + } consumers.push((m, f)); } } @@ -152,6 +153,7 @@ impl MoveTypeGraph { pub fn find_producers( &self, ty: &MoveAbiSignatureToken, + public_only: bool, ) -> Vec<(MoveModuleId, MoveFunctionAbi)> { let mut producers = vec![]; for (graph_ty, node) in self.tys.iter() { @@ -163,6 +165,9 @@ impl MoveTypeGraph { .edges_directed(*node, petgraph::Direction::Incoming) { if let TypeGraphNode::Function(m, f) = &self.graph[edge.source()] { + if public_only && f.visibility != MoveFunctionVisibility::Public { + continue; + } producers.push((m.clone(), f.clone())); } } diff --git a/crates/movy-fuzz/src/meta.rs b/crates/movy-fuzz/src/meta.rs index 8a02cd7..ad365d3 100644 --- a/crates/movy-fuzz/src/meta.rs +++ b/crates/movy-fuzz/src/meta.rs @@ -216,7 +216,7 @@ fn should_skip_function(base: &Metadata, func_data: &MoveFunctionAbi) -> bool { MoveAbiSignatureToken::Reference(_) | MoveAbiSignatureToken::MutableReference(_) ); let hanging_hot_potato = - ret_ty.is_hot_potato() && base.type_graph.find_consumers(ret_ty).is_empty(); + ret_ty.is_hot_potato() && base.type_graph.find_consumers(ret_ty, true).is_empty(); if self_used || ret_ref || hanging_hot_potato { return true; } diff --git a/crates/movy-fuzz/src/mutators/object_data.rs b/crates/movy-fuzz/src/mutators/object_data.rs index ba533a9..5337c67 100644 --- a/crates/movy-fuzz/src/mutators/object_data.rs +++ b/crates/movy-fuzz/src/mutators/object_data.rs @@ -902,7 +902,7 @@ where .flat_map(|type_tag| { meta_state .type_graph - .find_consumers(&MoveAbiSignatureToken::from_type_tag_lossy(type_tag)) + .find_consumers(&MoveAbiSignatureToken::from_type_tag_lossy(type_tag), true) .iter() .map(|(module_id, consumer_function)| { FunctionIdent::new( diff --git a/crates/movy-fuzz/src/mutators/sequence/append.rs b/crates/movy-fuzz/src/mutators/sequence/append.rs index 38ad8f7..ad4b8a5 100644 --- a/crates/movy-fuzz/src/mutators/sequence/append.rs +++ b/crates/movy-fuzz/src/mutators/sequence/append.rs @@ -209,7 +209,10 @@ where } let arg_type = struct_params.remove(0).partial_subst(&ty_args); - let funcs = state.fuzz_state().type_graph.find_producers(&arg_type); + let funcs = state + .fuzz_state() + .type_graph + .find_producers(&arg_type, true); // except itself let funcs = funcs .iter()