Skip to content
This repository was archived by the owner on May 4, 2024. It is now read-only.

Commit 8729cf2

Browse files
authored
add VMRuntimeLimitsConfig and vector len limit (#963)
* add vector len limit
1 parent 2ceb016 commit 8729cf2

File tree

13 files changed

+298
-11
lines changed

13 files changed

+298
-11
lines changed

language/move-bytecode-verifier/bytecode-verifier-tests/src/unit_tests/limit_tests.rs

Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,167 @@ fn max_mixed_config_test() {
587587
);
588588
}
589589

590+
#[test]
591+
fn max_vec_len() {
592+
let config = VerifierConfig {
593+
max_constant_vector_len: 0xFFFF - 1,
594+
..Default::default()
595+
};
596+
let double_vec = |item: Vec<u8>| -> Vec<u8> {
597+
let mut items = vec![2];
598+
items.extend(item.clone());
599+
items.extend(item);
600+
items
601+
};
602+
let large_vec = |item: Vec<u8>| -> Vec<u8> {
603+
let mut items = vec![0xFF, 0xFF, 3];
604+
(0..0xFFFF).for_each(|_| items.extend(item.clone()));
605+
items
606+
};
607+
fn tvec(s: SignatureToken) -> SignatureToken {
608+
SignatureToken::Vector(Box::new(s))
609+
}
610+
611+
let mut module = empty_module();
612+
module.constant_pool = vec![Constant {
613+
type_: tvec(SignatureToken::Bool),
614+
data: large_vec(vec![0]),
615+
}];
616+
let res = LimitsVerifier::verify_module(&config, &module);
617+
assert_eq!(
618+
res.unwrap_err().major_status(),
619+
StatusCode::TOO_MANY_VECTOR_ELEMENTS,
620+
);
621+
622+
let mut module = empty_module();
623+
module.constant_pool = vec![Constant {
624+
type_: tvec(SignatureToken::U256),
625+
data: large_vec(vec![
626+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
627+
0, 0, 0,
628+
]),
629+
}];
630+
let res = LimitsVerifier::verify_module(&config, &module);
631+
assert_eq!(
632+
res.unwrap_err().major_status(),
633+
StatusCode::TOO_MANY_VECTOR_ELEMENTS,
634+
);
635+
636+
let config = VerifierConfig {
637+
max_constant_vector_len: 0xFFFF,
638+
..Default::default()
639+
};
640+
641+
let mut module = empty_module();
642+
module.constant_pool = vec![
643+
// empty
644+
Constant {
645+
type_: tvec(SignatureToken::Bool),
646+
data: vec![0],
647+
},
648+
Constant {
649+
type_: tvec(tvec(SignatureToken::Bool)),
650+
data: vec![0],
651+
},
652+
Constant {
653+
type_: tvec(tvec(tvec(tvec(SignatureToken::Bool)))),
654+
data: vec![0],
655+
},
656+
Constant {
657+
type_: tvec(tvec(tvec(tvec(SignatureToken::Bool)))),
658+
data: double_vec(vec![0]),
659+
},
660+
// small
661+
Constant {
662+
type_: tvec(SignatureToken::Bool),
663+
data: vec![9, 1, 1, 1, 1, 1, 1, 1, 1, 1],
664+
},
665+
Constant {
666+
type_: tvec(SignatureToken::U8),
667+
data: vec![9, 1, 1, 1, 1, 1, 1, 1, 1, 1],
668+
},
669+
// large
670+
Constant {
671+
type_: tvec(SignatureToken::Bool),
672+
data: large_vec(vec![0]),
673+
},
674+
Constant {
675+
type_: tvec(SignatureToken::U8),
676+
data: large_vec(vec![0]),
677+
},
678+
Constant {
679+
type_: tvec(SignatureToken::U16),
680+
data: large_vec(vec![0, 0]),
681+
},
682+
Constant {
683+
type_: tvec(SignatureToken::U32),
684+
data: large_vec(vec![0, 0, 0, 0]),
685+
},
686+
Constant {
687+
type_: tvec(SignatureToken::U64),
688+
data: large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0]),
689+
},
690+
Constant {
691+
type_: tvec(SignatureToken::U128),
692+
data: large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
693+
},
694+
Constant {
695+
type_: tvec(SignatureToken::U256),
696+
data: large_vec(vec![
697+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
698+
0, 0, 0, 0,
699+
]),
700+
},
701+
Constant {
702+
type_: tvec(SignatureToken::Address),
703+
data: large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]),
704+
},
705+
// double large
706+
Constant {
707+
type_: tvec(tvec(SignatureToken::Bool)),
708+
data: double_vec(large_vec(vec![0])),
709+
},
710+
Constant {
711+
type_: tvec(tvec(SignatureToken::U8)),
712+
data: double_vec(large_vec(vec![0])),
713+
},
714+
Constant {
715+
type_: tvec(tvec(SignatureToken::U16)),
716+
data: double_vec(large_vec(vec![0, 0])),
717+
},
718+
Constant {
719+
type_: tvec(tvec(SignatureToken::U32)),
720+
data: double_vec(large_vec(vec![0, 0, 0, 0])),
721+
},
722+
Constant {
723+
type_: tvec(tvec(SignatureToken::U64)),
724+
data: double_vec(large_vec(vec![0, 0, 0, 0, 0, 0, 0, 0])),
725+
},
726+
Constant {
727+
type_: tvec(tvec(SignatureToken::U128)),
728+
data: double_vec(large_vec(vec![
729+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
730+
])),
731+
},
732+
Constant {
733+
type_: tvec(tvec(SignatureToken::U256)),
734+
data: double_vec(large_vec(vec![
735+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
736+
0, 0, 0, 0,
737+
])),
738+
},
739+
Constant {
740+
type_: tvec(tvec(SignatureToken::Address)),
741+
data: double_vec(large_vec(vec![
742+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
743+
])),
744+
},
745+
];
746+
let res = LimitsVerifier::verify_module(&config, &module);
747+
748+
assert!(res.is_ok());
749+
}
750+
590751
fn multi_struct(module: &mut CompiledModule, count: usize) {
591752
for i in 0..count {
592753
module

language/move-bytecode-verifier/bytecode-verifier-tests/src/unit_tests/signature_tests.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ use move_binary_format::file_format::{
77
Bytecode::*, CompiledModule, SignatureToken::*, Visibility::Public, *,
88
};
99
use move_bytecode_verifier::{
10-
verify_module, verify_module_with_config, SignatureChecker, VerifierConfig,
10+
verifier::MAX_CONSTANT_VECTOR_LEN, verify_module, verify_module_with_config, SignatureChecker,
11+
VerifierConfig,
1112
};
1213
use move_core_types::{
1314
account_address::AccountAddress, identifier::Identifier, vm_status::StatusCode,
@@ -228,6 +229,7 @@ fn big_signature_test() {
228229
max_struct_definitions: Some(200),
229230
max_fields_in_struct: Some(30),
230231
max_function_definitions: Some(1000),
232+
max_constant_vector_len: MAX_CONSTANT_VECTOR_LEN,
231233
},
232234
&module,
233235
)

language/move-bytecode-verifier/bytecode-verifier-tests/src/unit_tests/vec_pack_tests.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use move_binary_format::file_format::{
55
empty_module, Bytecode, CodeUnit, FunctionDefinition, FunctionHandle, FunctionHandleIndex,
66
IdentifierIndex, ModuleHandleIndex, Signature, SignatureIndex, SignatureToken, Visibility,
77
};
8-
use move_bytecode_verifier::VerifierConfig;
8+
use move_bytecode_verifier::{verifier::MAX_CONSTANT_VECTOR_LEN, VerifierConfig};
99
use move_core_types::{identifier::Identifier, vm_status::StatusCode};
1010

1111
fn vec_sig(len: usize) -> SignatureToken {
@@ -72,6 +72,7 @@ fn test_vec_pack() {
7272
max_struct_definitions: Some(200),
7373
max_fields_in_struct: Some(30),
7474
max_function_definitions: Some(1000),
75+
max_constant_vector_len: MAX_CONSTANT_VECTOR_LEN,
7576
},
7677
&m,
7778
)

language/move-bytecode-verifier/src/limits.rs

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44
use crate::VerifierConfig;
55
use move_binary_format::{
66
binary_views::BinaryIndexedView,
7-
errors::{Location, PartialVMError, PartialVMResult, VMResult},
8-
file_format::{CompiledModule, CompiledScript, SignatureToken, StructFieldInformation},
7+
errors::{verification_error, Location, PartialVMError, PartialVMResult, VMResult},
8+
file_format::{
9+
CompiledModule, CompiledScript, SignatureToken, StructFieldInformation, TableIndex,
10+
},
911
IndexKind,
1012
};
11-
use move_core_types::vm_status::StatusCode;
13+
use move_core_types::{value::MoveValue, vm_status::StatusCode};
1214

1315
pub struct LimitsVerifier<'a> {
1416
resolver: BinaryIndexedView<'a>,
@@ -27,6 +29,7 @@ impl<'a> LimitsVerifier<'a> {
2729
let limit_check = Self {
2830
resolver: BinaryIndexedView::Module(module),
2931
};
32+
limit_check.verify_constants(config)?;
3033
limit_check.verify_function_handles(config)?;
3134
limit_check.verify_struct_handles(config)?;
3235
limit_check.verify_type_nodes(config)?;
@@ -170,4 +173,35 @@ impl<'a> LimitsVerifier<'a> {
170173
}
171174
Ok(())
172175
}
176+
177+
fn verify_constants(&self, config: &VerifierConfig) -> PartialVMResult<()> {
178+
for (idx, constant) in self.resolver.constant_pool().iter().enumerate() {
179+
if let SignatureToken::Vector(_) = constant.type_ {
180+
if let MoveValue::Vector(cons) =
181+
constant.deserialize_constant().ok_or_else(|| {
182+
verification_error(
183+
StatusCode::MALFORMED_CONSTANT_DATA,
184+
IndexKind::ConstantPool,
185+
idx as TableIndex,
186+
)
187+
})?
188+
{
189+
if cons.len() > config.max_constant_vector_len as usize {
190+
return Err(PartialVMError::new(StatusCode::TOO_MANY_VECTOR_ELEMENTS)
191+
.with_message(format!(
192+
"vector size limit is {}",
193+
config.max_constant_vector_len as usize
194+
)));
195+
}
196+
} else {
197+
return Err(verification_error(
198+
StatusCode::INVALID_CONSTANT_TYPE,
199+
IndexKind::ConstantPool,
200+
idx as TableIndex,
201+
));
202+
}
203+
}
204+
}
205+
Ok(())
206+
}
173207
}

language/move-bytecode-verifier/src/verifier.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ use move_binary_format::{
1818
};
1919
use move_core_types::{state::VMState, vm_status::StatusCode};
2020

21+
pub const MAX_CONSTANT_VECTOR_LEN: u64 = 1024 * 1024;
22+
2123
#[derive(Debug, Clone)]
2224
pub struct VerifierConfig {
2325
pub max_loop_depth: Option<usize>,
@@ -31,6 +33,7 @@ pub struct VerifierConfig {
3133
pub max_struct_definitions: Option<usize>,
3234
pub max_fields_in_struct: Option<usize>,
3335
pub max_function_definitions: Option<usize>,
36+
pub max_constant_vector_len: u64,
3437
}
3538

3639
/// Helper for a "canonical" verification of a module.
@@ -139,6 +142,8 @@ impl Default for VerifierConfig {
139142
max_fields_in_struct: None,
140143
// Max count of functions in a module
141144
max_function_definitions: None,
145+
// Max len of vector constant
146+
max_constant_vector_len: MAX_CONSTANT_VECTOR_LEN,
142147
}
143148
}
144149
}

