srml-contract: Validate that modules do not declare oversized tables. (#2969)

* srml-contract: Validate that modules do not declare oversized tables.

* Bump node runtime spec/impl versions.
This commit is contained in:
Jim Posen
2019-07-02 20:07:00 +02:00
committed by Gavin Wood
parent 6acef99561
commit 24aa882ebc
3 changed files with 69 additions and 2 deletions
+2 -2
View File
@@ -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,
};
+4
View File
@@ -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,
}
@@ -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<Self, &'static str> {
let gas_rules =
rules::Set::new(
@@ -271,6 +290,7 @@ pub fn prepare_contract<C: ImportSatisfyCheck>(
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::*;