Skip to content
This repository was archived by the owner on Nov 15, 2023. It is now read-only.

Commit 12896e9

Browse files
authored
Never panic during execution proof check (#3504)
* do not panic during execution proof check * Update core/state-machine/src/lib.rs Co-Authored-By: DemiMarie-parity <[email protected]> * Update core/state-machine/src/lib.rs Co-Authored-By: DemiMarie-parity <[email protected]> * Update core/state-machine/src/lib.rs Co-Authored-By: DemiMarie-parity <[email protected]> * BackendTrustLevel enum * up runtime version * Update core/state-machine/src/lib.rs Co-Authored-By: DemiMarie-parity <[email protected]> * Update core/state-machine/src/lib.rs Co-Authored-By: DemiMarie-parity <[email protected]> * fixed some grumbles * Update core/state-machine/src/testing.rs Co-Authored-By: Gavin Wood <[email protected]> * Update core/state-machine/src/lib.rs Co-Authored-By: Gavin Wood <[email protected]> * mov where * spaces -> tabs (to restart build)
1 parent c768a7e commit 12896e9

File tree

18 files changed

+254
-78
lines changed

18 files changed

+254
-78
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

core/client/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ env_logger = "0.6"
3333
tempfile = "3.1"
3434
test-client = { package = "substrate-test-runtime-client", path = "../test-runtime/client" }
3535
kvdb-memorydb = { git = "https://github.com/paritytech/parity-common", rev="b0317f649ab2c665b7987b8475878fc4d2e1f81d" }
36+
panic-handler = { package = "substrate-panic-handler", path = "../panic-handler" }
3637

3738
[features]
3839
default = ["std"]

core/client/src/client.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use state_machine::{
4343
DBValue, Backend as StateBackend, ChangesTrieAnchorBlockId, ExecutionStrategy, ExecutionManager,
4444
prove_read, prove_child_read, ChangesTrieRootsStorage, ChangesTrieStorage,
4545
ChangesTrieTransaction, ChangesTrieConfigurationRange, key_changes, key_changes_proof,
46-
OverlayedChanges,
46+
OverlayedChanges, BackendTrustLevel,
4747
};
4848
use executor::{RuntimeVersion, RuntimeInfo};
4949
use consensus::{
@@ -1043,7 +1043,7 @@ impl<B, E, Block, RA> Client<B, E, Block, RA> where
10431043
let get_execution_manager = |execution_strategy: ExecutionStrategy| {
10441044
match execution_strategy {
10451045
ExecutionStrategy::NativeElseWasm => ExecutionManager::NativeElseWasm,
1046-
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm,
1046+
ExecutionStrategy::AlwaysWasm => ExecutionManager::AlwaysWasm(BackendTrustLevel::Trusted),
10471047
ExecutionStrategy::NativeWhenPossible => ExecutionManager::NativeWhenPossible,
10481048
ExecutionStrategy::Both => ExecutionManager::Both(|wasm_result, native_result| {
10491049
let header = import_headers.post();

core/client/src/light/call_executor.rs

Lines changed: 75 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -460,26 +460,46 @@ pub fn check_execution_proof<Header, E, H>(
460460
E: CodeExecutor<H>,
461461
H: Hasher,
462462
H::Out: Ord + 'static,
463+
{
464+
check_execution_proof_with_make_header(
465+
executor,
466+
request,
467+
remote_proof,
468+
|header| <Header as HeaderT>::new(
469+
*header.number() + One::one(),
470+
Default::default(),
471+
Default::default(),
472+
header.hash(),
473+
Default::default(),
474+
),
475+
)
476+
}
477+
478+
fn check_execution_proof_with_make_header<Header, E, H, MakeNextHeader: Fn(&Header) -> Header>(
479+
executor: &E,
480+
request: &RemoteCallRequest<Header>,
481+
remote_proof: Vec<Vec<u8>>,
482+
make_next_header: MakeNextHeader,
483+
) -> ClientResult<Vec<u8>>
484+
where
485+
Header: HeaderT,
486+
E: CodeExecutor<H>,
487+
H: Hasher,
488+
H::Out: Ord + 'static,
463489
{
464490
let local_state_root = request.header.state_root();
465491
let root: H::Out = convert_hash(&local_state_root);
466492

467493
// prepare execution environment + check preparation proof
468494
let mut changes = OverlayedChanges::default();
469495
let trie_backend = create_proof_check_backend(root, remote_proof)?;
470-
let next_block = <Header as HeaderT>::new(
471-
*request.header.number() + One::one(),
472-
Default::default(),
473-
Default::default(),
474-
request.header.hash(),
475-
Default::default(),
476-
);
496+
let next_header = make_next_header(&request.header);
477497
execution_proof_check_on_trie_backend::<H, _>(
478498
&trie_backend,
479499
&mut changes,
480500
executor,
481501
"Core_initialize_block",
482-
&next_block.encode(),
502+
&next_header.encode(),
483503
None,
484504
)?;
485505

@@ -530,6 +550,43 @@ mod tests {
530550
(remote_result, local_result)
531551
}
532552

553+
fn execute_with_proof_failure(remote_client: &TestClient, at: u64, method: &'static str) {
554+
let remote_block_id = BlockId::Number(at);
555+
let remote_header = remote_client.header(&remote_block_id).unwrap().unwrap();
556+
557+
// 'fetch' execution proof from remote node
558+
let (_, remote_execution_proof) = remote_client.execution_proof(
559+
&remote_block_id,
560+
method,
561+
&[]
562+
).unwrap();
563+
564+
// check remote execution proof locally
565+
let local_executor = NativeExecutor::<test_client::LocalExecutor>::new(None);
566+
let execution_result = check_execution_proof_with_make_header(
567+
&local_executor,
568+
&RemoteCallRequest {
569+
block: test_client::runtime::Hash::default(),
570+
header: remote_header,
571+
method: method.into(),
572+
call_data: vec![],
573+
retry_count: None,
574+
},
575+
remote_execution_proof,
576+
|header| <Header as HeaderT>::new(
577+
at + 1,
578+
Default::default(),
579+
Default::default(),
580+
header.hash(),
581+
header.digest().clone(), // this makes next header wrong
582+
),
583+
);
584+
match execution_result {
585+
Err(crate::error::Error::Execution(_)) => (),
586+
_ => panic!("Unexpected execution result: {:?}", execution_result),
587+
}
588+
}
589+
533590
// prepare remote client
534591
let remote_client = test_client::new();
535592
for i in 1u32..3u32 {
@@ -546,15 +603,24 @@ mod tests {
546603
let (remote, local) = execute(&remote_client, 0, "Core_version");
547604
assert_eq!(remote, local);
548605

606+
let (remote, local) = execute(&remote_client, 2, "Core_version");
607+
assert_eq!(remote, local);
608+
549609
// check method that requires environment
550610
let (_, block) = execute(&remote_client, 0, "BlockBuilder_finalize_block");
551611
let local_block: Header = Decode::decode(&mut &block[..]).unwrap();
552612
assert_eq!(local_block.number, 1);
553613

554-
// check method that requires environment
555614
let (_, block) = execute(&remote_client, 2, "BlockBuilder_finalize_block");
556615
let local_block: Header = Decode::decode(&mut &block[..]).unwrap();
557616
assert_eq!(local_block.number, 3);
617+
618+
// check that proof check doesn't panic even if proof is incorrect AND no panic handler is set
619+
execute_with_proof_failure(&remote_client, 2, "Core_version");
620+
621+
// check that proof check doesn't panic even if proof is incorrect AND panic handler is set
622+
panic_handler::set("TEST");
623+
execute_with_proof_failure(&remote_client, 2, "Core_version");
558624
}
559625

560626
#[test]

core/consensus/aura/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -806,7 +806,7 @@ mod tests {
806806
keystore.write().insert_ephemeral_from_seed::<AuthorityPair>(&key.to_seed())
807807
.expect("Creates authority key");
808808
keystore_paths.push(keystore_path);
809-
809+
810810
let environ = DummyFactory(client.clone());
811811
import_notifications.push(
812812
client.import_notification_stream()

core/consensus/babe/src/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ fn run_one_test() {
207207
let peer = net.peer(*peer_id);
208208
let client = peer.client().as_full().expect("Only full clients are used in tests").clone();
209209
let select_chain = peer.select_chain().expect("Full client has select_chain");
210-
210+
211211
let keystore_path = tempfile::tempdir().expect("Creates keystore path");
212212
let keystore = keystore::Store::open(keystore_path.path(), None).expect("Creates keystore");
213213
keystore.write().insert_ephemeral_from_seed::<AuthorityPair>(seed).expect("Generates authority key");

core/executor/src/wasm_executor.rs

Lines changed: 70 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
//! This module defines and implements the wasm part of Substrate Host Interface and provides
2020
//! an interface for calling into the wasm runtime.
2121
22-
use std::{convert::TryFrom, str};
22+
use std::{convert::TryFrom, str, panic};
2323
use tiny_keccak;
2424
use secp256k1;
2525

@@ -31,8 +31,8 @@ use crate::error::{Error, Result};
3131
use codec::{Encode, Decode};
3232
use primitives::{
3333
blake2_128, blake2_256, twox_64, twox_128, twox_256, ed25519, sr25519, Pair, crypto::KeyTypeId,
34-
offchain, hexdisplay::HexDisplay, sandbox as sandbox_primitives, H256, Blake2Hasher,
35-
traits::Externalities, child_storage_key::ChildStorageKey,
34+
offchain, hexdisplay::HexDisplay, sandbox as sandbox_primitives, Blake2Hasher,
35+
traits::Externalities,
3636
};
3737
use trie::{TrieConfiguration, trie_types::Layout};
3838
use crate::sandbox;
@@ -447,7 +447,9 @@ impl_wasm_host_interface! {
447447
.map_err(|_| "Invalid attempt to determine key in ext_set_storage")?;
448448
let value = context.read_memory(value_data, value_len)
449449
.map_err(|_| "Invalid attempt to determine value in ext_set_storage")?;
450-
runtime_io::set_storage(&key, &value);
450+
with_external_storage(move ||
451+
Ok(runtime_io::set_storage(&key, &value))
452+
)?;
451453
Ok(())
452454
}
453455

@@ -466,7 +468,9 @@ impl_wasm_host_interface! {
466468
let value = context.read_memory(value_data, value_len)
467469
.map_err(|_| "Invalid attempt to determine value in ext_set_child_storage")?;
468470

469-
runtime_io::set_child_storage(&storage_key, &key, &value);
471+
with_external_storage(move ||
472+
Ok(runtime_io::set_child_storage(&storage_key, &key, &value))
473+
)?;
470474
Ok(())
471475
}
472476

@@ -481,21 +485,27 @@ impl_wasm_host_interface! {
481485
let key = context.read_memory(key_data, key_len)
482486
.map_err(|_| "Invalid attempt to determine key in ext_clear_child_storage")?;
483487

484-
runtime_io::clear_child_storage(&storage_key, &key);
488+
with_external_storage(move ||
489+
Ok(runtime_io::clear_child_storage(&storage_key, &key))
490+
)?;
485491
Ok(())
486492
}
487493

488494
ext_clear_storage(key_data: Pointer<u8>, key_len: WordSize) {
489495
let key = context.read_memory(key_data, key_len)
490496
.map_err(|_| "Invalid attempt to determine key in ext_clear_storage")?;
491-
runtime_io::clear_storage(&key);
497+
with_external_storage(move ||
498+
Ok(runtime_io::clear_storage(&key))
499+
)?;
492500
Ok(())
493501
}
494502

495503
ext_exists_storage(key_data: Pointer<u8>, key_len: WordSize) -> u32 {
496504
let key = context.read_memory(key_data, key_len)
497505
.map_err(|_| "Invalid attempt to determine key in ext_exists_storage")?;
498-
Ok(if runtime_io::exists_storage(&key) { 1 } else { 0 })
506+
with_external_storage(move ||
507+
Ok(if runtime_io::exists_storage(&key) { 1 } else { 0 })
508+
)
499509
}
500510

501511
ext_exists_child_storage(
@@ -509,13 +519,17 @@ impl_wasm_host_interface! {
509519
let key = context.read_memory(key_data, key_len)
510520
.map_err(|_| "Invalid attempt to determine key in ext_exists_child_storage")?;
511521

512-
Ok(if runtime_io::exists_child_storage(&storage_key, &key) { 1 } else { 0 })
522+
with_external_storage(move ||
523+
Ok(if runtime_io::exists_child_storage(&storage_key, &key) { 1 } else { 0 })
524+
)
513525
}
514526

515527
ext_clear_prefix(prefix_data: Pointer<u8>, prefix_len: WordSize) {
516528
let prefix = context.read_memory(prefix_data, prefix_len)
517529
.map_err(|_| "Invalid attempt to determine prefix in ext_clear_prefix")?;
518-
runtime_io::clear_prefix(&prefix);
530+
with_external_storage(move ||
531+
Ok(runtime_io::clear_prefix(&prefix))
532+
)?;
519533
Ok(())
520534
}
521535

@@ -529,15 +543,19 @@ impl_wasm_host_interface! {
529543
.map_err(|_| "Invalid attempt to determine storage_key in ext_clear_child_prefix")?;
530544
let prefix = context.read_memory(prefix_data, prefix_len)
531545
.map_err(|_| "Invalid attempt to determine prefix in ext_clear_child_prefix")?;
532-
runtime_io::clear_child_prefix(&storage_key, &prefix);
546+
with_external_storage(move ||
547+
Ok(runtime_io::clear_child_prefix(&storage_key, &prefix))
548+
)?;
533549

534550
Ok(())
535551
}
536552

537553
ext_kill_child_storage(storage_key_data: Pointer<u8>, storage_key_len: WordSize) {
538554
let storage_key = context.read_memory(storage_key_data, storage_key_len)
539555
.map_err(|_| "Invalid attempt to determine storage_key in ext_kill_child_storage")?;
540-
runtime_io::kill_child_storage(&storage_key);
556+
with_external_storage(move ||
557+
Ok(runtime_io::kill_child_storage(&storage_key))
558+
)?;
541559

542560
Ok(())
543561
}
@@ -549,7 +567,9 @@ impl_wasm_host_interface! {
549567
) -> Pointer<u8> {
550568
let key = context.read_memory(key_data, key_len)
551569
.map_err(|_| "Invalid attempt to determine key in ext_get_allocated_storage")?;
552-
let maybe_value = runtime_io::storage(&key);
570+
let maybe_value = with_external_storage(move ||
571+
Ok(runtime_io::storage(&key))
572+
)?;
553573

554574
if let Some(value) = maybe_value {
555575
let offset = context.allocate_memory(value.len() as u32)?;
@@ -577,7 +597,9 @@ impl_wasm_host_interface! {
577597
let key = context.read_memory(key_data, key_len)
578598
.map_err(|_| "Invalid attempt to determine key in ext_get_allocated_child_storage")?;
579599

580-
let maybe_value = runtime_io::child_storage(&storage_key, &key);
600+
let maybe_value = with_external_storage(move ||
601+
Ok(runtime_io::child_storage(&storage_key, &key))
602+
)?;
581603

582604
if let Some(value) = maybe_value {
583605
let offset = context.allocate_memory(value.len() as u32)?;
@@ -602,7 +624,9 @@ impl_wasm_host_interface! {
602624
) -> WordSize {
603625
let key = context.read_memory(key_data, key_len)
604626
.map_err(|_| "Invalid attempt to get key in ext_get_storage_into")?;
605-
let maybe_value = runtime_io::storage(&key);
627+
let maybe_value = with_external_storage(move ||
628+
Ok(runtime_io::storage(&key))
629+
)?;
606630

607631
if let Some(value) = maybe_value {
608632
let value = &value[value_offset as usize..];
@@ -629,7 +653,9 @@ impl_wasm_host_interface! {
629653
let key = context.read_memory(key_data, key_len)
630654
.map_err(|_| "Invalid attempt to get key in ext_get_child_storage_into")?;
631655

632-
let maybe_value = runtime_io::child_storage(&storage_key, &key);
656+
let maybe_value = with_external_storage(move ||
657+
Ok(runtime_io::child_storage(&storage_key, &key))
658+
)?;
633659

634660
if let Some(value) = maybe_value {
635661
let value = &value[value_offset as usize..];
@@ -643,7 +669,9 @@ impl_wasm_host_interface! {
643669
}
644670

645671
ext_storage_root(result: Pointer<u8>) {
646-
let r = runtime_io::storage_root();
672+
let r = with_external_storage(move ||
673+
Ok(runtime_io::storage_root())
674+
)?;
647675
context.write_memory(result, r.as_ref())
648676
.map_err(|_| "Invalid attempt to set memory in ext_storage_root")?;
649677
Ok(())
@@ -656,7 +684,9 @@ impl_wasm_host_interface! {
656684
) -> Pointer<u8> {
657685
let storage_key = context.read_memory(storage_key_data, storage_key_len)
658686
.map_err(|_| "Invalid attempt to determine storage_key in ext_child_storage_root")?;
659-
let value = runtime_io::child_storage_root(&storage_key);
687+
let value = with_external_storage(move ||
688+
Ok(runtime_io::child_storage_root(&storage_key))
689+
)?;
660690

661691
let offset = context.allocate_memory(value.len() as u32)?;
662692
context.write_memory(offset, &value)
@@ -674,7 +704,9 @@ impl_wasm_host_interface! {
674704
let mut parent_hash = [0u8; 32];
675705
context.read_memory_into(parent_hash_data, &mut parent_hash[..])
676706
.map_err(|_| "Invalid attempt to get parent_hash in ext_storage_changes_root")?;
677-
let r = runtime_io::storage_changes_root(parent_hash);
707+
let r = with_external_storage(move ||
708+
Ok(runtime_io::storage_changes_root(parent_hash))
709+
)?;
678710

679711
if let Some(r) = r {
680712
context.write_memory(result, &r[..])
@@ -1331,6 +1363,25 @@ impl_wasm_host_interface! {
13311363
}
13321364
}
13331365

1366+
/// Execute closure that access external storage.
1367+
///
1368+
/// All panics that happen within closure are captured and transformed into
1369+
/// runtime error. This requires special panic handler mode to be enabled
1370+
/// during the call (see `panic_handler::AbortGuard::never_abort`).
1371+
/// If this mode isn't enabled, then all panics within externalities are
1372+
/// leading to process abort.
1373+
fn with_external_storage<T, F>(f: F) -> std::result::Result<T, String>
1374+
where
1375+
F: panic::UnwindSafe + FnOnce() -> Result<T>
1376+
{
1377+
// it is safe beause basic methods of StorageExternalities are guaranteed to touch only
1378+
// its internal state + we should discard it on error
1379+
panic::catch_unwind(move || f())
1380+
.map_err(|_| Error::Runtime)
1381+
.and_then(|result| result)
1382+
.map_err(|err| format!("{}", err))
1383+
}
1384+
13341385
/// Wasm rust executor for contracts.
13351386
///
13361387
/// Executes the provided code in a sandboxed wasm runtime.

0 commit comments

Comments
 (0)