language/move-core/types/src/vm_status.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,7 @@ pub enum StatusCode {
608608
MAX_FUNCTION_DEFINITIONS_REACHED = 1119,
609609
MAX_STRUCT_DEFINITIONS_REACHED = 1120,
610610
MAX_FIELD_DEFINITIONS_REACHED = 1121,
611+
TOO_MANY_VECTOR_ELEMENTS = 1122,
611612

612613
// These are errors that the VM might raise if a violation of internal
613614
// invariants takes place.

language/move-stdlib/src/natives/vector.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,11 @@ pub fn native_push_back(
132132

133133
NativeResult::map_partial_vm_result_empty(
134134
native_gas_total_cost!(context, gas_left),
135-
r.push_back(e, &ty_args[0]),
135+
r.push_back(
136+
e,
137+
&ty_args[0],
138+
context.runtime_limits_config().vector_len_max,
139+
),
136140
)
137141
}
138142

language/move-stdlib/tests/move_unit_test.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ fn run_tests_for_pkg(path_to_pkg: impl Into<String>, include_nursery_natives: bo
3232
install_dir: Some(tempdir().unwrap().path().to_path_buf()),
3333
..Default::default()
3434
},
35-
UnitTestingConfig::default_with_bound(Some(100_000)),
35+
UnitTestingConfig::default_with_bound(Some(1_000_000_000)),
3636
natives,
3737
None,
3838
/* compute_coverage */ false,

language/move-stdlib/tests/vector_tests.move

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,4 +539,31 @@ module std::vector_tests {
539539
let v = vector[7];
540540
V::insert(&mut v, 6, 2);
541541
}
542+
543+
#[test]
544+
fun size_limit_ok() {
545+
let v = V::empty();
546+
let i = 0;
547+
// Limit is currently 1024 * 1024
548+
let max_len = 1024 * 1024;
549+
550+
while (i < max_len) {
551+
V::push_back(&mut v, i);
552+
i = i + 1;
553+
};
554+
}
555+
556+
#[test]
557+
#[expected_failure(vector_error, minor_status = 4, location = Self)]
558+
fun size_limit_fail() {
559+
let v = V::empty();
560+
let i = 0;
561+
// Limit is currently 1024 * 1024
562+
let max_len = 1024 * 1024 + 1;
563+
564+
while (i < max_len) {
565+
V::push_back(&mut v, i);
566+
i = i + 1;
567+
};
568+
}
542569
}

