1use move_binary_format::{
5 file_format::{AbilitySet, Bytecode, FunctionDefinition, SignatureToken, Visibility},
6 CompiledModule,
7};
8use move_bytecode_utils::format_signature_token;
9use move_vm_config::verifier::VerifierConfig;
10use sui_types::randomness_state::is_mutable_random;
11use sui_types::{
12 base_types::{TxContext, TxContextKind, TX_CONTEXT_MODULE_NAME, TX_CONTEXT_STRUCT_NAME},
13 clock::Clock,
14 error::ExecutionError,
15 is_object, is_object_vector, is_primitive,
16 move_package::{is_test_fun, FnInfoMap},
17 transfer::Receiving,
18 SUI_FRAMEWORK_ADDRESS,
19};
20
21use crate::{verification_failure, INIT_FN_NAME};
22
23pub fn verify_module(
40 module: &CompiledModule,
41 fn_info_map: &FnInfoMap,
42 verifier_config: &VerifierConfig,
43) -> Result<(), ExecutionError> {
44 for func_def in &module.function_defs {
48 let handle = module.function_handle_at(func_def.function);
49 let name = module.identifier_at(handle.name);
50
51 if !is_test_fun(name, module, fn_info_map) {
53 verify_init_not_called(module, func_def).map_err(verification_failure)?;
54 }
55
56 if name == INIT_FN_NAME {
57 verify_init_function(module, func_def).map_err(verification_failure)?;
58 continue;
59 }
60
61 if !func_def.is_entry {
64 continue;
66 }
67 verify_entry_function_impl(module, func_def, verifier_config)
68 .map_err(verification_failure)?;
69 }
70 Ok(())
71}
72
73fn verify_init_not_called(
74 module: &CompiledModule,
75 fdef: &FunctionDefinition,
76) -> Result<(), String> {
77 let code = match &fdef.code {
78 None => return Ok(()),
79 Some(code) => code,
80 };
81 code.code
82 .iter()
83 .enumerate()
84 .filter_map(|(idx, instr)| match instr {
85 Bytecode::Call(fhandle_idx) => Some((idx, module.function_handle_at(*fhandle_idx))),
86 Bytecode::CallGeneric(finst_idx) => {
87 let finst = module.function_instantiation_at(*finst_idx);
88 Some((idx, module.function_handle_at(finst.handle)))
89 }
90 _ => None,
91 })
92 .try_for_each(|(idx, fhandle)| {
93 let name = module.identifier_at(fhandle.name);
94 if name == INIT_FN_NAME {
95 Err(format!(
96 "{}::{} at offset {}. Cannot call a module's '{}' function from another Move function",
97 module.self_id(),
98 name,
99 idx,
100 INIT_FN_NAME
101 ))
102 } else {
103 Ok(())
104 }
105 })
106}
107
108fn verify_init_function(module: &CompiledModule, fdef: &FunctionDefinition) -> Result<(), String> {
110 if fdef.visibility != Visibility::Private {
111 return Err(format!(
112 "{}. '{}' function must be private",
113 module.self_id(),
114 INIT_FN_NAME
115 ));
116 }
117
118 if fdef.is_entry {
119 return Err(format!(
120 "{}. '{}' cannot be 'entry'",
121 module.self_id(),
122 INIT_FN_NAME
123 ));
124 }
125
126 let fhandle = module.function_handle_at(fdef.function);
127 if !fhandle.type_parameters.is_empty() {
128 return Err(format!(
129 "{}. '{}' function cannot have type parameters",
130 module.self_id(),
131 INIT_FN_NAME
132 ));
133 }
134
135 if !module.signature_at(fhandle.return_).is_empty() {
136 return Err(format!(
137 "{}, '{}' function cannot have return values",
138 module.self_id(),
139 INIT_FN_NAME
140 ));
141 }
142
143 let parameters = &module.signature_at(fhandle.parameters).0;
144 if parameters.is_empty() || parameters.len() > 2 {
145 return Err(format!(
146 "Expected at least one and at most two parameters for {}::{}",
147 module.self_id(),
148 INIT_FN_NAME,
149 ));
150 }
151
152 if TxContext::kind(module, ¶meters[parameters.len() - 1]) != TxContextKind::None {
157 Ok(())
158 } else {
159 Err(format!(
160 "Expected last parameter for {0}::{1} to be &mut {2}::{3}::{4} or &{2}::{3}::{4}, \
161 but found {5}",
162 module.self_id(),
163 INIT_FN_NAME,
164 SUI_FRAMEWORK_ADDRESS,
165 TX_CONTEXT_MODULE_NAME,
166 TX_CONTEXT_STRUCT_NAME,
167 format_signature_token(module, ¶meters[0]),
168 ))
169 }
170}
171
172fn verify_entry_function_impl(
173 module: &CompiledModule,
174 func_def: &FunctionDefinition,
175 verifier_config: &VerifierConfig,
176) -> Result<(), String> {
177 let handle = module.function_handle_at(func_def.function);
178 let params = module.signature_at(handle.parameters);
179
180 let all_non_ctx_params = match params.0.last() {
181 Some(last_param) if TxContext::kind(module, last_param) != TxContextKind::None => {
182 ¶ms.0[0..params.0.len() - 1]
183 }
184 _ => ¶ms.0,
185 };
186 for param in all_non_ctx_params {
187 verify_param_type(module, &handle.type_parameters, param, verifier_config)?;
188 }
189
190 for return_ty in &module.signature_at(handle.return_).0 {
191 verify_return_type(module, &handle.type_parameters, return_ty)?;
192 }
193
194 Ok(())
195}
196
197fn verify_return_type(
198 view: &CompiledModule,
199 type_parameters: &[AbilitySet],
200 return_ty: &SignatureToken,
201) -> Result<(), String> {
202 if matches!(
203 return_ty,
204 SignatureToken::Reference(_) | SignatureToken::MutableReference(_)
205 ) {
206 return Err("Invalid entry point return type. Expected a non reference type.".to_owned());
207 }
208 let abilities = view
209 .abilities(return_ty, type_parameters)
210 .map_err(|e| format!("Unexpected CompiledModule error: {}", e))?;
211 if abilities.has_drop() {
212 Ok(())
213 } else {
214 Err(format!(
215 "Invalid entry point return type. \
216 The specified return type does not have the 'drop' ability: {}",
217 format_signature_token(view, return_ty),
218 ))
219 }
220}
221
222fn verify_param_type(
223 view: &CompiledModule,
224 function_type_args: &[AbilitySet],
225 param: &SignatureToken,
226 verifier_config: &VerifierConfig,
227) -> Result<(), String> {
228 if Clock::is_mutable(view, param) {
231 return Err(format!(
232 "Invalid entry point parameter type. Clock must be passed by immutable reference. got: \
233 {}",
234 format_signature_token(view, param),
235 ));
236 }
237
238 if verifier_config.reject_mutable_random_on_entry_functions && is_mutable_random(view, param) {
241 return Err(format!(
242 "Invalid entry point parameter type. Random must be passed by immutable reference. got: \
243 {}",
244 format_signature_token(view, param),
245 ));
246 }
247
248 if is_primitive(view, function_type_args, param)
249 || is_object(view, function_type_args, param)?
250 || is_object_vector(view, function_type_args, param)?
251 || Receiving::is_receiving(view, param)
252 {
253 Ok(())
254 } else {
255 Err(format!(
256 "Invalid entry point parameter type. Expected primitive or object type. Got: {}",
257 format_signature_token(view, param)
258 ))
259 }
260}