Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
Fix potential huge allocation as a result of validate_block output
  • Loading branch information
mrcnski committed Jan 19, 2023
commit c132ce70a95e8f7a873682bcc3cc22aa603ca9ad
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions client/executor/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ sp-wasm-interface = { version = "7.0.0", path = "../../primitives/wasm-interface

[dev-dependencies]
array-bytes = "4.1"
assert_matches = "1.3.0"
wat = "1.0"
sc-runtime-test = { version = "2.0.0", path = "runtime-test" }
substrate-test-runtime = { version = "2.0.0", path = "../../test-utils/runtime" }
Expand Down
1 change: 1 addition & 0 deletions client/executor/runtime-test/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ targets = ["x86_64-unknown-linux-gnu"]
sp-core = { version = "7.0.0", default-features = false, path = "../../../primitives/core" }
sp-io = { version = "7.0.0", default-features = false, features = ["improved_panic_error_reporting"], path = "../../../primitives/io" }
sp-runtime = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime" }
sp-runtime-interface = { version = "7.0.0", default-features = false, path = "../../../primitives/runtime-interface" }
sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" }

[build-dependencies]
Expand Down
14 changes: 14 additions & 0 deletions client/executor/runtime-test/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ use sp_runtime::{
traits::{BlakeTwo256, Hash},
};

use sp_runtime_interface::pack_ptr_and_len;

extern "C" {
#[allow(dead_code)]
fn missing_external();
Expand Down Expand Up @@ -337,3 +339,15 @@ sp_core::wasm_export_functions! {
return 1234;
}
}

// Returns a huge len. It should result in an error, and not an allocation.
#[no_mangle]
pub extern "C" fn test_return_huge_len(_params: *const u8, _len: usize) -> u64 {
pack_ptr_and_len(0, u32::MAX)
}

// Returns an output that overflows the u32 range. It should result in an error.
#[no_mangle]
pub extern "C" fn test_return_overflow(_params: *const u8, _len: usize) -> u64 {
pack_ptr_and_len(u32::MAX, 1)
}
37 changes: 36 additions & 1 deletion client/executor/src/integration_tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
mod linux;

use codec::{Decode, Encode};
use sc_executor_common::{error::Error, runtime_blob::RuntimeBlob, wasm_runtime::WasmModule};
use sc_executor_common::{error::{Error, WasmError}, runtime_blob::RuntimeBlob, wasm_runtime::WasmModule};
use sc_runtime_test::wasm_binary_unwrap;
use sp_core::{
blake2_128, blake2_256, ed25519, map,
Expand All @@ -33,6 +33,7 @@ use sp_runtime::traits::BlakeTwo256;
use sp_state_machine::TestExternalities as CoreTestExternalities;
use sp_trie::{LayoutV1 as Layout, TrieConfiguration};
use std::sync::Arc;
use assert_matches::assert_matches;
use tracing_subscriber::layer::SubscriberExt;

use crate::WasmExecutionMethod;
Expand Down Expand Up @@ -781,3 +782,37 @@ fn return_value(wasm_method: WasmExecutionMethod) {
(1234u64).encode()
);
}

test_wasm_execution!(return_huge_len);
fn return_huge_len(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();

match call_in_wasm("test_return_huge_len", &[], wasm_method, &mut ext).unwrap_err() {
Error::Runtime => {
assert_matches!(wasm_method, WasmExecutionMethod::Interpreted);
}
Error::RuntimeConstruction(WasmError::Other(error)) => {
assert_matches!(wasm_method, WasmExecutionMethod::Compiled{..});
assert_eq!(error, "output exceeds bounds of wasm memory");
},
error => panic!("unexpected error: {:?}", error),
}
}

test_wasm_execution!(return_overflow);
fn return_overflow(wasm_method: WasmExecutionMethod) {
let mut ext = TestExternalities::default();
let mut ext = ext.ext();

match call_in_wasm("test_return_overflow", &[], wasm_method, &mut ext).unwrap_err() {
Error::Runtime => {
assert_matches!(wasm_method, WasmExecutionMethod::Interpreted);
}
Error::RuntimeConstruction(WasmError::Other(error)) => {
assert_matches!(wasm_method, WasmExecutionMethod::Compiled{..});
assert_eq!(error, "output exceeds bounds of wasm memory");
},
error => panic!("unexpected error: {:?}", error),
}
}
17 changes: 15 additions & 2 deletions client/executor/wasmtime/src/runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ use sc_executor_common::{
runtime_blob::{
self, DataSegmentsSnapshot, ExposedMutableGlobalsSet, GlobalsSnapshot, RuntimeBlob,
},
util::checked_range,
wasm_runtime::{InvokeMethod, WasmInstance, WasmModule},
};
use sp_runtime_interface::unpack_ptr_and_len;
Expand All @@ -41,7 +42,7 @@ use std::{
Arc,
},
};
use wasmtime::{Engine, Memory, StoreLimits, Table};
use wasmtime::{Engine, Memory, StoreLimits, Table, AsContext};

pub(crate) struct StoreData {
/// The limits we apply to the store. We need to store it here to return a reference to this
Expand Down Expand Up @@ -793,7 +794,19 @@ fn extract_output_data(
output_ptr: u32,
output_len: u32,
) -> Result<Vec<u8>> {
let ctx = instance.store();

// Do a length check before allocating. The returned output should not be bigger than the
// available WASM memory. Otherwise, a malicious parachain can trigger a large allocation,
// potentially causing memory exhaustion.
//
// Get the size of the WASM memory in bytes.
let memory_size = ctx.as_context().data().memory().data_size(ctx);
if let None = checked_range(output_ptr as usize, output_len as usize, memory_size) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if let None = checked_range(output_ptr as usize, output_len as usize, memory_size) {
if checked_range(output_ptr as usize, output_len as usize, memory_size).is_none() {

return Err(WasmError::Other("output exceeds bounds of wasm memory".into()))?
}
let mut output = vec![0; output_len as usize];
util::read_memory_into(instance.store(), Pointer::new(output_ptr), &mut output)?;

util::read_memory_into(ctx, Pointer::new(output_ptr), &mut output)?;
Ok(output)
}