Module can be duplicated in module publish requests
Description
Initia supports publishing Move modules from Move code. It is implemented as a publish request, which is posted from the Move code, and is executed at the end of the transaction. The newly published module will not be available until the next transaction. There can only be one pending request per transaction, but it is possible to publish an arbitrary number of modules simultaneously at the same owner address.
The following is the signature for the Move function to submit a publication request.
public entry fun publish(
owner: &signer,
module_ids: vector<String>, // 0x1::coin
code: vector<vector<u8>>,
upgrade_policy: u8,
) acquires ModuleStore, MetadataStore {
The two arrays, module_ids
and code
, can both contain duplicates. The same module name may appear multiple times in module_ids
, and binary modules with the same name may appear multiple times in code
.
The module_ids
are immediately deduplicated in the native code.
let mut expected_modules: BTreeSet<String> = BTreeSet::new();
for name in safely_pop_vec_arg!(arguments, Struct) {
let str_bytes = get_string(name)?;
context.charge(gas_params.per_byte * NumBytes::new(str_bytes.len() as u64))?;
expected_modules.insert(String::from_utf8(str_bytes).map_err(|_| {
SafeNativeError::Abort {
abort_code: EUNABLE_TO_PARSE_STRING,
}
})?);
}
Note the use of BTreeSet
, which is a deduplicating container.
The binary modules in code
eventually arrive at the following code, which also deduplicates the modules, in a last-overrides-first manner.
let mut map = codes
.iter()
.map(|c| {
let m = CompiledModule::deserialize(c).unwrap();
(m.self_id(), (c.as_slice(), m))
})
.collect::<BTreeMap<_, _>>();
Impact
Fortunately, the impact is limited to unintuitive behavior. Only the final set of deduplicated modules are actually used, with some duplicates silently discarded.
Recommendations
We recommend that duplicate checks be added and an intuitive error be returned.
Remediation
This issue has been acknowledged by Initia Labs, and fixes were implemented in the following commits: