Skip to content

Commit

Permalink
[Compiler-v2][VM] add metadata check for script (#14099)
Browse files Browse the repository at this point in the history
* add metadata check

* script cache
  • Loading branch information
rahxephon89 authored Jul 29, 2024
1 parent 0a89990 commit f991f0d
Show file tree
Hide file tree
Showing 6 changed files with 173 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ pub enum FeatureFlag {
UseCompatibilityCheckerV2,
EnableEnumTypes,
EnableResourceAccessControl,
RejectUnstableBytecodeForScript,
}

fn generate_features_blob(writer: &CodeWriter, data: &[u64]) {
Expand Down Expand Up @@ -332,6 +333,9 @@ impl From<FeatureFlag> for AptosFeatureFlag {
FeatureFlag::EnableResourceAccessControl => {
AptosFeatureFlag::ENABLE_RESOURCE_ACCESS_CONTROL
},
FeatureFlag::RejectUnstableBytecodeForScript => {
AptosFeatureFlag::REJECT_UNSTABLE_BYTECODE_FOR_SCRIPT
},
}
}
}
Expand Down Expand Up @@ -467,6 +471,9 @@ impl From<AptosFeatureFlag> for FeatureFlag {
AptosFeatureFlag::ENABLE_RESOURCE_ACCESS_CONTROL => {
FeatureFlag::EnableResourceAccessControl
},
AptosFeatureFlag::REJECT_UNSTABLE_BYTECODE_FOR_SCRIPT => {
FeatureFlag::RejectUnstableBytecodeForScript
},
}
}
}
Expand Down
42 changes: 39 additions & 3 deletions aptos-move/aptos-vm/src/aptos_vm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ use move_binary_format::{
compatibility::Compatibility,
deserializer::DeserializerConfig,
errors::{Location, PartialVMError, PartialVMResult, VMError, VMResult},
file_format::CompiledScript,
CompiledModule,
};
use move_core_types::{
Expand Down Expand Up @@ -738,11 +739,30 @@ impl AptosVM {

let func = session.load_script(script.code(), script.ty_args())?;

// TODO(Gerardo): consolidate the extended validation to verifier.
verifier::event_validation::verify_no_event_emission_in_script(
let compiled_script = match CompiledScript::deserialize_with_config(
script.code(),
self.deserializer_config(),
)?;
) {
Ok(script) => script,
Err(err) => {
let msg = format!("[VM] deserializer for script returned error: {:?}", err);
let partial_err = PartialVMError::new(StatusCode::CODE_DESERIALIZATION_ERROR)
.with_message(msg)
.finish(Location::Script);
return Err(partial_err.into_vm_status());
},
};

// Check that unstable bytecode cannot be executed on mainnet
if self
.features()
.is_enabled(FeatureFlag::REJECT_UNSTABLE_BYTECODE_FOR_SCRIPT)
{
self.reject_unstable_bytecode_for_script(&compiled_script)?;
}

// TODO(Gerardo): consolidate the extended validation to verifier.
verifier::event_validation::verify_no_event_emission_in_compiled_script(&compiled_script)?;

let args = verifier::transaction_arg_validation::validate_combine_signer_and_txn_args(
session,
Expand Down Expand Up @@ -1622,6 +1642,22 @@ impl AptosVM {
Ok(())
}

/// Check whether the script can be run on mainnet based on the unstable tag in the metadata
pub fn reject_unstable_bytecode_for_script(&self, module: &CompiledScript) -> VMResult<()> {
if self.chain_id().is_mainnet() {
if let Some(metadata) =
aptos_framework::get_compilation_metadata_from_compiled_script(module)
{
if metadata.unstable {
return Err(PartialVMError::new(StatusCode::UNSTABLE_BYTECODE_REJECTED)
.with_message("script marked unstable cannot be run on mainnet".to_string())
.finish(Location::Script));
}
}
}
Ok(())
}

fn metadata_validation_error(msg: &str) -> VMError {
PartialVMError::new(StatusCode::CONSTRAINT_NOT_SATISFIED)
.with_message(format!("metadata and code bundle mismatch: {}", msg))
Expand Down
15 changes: 1 addition & 14 deletions aptos-move/aptos-vm/src/verifier/event_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use crate::move_vm_ext::SessionExt;
use aptos_framework::RuntimeModuleMetadataV1;
use move_binary_format::{
access::{ModuleAccess, ScriptAccess},
deserializer::DeserializerConfig,
errors::{Location, PartialVMError, VMError, VMResult},
file_format::{
Bytecode, CompiledScript,
Expand Down Expand Up @@ -151,19 +150,7 @@ pub(crate) fn extract_event_metadata(
Ok(event_structs)
}

pub(crate) fn verify_no_event_emission_in_script(
script_code: &[u8],
config: &DeserializerConfig,
) -> VMResult<()> {
let script = match CompiledScript::deserialize_with_config(script_code, config) {
Ok(script) => script,
Err(err) => {
let msg = format!("[VM] deserializer for script returned error: {:?}", err);
return Err(PartialVMError::new(StatusCode::CODE_DESERIALIZATION_ERROR)
.with_message(msg)
.finish(Location::Script));
},
};
pub(crate) fn verify_no_event_emission_in_compiled_script(script: &CompiledScript) -> VMResult<()> {
for bc in &script.code().code {
if let Bytecode::CallGeneric(index) = bc {
let func_instantiation = &script.function_instantiation_at(*index);
Expand Down
114 changes: 113 additions & 1 deletion aptos-move/e2e-move-tests/src/tests/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ use aptos_package_builder::PackageBuilder;
use aptos_types::{
chain_id::ChainId,
on_chain_config::{FeatureFlag, OnChainConfig},
transaction::TransactionStatus,
transaction::{Script, TransactionPayload, TransactionStatus},
};
use move_binary_format::CompiledModule;
use move_core_types::{
Expand Down Expand Up @@ -269,6 +269,118 @@ fn test_compilation_metadata_internal(
}
}

fn test_compilation_metadata_script_internal(
mainnet_flag: bool,
v2_flag: bool,
feature_enabled: bool,
) -> TransactionStatus {
let mut h = MoveHarness::new();
if feature_enabled {
h.enable_features(
vec![FeatureFlag::REJECT_UNSTABLE_BYTECODE_FOR_SCRIPT],
vec![],
);
} else {
h.enable_features(vec![], vec![
FeatureFlag::REJECT_UNSTABLE_BYTECODE_FOR_SCRIPT,
]);
}
let account = h.new_account_at(AccountAddress::from_hex_literal("0xf00d").unwrap());
let mut builder = PackageBuilder::new("Package");
builder.add_source(
"m.move",
r#"
script {
fun main() { }
}
"#,
);
let path = builder.write_to_temp().unwrap();

let compiler_version = if v2_flag {
CompilerVersion::V2_0
} else {
CompilerVersion::V1
};
let package = build_package_with_compiler_version(
path.path().to_path_buf(),
BuildOptions::default(),
compiler_version,
)
.expect("building package must succeed");

let code = package.extract_script_code().into_iter().next().unwrap();

let script = TransactionPayload::Script(Script::new(code, vec![], vec![]));

if mainnet_flag {
h.set_resource(
CORE_CODE_ADDRESS,
ChainId::struct_tag(),
&ChainId::mainnet().id(),
);
h.run_transaction_payload_mainnet(&account, script)
} else {
h.run_transaction_payload(&account, script)
}
}

#[test]
fn test_compilation_metadata_for_script() {
let mut enable_check = true;
// run compiler v2 code to mainnet
assert_vm_status!(
test_compilation_metadata_script_internal(true, true, enable_check),
StatusCode::UNSTABLE_BYTECODE_REJECTED
);
// run compiler v1 code to mainnet
assert_success!(test_compilation_metadata_script_internal(
true,
false,
enable_check
));
// run compiler v2 code to test
assert_success!(test_compilation_metadata_script_internal(
false,
true,
enable_check
));
// run compiler v1 code to test
assert_success!(test_compilation_metadata_script_internal(
false,
false,
enable_check
));

enable_check = false;
// run compiler v2 code to mainnet
// success because the feature flag is turned off
assert_success!(test_compilation_metadata_script_internal(
true,
true,
enable_check
),);
// run compiler v1 code to mainnet
assert_success!(test_compilation_metadata_script_internal(
true,
false,
enable_check
));
// run compiler v2 code to test
// success because the feature flag is turned off
assert_success!(test_compilation_metadata_script_internal(
false,
true,
enable_check
),);
// run compiler v1 code to test
assert_success!(test_compilation_metadata_script_internal(
false,
false,
enable_check
));
}

#[test]
fn test_compilation_metadata() {
let mut enable_check = true;
Expand Down
11 changes: 11 additions & 0 deletions aptos-move/framework/src/module_metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,17 @@ pub fn get_compilation_metadata_from_compiled_module(
}
}

/// Extract compilation metadata from a compiled script
pub fn get_compilation_metadata_from_compiled_script(
module: &CompiledScript,
) -> Option<CompilationMetadata> {
if let Some(data) = find_metadata_in_script(module, COMPILATION_METADATA_KEY) {
bcs::from_bytes::<CompilationMetadata>(&data.value).ok()
} else {
None
}
}

// This is mostly a copy paste of the existing function
// get_metadata_from_compiled_module. In the API types there is a unifying trait for
// modules and scripts called Bytecode that could help eliminate this duplication,
Expand Down
2 changes: 2 additions & 0 deletions types/src/on_chain_config/aptos_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ pub enum FeatureFlag {
USE_COMPATIBILITY_CHECKER_V2 = 73,
ENABLE_ENUM_TYPES = 74,
ENABLE_RESOURCE_ACCESS_CONTROL = 75,
REJECT_UNSTABLE_BYTECODE_FOR_SCRIPT = 76,
}

impl FeatureFlag {
Expand Down Expand Up @@ -165,6 +166,7 @@ impl FeatureFlag {
FeatureFlag::USE_COMPATIBILITY_CHECKER_V2,
FeatureFlag::ENABLE_ENUM_TYPES,
FeatureFlag::ENABLE_RESOURCE_ACCESS_CONTROL,
FeatureFlag::REJECT_UNSTABLE_BYTECODE_FOR_SCRIPT,
]
}
}
Expand Down

0 comments on commit f991f0d

Please sign in to comment.