language/move-vm/runtime/src/config.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
use move_binary_format::file_format_common::VERSION_MAX;
5-
use move_bytecode_verifier::VerifierConfig;
5+
use move_bytecode_verifier::{verifier::MAX_CONSTANT_VECTOR_LEN, VerifierConfig};
66

77
/// Dynamic config options for the Move VM.
88
pub struct VMConfig {
@@ -11,6 +11,7 @@ pub struct VMConfig {
1111
// When this flag is set to true, MoveVM will perform type check at every instruction
1212
// execution to ensure that type safety cannot be violated at runtime.
1313
pub paranoid_type_checks: bool,
14+
pub runtime_limits_config: VMRuntimeLimitsConfig,
1415
}
1516

1617
impl Default for VMConfig {
@@ -19,6 +20,20 @@ impl Default for VMConfig {
1920
verifier: VerifierConfig::default(),
2021
max_binary_format_version: VERSION_MAX,
2122
paranoid_type_checks: false,
23+
runtime_limits_config: VMRuntimeLimitsConfig::default(),
24+
}
25+
}
26+
}
27+
28+
#[derive(Clone, Debug)]
29+
pub struct VMRuntimeLimitsConfig {
30+
/// Maximum number of items that can be pushed into a vec
31+
pub vector_len_max: u64,
32+
}
33+
impl Default for VMRuntimeLimitsConfig {
34+
fn default() -> Self {
35+
Self {
36+
vector_len_max: MAX_CONSTANT_VECTOR_LEN,
2237
}
2338
}
2439
}

0 commit comments

Comments
 (0)