Skip to content

[Bug] CompiledModule serialize→deserialize roundtrip broken for arbitrary-generated modules #19789

Description

@Esorat

Bug

CompiledModule::serialize_for_version produces binary output that CompiledModule::deserialize cannot parse — breaking the serialization roundtrip invariant.

To reproduce

use arbitrary::{Arbitrary, Unstructured};
use move_binary_format::{file_format::CompiledModule, file_format_common::VERSION_MAX};

let mut rng = Unstructured::new(&[0u8; 4096]);
let module = CompiledModule::arbitrary(&mut rng).unwrap();

let mut code = vec![];
module.serialize_for_version(Some(VERSION_MAX), &mut code).unwrap();
// serialize_for_version returns Ok — no error

CompiledModule::deserialize(&code).unwrap_err();
// deserialize fails — roundtrip broken

Native reproduction output:

self_module_handle_idx: ModuleHandleIndex(0)
module_handles: 0
serialized: 10 bytes (serialize_for_version → Ok)
deserialize FAILED: NO_MODULE_HANDLES

Root cause

The Arbitrary impl (behind fuzzing feature) generates modules where self_module_handle_idx points to index 0 but module_handles is empty. serialize_for_version does NOT validate the module before serializing — it writes 10 bytes successfully. deserialize correctly catches the invalid index via BoundsChecker.

Impact

Affected

third_party/move/move-binary-formatCompiledModule Arbitrary impl + serialize_for_version

Suggested fix

Fix the Arbitrary impl to ensure module_handles.len() > 0 when self_module_handle_idx is set — or add validation in serialize_for_version to reject modules with invalid self-references.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions