diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index fd1b13fdd0..5a43ccfda2 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -66,8 +66,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // Per convention: if the runtime behavior changes, increment spec_version and set impl_version // to equal spec_version. If only runtime implementation changes and behavior does not, then // leave spec_version as is and increment impl_version. - spec_version: 101, - impl_version: 101, + spec_version: 102, + impl_version: 102, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/srml/contracts/src/lib.rs b/substrate/srml/contracts/src/lib.rs index ec836419ab..1a9e09ba3f 100644 --- a/substrate/srml/contracts/src/lib.rs +++ b/substrate/srml/contracts/src/lib.rs @@ -886,6 +886,9 @@ pub struct Schedule { /// Maximum number of memory pages allowed for a contract. pub max_memory_pages: u32, + /// Maximum allowed size of a declared table. + pub max_table_size: u32, + /// Whether the `ext_println` function is allowed to be used contracts. /// MUST only be enabled for `dev` chains, NOT for production chains pub enable_println: bool, @@ -912,6 +915,7 @@ impl Default for Schedule { max_event_topics: 4, max_stack_height: 64 * 1024, max_memory_pages: 16, + max_table_size: 16 * 1024, enable_println: false, max_subject_len: 32, } diff --git a/substrate/srml/contracts/src/wasm/prepare.rs b/substrate/srml/contracts/src/wasm/prepare.rs index 0aed3655e8..c135c45d3a 100644 --- a/substrate/srml/contracts/src/wasm/prepare.rs +++ b/substrate/srml/contracts/src/wasm/prepare.rs @@ -74,6 +74,25 @@ impl<'a> ContractModule<'a> { Ok(()) } + /// Ensures that tables declared in the module are not too big. + fn ensure_table_size_limit(&self, limit: u32) -> Result<(), &'static str> { + if let Some(table_section) = self.module.table_section() { + // In Wasm MVP spec, there may be at most one table declared. Double check this + // explicitly just in case the Wasm version changes. + if table_section.entries().len() > 1 { + return Err("multiple tables declared"); + } + if let Some(table_type) = table_section.entries().first() { + // Check the table's initial size as there is no instruction or environment function + // capable of growing the table. + if table_type.limits().initial() > limit { + return Err("table exceeds maximum size allowed") + } + } + } + Ok(()) + } + fn inject_gas_metering(self) -> Result { let gas_rules = rules::Set::new( @@ -271,6 +290,7 @@ pub fn prepare_contract( let mut contract_module = ContractModule::new(original_code, schedule)?; contract_module.scan_exports()?; contract_module.ensure_no_internal_memory()?; + contract_module.ensure_table_size_limit(schedule.max_table_size)?; struct MemoryDefinition { initial: u32, @@ -503,6 +523,49 @@ mod tests { ); } + mod tables { + use super::*; + + // Tests below assumes that maximum table size is configured to a certain number. + #[test] + fn assume_table_size() { + assert_eq!(Schedule::default().max_table_size, 16384); + } + + prepare_test!(no_tables, + r#" + (module + (func (export "call")) + (func (export "deploy")) + ) + "#, + Ok(_) + ); + + prepare_test!(table_valid_size, + r#" + (module + (table 10000 funcref) + + (func (export "call")) + (func (export "deploy")) + ) + "#, + Ok(_) + ); + + prepare_test!(table_too_big, + r#" + (module + (table 20000 funcref) + + (func (export "call")) + (func (export "deploy")) + )"#, + Err("table exceeds maximum size allowed") + ); + } + mod imports { use super::*;