From 5722ece5efb61cc82307f3e919a60bd4e807f1df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexander=20Thei=C3=9Fen?= Date: Wed, 7 Dec 2022 13:48:30 +0100 Subject: [PATCH] Remove sandboxing host function interface (#12852) * Remove sandboxing interface * Remove unused struct --- .gitlab-ci.yml | 20 - Cargo.lock | 634 +----------------- Cargo.toml | 1 - bin/node/runtime/Cargo.toml | 7 - client/executor/Cargo.toml | 3 - client/executor/common/Cargo.toml | 7 - client/executor/common/src/error.rs | 3 - client/executor/common/src/lib.rs | 1 - client/executor/common/src/sandbox.rs | 585 ---------------- .../common/src/sandbox/wasmer_backend.rs | 449 ------------- .../common/src/sandbox/wasmi_backend.rs | 339 ---------- client/executor/runtime-test/Cargo.toml | 3 - client/executor/runtime-test/src/lib.rs | 159 ----- client/executor/src/integration_tests/mod.rs | 106 --- .../executor/src/integration_tests/sandbox.rs | 339 ---------- client/executor/src/lib.rs | 2 +- client/executor/wasmi/Cargo.toml | 2 - client/executor/wasmi/src/lib.rs | 232 +------ client/executor/wasmtime/Cargo.toml | 4 +- client/executor/wasmtime/src/host.rs | 277 +------- client/executor/wasmtime/src/runtime.rs | 10 - client/executor/wasmtime/src/util.rs | 18 - primitives/io/src/lib.rs | 94 --- primitives/sandbox/Cargo.toml | 40 -- primitives/sandbox/README.md | 21 - primitives/sandbox/src/embedded_executor.rs | 478 ------------- primitives/sandbox/src/env.rs | 120 ---- primitives/sandbox/src/host_executor.rs | 274 -------- primitives/sandbox/src/lib.rs | 190 ------ primitives/wasm-interface/src/lib.rs | 57 -- scripts/ci/gitlab/pipeline/test.yml | 37 +- 31 files changed, 34 insertions(+), 4478 deletions(-) delete mode 100644 client/executor/common/src/sandbox.rs delete mode 100644 client/executor/common/src/sandbox/wasmer_backend.rs delete mode 100644 client/executor/common/src/sandbox/wasmi_backend.rs delete mode 100644 client/executor/src/integration_tests/sandbox.rs delete mode 100644 primitives/sandbox/Cargo.toml delete mode 100644 primitives/sandbox/README.md delete mode 100644 primitives/sandbox/src/embedded_executor.rs delete mode 100644 primitives/sandbox/src/env.rs delete mode 100644 primitives/sandbox/src/host_executor.rs delete mode 100644 primitives/sandbox/src/lib.rs diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 992a2d491ae02..5b6748155444d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -166,26 +166,6 @@ default: - if: $CI_PIPELINE_SOURCE == "schedule" - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs -.test-refs-wasmer-sandbox: - rules: - - if: $CI_PIPELINE_SOURCE == "web" - - if: $CI_PIPELINE_SOURCE == "schedule" - - if: $CI_COMMIT_REF_NAME == "master" - changes: - - client/executor/**/* - - frame/contracts/**/* - - primitives/sandbox/**/* - - if: $CI_COMMIT_REF_NAME =~ /^[0-9]+$/ # PRs - changes: - - client/executor/**/* - - frame/contracts/**/* - - primitives/sandbox/**/* - - if: $CI_COMMIT_REF_NAME =~ /^v[0-9]+\.[0-9]+.*$/ # i.e. v1.0, v2.1rc1 - changes: - - client/executor/**/* - - frame/contracts/**/* - - primitives/sandbox/**/* - .build-refs: rules: - if: $CI_PIPELINE_SOURCE == "pipeline" diff --git a/Cargo.lock b/Cargo.lock index 17b66c920cd79..f2b394e099381 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,7 +18,7 @@ version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" dependencies = [ - "gimli 0.26.1", + "gimli", ] [[package]] @@ -648,27 +648,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3b5ca7a04898ad4bcd41c90c5285445ff5b791899bb1b0abdd2a2aa791211d7" -[[package]] -name = "bytecheck" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "314889ea31cda264cb7c3d6e6e5c9415a987ecb0e72c17c00d36fbb881d34abe" -dependencies = [ - "bytecheck_derive", - "ptr_meta", -] - -[[package]] -name = "bytecheck_derive" -version = "0.6.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a2b3b92c135dae665a6f760205b89187638e83bed17ef3e44e83c712cf30600" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "byteorder" version = "1.4.3" @@ -1020,39 +999,13 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8aebca1129a03dc6dc2b127edd729435bbc4a37e1d5f4d7513165089ceb02634" -[[package]] -name = "cranelift-bforest" -version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e6bea67967505247f54fa2c85cf4f6e0e31c4e5692c9b70e4ae58e339067333" -dependencies = [ - "cranelift-entity 0.76.0", -] - [[package]] name = "cranelift-bforest" version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b27bbd3e6c422cf6282b047bcdd51ecd9ca9f3497a3be0132ffa08e509b824b0" dependencies = [ - "cranelift-entity 0.88.0", -] - -[[package]] -name = "cranelift-codegen" -version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48194035d2752bdd5bdae429e3ab88676e95f52a2b1355a5d4e809f9e39b1d74" -dependencies = [ - "cranelift-bforest 0.76.0", - "cranelift-codegen-meta 0.76.0", - "cranelift-codegen-shared 0.76.0", - "cranelift-entity 0.76.0", - "gimli 0.25.0", - "log", - "regalloc", - "smallvec", - "target-lexicon", + "cranelift-entity", ] [[package]] @@ -1063,55 +1016,33 @@ checksum = "872f5d4557a411b087bd731df6347c142ae1004e6467a144a7e33662e5715a01" dependencies = [ "arrayvec 0.7.2", "bumpalo", - "cranelift-bforest 0.88.0", - "cranelift-codegen-meta 0.88.0", - "cranelift-codegen-shared 0.88.0", - "cranelift-entity 0.88.0", + "cranelift-bforest", + "cranelift-codegen-meta", + "cranelift-codegen-shared", + "cranelift-entity", "cranelift-isle", - "gimli 0.26.1", + "gimli", "log", "regalloc2", "smallvec", "target-lexicon", ] -[[package]] -name = "cranelift-codegen-meta" -version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976efb22fcab4f2cd6bd4e9913764616a54d895c1a23530128d04e03633c555f" -dependencies = [ - "cranelift-codegen-shared 0.76.0", - "cranelift-entity 0.76.0", -] - [[package]] name = "cranelift-codegen-meta" version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21b49fdebb29c62c1fc4da1eeebd609e9d530ecde24a9876def546275f73a244" dependencies = [ - "cranelift-codegen-shared 0.88.0", + "cranelift-codegen-shared", ] -[[package]] -name = "cranelift-codegen-shared" -version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dabb5fe66e04d4652e434195b45ae65b5c8172d520247b8f66d8df42b2b45dc" - [[package]] name = "cranelift-codegen-shared" version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fc0c091e2db055d4d7f6b7cec2d2ead286bcfaea3357c6a52c2a2613a8cb5ac" -[[package]] -name = "cranelift-entity" -version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3329733e4d4b8e91c809efcaa4faee80bf66f20164e3dd16d707346bd3494799" - [[package]] name = "cranelift-entity" version = "0.88.0" @@ -1121,25 +1052,13 @@ dependencies = [ "serde", ] -[[package]] -name = "cranelift-frontend" -version = "0.76.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "279afcc0d3e651b773f94837c3d581177b348c8d69e928104b2e9fccb226f921" -dependencies = [ - "cranelift-codegen 0.76.0", - "log", - "smallvec", - "target-lexicon", -] - [[package]] name = "cranelift-frontend" version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cd8dd3fb8b82c772f4172e87ae1677b971676fffa7c4e3398e3047e650a266b" dependencies = [ - "cranelift-codegen 0.88.0", + "cranelift-codegen", "log", "smallvec", "target-lexicon", @@ -1157,7 +1076,7 @@ version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c30ba8b910f1be023af0c39109cb28a8809734942a6b3eecbf2de8993052ea5e" dependencies = [ - "cranelift-codegen 0.88.0", + "cranelift-codegen", "libc", "target-lexicon", ] @@ -1168,13 +1087,13 @@ version = "0.88.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "776a8916d201894aca9637a20814f1e11abc62acd5cfbe0b4eb2e63922756971" dependencies = [ - "cranelift-codegen 0.88.0", - "cranelift-entity 0.88.0", - "cranelift-frontend 0.88.0", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", "itertools", "log", "smallvec", - "wasmparser 0.89.1", + "wasmparser", "wasmtime-types", ] @@ -1441,41 +1360,6 @@ dependencies = [ "syn", ] -[[package]] -name = "darling" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "757c0ded2af11d8e739c4daea1ac623dd1624b06c844cf3f5a39f1bdbd99bb12" -dependencies = [ - "darling_core", - "darling_macro", -] - -[[package]] -name = "darling_core" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c34d8efb62d0c2d7f60ece80f75e5c63c1588ba68032740494b0b9a996466e3" -dependencies = [ - "fnv", - "ident_case", - "proc-macro2", - "quote", - "strsim", - "syn", -] - -[[package]] -name = "darling_macro" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade7bff147130fe5e6d39f089c6bd49ec0250f35d70b2eebf72afdfc919f15cc" -dependencies = [ - "darling_core", - "quote", - "syn", -] - [[package]] name = "data-encoding" version = "2.3.2" @@ -1672,32 +1556,6 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e50f3adc76d6a43f5ed73b698a87d0760ca74617f60f7c3b879003536fdd28" -[[package]] -name = "dynasm" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "add9a102807b524ec050363f09e06f1504214b0e1c7797f64261c891022dce8b" -dependencies = [ - "bitflags", - "byteorder", - "lazy_static", - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "dynasmrt" -version = "1.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fba5a42bd76a17cad4bfa00de168ee1cbfa06a5e8ce992ae880218c05641a9" -dependencies = [ - "byteorder", - "dynasm", - "memmap2", -] - [[package]] name = "ecdsa" version = "0.14.7" @@ -1784,26 +1642,6 @@ dependencies = [ "syn", ] -[[package]] -name = "enum-iterator" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eeac5c5edb79e4e39fe8439ef35207780a11f69c52cbe424ce3dfad4cb78de6" -dependencies = [ - "enum-iterator-derive", -] - -[[package]] -name = "enum-iterator-derive" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c134c37760b27a871ba422106eedbb8247da973a09e82558bf26d619c882b159" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "enumflags2" version = "0.7.4" @@ -1824,27 +1662,6 @@ dependencies = [ "syn", ] -[[package]] -name = "enumset" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e76129da36102af021b8e5000dab2c1c30dbef85c1e482beeff8da5dde0e0b0" -dependencies = [ - "enumset_derive", -] - -[[package]] -name = "enumset_derive" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6451128aa6655d880755345d085494cf7561a6bee7c8dc821e5d77e6d267ecd4" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "env_logger" version = "0.9.0" @@ -2630,17 +2447,6 @@ dependencies = [ "polyval", ] -[[package]] -name = "gimli" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a01e0497841a3b2db4f8afa483cce65f7e96a3498bd6c541734792aeac8fe7" -dependencies = [ - "fallible-iterator", - "indexmap", - "stable_deref_trait", -] - [[package]] name = "gimli" version = "0.26.1" @@ -2754,9 +2560,6 @@ name = "hashbrown" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e" -dependencies = [ - "ahash", -] [[package]] name = "hashbrown" @@ -2930,12 +2733,6 @@ dependencies = [ "tokio-rustls", ] -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - [[package]] name = "idna" version = "0.2.3" @@ -3365,7 +3162,6 @@ dependencies = [ "sp-io", "sp-offchain", "sp-runtime", - "sp-sandbox", "sp-session", "sp-staking", "sp-std", @@ -3943,27 +3739,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "loupe" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b6a72dfa44fe15b5e76b94307eeb2ff995a8c5b283b55008940c02e0c5b634d" -dependencies = [ - "indexmap", - "loupe-derive", - "rustversion", -] - -[[package]] -name = "loupe-derive" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fbfc88337168279f2e9ae06e157cfed4efd3316e14dc96ed074d4f2e6c5952" -dependencies = [ - "quote", - "syn", -] - [[package]] name = "lru" version = "0.8.1" @@ -4197,12 +3972,6 @@ dependencies = [ "syn", ] -[[package]] -name = "more-asserts" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0debeb9fcf88823ea64d64e4a815ab1643f33127d995978e099942ce38f25238" - [[package]] name = "multiaddr" version = "0.14.0" @@ -4854,18 +4623,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "object" -version = "0.28.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40bec70ba014595f99f7aa110b84331ffe1ee9aece7fe6f387cc7e3ecda4d456" -dependencies = [ - "crc32fast", - "hashbrown 0.11.2", - "indexmap", - "memchr", -] - [[package]] name = "object" version = "0.29.0" @@ -6865,26 +6622,6 @@ dependencies = [ "cc", ] -[[package]] -name = "ptr_meta" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" -dependencies = [ - "ptr_meta_derive", -] - -[[package]] -name = "ptr_meta_derive" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "quick-error" version = "1.2.3" @@ -7096,17 +6833,6 @@ dependencies = [ "syn", ] -[[package]] -name = "regalloc" -version = "0.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "571f7f397d61c4755285cd37853fe8e03271c243424a907415909379659381c5" -dependencies = [ - "log", - "rustc-hash", - "smallvec", -] - [[package]] name = "regalloc2" version = "0.3.2" @@ -7146,18 +6872,6 @@ version = "0.6.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3f87b73ce11b1619a3c6332f45341e0047173771e8b8b73f87bfeefb7b56244" -[[package]] -name = "region" -version = "3.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76e189c2369884dce920945e2ddf79b3dff49e071a167dd1817fa9c4c00d512e" -dependencies = [ - "bitflags", - "libc", - "mach", - "winapi", -] - [[package]] name = "remove_dir_all" version = "0.5.3" @@ -7167,15 +6881,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rend" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79af64b4b6362ffba04eef3a4e10829718a4896dac19daa741851c86781edf95" -dependencies = [ - "bytecheck", -] - [[package]] name = "resolv-conf" version = "0.7.0" @@ -7212,31 +6917,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "rkyv" -version = "0.7.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f08c8062c1fe1253064043b8fc07bfea1b9702b71b4a86c11ea3588183b12e1" -dependencies = [ - "bytecheck", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e289706df51226e84814bf6ba1a9e1013112ae29bc7a9878f73fce360520c403" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "rocksdb" version = "0.19.0" @@ -7867,7 +7547,6 @@ dependencies = [ "array-bytes", "criterion", "env_logger", - "lazy_static", "lru", "num_cpus", "parity-scale-codec", @@ -7881,7 +7560,6 @@ dependencies = [ "sc-tracing", "sp-api", "sp-core", - "sp-core-hashing-proc-macro", "sp-externalities", "sp-io", "sp-maybe-compressed-blob", @@ -7904,15 +7582,11 @@ dependencies = [ name = "sc-executor-common" version = "0.10.0-dev" dependencies = [ - "environmental", - "parity-scale-codec", "sc-allocator", "sp-maybe-compressed-blob", - "sp-sandbox", "sp-wasm-interface", "thiserror", "wasm-instrument 0.3.0", - "wasmer", "wasmi 0.13.0", ] @@ -7921,11 +7595,9 @@ name = "sc-executor-wasmi" version = "0.10.0-dev" dependencies = [ "log", - "parity-scale-codec", "sc-allocator", "sc-executor-common", "sp-runtime-interface", - "sp-sandbox", "sp-wasm-interface", "wasmi 0.13.0", ] @@ -7939,7 +7611,6 @@ dependencies = [ "log", "once_cell", "parity-scale-codec", - "parity-wasm", "paste", "rustix", "sc-allocator", @@ -7947,7 +7618,6 @@ dependencies = [ "sc-runtime-test", "sp-io", "sp-runtime-interface", - "sp-sandbox", "sp-wasm-interface", "tempfile", "wasmtime", @@ -8443,11 +8113,9 @@ dependencies = [ name = "sc-runtime-test" version = "2.0.0" dependencies = [ - "paste", "sp-core", "sp-io", "sp-runtime", - "sp-sandbox", "sp-std", "substrate-wasm-builder", ] @@ -8802,12 +8470,6 @@ dependencies = [ "untrusted", ] -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - [[package]] name = "sec1" version = "0.3.0" @@ -8914,15 +8576,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_bytes" -version = "0.11.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16ae07dd2f88a366f15bd0632ba725227018c69a1c8550a927324f8eb8368bb9" -dependencies = [ - "serde", -] - [[package]] name = "serde_cbor" version = "0.11.1" @@ -9777,21 +9430,6 @@ dependencies = [ "substrate-wasm-builder", ] -[[package]] -name = "sp-sandbox" -version = "0.10.0-dev" -dependencies = [ - "assert_matches", - "log", - "parity-scale-codec", - "sp-core", - "sp-io", - "sp-std", - "sp-wasm-interface", - "wasmi 0.13.0", - "wat", -] - [[package]] name = "sp-serializer" version = "4.0.0-dev" @@ -10721,7 +10359,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a400e31aa60b9d44a52a8ee0343b5b18566b03a8321e0d321f695cf56e940160" dependencies = [ "cfg-if", - "log", "pin-project-lite 0.2.6", "tracing-attributes", "tracing-core", @@ -11301,219 +10938,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "wasmer" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f727a39e7161f7438ddb8eafe571b67c576a8c2fb459f666d9053b5bba4afdea" -dependencies = [ - "cfg-if", - "indexmap", - "js-sys", - "loupe", - "more-asserts", - "target-lexicon", - "thiserror", - "wasm-bindgen", - "wasmer-compiler", - "wasmer-compiler-cranelift", - "wasmer-compiler-singlepass", - "wasmer-derive", - "wasmer-engine", - "wasmer-engine-dylib", - "wasmer-engine-universal", - "wasmer-types", - "wasmer-vm", - "wat", - "winapi", -] - -[[package]] -name = "wasmer-compiler" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e9951599222eb12bd13d4d91bcded0a880e4c22c2dfdabdf5dc7e5e803b7bf3" -dependencies = [ - "enumset", - "loupe", - "rkyv", - "serde", - "serde_bytes", - "smallvec", - "target-lexicon", - "thiserror", - "wasmer-types", - "wasmer-vm", - "wasmparser 0.78.2", -] - -[[package]] -name = "wasmer-compiler-cranelift" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c83273bce44e668f3a2b9ccb7f1193db918b1d6806f64acc5ff71f6ece5f20" -dependencies = [ - "cranelift-codegen 0.76.0", - "cranelift-entity 0.76.0", - "cranelift-frontend 0.76.0", - "gimli 0.25.0", - "loupe", - "more-asserts", - "rayon", - "smallvec", - "target-lexicon", - "tracing", - "wasmer-compiler", - "wasmer-types", - "wasmer-vm", -] - -[[package]] -name = "wasmer-compiler-singlepass" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5432e993840cdb8e6875ddc8c9eea64e7a129579b4706bd91b8eb474d9c4a860" -dependencies = [ - "byteorder", - "dynasm", - "dynasmrt", - "lazy_static", - "loupe", - "more-asserts", - "rayon", - "smallvec", - "wasmer-compiler", - "wasmer-types", - "wasmer-vm", -] - -[[package]] -name = "wasmer-derive" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "458dbd9718a837e6dbc52003aef84487d79eedef5fa28c7d28b6784be98ac08e" -dependencies = [ - "proc-macro-error", - "proc-macro2", - "quote", - "syn", -] - -[[package]] -name = "wasmer-engine" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ed603a6d037ebbb14014d7f739ae996a78455a4b86c41cfa4e81c590a1253b9" -dependencies = [ - "backtrace", - "enumset", - "lazy_static", - "loupe", - "memmap2", - "more-asserts", - "rustc-demangle", - "serde", - "serde_bytes", - "target-lexicon", - "thiserror", - "wasmer-compiler", - "wasmer-types", - "wasmer-vm", -] - -[[package]] -name = "wasmer-engine-dylib" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd7fdc60e252a795c849b3f78a81a134783051407e7e279c10b7019139ef8dc" -dependencies = [ - "cfg-if", - "enum-iterator", - "enumset", - "leb128", - "libloading", - "loupe", - "object 0.28.3", - "rkyv", - "serde", - "tempfile", - "tracing", - "wasmer-compiler", - "wasmer-engine", - "wasmer-object", - "wasmer-types", - "wasmer-vm", - "which", -] - -[[package]] -name = "wasmer-engine-universal" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcff0cd2c01a8de6009fd863b14ea883132a468a24f2d2ee59dc34453d3a31b5" -dependencies = [ - "cfg-if", - "enum-iterator", - "enumset", - "leb128", - "loupe", - "region", - "rkyv", - "wasmer-compiler", - "wasmer-engine", - "wasmer-types", - "wasmer-vm", - "winapi", -] - -[[package]] -name = "wasmer-object" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24ce18ac2877050e59580d27ee1a88f3192d7a31e77fbba0852abc7888d6e0b5" -dependencies = [ - "object 0.28.3", - "thiserror", - "wasmer-compiler", - "wasmer-types", -] - -[[package]] -name = "wasmer-types" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "659fa3dd6c76f62630deff4ac8c7657b07f0b1e4d7e0f8243a552b9d9b448e24" -dependencies = [ - "indexmap", - "loupe", - "rkyv", - "serde", - "thiserror", -] - -[[package]] -name = "wasmer-vm" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdc46158517c2769f9938bc222a7d41b3bb330824196279d8aa2d667cd40641" -dependencies = [ - "backtrace", - "cc", - "cfg-if", - "enum-iterator", - "indexmap", - "libc", - "loupe", - "memoffset", - "more-asserts", - "region", - "rkyv", - "serde", - "thiserror", - "wasmer-types", - "winapi", -] - [[package]] name = "wasmi" version = "0.13.0" @@ -11576,12 +11000,6 @@ dependencies = [ "num-traits", ] -[[package]] -name = "wasmparser" -version = "0.78.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52144d4c78e5cf8b055ceab8e5fa22814ce4315d6002ad32cfd914f37c12fd65" - [[package]] name = "wasmparser" version = "0.89.1" @@ -11619,7 +11037,7 @@ dependencies = [ "rayon", "serde", "target-lexicon", - "wasmparser 0.89.1", + "wasmparser", "wasmtime-cache", "wasmtime-cranelift", "wasmtime-environ", @@ -11664,17 +11082,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f03cf79d982fc68e94ba0bea6a300a3b94621c4eb9705eece0a4f06b235a3b5" dependencies = [ "anyhow", - "cranelift-codegen 0.88.0", - "cranelift-entity 0.88.0", - "cranelift-frontend 0.88.0", + "cranelift-codegen", + "cranelift-entity", + "cranelift-frontend", "cranelift-native", "cranelift-wasm", - "gimli 0.26.1", + "gimli", "log", "object 0.29.0", "target-lexicon", "thiserror", - "wasmparser 0.89.1", + "wasmparser", "wasmtime-environ", ] @@ -11685,15 +11103,15 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c587c62e91c5499df62012b87b88890d0eb470b2ffecc5964e9da967b70c77c" dependencies = [ "anyhow", - "cranelift-entity 0.88.0", - "gimli 0.26.1", + "cranelift-entity", + "gimli", "indexmap", "log", "object 0.29.0", "serde", "target-lexicon", "thiserror", - "wasmparser 0.89.1", + "wasmparser", "wasmtime-types", ] @@ -11708,7 +11126,7 @@ dependencies = [ "bincode", "cfg-if", "cpp_demangle", - "gimli 0.26.1", + "gimli", "log", "object 0.29.0", "rustc-demangle", @@ -11764,10 +11182,10 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "790cf43ee8e2d5dad1780af30f00d7a972b74725fb1e4f90c28d62733819b185" dependencies = [ - "cranelift-entity 0.88.0", + "cranelift-entity", "serde", "thiserror", - "wasmparser 0.89.1", + "wasmparser", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e885f0916ca8e..64ceb104c649c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -199,7 +199,6 @@ members = [ "primitives/runtime-interface/test", "primitives/runtime-interface/test-wasm", "primitives/runtime-interface/test-wasm-deprecated", - "primitives/sandbox", "primitives/serializer", "primitives/session", "primitives/staking", diff --git a/bin/node/runtime/Cargo.toml b/bin/node/runtime/Cargo.toml index dfddf6a89499b..706d2c7720e2f 100644 --- a/bin/node/runtime/Cargo.toml +++ b/bin/node/runtime/Cargo.toml @@ -39,7 +39,6 @@ sp-session = { version = "4.0.0-dev", default-features = false, path = "../../.. sp-transaction-pool = { version = "4.0.0-dev", default-features = false, path = "../../../primitives/transaction-pool" } sp-version = { version = "5.0.0", default-features = false, path = "../../../primitives/version" } sp-io = { version = "7.0.0", default-features = false, path = "../../../primitives/io" } -sp-sandbox = { version = "0.10.0-dev", default-features = false, path = "../../../primitives/sandbox" } # frame dependencies frame-executive = { version = "4.0.0-dev", default-features = false, path = "../../../frame/executive" } @@ -117,7 +116,6 @@ substrate-wasm-builder = { version = "5.0.0-dev", path = "../../../utils/wasm-bu default = ["std"] with-tracing = ["frame-executive/with-tracing"] std = [ - "sp-sandbox/std", "pallet-whitelist/std", "pallet-offences-benchmarking?/std", "pallet-election-provider-support-benchmarking?/std", @@ -312,8 +310,3 @@ try-runtime = [ "pallet-vesting/try-runtime", "pallet-whitelist/try-runtime", ] -# Force `sp-sandbox` to call into the host resident executor. One still need to make sure -# that `sc-executor` gets the `wasmer-sandbox` feature which happens automatically when -# specified on the command line. -# Don't use that on a production chain. -wasmer-sandbox = ["sp-sandbox/wasmer-sandbox"] diff --git a/client/executor/Cargo.toml b/client/executor/Cargo.toml index 7cba725a9ea45..83d4801d1c92b 100644 --- a/client/executor/Cargo.toml +++ b/client/executor/Cargo.toml @@ -14,7 +14,6 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -lazy_static = "1.4.0" lru = "0.8.1" parking_lot = "0.12.1" tracing = "0.1.29" @@ -26,7 +25,6 @@ sc-executor-wasmi = { version = "0.10.0-dev", path = "wasmi" } sc-executor-wasmtime = { version = "0.10.0-dev", path = "wasmtime" } sp-api = { version = "4.0.0-dev", path = "../../primitives/api" } sp-core = { version = "7.0.0", path = "../../primitives/core" } -sp-core-hashing-proc-macro = { version = "5.0.0", path = "../../primitives/core/hashing/proc-macro" } sp-externalities = { version = "0.13.0", path = "../../primitives/externalities" } sp-io = { version = "7.0.0", path = "../../primitives/io" } sp-panic-handler = { version = "5.0.0", path = "../../primitives/panic-handler" } @@ -61,4 +59,3 @@ default = ["std"] # This crate does not have `no_std` support, we just require this for tests std = [] wasm-extern-trace = [] -wasmer-sandbox = ["sc-executor-common/wasmer-sandbox"] diff --git a/client/executor/common/Cargo.toml b/client/executor/common/Cargo.toml index 4b83e9fcc9b92..648e937d371ff 100644 --- a/client/executor/common/Cargo.toml +++ b/client/executor/common/Cargo.toml @@ -14,19 +14,12 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } -environmental = "1.1.3" thiserror = "1.0.30" wasm-instrument = "0.3" -wasmer = { version = "2.2", features = ["singlepass"], optional = true } wasmi = "0.13" sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sp-maybe-compressed-blob = { version = "4.1.0-dev", path = "../../../primitives/maybe-compressed-blob" } -sp-sandbox = { version = "0.10.0-dev", path = "../../../primitives/sandbox" } sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" } [features] default = [] -wasmer-sandbox = [ - "wasmer", -] diff --git a/client/executor/common/src/error.rs b/client/executor/common/src/error.rs index 376ac190bd7b7..c35a874b7796d 100644 --- a/client/executor/common/src/error.rs +++ b/client/executor/common/src/error.rs @@ -30,9 +30,6 @@ pub enum Error { #[error(transparent)] Wasmi(#[from] wasmi::Error), - #[error("Sandbox error: {0}")] - Sandbox(String), - #[error("Error calling api function: {0}")] ApiError(Box), diff --git a/client/executor/common/src/lib.rs b/client/executor/common/src/lib.rs index b69883afbaac2..79bb74b62a41e 100644 --- a/client/executor/common/src/lib.rs +++ b/client/executor/common/src/lib.rs @@ -23,6 +23,5 @@ pub mod error; pub mod runtime_blob; -pub mod sandbox; pub mod util; pub mod wasm_runtime; diff --git a/client/executor/common/src/sandbox.rs b/client/executor/common/src/sandbox.rs deleted file mode 100644 index 1e925bd5a7835..0000000000000 --- a/client/executor/common/src/sandbox.rs +++ /dev/null @@ -1,585 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! This module implements sandboxing support in the runtime. -//! -//! Sandboxing is backed by wasmi and wasmer, depending on the configuration. - -#[cfg(feature = "wasmer-sandbox")] -mod wasmer_backend; -mod wasmi_backend; - -use std::{collections::HashMap, rc::Rc}; - -use codec::Decode; -use sp_sandbox::env as sandbox_env; -use sp_wasm_interface::{FunctionContext, Pointer, WordSize}; - -use crate::{ - error::{self, Result}, - util, -}; - -#[cfg(feature = "wasmer-sandbox")] -use self::wasmer_backend::{ - get_global as wasmer_get_global, instantiate as wasmer_instantiate, invoke as wasmer_invoke, - new_memory as wasmer_new_memory, Backend as WasmerBackend, - MemoryWrapper as WasmerMemoryWrapper, -}; -use self::wasmi_backend::{ - get_global as wasmi_get_global, instantiate as wasmi_instantiate, invoke as wasmi_invoke, - new_memory as wasmi_new_memory, MemoryWrapper as WasmiMemoryWrapper, -}; - -/// Index of a function inside the supervisor. -/// -/// This is a typically an index in the default table of the supervisor, however -/// the exact meaning of this index is depends on the implementation of dispatch function. -#[derive(Copy, Clone, Debug, PartialEq)] -pub struct SupervisorFuncIndex(usize); - -impl From for usize { - fn from(index: SupervisorFuncIndex) -> Self { - index.0 - } -} - -/// Index of a function within guest index space. -/// -/// This index is supposed to be used as index for `Externals`. -#[derive(Copy, Clone, Debug, PartialEq)] -struct GuestFuncIndex(usize); - -/// This struct holds a mapping from guest index space to supervisor. -struct GuestToSupervisorFunctionMapping { - /// Position of elements in this vector are interpreted - /// as indices of guest functions and are mapped to - /// corresponding supervisor function indices. - funcs: Vec, -} - -impl GuestToSupervisorFunctionMapping { - /// Create an empty function mapping - fn new() -> GuestToSupervisorFunctionMapping { - GuestToSupervisorFunctionMapping { funcs: Vec::new() } - } - - /// Add a new supervisor function to the mapping. - /// Returns a newly assigned guest function index. - fn define(&mut self, supervisor_func: SupervisorFuncIndex) -> GuestFuncIndex { - let idx = self.funcs.len(); - self.funcs.push(supervisor_func); - GuestFuncIndex(idx) - } - - /// Find supervisor function index by its corresponding guest function index - fn func_by_guest_index(&self, guest_func_idx: GuestFuncIndex) -> Option { - self.funcs.get(guest_func_idx.0).cloned() - } -} - -/// Holds sandbox function and memory imports and performs name resolution -struct Imports { - /// Maps qualified function name to its guest function index - func_map: HashMap<(Vec, Vec), GuestFuncIndex>, - - /// Maps qualified field name to its memory reference - memories_map: HashMap<(Vec, Vec), Memory>, -} - -impl Imports { - fn func_by_name(&self, module_name: &str, func_name: &str) -> Option { - self.func_map - .get(&(module_name.as_bytes().to_owned(), func_name.as_bytes().to_owned())) - .cloned() - } - - fn memory_by_name(&self, module_name: &str, memory_name: &str) -> Option { - self.memories_map - .get(&(module_name.as_bytes().to_owned(), memory_name.as_bytes().to_owned())) - .cloned() - } -} - -/// The sandbox context used to execute sandboxed functions. -pub trait SandboxContext { - /// Invoke a function in the supervisor environment. - /// - /// This first invokes the dispatch thunk function, passing in the function index of the - /// desired function to call and serialized arguments. The thunk calls the desired function - /// with the deserialized arguments, then serializes the result into memory and returns - /// reference. The pointer to and length of the result in linear memory is encoded into an - /// `i64`, with the upper 32 bits representing the pointer and the lower 32 bits representing - /// the length. - /// - /// # Errors - /// - /// Returns `Err` if the dispatch_thunk function has an incorrect signature or traps during - /// execution. - fn invoke( - &mut self, - invoke_args_ptr: Pointer, - invoke_args_len: WordSize, - state: u32, - func_idx: SupervisorFuncIndex, - ) -> Result; - - /// Returns the supervisor context. - fn supervisor_context(&mut self) -> &mut dyn FunctionContext; -} - -/// Implementation of [`Externals`] that allows execution of guest module with -/// [externals][`Externals`] that might refer functions defined by supervisor. -/// -/// [`Externals`]: ../wasmi/trait.Externals.html -pub struct GuestExternals<'a> { - /// Instance of sandboxed module to be dispatched - sandbox_instance: &'a SandboxInstance, - - /// External state passed to guest environment, see the `instantiate` function - state: u32, -} - -/// Module instance in terms of selected backend -enum BackendInstance { - /// Wasmi module instance - Wasmi(wasmi::ModuleRef), - - /// Wasmer module instance - #[cfg(feature = "wasmer-sandbox")] - Wasmer(wasmer::Instance), -} - -/// Sandboxed instance of a wasm module. -/// -/// It's primary purpose is to [`invoke`] exported functions on it. -/// -/// All imports of this instance are specified at the creation time and -/// imports are implemented by the supervisor. -/// -/// Hence, in order to invoke an exported function on a sandboxed module instance, -/// it's required to provide supervisor externals: it will be used to execute -/// code in the supervisor context. -/// -/// This is generic over a supervisor function reference type. -/// -/// [`invoke`]: #method.invoke -pub struct SandboxInstance { - backend_instance: BackendInstance, - guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping, -} - -impl SandboxInstance { - /// Invoke an exported function by a name. - /// - /// `supervisor_externals` is required to execute the implementations - /// of the syscalls that published to a sandboxed module instance. - /// - /// The `state` parameter can be used to provide custom data for - /// these syscall implementations. - pub fn invoke( - &self, - export_name: &str, - args: &[sp_wasm_interface::Value], - state: u32, - sandbox_context: &mut dyn SandboxContext, - ) -> std::result::Result, error::Error> { - match &self.backend_instance { - BackendInstance::Wasmi(wasmi_instance) => - wasmi_invoke(self, wasmi_instance, export_name, args, state, sandbox_context), - - #[cfg(feature = "wasmer-sandbox")] - BackendInstance::Wasmer(wasmer_instance) => - wasmer_invoke(wasmer_instance, export_name, args, state, sandbox_context), - } - } - - /// Get the value from a global with the given `name`. - /// - /// Returns `Some(_)` if the global could be found. - pub fn get_global_val(&self, name: &str) -> Option { - match &self.backend_instance { - BackendInstance::Wasmi(wasmi_instance) => wasmi_get_global(wasmi_instance, name), - - #[cfg(feature = "wasmer-sandbox")] - BackendInstance::Wasmer(wasmer_instance) => wasmer_get_global(wasmer_instance, name), - } - } -} - -/// Error occurred during instantiation of a sandboxed module. -pub enum InstantiationError { - /// Something wrong with the environment definition. It either can't - /// be decoded, have a reference to a non-existent or torn down memory instance. - EnvironmentDefinitionCorrupted, - /// Provided module isn't recognized as a valid webassembly binary. - ModuleDecoding, - /// Module is a well-formed webassembly binary but could not be instantiated. This could - /// happen because, e.g. the module imports entries not provided by the environment. - Instantiation, - /// Module is well-formed, instantiated and linked, but while executing the start function - /// a trap was generated. - StartTrapped, - /// The code was compiled with a CPU feature not available on the host. - CpuFeature, -} - -fn decode_environment_definition( - mut raw_env_def: &[u8], - memories: &[Option], -) -> std::result::Result<(Imports, GuestToSupervisorFunctionMapping), InstantiationError> { - let env_def = sandbox_env::EnvironmentDefinition::decode(&mut raw_env_def) - .map_err(|_| InstantiationError::EnvironmentDefinitionCorrupted)?; - - let mut func_map = HashMap::new(); - let mut memories_map = HashMap::new(); - let mut guest_to_supervisor_mapping = GuestToSupervisorFunctionMapping::new(); - - for entry in &env_def.entries { - let module = entry.module_name.clone(); - let field = entry.field_name.clone(); - - match entry.entity { - sandbox_env::ExternEntity::Function(func_idx) => { - let externals_idx = - guest_to_supervisor_mapping.define(SupervisorFuncIndex(func_idx as usize)); - func_map.insert((module, field), externals_idx); - }, - sandbox_env::ExternEntity::Memory(memory_idx) => { - let memory_ref = memories - .get(memory_idx as usize) - .cloned() - .ok_or(InstantiationError::EnvironmentDefinitionCorrupted)? - .ok_or(InstantiationError::EnvironmentDefinitionCorrupted)?; - memories_map.insert((module, field), memory_ref); - }, - } - } - - Ok((Imports { func_map, memories_map }, guest_to_supervisor_mapping)) -} - -/// An environment in which the guest module is instantiated. -pub struct GuestEnvironment { - /// Function and memory imports of the guest module - imports: Imports, - - /// Supervisor functinons mapped to guest index space - guest_to_supervisor_mapping: GuestToSupervisorFunctionMapping, -} - -impl GuestEnvironment { - /// Decodes an environment definition from the given raw bytes. - /// - /// Returns `Err` if the definition cannot be decoded. - pub fn decode
( - store: &Store
, - raw_env_def: &[u8], - ) -> std::result::Result { - let (imports, guest_to_supervisor_mapping) = - decode_environment_definition(raw_env_def, &store.memories)?; - Ok(Self { imports, guest_to_supervisor_mapping }) - } -} - -/// An unregistered sandboxed instance. -/// -/// To finish off the instantiation the user must call `register`. -#[must_use] -pub struct UnregisteredInstance { - sandbox_instance: Rc, -} - -impl UnregisteredInstance { - /// Finalizes instantiation of this module. - pub fn register
(self, store: &mut Store
, dispatch_thunk: DT) -> u32 { - // At last, register the instance. - store.register_sandbox_instance(self.sandbox_instance, dispatch_thunk) - } -} - -/// Sandbox backend to use -pub enum SandboxBackend { - /// Wasm interpreter - Wasmi, - - /// Wasmer environment - #[cfg(feature = "wasmer-sandbox")] - Wasmer, - - /// Use wasmer backend if available. Fall back to wasmi otherwise. - TryWasmer, -} - -/// Memory reference in terms of a selected backend -#[derive(Clone, Debug)] -pub enum Memory { - /// Wasmi memory reference - Wasmi(WasmiMemoryWrapper), - - /// Wasmer memory refernce - #[cfg(feature = "wasmer-sandbox")] - Wasmer(WasmerMemoryWrapper), -} - -impl Memory { - /// View as wasmi memory - pub fn as_wasmi(&self) -> Option { - match self { - Memory::Wasmi(memory) => Some(memory.clone()), - - #[cfg(feature = "wasmer-sandbox")] - Memory::Wasmer(_) => None, - } - } - - /// View as wasmer memory - #[cfg(feature = "wasmer-sandbox")] - pub fn as_wasmer(&self) -> Option { - match self { - Memory::Wasmer(memory) => Some(memory.clone()), - Memory::Wasmi(_) => None, - } - } -} - -impl util::MemoryTransfer for Memory { - fn read(&self, source_addr: Pointer, size: usize) -> Result> { - match self { - Memory::Wasmi(sandboxed_memory) => sandboxed_memory.read(source_addr, size), - - #[cfg(feature = "wasmer-sandbox")] - Memory::Wasmer(sandboxed_memory) => sandboxed_memory.read(source_addr, size), - } - } - - fn read_into(&self, source_addr: Pointer, destination: &mut [u8]) -> Result<()> { - match self { - Memory::Wasmi(sandboxed_memory) => sandboxed_memory.read_into(source_addr, destination), - - #[cfg(feature = "wasmer-sandbox")] - Memory::Wasmer(sandboxed_memory) => sandboxed_memory.read_into(source_addr, destination), - } - } - - fn write_from(&self, dest_addr: Pointer, source: &[u8]) -> Result<()> { - match self { - Memory::Wasmi(sandboxed_memory) => sandboxed_memory.write_from(dest_addr, source), - - #[cfg(feature = "wasmer-sandbox")] - Memory::Wasmer(sandboxed_memory) => sandboxed_memory.write_from(dest_addr, source), - } - } -} - -/// Information specific to a particular execution backend -enum BackendContext { - /// Wasmi specific context - Wasmi, - - /// Wasmer specific context - #[cfg(feature = "wasmer-sandbox")] - Wasmer(WasmerBackend), -} - -impl BackendContext { - pub fn new(backend: SandboxBackend) -> BackendContext { - match backend { - SandboxBackend::Wasmi => BackendContext::Wasmi, - - #[cfg(not(feature = "wasmer-sandbox"))] - SandboxBackend::TryWasmer => BackendContext::Wasmi, - - #[cfg(feature = "wasmer-sandbox")] - SandboxBackend::Wasmer | SandboxBackend::TryWasmer => - BackendContext::Wasmer(WasmerBackend::new()), - } - } -} - -/// This struct keeps track of all sandboxed components. -/// -/// This is generic over a supervisor function reference type. -pub struct Store
{ - /// Stores the instance and the dispatch thunk associated to per instance. - /// - /// Instances are `Some` until torn down. - instances: Vec, DT)>>, - /// Memories are `Some` until torn down. - memories: Vec>, - backend_context: BackendContext, -} - -impl Store
{ - /// Create a new empty sandbox store. - pub fn new(backend: SandboxBackend) -> Self { - Store { - instances: Vec::new(), - memories: Vec::new(), - backend_context: BackendContext::new(backend), - } - } - - /// Create a new memory instance and return it's index. - /// - /// # Errors - /// - /// Returns `Err` if the memory couldn't be created. - /// Typically happens if `initial` is more than `maximum`. - pub fn new_memory(&mut self, initial: u32, maximum: u32) -> Result { - let memories = &mut self.memories; - let backend_context = &self.backend_context; - - let maximum = match maximum { - sandbox_env::MEM_UNLIMITED => None, - specified_limit => Some(specified_limit), - }; - - let memory = match &backend_context { - BackendContext::Wasmi => wasmi_new_memory(initial, maximum)?, - - #[cfg(feature = "wasmer-sandbox")] - BackendContext::Wasmer(context) => wasmer_new_memory(context, initial, maximum)?, - }; - - let mem_idx = memories.len(); - memories.push(Some(memory)); - - Ok(mem_idx as u32) - } - - /// Returns `SandboxInstance` by `instance_idx`. - /// - /// # Errors - /// - /// Returns `Err` If `instance_idx` isn't a valid index of an instance or - /// instance is already torndown. - pub fn instance(&self, instance_idx: u32) -> Result> { - self.instances - .get(instance_idx as usize) - .ok_or("Trying to access a non-existent instance")? - .as_ref() - .map(|v| v.0.clone()) - .ok_or_else(|| "Trying to access a torndown instance".into()) - } - - /// Returns dispatch thunk by `instance_idx`. - /// - /// # Errors - /// - /// Returns `Err` If `instance_idx` isn't a valid index of an instance or - /// instance is already torndown. - pub fn dispatch_thunk(&self, instance_idx: u32) -> Result
{ - self.instances - .get(instance_idx as usize) - .as_ref() - .ok_or("Trying to access a non-existent instance")? - .as_ref() - .map(|v| v.1.clone()) - .ok_or_else(|| "Trying to access a torndown instance".into()) - } - - /// Returns reference to a memory instance by `memory_idx`. - /// - /// # Errors - /// - /// Returns `Err` If `memory_idx` isn't a valid index of an memory or - /// if memory has been torn down. - pub fn memory(&self, memory_idx: u32) -> Result { - self.memories - .get(memory_idx as usize) - .cloned() - .ok_or("Trying to access a non-existent sandboxed memory")? - .ok_or_else(|| "Trying to access a torndown sandboxed memory".into()) - } - - /// Tear down the memory at the specified index. - /// - /// # Errors - /// - /// Returns `Err` if `memory_idx` isn't a valid index of an memory or - /// if it has been torn down. - pub fn memory_teardown(&mut self, memory_idx: u32) -> Result<()> { - match self.memories.get_mut(memory_idx as usize) { - None => Err("Trying to teardown a non-existent sandboxed memory".into()), - Some(None) => Err("Double teardown of a sandboxed memory".into()), - Some(memory) => { - *memory = None; - Ok(()) - }, - } - } - - /// Tear down the instance at the specified index. - /// - /// # Errors - /// - /// Returns `Err` if `instance_idx` isn't a valid index of an instance or - /// if it has been torn down. - pub fn instance_teardown(&mut self, instance_idx: u32) -> Result<()> { - match self.instances.get_mut(instance_idx as usize) { - None => Err("Trying to teardown a non-existent instance".into()), - Some(None) => Err("Double teardown of an instance".into()), - Some(instance) => { - *instance = None; - Ok(()) - }, - } - } - - /// Instantiate a guest module and return it's index in the store. - /// - /// The guest module's code is specified in `wasm`. Environment that will be available to - /// guest module is specified in `guest_env`. A dispatch thunk is used as function that - /// handle calls from guests. `state` is an opaque pointer to caller's arbitrary context - /// normally created by `sp_sandbox::Instance` primitive. - /// - /// Note: Due to borrowing constraints dispatch thunk is now propagated using DTH - /// - /// Returns uninitialized sandboxed module instance or an instantiation error. - pub fn instantiate( - &mut self, - wasm: &[u8], - guest_env: GuestEnvironment, - state: u32, - sandbox_context: &mut dyn SandboxContext, - ) -> std::result::Result { - let sandbox_instance = match self.backend_context { - BackendContext::Wasmi => wasmi_instantiate(wasm, guest_env, state, sandbox_context)?, - - #[cfg(feature = "wasmer-sandbox")] - BackendContext::Wasmer(ref context) => - wasmer_instantiate(context, wasm, guest_env, state, sandbox_context)?, - }; - - Ok(UnregisteredInstance { sandbox_instance }) - } -} - -// Private routines -impl
Store
{ - fn register_sandbox_instance( - &mut self, - sandbox_instance: Rc, - dispatch_thunk: DT, - ) -> u32 { - let instance_idx = self.instances.len(); - self.instances.push(Some((sandbox_instance, dispatch_thunk))); - instance_idx as u32 - } -} diff --git a/client/executor/common/src/sandbox/wasmer_backend.rs b/client/executor/common/src/sandbox/wasmer_backend.rs deleted file mode 100644 index 29926141ed8b8..0000000000000 --- a/client/executor/common/src/sandbox/wasmer_backend.rs +++ /dev/null @@ -1,449 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Wasmer specific impls for sandbox - -use std::{cell::RefCell, collections::HashMap, rc::Rc}; - -use wasmer::RuntimeError; - -use codec::{Decode, Encode}; -use sp_sandbox::HostError; -use sp_wasm_interface::{FunctionContext, Pointer, ReturnValue, Value, WordSize}; - -use crate::{ - error::{Error, Result}, - sandbox::{ - BackendInstance, GuestEnvironment, InstantiationError, Memory, SandboxContext, - SandboxInstance, SupervisorFuncIndex, - }, - util::{checked_range, MemoryTransfer}, -}; - -environmental::environmental!(SandboxContextStore: trait SandboxContext); - -/// Wasmer specific context -pub struct Backend { - store: wasmer::Store, -} - -impl Backend { - pub fn new() -> Self { - let compiler = wasmer::Singlepass::default(); - Backend { store: wasmer::Store::new(&wasmer::Universal::new(compiler).engine()) } - } -} - -/// Invoke a function within a sandboxed module -pub fn invoke( - instance: &wasmer::Instance, - export_name: &str, - args: &[Value], - _state: u32, - sandbox_context: &mut dyn SandboxContext, -) -> std::result::Result, Error> { - let function = instance - .exports - .get_function(export_name) - .map_err(|error| Error::Sandbox(error.to_string()))?; - - let args: Vec = args - .iter() - .map(|v| match *v { - Value::I32(val) => wasmer::Val::I32(val), - Value::I64(val) => wasmer::Val::I64(val), - Value::F32(val) => wasmer::Val::F32(f32::from_bits(val)), - Value::F64(val) => wasmer::Val::F64(f64::from_bits(val)), - }) - .collect(); - - let wasmer_result = SandboxContextStore::using(sandbox_context, || { - function.call(&args).map_err(|error| Error::Sandbox(error.to_string())) - })?; - - match wasmer_result.as_ref() { - [] => Ok(None), - - [wasm_value] => { - let wasmer_value = match *wasm_value { - wasmer::Val::I32(val) => Value::I32(val), - wasmer::Val::I64(val) => Value::I64(val), - wasmer::Val::F32(val) => Value::F32(f32::to_bits(val)), - wasmer::Val::F64(val) => Value::F64(f64::to_bits(val)), - _ => - return Err(Error::Sandbox(format!( - "Unsupported return value: {:?}", - wasm_value, - ))), - }; - - Ok(Some(wasmer_value)) - }, - - _ => Err(Error::Sandbox("multiple return types are not supported yet".into())), - } -} - -/// Instantiate a module within a sandbox context -pub fn instantiate( - context: &Backend, - wasm: &[u8], - guest_env: GuestEnvironment, - state: u32, - sandbox_context: &mut dyn SandboxContext, -) -> std::result::Result, InstantiationError> { - let module = wasmer::Module::new(&context.store, wasm) - .map_err(|_| InstantiationError::ModuleDecoding)?; - - type Exports = HashMap; - let mut exports_map = Exports::new(); - - for import in module.imports() { - match import.ty() { - // Nothing to do here - wasmer::ExternType::Global(_) | wasmer::ExternType::Table(_) => (), - - wasmer::ExternType::Memory(_) => { - let exports = exports_map - .entry(import.module().to_string()) - .or_insert_with(wasmer::Exports::new); - - let memory = guest_env - .imports - .memory_by_name(import.module(), import.name()) - .ok_or(InstantiationError::ModuleDecoding)?; - - let wasmer_memory_ref = memory.as_wasmer().expect( - "memory is created by wasmer; \ - exported by the same module and backend; \ - thus the operation can't fail; \ - qed", - ); - - // This is safe since we're only instantiating the module and populating - // the export table, so no memory access can happen at this time. - // All subsequent memory accesses should happen through the wrapper, - // that enforces the memory access protocol. - // - // We take exclusive lock to ensure that we're the only one here, - // since during instantiation phase the memory should only be created - // and not yet accessed. - let wasmer_memory = wasmer_memory_ref - .buffer - .try_borrow_mut() - .map_err(|_| InstantiationError::EnvironmentDefinitionCorrupted)? - .clone(); - - exports.insert(import.name(), wasmer::Extern::Memory(wasmer_memory)); - }, - - wasmer::ExternType::Function(func_ty) => { - let guest_func_index = - guest_env.imports.func_by_name(import.module(), import.name()); - - let guest_func_index = if let Some(index) = guest_func_index { - index - } else { - // Missing import (should we abort here?) - continue - }; - - let supervisor_func_index = guest_env - .guest_to_supervisor_mapping - .func_by_guest_index(guest_func_index) - .ok_or(InstantiationError::ModuleDecoding)?; - - let function = - dispatch_function(supervisor_func_index, &context.store, func_ty, state); - - let exports = exports_map - .entry(import.module().to_string()) - .or_insert_with(wasmer::Exports::new); - - exports.insert(import.name(), wasmer::Extern::Function(function)); - }, - } - } - - let mut import_object = wasmer::ImportObject::new(); - for (module_name, exports) in exports_map.into_iter() { - import_object.register(module_name, exports); - } - - let instance = SandboxContextStore::using(sandbox_context, || { - wasmer::Instance::new(&module, &import_object).map_err(|error| match error { - wasmer::InstantiationError::Link(_) => InstantiationError::Instantiation, - wasmer::InstantiationError::Start(_) => InstantiationError::StartTrapped, - wasmer::InstantiationError::HostEnvInitialization(_) => - InstantiationError::EnvironmentDefinitionCorrupted, - wasmer::InstantiationError::CpuFeature(_) => InstantiationError::CpuFeature, - }) - })?; - - Ok(Rc::new(SandboxInstance { - backend_instance: BackendInstance::Wasmer(instance), - guest_to_supervisor_mapping: guest_env.guest_to_supervisor_mapping, - })) -} - -fn dispatch_function( - supervisor_func_index: SupervisorFuncIndex, - store: &wasmer::Store, - func_ty: &wasmer::FunctionType, - state: u32, -) -> wasmer::Function { - wasmer::Function::new(store, func_ty, move |params| { - SandboxContextStore::with(|sandbox_context| { - // Serialize arguments into a byte vector. - let invoke_args_data = params - .iter() - .map(|val| match val { - wasmer::Val::I32(val) => Ok(Value::I32(*val)), - wasmer::Val::I64(val) => Ok(Value::I64(*val)), - wasmer::Val::F32(val) => Ok(Value::F32(f32::to_bits(*val))), - wasmer::Val::F64(val) => Ok(Value::F64(f64::to_bits(*val))), - _ => - Err(RuntimeError::new(format!("Unsupported function argument: {:?}", val))), - }) - .collect::, _>>()? - .encode(); - - // Move serialized arguments inside the memory, invoke dispatch thunk and - // then free allocated memory. - let invoke_args_len = invoke_args_data.len() as WordSize; - let invoke_args_ptr = - sandbox_context.supervisor_context().allocate_memory(invoke_args_len).map_err( - |_| RuntimeError::new("Can't allocate memory in supervisor for the arguments"), - )?; - - let deallocate = |fe: &mut dyn FunctionContext, ptr, fail_msg| { - fe.deallocate_memory(ptr).map_err(|_| RuntimeError::new(fail_msg)) - }; - - if sandbox_context - .supervisor_context() - .write_memory(invoke_args_ptr, &invoke_args_data) - .is_err() - { - deallocate( - sandbox_context.supervisor_context(), - invoke_args_ptr, - "Failed dealloction after failed write of invoke arguments", - )?; - - return Err(RuntimeError::new("Can't write invoke args into memory")) - } - - // Perform the actuall call - let serialized_result = sandbox_context - .invoke(invoke_args_ptr, invoke_args_len, state, supervisor_func_index) - .map_err(|e| RuntimeError::new(e.to_string())); - - deallocate( - sandbox_context.supervisor_context(), - invoke_args_ptr, - "Failed dealloction after invoke", - )?; - - let serialized_result = serialized_result?; - - // dispatch_thunk returns pointer to serialized arguments. - // Unpack pointer and len of the serialized result data. - let (serialized_result_val_ptr, serialized_result_val_len) = { - // Cast to u64 to use zero-extension. - let v = serialized_result as u64; - let ptr = (v as u64 >> 32) as u32; - let len = (v & 0xFFFFFFFF) as u32; - (Pointer::new(ptr), len) - }; - - let serialized_result_val = sandbox_context - .supervisor_context() - .read_memory(serialized_result_val_ptr, serialized_result_val_len) - .map_err(|_| { - RuntimeError::new("Can't read the serialized result from dispatch thunk") - }); - - deallocate( - sandbox_context.supervisor_context(), - serialized_result_val_ptr, - "Can't deallocate memory for dispatch thunk's result", - )?; - - let serialized_result_val = serialized_result_val?; - - let deserialized_result = std::result::Result::::decode( - &mut serialized_result_val.as_slice(), - ) - .map_err(|_| RuntimeError::new("Decoding Result failed!"))? - .map_err(|_| RuntimeError::new("Supervisor function returned sandbox::HostError"))?; - - let result = match deserialized_result { - ReturnValue::Value(Value::I32(val)) => vec![wasmer::Val::I32(val)], - ReturnValue::Value(Value::I64(val)) => vec![wasmer::Val::I64(val)], - ReturnValue::Value(Value::F32(val)) => vec![wasmer::Val::F32(f32::from_bits(val))], - ReturnValue::Value(Value::F64(val)) => vec![wasmer::Val::F64(f64::from_bits(val))], - - ReturnValue::Unit => vec![], - }; - - Ok(result) - }) - .expect("SandboxContextStore is set when invoking sandboxed functions; qed") - }) -} - -/// Allocate new memory region -pub fn new_memory( - context: &Backend, - initial: u32, - maximum: Option, -) -> crate::error::Result { - let ty = wasmer::MemoryType::new(initial, maximum, false); - let memory = Memory::Wasmer(MemoryWrapper::new( - wasmer::Memory::new(&context.store, ty).map_err(|_| Error::InvalidMemoryReference)?, - )); - - Ok(memory) -} - -/// In order to enforce memory access protocol to the backend memory -/// we wrap it with `RefCell` and encapsulate all memory operations. -#[derive(Debug, Clone)] -pub struct MemoryWrapper { - buffer: Rc>, -} - -impl MemoryWrapper { - /// Take ownership of the memory region and return a wrapper object - pub fn new(memory: wasmer::Memory) -> Self { - Self { buffer: Rc::new(RefCell::new(memory)) } - } - - /// Returns linear memory of the wasm instance as a slice. - /// - /// # Safety - /// - /// Wasmer doesn't provide comprehensive documentation about the exact behavior of the data - /// pointer. If a dynamic style heap is used the base pointer of the heap can change. Since - /// growing, we cannot guarantee the lifetime of the returned slice reference. - unsafe fn memory_as_slice(memory: &wasmer::Memory) -> &[u8] { - let ptr = memory.data_ptr() as *const _; - - let len: usize = memory.data_size().try_into().expect( - "maximum memory object size never exceeds pointer size on any architecture; \ - usize by design and definition is enough to store any memory object size \ - possible on current achitecture; thus the conversion can not fail; qed", - ); - - if len == 0 { - &[] - } else { - core::slice::from_raw_parts(ptr, len) - } - } - - /// Returns linear memory of the wasm instance as a slice. - /// - /// # Safety - /// - /// See `[memory_as_slice]`. In addition to those requirements, since a mutable reference is - /// returned it must be ensured that only one mutable and no shared references to memory - /// exists at the same time. - unsafe fn memory_as_slice_mut(memory: &mut wasmer::Memory) -> &mut [u8] { - let ptr = memory.data_ptr(); - - let len: usize = memory.data_size().try_into().expect( - "maximum memory object size never exceeds pointer size on any architecture; \ - usize by design and definition is enough to store any memory object size \ - possible on current achitecture; thus the conversion can not fail; qed", - ); - - if len == 0 { - &mut [] - } else { - core::slice::from_raw_parts_mut(ptr, len) - } - } -} - -impl MemoryTransfer for MemoryWrapper { - fn read(&self, source_addr: Pointer, size: usize) -> Result> { - let memory = self.buffer.borrow(); - - let data_size: usize = memory.data_size().try_into().expect( - "maximum memory object size never exceeds pointer size on any architecture; \ - usize by design and definition is enough to store any memory object size \ - possible on current achitecture; thus the conversion can not fail; qed", - ); - - let range = checked_range(source_addr.into(), size, data_size) - .ok_or_else(|| Error::Other("memory read is out of bounds".into()))?; - - let mut buffer = vec![0; range.len()]; - self.read_into(source_addr, &mut buffer)?; - - Ok(buffer) - } - - fn read_into(&self, source_addr: Pointer, destination: &mut [u8]) -> Result<()> { - unsafe { - let memory = self.buffer.borrow(); - - // This should be safe since we don't grow up memory while caching this reference - // and we give up the reference before returning from this function. - let source = Self::memory_as_slice(&memory); - - let range = checked_range(source_addr.into(), destination.len(), source.len()) - .ok_or_else(|| Error::Other("memory read is out of bounds".into()))?; - - destination.copy_from_slice(&source[range]); - Ok(()) - } - } - - fn write_from(&self, dest_addr: Pointer, source: &[u8]) -> Result<()> { - unsafe { - let memory = &mut self.buffer.borrow_mut(); - - // This should be safe since we don't grow up memory while caching this reference - // and we give up the reference before returning from this function. - let destination = Self::memory_as_slice_mut(memory); - - let range = checked_range(dest_addr.into(), source.len(), destination.len()) - .ok_or_else(|| Error::Other("memory write is out of bounds".into()))?; - - destination[range].copy_from_slice(source); - Ok(()) - } - } -} - -/// Get global value by name -pub fn get_global(instance: &wasmer::Instance, name: &str) -> Option { - let global = instance.exports.get_global(name).ok()?; - let wasmtime_value = match global.get() { - wasmer::Val::I32(val) => Value::I32(val), - wasmer::Val::I64(val) => Value::I64(val), - wasmer::Val::F32(val) => Value::F32(f32::to_bits(val)), - wasmer::Val::F64(val) => Value::F64(f64::to_bits(val)), - _ => None?, - }; - - Some(wasmtime_value) -} diff --git a/client/executor/common/src/sandbox/wasmi_backend.rs b/client/executor/common/src/sandbox/wasmi_backend.rs deleted file mode 100644 index 2ba133f5f15b1..0000000000000 --- a/client/executor/common/src/sandbox/wasmi_backend.rs +++ /dev/null @@ -1,339 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -//! Wasmi specific impls for sandbox - -use std::{fmt, rc::Rc}; - -use codec::{Decode, Encode}; -use sp_sandbox::HostError; -use sp_wasm_interface::{FunctionContext, Pointer, ReturnValue, Value, WordSize}; -use wasmi::{ - memory_units::Pages, ImportResolver, MemoryInstance, Module, ModuleInstance, RuntimeArgs, - RuntimeValue, Trap, -}; - -use crate::{ - error::{self, Error}, - sandbox::{ - BackendInstance, GuestEnvironment, GuestExternals, GuestFuncIndex, Imports, - InstantiationError, Memory, SandboxContext, SandboxInstance, - }, - util::{checked_range, MemoryTransfer}, -}; - -environmental::environmental!(SandboxContextStore: trait SandboxContext); - -#[derive(Debug)] -struct CustomHostError(String); - -impl fmt::Display for CustomHostError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "HostError: {}", self.0) - } -} - -impl wasmi::HostError for CustomHostError {} - -/// Construct trap error from specified message -fn trap(msg: &'static str) -> Trap { - Trap::host(CustomHostError(msg.into())) -} - -impl ImportResolver for Imports { - fn resolve_func( - &self, - module_name: &str, - field_name: &str, - signature: &wasmi::Signature, - ) -> std::result::Result { - let idx = self.func_by_name(module_name, field_name).ok_or_else(|| { - wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)) - })?; - - Ok(wasmi::FuncInstance::alloc_host(signature.clone(), idx.0)) - } - - fn resolve_memory( - &self, - module_name: &str, - field_name: &str, - _memory_type: &wasmi::MemoryDescriptor, - ) -> std::result::Result { - let mem = self.memory_by_name(module_name, field_name).ok_or_else(|| { - wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name)) - })?; - - let wrapper = mem.as_wasmi().ok_or_else(|| { - wasmi::Error::Instantiation(format!( - "Unsupported non-wasmi export {}:{}", - module_name, field_name - )) - })?; - - // Here we use inner memory reference only to resolve the imports - // without accessing the memory contents. All subsequent memory accesses - // should happen through the wrapper, that enforces the memory access protocol. - let mem = wrapper.0; - - Ok(mem) - } - - fn resolve_global( - &self, - module_name: &str, - field_name: &str, - _global_type: &wasmi::GlobalDescriptor, - ) -> std::result::Result { - Err(wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name))) - } - - fn resolve_table( - &self, - module_name: &str, - field_name: &str, - _table_type: &wasmi::TableDescriptor, - ) -> std::result::Result { - Err(wasmi::Error::Instantiation(format!("Export {}:{} not found", module_name, field_name))) - } -} - -/// Allocate new memory region -pub fn new_memory(initial: u32, maximum: Option) -> crate::error::Result { - let memory = Memory::Wasmi(MemoryWrapper::new( - MemoryInstance::alloc(Pages(initial as usize), maximum.map(|m| Pages(m as usize))) - .map_err(|error| Error::Sandbox(error.to_string()))?, - )); - - Ok(memory) -} - -/// Wasmi provides direct access to its memory using slices. -/// -/// This wrapper limits the scope where the slice can be taken to -#[derive(Debug, Clone)] -pub struct MemoryWrapper(wasmi::MemoryRef); - -impl MemoryWrapper { - /// Take ownership of the memory region and return a wrapper object - fn new(memory: wasmi::MemoryRef) -> Self { - Self(memory) - } -} - -impl MemoryTransfer for MemoryWrapper { - fn read(&self, source_addr: Pointer, size: usize) -> error::Result> { - self.0.with_direct_access(|source| { - let range = checked_range(source_addr.into(), size, source.len()) - .ok_or_else(|| error::Error::Other("memory read is out of bounds".into()))?; - - Ok(Vec::from(&source[range])) - }) - } - - fn read_into(&self, source_addr: Pointer, destination: &mut [u8]) -> error::Result<()> { - self.0.with_direct_access(|source| { - let range = checked_range(source_addr.into(), destination.len(), source.len()) - .ok_or_else(|| error::Error::Other("memory read is out of bounds".into()))?; - - destination.copy_from_slice(&source[range]); - Ok(()) - }) - } - - fn write_from(&self, dest_addr: Pointer, source: &[u8]) -> error::Result<()> { - self.0.with_direct_access_mut(|destination| { - let range = checked_range(dest_addr.into(), source.len(), destination.len()) - .ok_or_else(|| error::Error::Other("memory write is out of bounds".into()))?; - - destination[range].copy_from_slice(source); - Ok(()) - }) - } -} - -impl<'a> wasmi::Externals for GuestExternals<'a> { - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> std::result::Result, Trap> { - SandboxContextStore::with(|sandbox_context| { - // Make `index` typesafe again. - let index = GuestFuncIndex(index); - - // Convert function index from guest to supervisor space - let func_idx = self.sandbox_instance - .guest_to_supervisor_mapping - .func_by_guest_index(index) - .expect( - "`invoke_index` is called with indexes registered via `FuncInstance::alloc_host`; - `FuncInstance::alloc_host` is called with indexes that were obtained from `guest_to_supervisor_mapping`; - `func_by_guest_index` called with `index` can't return `None`; - qed" - ); - - // Serialize arguments into a byte vector. - let invoke_args_data: Vec = args - .as_ref() - .iter() - .cloned() - .map(sp_wasm_interface::Value::from) - .collect::>() - .encode(); - - let state = self.state; - - // Move serialized arguments inside the memory, invoke dispatch thunk and - // then free allocated memory. - let invoke_args_len = invoke_args_data.len() as WordSize; - let invoke_args_ptr = sandbox_context - .supervisor_context() - .allocate_memory(invoke_args_len) - .map_err(|_| trap("Can't allocate memory in supervisor for the arguments"))?; - - let deallocate = |supervisor_context: &mut dyn FunctionContext, ptr, fail_msg| { - supervisor_context.deallocate_memory(ptr).map_err(|_| trap(fail_msg)) - }; - - if sandbox_context - .supervisor_context() - .write_memory(invoke_args_ptr, &invoke_args_data) - .is_err() - { - deallocate( - sandbox_context.supervisor_context(), - invoke_args_ptr, - "Failed dealloction after failed write of invoke arguments", - )?; - return Err(trap("Can't write invoke args into memory")) - } - - let result = sandbox_context.invoke( - invoke_args_ptr, - invoke_args_len, - state, - func_idx, - ); - - deallocate( - sandbox_context.supervisor_context(), - invoke_args_ptr, - "Can't deallocate memory for dispatch thunk's invoke arguments", - )?; - let result = result?; - - // dispatch_thunk returns pointer to serialized arguments. - // Unpack pointer and len of the serialized result data. - let (serialized_result_val_ptr, serialized_result_val_len) = { - // Cast to u64 to use zero-extension. - let v = result as u64; - let ptr = (v as u64 >> 32) as u32; - let len = (v & 0xFFFFFFFF) as u32; - (Pointer::new(ptr), len) - }; - - let serialized_result_val = sandbox_context - .supervisor_context() - .read_memory(serialized_result_val_ptr, serialized_result_val_len) - .map_err(|_| trap("Can't read the serialized result from dispatch thunk")); - - deallocate( - sandbox_context.supervisor_context(), - serialized_result_val_ptr, - "Can't deallocate memory for dispatch thunk's result", - ) - .and(serialized_result_val) - .and_then(|serialized_result_val| { - let result_val = std::result::Result::::decode(&mut serialized_result_val.as_slice()) - .map_err(|_| trap("Decoding Result failed!"))?; - - match result_val { - Ok(return_value) => Ok(match return_value { - ReturnValue::Unit => None, - ReturnValue::Value(typed_value) => Some(RuntimeValue::from(typed_value)), - }), - Err(HostError) => Err(trap("Supervisor function returned sandbox::HostError")), - } - }) - }).expect("SandboxContextStore is set when invoking sandboxed functions; qed") - } -} - -fn with_guest_externals(sandbox_instance: &SandboxInstance, state: u32, f: F) -> R -where - F: FnOnce(&mut GuestExternals) -> R, -{ - f(&mut GuestExternals { sandbox_instance, state }) -} - -/// Instantiate a module within a sandbox context -pub fn instantiate( - wasm: &[u8], - guest_env: GuestEnvironment, - state: u32, - sandbox_context: &mut dyn SandboxContext, -) -> std::result::Result, InstantiationError> { - let wasmi_module = Module::from_buffer(wasm).map_err(|_| InstantiationError::ModuleDecoding)?; - let wasmi_instance = ModuleInstance::new(&wasmi_module, &guest_env.imports) - .map_err(|_| InstantiationError::Instantiation)?; - - let sandbox_instance = Rc::new(SandboxInstance { - // In general, it's not a very good idea to use `.not_started_instance()` for - // anything but for extracting memory and tables. But in this particular case, we - // are extracting for the purpose of running `start` function which should be ok. - backend_instance: BackendInstance::Wasmi(wasmi_instance.not_started_instance().clone()), - guest_to_supervisor_mapping: guest_env.guest_to_supervisor_mapping, - }); - - with_guest_externals(&sandbox_instance, state, |guest_externals| { - SandboxContextStore::using(sandbox_context, || { - wasmi_instance - .run_start(guest_externals) - .map_err(|_| InstantiationError::StartTrapped) - }) - })?; - - Ok(sandbox_instance) -} - -/// Invoke a function within a sandboxed module -pub fn invoke( - instance: &SandboxInstance, - module: &wasmi::ModuleRef, - export_name: &str, - args: &[Value], - state: u32, - sandbox_context: &mut dyn SandboxContext, -) -> std::result::Result, error::Error> { - with_guest_externals(instance, state, |guest_externals| { - SandboxContextStore::using(sandbox_context, || { - let args = args.iter().cloned().map(Into::into).collect::>(); - - module - .invoke_export(export_name, &args, guest_externals) - .map(|result| result.map(Into::into)) - .map_err(|error| error::Error::Sandbox(error.to_string())) - }) - }) -} - -/// Get global value by name -pub fn get_global(instance: &wasmi::ModuleRef, name: &str) -> Option { - Some(instance.export_by_name(name)?.as_global()?.get().into()) -} diff --git a/client/executor/runtime-test/Cargo.toml b/client/executor/runtime-test/Cargo.toml index c8b173de16e9f..eadf547823bb2 100644 --- a/client/executor/runtime-test/Cargo.toml +++ b/client/executor/runtime-test/Cargo.toml @@ -13,11 +13,9 @@ repository = "https://github.com/paritytech/substrate/" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -paste = "1.0.6" 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-sandbox = { version = "0.10.0-dev", default-features = false, path = "../../../primitives/sandbox" } sp-std = { version = "5.0.0", default-features = false, path = "../../../primitives/std" } [build-dependencies] @@ -29,6 +27,5 @@ std = [ "sp-core/std", "sp-io/std", "sp-runtime/std", - "sp-sandbox/std", "sp-std/std", ] diff --git a/client/executor/runtime-test/src/lib.rs b/client/executor/runtime-test/src/lib.rs index 0424ad418617b..fc98d1909d00d 100644 --- a/client/executor/runtime-test/src/lib.rs +++ b/client/executor/runtime-test/src/lib.rs @@ -29,8 +29,6 @@ use sp_runtime::{ print, traits::{BlakeTwo256, Hash}, }; -#[cfg(not(feature = "std"))] -use sp_sandbox::{SandboxEnvironmentBuilder, SandboxInstance, SandboxMemory, Value}; extern "C" { #[allow(dead_code)] @@ -339,160 +337,3 @@ sp_core::wasm_export_functions! { return 1234; } } - -/// A macro to define a test entrypoint for each available sandbox executor. -macro_rules! wasm_export_sandbox_test_functions { - ( - $( - fn $name:ident( - $( $arg_name:ident: $arg_ty:ty ),* $(,)? - ) $( -> $ret_ty:ty )? where T: SandboxInstance<$state:ty> $(,)? - { $( $fn_impl:tt )* } - )* - ) => { - $( - #[cfg(not(feature = "std"))] - fn $name( $($arg_name: $arg_ty),* ) $( -> $ret_ty )? where T: SandboxInstance<$state> { - $( $fn_impl )* - } - - paste::paste! { - sp_core::wasm_export_functions! { - fn [<$name _host>]( $($arg_name: $arg_ty),* ) $( -> $ret_ty )? { - $name::>( $( $arg_name ),* ) - } - - fn [<$name _embedded>]( $($arg_name: $arg_ty),* ) $( -> $ret_ty )? { - $name::>( $( $arg_name ),* ) - } - } - } - )* - }; -} - -wasm_export_sandbox_test_functions! { - fn test_sandbox(code: Vec) -> bool - where - T: SandboxInstance, - { - execute_sandboxed::(&code, &[]).is_ok() - } - - fn test_sandbox_args(code: Vec) -> bool - where - T: SandboxInstance, - { - execute_sandboxed::(&code, &[Value::I32(0x12345678), Value::I64(0x1234567887654321)]) - .is_ok() - } - - fn test_sandbox_return_val(code: Vec) -> bool - where - T: SandboxInstance, - { - let ok = match execute_sandboxed::(&code, &[Value::I32(0x1336)]) { - Ok(sp_sandbox::ReturnValue::Value(Value::I32(0x1337))) => true, - _ => false, - }; - - ok - } - - fn test_sandbox_instantiate(code: Vec) -> u8 - where - T: SandboxInstance<()>, - { - let env_builder = T::EnvironmentBuilder::new(); - let code = match T::new(&code, &env_builder, &mut ()) { - Ok(_) => 0, - Err(sp_sandbox::Error::Module) => 1, - Err(sp_sandbox::Error::Execution) => 2, - Err(sp_sandbox::Error::OutOfBounds) => 3, - }; - - code - } - - fn test_sandbox_get_global_val(code: Vec) -> i64 - where - T: SandboxInstance<()>, - { - let env_builder = T::EnvironmentBuilder::new(); - let instance = if let Ok(i) = T::new(&code, &env_builder, &mut ()) { - i - } else { - return 20 - }; - - match instance.get_global_val("test_global") { - Some(sp_sandbox::Value::I64(val)) => val, - None => 30, - _ => 40, - } - } -} - -#[cfg(not(feature = "std"))] -struct State { - counter: u32, -} - -#[cfg(not(feature = "std"))] -fn execute_sandboxed( - code: &[u8], - args: &[Value], -) -> Result -where - T: sp_sandbox::SandboxInstance, -{ - fn env_assert( - _e: &mut State, - args: &[Value], - ) -> Result { - if args.len() != 1 { - return Err(sp_sandbox::HostError) - } - let condition = args[0].as_i32().ok_or_else(|| sp_sandbox::HostError)?; - if condition != 0 { - Ok(sp_sandbox::ReturnValue::Unit) - } else { - Err(sp_sandbox::HostError) - } - } - fn env_inc_counter( - e: &mut State, - args: &[Value], - ) -> Result { - if args.len() != 1 { - return Err(sp_sandbox::HostError) - } - let inc_by = args[0].as_i32().ok_or_else(|| sp_sandbox::HostError)?; - e.counter += inc_by as u32; - Ok(sp_sandbox::ReturnValue::Value(Value::I32(e.counter as i32))) - } - - let mut state = State { counter: 0 }; - - let env_builder = { - let mut env_builder = T::EnvironmentBuilder::new(); - env_builder.add_host_func("env", "assert", env_assert); - env_builder.add_host_func("env", "inc_counter", env_inc_counter); - let memory = match T::Memory::new(1, Some(16)) { - Ok(m) => m, - Err(_) => unreachable!( - " - Memory::new() can return Err only if parameters are borked; \ - We passing params here explicitly and they're correct; \ - Memory::new() can't return a Error qed" - ), - }; - env_builder.add_memory("env", "memory", memory); - env_builder - }; - - let mut instance = T::new(code, &env_builder, &mut state)?; - let result = instance.invoke("call", args, &mut state); - - result.map_err(|_| sp_sandbox::HostError) -} diff --git a/client/executor/src/integration_tests/mod.rs b/client/executor/src/integration_tests/mod.rs index 9b5c4b12fca99..25b999f115363 100644 --- a/client/executor/src/integration_tests/mod.rs +++ b/client/executor/src/integration_tests/mod.rs @@ -18,7 +18,6 @@ #[cfg(target_os = "linux")] mod linux; -mod sandbox; use codec::{Decode, Encode}; use sc_executor_common::{error::Error, runtime_blob::RuntimeBlob, wasm_runtime::WasmModule}; @@ -98,111 +97,6 @@ macro_rules! test_wasm_execution { }; } -/// A macro to run a given test for each available WASM execution method *and* for each -/// sandbox execution method. -#[macro_export] -macro_rules! test_wasm_execution_sandbox { - ($method_name:ident) => { - paste::item! { - #[test] - fn [<$method_name _interpreted_host_executor>]() { - $method_name(WasmExecutionMethod::Interpreted, "_host"); - } - - #[test] - fn [<$method_name _interpreted_embedded_executor>]() { - $method_name(WasmExecutionMethod::Interpreted, "_embedded"); - } - - #[test] - fn [<$method_name _compiled_pooling_cow_host_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite - }, "_host"); - } - - #[test] - fn [<$method_name _compiled_pooling_cow_embedded_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::PoolingCopyOnWrite - }, "_embedded"); - } - - #[test] - fn [<$method_name _compiled_pooling_vanilla_host_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::Pooling - }, "_host"); - } - - #[test] - fn [<$method_name _compiled_pooling_vanilla_embedded_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::Pooling - }, "_embedded"); - } - - #[test] - fn [<$method_name _compiled_recreate_instance_cow_host_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::RecreateInstanceCopyOnWrite - }, "_host"); - } - - #[test] - fn [<$method_name _compiled_recreate_instance_cow_embedded_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::RecreateInstanceCopyOnWrite - }, "_embedded"); - } - - #[test] - fn [<$method_name _compiled_recreate_instance_vanilla_host_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::RecreateInstance - }, "_host"); - } - - #[test] - fn [<$method_name _compiled_recreate_instance_vanilla_embedded_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::RecreateInstance - }, "_embedded"); - } - - #[test] - fn [<$method_name _compiled_legacy_instance_reuse_host_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::LegacyInstanceReuse - }, "_host"); - } - - #[test] - fn [<$method_name _compiled_legacy_instance_reuse_embedded_executor>]() { - $method_name(WasmExecutionMethod::Compiled { - instantiation_strategy: sc_executor_wasmtime::InstantiationStrategy::LegacyInstanceReuse - }, "_embedded"); - } - } - }; - - (interpreted_only $method_name:ident) => { - paste::item! { - #[test] - fn [<$method_name _interpreted_host_executor>]() { - $method_name(WasmExecutionMethod::Interpreted, "_host"); - } - } - - paste::item! { - #[test] - fn [<$method_name _interpreted_embedded_executor>]() { - $method_name(WasmExecutionMethod::Interpreted, "_embedded"); - } - } - }; -} - fn call_in_wasm( function: &str, call_data: &[u8], diff --git a/client/executor/src/integration_tests/sandbox.rs b/client/executor/src/integration_tests/sandbox.rs deleted file mode 100644 index 643db5097c6ad..0000000000000 --- a/client/executor/src/integration_tests/sandbox.rs +++ /dev/null @@ -1,339 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with this program. If not, see . - -use super::{call_in_wasm, TestExternalities}; -use crate::{test_wasm_execution_sandbox, WasmExecutionMethod}; - -use codec::Encode; - -test_wasm_execution_sandbox!(sandbox_should_work); -fn sandbox_should_work(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - let code = wat::parse_str( - r#" - (module - (import "env" "assert" (func $assert (param i32))) - (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) - (func (export "call") - (drop - (call $inc_counter (i32.const 5)) - ) - - (call $inc_counter (i32.const 3)) - ;; current counter value is on the stack - - ;; check whether current == 8 - i32.const 8 - i32.eq - - call $assert - ) - ) - "#, - ) - .unwrap() - .encode(); - - assert_eq!( - call_in_wasm(&format!("test_sandbox{}", fn_suffix), &code, wasm_method, &mut ext).unwrap(), - true.encode() - ); -} - -test_wasm_execution_sandbox!(sandbox_trap); -fn sandbox_trap(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - let code = wat::parse_str( - r#" - (module - (import "env" "assert" (func $assert (param i32))) - (func (export "call") - i32.const 0 - call $assert - ) - ) - "#, - ) - .unwrap(); - - assert_eq!( - call_in_wasm(&format!("test_sandbox{}", fn_suffix), &code, wasm_method, &mut ext).unwrap(), - vec![0] - ); -} - -test_wasm_execution_sandbox!(start_called); -fn start_called(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - let code = wat::parse_str( - r#" - (module - (import "env" "assert" (func $assert (param i32))) - (import "env" "inc_counter" (func $inc_counter (param i32) (result i32))) - - ;; Start function - (start $start) - (func $start - ;; Increment counter by 1 - (drop - (call $inc_counter (i32.const 1)) - ) - ) - - (func (export "call") - ;; Increment counter by 1. The current value is placed on the stack. - (call $inc_counter (i32.const 1)) - - ;; Counter is incremented twice by 1, once there and once in `start` func. - ;; So check the returned value is equal to 2. - i32.const 2 - i32.eq - call $assert - ) - ) - "#, - ) - .unwrap() - .encode(); - - assert_eq!( - call_in_wasm(&format!("test_sandbox{}", fn_suffix), &code, wasm_method, &mut ext).unwrap(), - true.encode() - ); -} - -test_wasm_execution_sandbox!(invoke_args); -fn invoke_args(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - let code = wat::parse_str( - r#" - (module - (import "env" "assert" (func $assert (param i32))) - - (func (export "call") (param $x i32) (param $y i64) - ;; assert that $x = 0x12345678 - (call $assert - (i32.eq - (get_local $x) - (i32.const 0x12345678) - ) - ) - - (call $assert - (i64.eq - (get_local $y) - (i64.const 0x1234567887654321) - ) - ) - ) - ) - "#, - ) - .unwrap() - .encode(); - - assert_eq!( - call_in_wasm(&format!("test_sandbox_args{}", fn_suffix), &code, wasm_method, &mut ext,) - .unwrap(), - true.encode(), - ); -} - -test_wasm_execution_sandbox!(return_val); -fn return_val(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - let code = wat::parse_str( - r#" - (module - (func (export "call") (param $x i32) (result i32) - (i32.add - (get_local $x) - (i32.const 1) - ) - ) - ) - "#, - ) - .unwrap() - .encode(); - - assert_eq!( - call_in_wasm( - &format!("test_sandbox_return_val{}", fn_suffix), - &code, - wasm_method, - &mut ext, - ) - .unwrap(), - true.encode(), - ); -} - -test_wasm_execution_sandbox!(unlinkable_module); -fn unlinkable_module(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - let code = wat::parse_str( - r#" - (module - (import "env" "non-existent" (func)) - - (func (export "call") - ) - ) - "#, - ) - .unwrap() - .encode(); - - assert_eq!( - call_in_wasm( - &format!("test_sandbox_instantiate{}", fn_suffix), - &code, - wasm_method, - &mut ext, - ) - .unwrap(), - 1u8.encode(), - ); -} - -test_wasm_execution_sandbox!(corrupted_module); -fn corrupted_module(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - // Corrupted wasm file - let code = vec![0u8, 0, 0, 0, 1, 0, 0, 0].encode(); - - assert_eq!( - call_in_wasm( - &format!("test_sandbox_instantiate{}", fn_suffix), - &code, - wasm_method, - &mut ext, - ) - .unwrap(), - 1u8.encode(), - ); -} - -test_wasm_execution_sandbox!(start_fn_ok); -fn start_fn_ok(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - let code = wat::parse_str( - r#" - (module - (func (export "call") - ) - - (func $start - ) - - (start $start) - ) - "#, - ) - .unwrap() - .encode(); - - assert_eq!( - call_in_wasm( - &format!("test_sandbox_instantiate{}", fn_suffix), - &code, - wasm_method, - &mut ext, - ) - .unwrap(), - 0u8.encode(), - ); -} - -test_wasm_execution_sandbox!(start_fn_traps); -fn start_fn_traps(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - let code = wat::parse_str( - r#" - (module - (func (export "call") - ) - - (func $start - unreachable - ) - - (start $start) - ) - "#, - ) - .unwrap() - .encode(); - - assert_eq!( - call_in_wasm( - &format!("test_sandbox_instantiate{}", fn_suffix), - &code, - wasm_method, - &mut ext, - ) - .unwrap(), - 2u8.encode(), - ); -} - -test_wasm_execution_sandbox!(get_global_val_works); -fn get_global_val_works(wasm_method: WasmExecutionMethod, fn_suffix: &str) { - let mut ext = TestExternalities::default(); - let mut ext = ext.ext(); - - let code = wat::parse_str( - r#" - (module - (global (export "test_global") i64 (i64.const 500)) - ) - "#, - ) - .unwrap() - .encode(); - - assert_eq!( - call_in_wasm( - &format!("test_sandbox_get_global_val{}", fn_suffix), - &code, - wasm_method, - &mut ext, - ) - .unwrap(), - 500i64.encode(), - ); -} diff --git a/client/executor/src/lib.rs b/client/executor/src/lib.rs index 1fb041c358fb1..0670b840949d7 100644 --- a/client/executor/src/lib.rs +++ b/client/executor/src/lib.rs @@ -49,7 +49,7 @@ pub use sp_wasm_interface; pub use wasm_runtime::{read_embedded_version, WasmExecutionMethod}; pub use wasmi; -pub use sc_executor_common::{error, sandbox}; +pub use sc_executor_common::error; pub use sc_executor_wasmtime::InstantiationStrategy as WasmtimeInstantiationStrategy; /// Extracts the runtime version of a given runtime code. diff --git a/client/executor/wasmi/Cargo.toml b/client/executor/wasmi/Cargo.toml index ef01f3784154d..4235440023c89 100644 --- a/client/executor/wasmi/Cargo.toml +++ b/client/executor/wasmi/Cargo.toml @@ -14,11 +14,9 @@ readme = "README.md" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0" } log = "0.4.17" wasmi = "0.13" sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sc-executor-common = { version = "0.10.0-dev", path = "../common" } sp-runtime-interface = { version = "7.0.0", path = "../../../primitives/runtime-interface" } -sp-sandbox = { version = "0.10.0-dev", path = "../../../primitives/sandbox" } sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" } diff --git a/client/executor/wasmi/src/lib.rs b/client/executor/wasmi/src/lib.rs index 1284cc23e4c96..6eb38146946b8 100644 --- a/client/executor/wasmi/src/lib.rs +++ b/client/executor/wasmi/src/lib.rs @@ -18,7 +18,7 @@ //! This crate provides an implementation of `WasmModule` that is baked by wasmi. -use std::{cell::RefCell, rc::Rc, str, sync::Arc}; +use std::{cell::RefCell, str, sync::Arc}; use log::{debug, error, trace}; use wasmi::{ @@ -28,26 +28,18 @@ use wasmi::{ TableRef, }; -use codec::{Decode, Encode}; use sc_allocator::AllocationStats; use sc_executor_common::{ error::{Error, MessageWithBacktrace, WasmError}, runtime_blob::{DataSegmentsSnapshot, RuntimeBlob}, - sandbox, - util::MemoryTransfer, wasm_runtime::{InvokeMethod, WasmInstance, WasmModule}, }; use sp_runtime_interface::unpack_ptr_and_len; -use sp_sandbox::env as sandbox_env; -use sp_wasm_interface::{ - Function, FunctionContext, MemoryId, Pointer, Result as WResult, Sandbox, WordSize, -}; +use sp_wasm_interface::{Function, FunctionContext, Pointer, Result as WResult, WordSize}; struct FunctionExecutor { - sandbox_store: Rc>>, heap: RefCell, memory: MemoryRef, - table: Option, host_functions: Arc>, allow_missing_func_imports: bool, missing_functions: Arc>, @@ -58,18 +50,13 @@ impl FunctionExecutor { fn new( m: MemoryRef, heap_base: u32, - t: Option, host_functions: Arc>, allow_missing_func_imports: bool, missing_functions: Arc>, ) -> Result { Ok(FunctionExecutor { - sandbox_store: Rc::new(RefCell::new(sandbox::Store::new( - sandbox::SandboxBackend::Wasmi, - ))), heap: RefCell::new(sc_allocator::FreeingBumpHeapAllocator::new(heap_base)), memory: m, - table: t, host_functions, allow_missing_func_imports, missing_functions, @@ -78,42 +65,6 @@ impl FunctionExecutor { } } -struct SandboxContext<'a> { - executor: &'a mut FunctionExecutor, - dispatch_thunk: wasmi::FuncRef, -} - -impl<'a> sandbox::SandboxContext for SandboxContext<'a> { - fn invoke( - &mut self, - invoke_args_ptr: Pointer, - invoke_args_len: WordSize, - state: u32, - func_idx: sandbox::SupervisorFuncIndex, - ) -> Result { - let result = wasmi::FuncInstance::invoke( - &self.dispatch_thunk, - &[ - RuntimeValue::I32(u32::from(invoke_args_ptr) as i32), - RuntimeValue::I32(invoke_args_len as i32), - RuntimeValue::I32(state as i32), - RuntimeValue::I32(usize::from(func_idx) as i32), - ], - self.executor, - ); - - match result { - Ok(Some(RuntimeValue::I64(val))) => Ok(val), - Ok(_) => Err("Supervisor function returned unexpected result!".into()), - Err(err) => Err(Error::Sandbox(err.to_string())), - } - } - - fn supervisor_context(&mut self) -> &mut dyn FunctionContext { - self.executor - } -} - impl FunctionContext for FunctionExecutor { fn read_memory_into(&self, address: Pointer, dest: &mut [u8]) -> WResult<()> { self.memory.get_into(address.into(), dest).map_err(|e| e.to_string()) @@ -135,189 +86,11 @@ impl FunctionContext for FunctionExecutor { .with_direct_access_mut(|mem| heap.deallocate(mem, ptr).map_err(|e| e.to_string())) } - fn sandbox(&mut self) -> &mut dyn Sandbox { - self - } - fn register_panic_error_message(&mut self, message: &str) { self.panic_message = Some(message.to_owned()); } } -impl Sandbox for FunctionExecutor { - fn memory_get( - &mut self, - memory_id: MemoryId, - offset: WordSize, - buf_ptr: Pointer, - buf_len: WordSize, - ) -> WResult { - let sandboxed_memory = - self.sandbox_store.borrow().memory(memory_id).map_err(|e| e.to_string())?; - - let len = buf_len as usize; - - let buffer = match sandboxed_memory.read(Pointer::new(offset as u32), len) { - Err(_) => return Ok(sandbox_env::ERR_OUT_OF_BOUNDS), - Ok(buffer) => buffer, - }; - - if self.memory.set(buf_ptr.into(), &buffer).is_err() { - return Ok(sandbox_env::ERR_OUT_OF_BOUNDS) - } - - Ok(sandbox_env::ERR_OK) - } - - fn memory_set( - &mut self, - memory_id: MemoryId, - offset: WordSize, - val_ptr: Pointer, - val_len: WordSize, - ) -> WResult { - let sandboxed_memory = - self.sandbox_store.borrow().memory(memory_id).map_err(|e| e.to_string())?; - - let len = val_len as usize; - - #[allow(deprecated)] - let buffer = match self.memory.get(val_ptr.into(), len) { - Err(_) => return Ok(sandbox_env::ERR_OUT_OF_BOUNDS), - Ok(buffer) => buffer, - }; - - if sandboxed_memory.write_from(Pointer::new(offset as u32), &buffer).is_err() { - return Ok(sandbox_env::ERR_OUT_OF_BOUNDS) - } - - Ok(sandbox_env::ERR_OK) - } - - fn memory_teardown(&mut self, memory_id: MemoryId) -> WResult<()> { - self.sandbox_store - .borrow_mut() - .memory_teardown(memory_id) - .map_err(|e| e.to_string()) - } - - fn memory_new(&mut self, initial: u32, maximum: u32) -> WResult { - self.sandbox_store - .borrow_mut() - .new_memory(initial, maximum) - .map_err(|e| e.to_string()) - } - - fn invoke( - &mut self, - instance_id: u32, - export_name: &str, - mut args: &[u8], - return_val: Pointer, - return_val_len: WordSize, - state: u32, - ) -> WResult { - trace!(target: "sp-sandbox", "invoke, instance_idx={}", instance_id); - - // Deserialize arguments and convert them into wasmi types. - let args = Vec::::decode(&mut args) - .map_err(|_| "Can't decode serialized arguments for the invocation")? - .into_iter() - .collect::>(); - - let instance = - self.sandbox_store.borrow().instance(instance_id).map_err(|e| e.to_string())?; - - let dispatch_thunk = self - .sandbox_store - .borrow() - .dispatch_thunk(instance_id) - .map_err(|e| e.to_string())?; - - match instance.invoke( - export_name, - &args, - state, - &mut SandboxContext { dispatch_thunk, executor: self }, - ) { - Ok(None) => Ok(sandbox_env::ERR_OK), - Ok(Some(val)) => { - // Serialize return value and write it back into the memory. - sp_wasm_interface::ReturnValue::Value(val).using_encoded(|val| { - if val.len() > return_val_len as usize { - return Err("Return value buffer is too small".into()) - } - self.write_memory(return_val, val).map_err(|_| "Return value buffer is OOB")?; - Ok(sandbox_env::ERR_OK) - }) - }, - Err(_) => Ok(sandbox_env::ERR_EXECUTION), - } - } - - fn instance_teardown(&mut self, instance_id: u32) -> WResult<()> { - self.sandbox_store - .borrow_mut() - .instance_teardown(instance_id) - .map_err(|e| e.to_string()) - } - - fn instance_new( - &mut self, - dispatch_thunk_id: u32, - wasm: &[u8], - raw_env_def: &[u8], - state: u32, - ) -> WResult { - // Extract a dispatch thunk from instance's table by the specified index. - let dispatch_thunk = { - let table = self - .table - .as_ref() - .ok_or("Runtime doesn't have a table; sandbox is unavailable")?; - table - .get(dispatch_thunk_id) - .map_err(|_| "dispatch_thunk_idx is out of the table bounds")? - .ok_or("dispatch_thunk_idx points on an empty table entry")? - }; - - let guest_env = - match sandbox::GuestEnvironment::decode(&*self.sandbox_store.borrow(), raw_env_def) { - Ok(guest_env) => guest_env, - Err(_) => return Ok(sandbox_env::ERR_MODULE as u32), - }; - - let store = self.sandbox_store.clone(); - let result = store.borrow_mut().instantiate( - wasm, - guest_env, - state, - &mut SandboxContext { executor: self, dispatch_thunk: dispatch_thunk.clone() }, - ); - - let instance_idx_or_err_code = - match result.map(|i| i.register(&mut store.borrow_mut(), dispatch_thunk)) { - Ok(instance_idx) => instance_idx, - Err(sandbox::InstantiationError::StartTrapped) => sandbox_env::ERR_EXECUTION, - Err(_) => sandbox_env::ERR_MODULE, - }; - - Ok(instance_idx_or_err_code) - } - - fn get_global_val( - &self, - instance_idx: u32, - name: &str, - ) -> WResult> { - self.sandbox_store - .borrow() - .instance(instance_idx) - .map(|i| i.get_global_val(name)) - .map_err(|e| e.to_string()) - } -} - /// Will be used on initialization of a module to resolve function and memory imports. struct Resolver<'a> { /// All the hot functions that we export for the WASM blob. @@ -502,7 +275,6 @@ fn call_in_wasm_module( let mut function_executor = FunctionExecutor::new( memory.clone(), heap_base, - table.clone(), host_functions, allow_missing_func_imports, missing_functions, diff --git a/client/executor/wasmtime/Cargo.toml b/client/executor/wasmtime/Cargo.toml index b12ca0779e7a2..7e38929f05a13 100644 --- a/client/executor/wasmtime/Cargo.toml +++ b/client/executor/wasmtime/Cargo.toml @@ -14,10 +14,8 @@ targets = ["x86_64-unknown-linux-gnu"] [dependencies] cfg-if = "1.0" -codec = { package = "parity-scale-codec", version = "3.0.0" } libc = "0.2.121" log = "0.4.17" -parity-wasm = "0.45" # When bumping wasmtime do not forget to also bump rustix # to exactly the same version as used by wasmtime! @@ -32,7 +30,6 @@ wasmtime = { version = "1.0.0", default-features = false, features = [ sc-allocator = { version = "4.1.0-dev", path = "../../allocator" } sc-executor-common = { version = "0.10.0-dev", path = "../common" } sp-runtime-interface = { version = "7.0.0", path = "../../../primitives/runtime-interface" } -sp-sandbox = { version = "0.10.0-dev", path = "../../../primitives/sandbox" } sp-wasm-interface = { version = "7.0.0", path = "../../../primitives/wasm-interface" } # Here we include the rustix crate in the exactly same semver-compatible version as used by @@ -50,3 +47,4 @@ sc-runtime-test = { version = "2.0.0", path = "../runtime-test" } sp-io = { version = "7.0.0", path = "../../../primitives/io" } tempfile = "3.3.0" paste = "1.0" +codec = { package = "parity-scale-codec", version = "3.0.0" } diff --git a/client/executor/wasmtime/src/host.rs b/client/executor/wasmtime/src/host.rs index 768a6e36e2390..0d9eac875170b 100644 --- a/client/executor/wasmtime/src/host.rs +++ b/client/executor/wasmtime/src/host.rs @@ -19,33 +19,17 @@ //! This module defines `HostState` and `HostContext` structs which provide logic and state //! required for execution of host. -use log::trace; -use wasmtime::{Caller, Func, Val}; +use wasmtime::Caller; -use codec::{Decode, Encode}; use sc_allocator::{AllocationStats, FreeingBumpHeapAllocator}; -use sc_executor_common::{ - error::Result, - sandbox::{self, SupervisorFuncIndex}, - util::MemoryTransfer, -}; -use sp_sandbox::env as sandbox_env; -use sp_wasm_interface::{FunctionContext, MemoryId, Pointer, Sandbox, WordSize}; +use sp_wasm_interface::{Pointer, WordSize}; use crate::{runtime::StoreData, util}; -// The sandbox store is inside of a Option>> so that we can temporarily borrow it. -struct SandboxStore(Option>>); - -// There are a bunch of `Rc`s within the sandbox store, however we only manipulate -// those within one thread so this should be safe. -unsafe impl Send for SandboxStore {} - /// The state required to construct a HostContext context. The context only lasts for one host /// call, whereas the state is maintained for the duration of a Wasm runtime call, which may make /// many different host calls that must share state. pub struct HostState { - sandbox_store: SandboxStore, allocator: FreeingBumpHeapAllocator, panic_message: Option, } @@ -53,13 +37,7 @@ pub struct HostState { impl HostState { /// Constructs a new `HostState`. pub fn new(allocator: FreeingBumpHeapAllocator) -> Self { - HostState { - sandbox_store: SandboxStore(Some(Box::new(sandbox::Store::new( - sandbox::SandboxBackend::TryWasmer, - )))), - allocator, - panic_message: None, - } + HostState { allocator, panic_message: None } } /// Takes the error message out of the host state, leaving a `None` in its place. @@ -80,35 +58,12 @@ pub(crate) struct HostContext<'a> { } impl<'a> HostContext<'a> { - fn host_state(&self) -> &HostState { - self.caller - .data() - .host_state() - .expect("host state is not empty when calling a function in wasm; qed") - } - fn host_state_mut(&mut self) -> &mut HostState { self.caller .data_mut() .host_state_mut() .expect("host state is not empty when calling a function in wasm; qed") } - - fn sandbox_store(&self) -> &sandbox::Store { - self.host_state() - .sandbox_store - .0 - .as_ref() - .expect("sandbox store is only empty when temporarily borrowed") - } - - fn sandbox_store_mut(&mut self) -> &mut sandbox::Store { - self.host_state_mut() - .sandbox_store - .0 - .as_mut() - .expect("sandbox store is only empty when temporarily borrowed") - } } impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> { @@ -144,233 +99,7 @@ impl<'a> sp_wasm_interface::FunctionContext for HostContext<'a> { .map_err(|e| e.to_string()) } - fn sandbox(&mut self) -> &mut dyn Sandbox { - self - } - fn register_panic_error_message(&mut self, message: &str) { self.host_state_mut().panic_message = Some(message.to_owned()); } } - -impl<'a> Sandbox for HostContext<'a> { - fn memory_get( - &mut self, - memory_id: MemoryId, - offset: WordSize, - buf_ptr: Pointer, - buf_len: WordSize, - ) -> sp_wasm_interface::Result { - let sandboxed_memory = self.sandbox_store().memory(memory_id).map_err(|e| e.to_string())?; - - let len = buf_len as usize; - - let buffer = match sandboxed_memory.read(Pointer::new(offset as u32), len) { - Err(_) => return Ok(sandbox_env::ERR_OUT_OF_BOUNDS), - Ok(buffer) => buffer, - }; - - if util::write_memory_from(&mut self.caller, buf_ptr, &buffer).is_err() { - return Ok(sandbox_env::ERR_OUT_OF_BOUNDS) - } - - Ok(sandbox_env::ERR_OK) - } - - fn memory_set( - &mut self, - memory_id: MemoryId, - offset: WordSize, - val_ptr: Pointer, - val_len: WordSize, - ) -> sp_wasm_interface::Result { - let sandboxed_memory = self.sandbox_store().memory(memory_id).map_err(|e| e.to_string())?; - - let len = val_len as usize; - - let buffer = match util::read_memory(&self.caller, val_ptr, len) { - Err(_) => return Ok(sandbox_env::ERR_OUT_OF_BOUNDS), - Ok(buffer) => buffer, - }; - - if sandboxed_memory.write_from(Pointer::new(offset as u32), &buffer).is_err() { - return Ok(sandbox_env::ERR_OUT_OF_BOUNDS) - } - - Ok(sandbox_env::ERR_OK) - } - - fn memory_teardown(&mut self, memory_id: MemoryId) -> sp_wasm_interface::Result<()> { - self.sandbox_store_mut().memory_teardown(memory_id).map_err(|e| e.to_string()) - } - - fn memory_new(&mut self, initial: u32, maximum: u32) -> sp_wasm_interface::Result { - self.sandbox_store_mut().new_memory(initial, maximum).map_err(|e| e.to_string()) - } - - fn invoke( - &mut self, - instance_id: u32, - export_name: &str, - mut args: &[u8], - return_val: Pointer, - return_val_len: u32, - state: u32, - ) -> sp_wasm_interface::Result { - trace!(target: "sp-sandbox", "invoke, instance_idx={}", instance_id); - - // Deserialize arguments and convert them into wasmi types. - let args = Vec::::decode(&mut args) - .map_err(|_| "Can't decode serialized arguments for the invocation")? - .into_iter() - .collect::>(); - - let instance = self.sandbox_store().instance(instance_id).map_err(|e| e.to_string())?; - - let dispatch_thunk = - self.sandbox_store().dispatch_thunk(instance_id).map_err(|e| e.to_string())?; - - let result = instance.invoke( - export_name, - &args, - state, - &mut SandboxContext { host_context: self, dispatch_thunk }, - ); - - match result { - Ok(None) => Ok(sandbox_env::ERR_OK), - Ok(Some(val)) => { - // Serialize return value and write it back into the memory. - sp_wasm_interface::ReturnValue::Value(val.into()).using_encoded(|val| { - if val.len() > return_val_len as usize { - return Err("Return value buffer is too small".into()) - } - ::write_memory(self, return_val, val) - .map_err(|_| "can't write return value")?; - Ok(sandbox_env::ERR_OK) - }) - }, - Err(_) => Ok(sandbox_env::ERR_EXECUTION), - } - } - - fn instance_teardown(&mut self, instance_id: u32) -> sp_wasm_interface::Result<()> { - self.sandbox_store_mut() - .instance_teardown(instance_id) - .map_err(|e| e.to_string()) - } - - fn instance_new( - &mut self, - dispatch_thunk_id: u32, - wasm: &[u8], - raw_env_def: &[u8], - state: u32, - ) -> sp_wasm_interface::Result { - // Extract a dispatch thunk from the instance's table by the specified index. - let dispatch_thunk = { - let table = self - .caller - .data() - .table() - .ok_or("Runtime doesn't have a table; sandbox is unavailable")?; - let table_item = table.get(&mut self.caller, dispatch_thunk_id); - - *table_item - .ok_or("dispatch_thunk_id is out of bounds")? - .funcref() - .ok_or("dispatch_thunk_idx should be a funcref")? - .ok_or("dispatch_thunk_idx should point to actual func")? - }; - - let guest_env = match sandbox::GuestEnvironment::decode(self.sandbox_store(), raw_env_def) { - Ok(guest_env) => guest_env, - Err(_) => return Ok(sandbox_env::ERR_MODULE as u32), - }; - - let mut store = self - .host_state_mut() - .sandbox_store - .0 - .take() - .expect("sandbox store is only empty when borrowed"); - - // Catch any potential panics so that we can properly restore the sandbox store - // which we've destructively borrowed. - let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| { - store.instantiate( - wasm, - guest_env, - state, - &mut SandboxContext { host_context: self, dispatch_thunk }, - ) - })); - - self.host_state_mut().sandbox_store.0 = Some(store); - - let result = match result { - Ok(result) => result, - Err(error) => std::panic::resume_unwind(error), - }; - - let instance_idx_or_err_code = match result { - Ok(instance) => instance.register(self.sandbox_store_mut(), dispatch_thunk), - Err(sandbox::InstantiationError::StartTrapped) => sandbox_env::ERR_EXECUTION, - Err(_) => sandbox_env::ERR_MODULE, - }; - - Ok(instance_idx_or_err_code as u32) - } - - fn get_global_val( - &self, - instance_idx: u32, - name: &str, - ) -> sp_wasm_interface::Result> { - self.sandbox_store() - .instance(instance_idx) - .map(|i| i.get_global_val(name)) - .map_err(|e| e.to_string()) - } -} - -struct SandboxContext<'a, 'b> { - host_context: &'a mut HostContext<'b>, - dispatch_thunk: Func, -} - -impl<'a, 'b> sandbox::SandboxContext for SandboxContext<'a, 'b> { - fn invoke( - &mut self, - invoke_args_ptr: Pointer, - invoke_args_len: WordSize, - state: u32, - func_idx: SupervisorFuncIndex, - ) -> Result { - let mut ret_vals = [Val::null()]; - let result = self.dispatch_thunk.call( - &mut self.host_context.caller, - &[ - Val::I32(u32::from(invoke_args_ptr) as i32), - Val::I32(invoke_args_len as i32), - Val::I32(state as i32), - Val::I32(usize::from(func_idx) as i32), - ], - &mut ret_vals, - ); - - match result { - Ok(()) => - if let Some(ret_val) = ret_vals[0].i64() { - Ok(ret_val) - } else { - Err("Supervisor function returned unexpected result!".into()) - }, - Err(err) => Err(err.to_string().into()), - } - } - - fn supervisor_context(&mut self) -> &mut dyn FunctionContext { - self.host_context - } -} diff --git a/client/executor/wasmtime/src/runtime.rs b/client/executor/wasmtime/src/runtime.rs index 5bca899648c34..b124fd627dc69 100644 --- a/client/executor/wasmtime/src/runtime.rs +++ b/client/executor/wasmtime/src/runtime.rs @@ -56,11 +56,6 @@ pub(crate) struct StoreData { } impl StoreData { - /// Returns a reference to the host state. - pub fn host_state(&self) -> Option<&HostState> { - self.host_state.as_ref() - } - /// Returns a mutable reference to the host state. pub fn host_state_mut(&mut self) -> Option<&mut HostState> { self.host_state.as_mut() @@ -70,11 +65,6 @@ impl StoreData { pub fn memory(&self) -> Memory { self.memory.expect("memory is always set; qed") } - - /// Returns the host table. - pub fn table(&self) -> Option { - self.table - } } pub(crate) type Store = wasmtime::Store; diff --git a/client/executor/wasmtime/src/util.rs b/client/executor/wasmtime/src/util.rs index 83745e21e86af..15f62e475033a 100644 --- a/client/executor/wasmtime/src/util.rs +++ b/client/executor/wasmtime/src/util.rs @@ -48,24 +48,6 @@ pub fn into_wasmtime_val(value: Value) -> wasmtime::Val { } } -/// Read data from a slice of memory into a newly allocated buffer. -/// -/// Returns an error if the read would go out of the memory bounds. -pub(crate) fn read_memory( - ctx: impl AsContext, - source_addr: Pointer, - size: usize, -) -> Result> { - let range = - checked_range(source_addr.into(), size, ctx.as_context().data().memory().data_size(&ctx)) - .ok_or_else(|| Error::Other("memory read is out of bounds".into()))?; - - let mut buffer = vec![0; range.len()]; - read_memory_into(ctx, source_addr, &mut buffer)?; - - Ok(buffer) -} - /// Read data from the instance memory into a slice. /// /// Returns an error if the read would go out of the memory bounds. diff --git a/primitives/io/src/lib.rs b/primitives/io/src/lib.rs index ead3ada1d1438..600d76b3b4300 100644 --- a/primitives/io/src/lib.rs +++ b/primitives/io/src/lib.rs @@ -1611,99 +1611,6 @@ mod tracing_setup { pub use tracing_setup::init_tracing; -/// Wasm-only interface that provides functions for interacting with the sandbox. -#[runtime_interface(wasm_only)] -pub trait Sandbox { - /// Instantiate a new sandbox instance with the given `wasm_code`. - fn instantiate( - &mut self, - dispatch_thunk: u32, - wasm_code: &[u8], - env_def: &[u8], - state_ptr: Pointer, - ) -> u32 { - self.sandbox() - .instance_new(dispatch_thunk, wasm_code, env_def, state_ptr.into()) - .expect("Failed to instantiate a new sandbox") - } - - /// Invoke `function` in the sandbox with `sandbox_idx`. - fn invoke( - &mut self, - instance_idx: u32, - function: &str, - args: &[u8], - return_val_ptr: Pointer, - return_val_len: u32, - state_ptr: Pointer, - ) -> u32 { - self.sandbox() - .invoke(instance_idx, function, args, return_val_ptr, return_val_len, state_ptr.into()) - .expect("Failed to invoke function with sandbox") - } - - /// Create a new memory instance with the given `initial` and `maximum` size. - fn memory_new(&mut self, initial: u32, maximum: u32) -> u32 { - self.sandbox() - .memory_new(initial, maximum) - .expect("Failed to create new memory with sandbox") - } - - /// Get the memory starting at `offset` from the instance with `memory_idx` into the buffer. - fn memory_get( - &mut self, - memory_idx: u32, - offset: u32, - buf_ptr: Pointer, - buf_len: u32, - ) -> u32 { - self.sandbox() - .memory_get(memory_idx, offset, buf_ptr, buf_len) - .expect("Failed to get memory with sandbox") - } - - /// Set the memory in the given `memory_idx` to the given value at `offset`. - fn memory_set( - &mut self, - memory_idx: u32, - offset: u32, - val_ptr: Pointer, - val_len: u32, - ) -> u32 { - self.sandbox() - .memory_set(memory_idx, offset, val_ptr, val_len) - .expect("Failed to set memory with sandbox") - } - - /// Teardown the memory instance with the given `memory_idx`. - fn memory_teardown(&mut self, memory_idx: u32) { - self.sandbox() - .memory_teardown(memory_idx) - .expect("Failed to teardown memory with sandbox") - } - - /// Teardown the sandbox instance with the given `instance_idx`. - fn instance_teardown(&mut self, instance_idx: u32) { - self.sandbox() - .instance_teardown(instance_idx) - .expect("Failed to teardown sandbox instance") - } - - /// Get the value from a global with the given `name`. The sandbox is determined by the given - /// `instance_idx`. - /// - /// Returns `Some(_)` when the requested global variable could be found. - fn get_global_val( - &mut self, - instance_idx: u32, - name: &str, - ) -> Option { - self.sandbox() - .get_global_val(instance_idx, name) - .expect("Failed to get global from sandbox") - } -} - /// Allocator used by Substrate when executing the Wasm runtime. #[cfg(all(target_arch = "wasm32", not(feature = "std")))] struct WasmAllocator; @@ -1779,7 +1686,6 @@ pub type SubstrateHostFunctions = ( allocator::HostFunctions, panic_handler::HostFunctions, logging::HostFunctions, - sandbox::HostFunctions, crate::trie::HostFunctions, offchain_index::HostFunctions, transaction_index::HostFunctions, diff --git a/primitives/sandbox/Cargo.toml b/primitives/sandbox/Cargo.toml deleted file mode 100644 index 024fe7209393c..0000000000000 --- a/primitives/sandbox/Cargo.toml +++ /dev/null @@ -1,40 +0,0 @@ -[package] -name = "sp-sandbox" -version = "0.10.0-dev" -authors = ["Parity Technologies "] -edition = "2021" -license = "Apache-2.0" -homepage = "https://substrate.io" -repository = "https://github.com/paritytech/substrate/" -description = "This crate provides means to instantiate and execute wasm modules." -readme = "README.md" - -[package.metadata.docs.rs] -targets = ["x86_64-unknown-linux-gnu"] - -[dependencies] -codec = { package = "parity-scale-codec", version = "3.0.0", default-features = false } -log = { version = "0.4", default-features = false } -wasmi = { version = "0.13", default-features = false } -sp-core = { version = "7.0.0", default-features = false, path = "../core" } -sp-io = { version = "7.0.0", default-features = false, path = "../io" } -sp-std = { version = "5.0.0", default-features = false, path = "../std" } -sp-wasm-interface = { version = "7.0.0", default-features = false, path = "../wasm-interface" } - -[dev-dependencies] -assert_matches = "1.3.0" -wat = "1.0" - -[features] -default = ["std"] -std = [ - "codec/std", - "log/std", - "sp-core/std", - "sp-io/std", - "sp-std/std", - "sp-wasm-interface/std", - "wasmi/std", -] -strict = [] -wasmer-sandbox = [] diff --git a/primitives/sandbox/README.md b/primitives/sandbox/README.md deleted file mode 100644 index 9335b53ae1fb9..0000000000000 --- a/primitives/sandbox/README.md +++ /dev/null @@ -1,21 +0,0 @@ -This crate provides means to instantiate and execute wasm modules. - -It works even when the user of this library executes from -inside the wasm VM. In this case the same VM is used for execution -of both the sandbox owner and the sandboxed module, without compromising security -and without the performance penalty of full wasm emulation inside wasm. - -This is achieved by using bindings to the wasm VM, which are published by the host API. -This API is thin and consists of only a handful functions. It contains functions for instantiating -modules and executing them, but doesn't contain functions for inspecting the module -structure. The user of this library is supposed to read the wasm module. - -When this crate is used in the `std` environment all these functions are implemented by directly -calling the wasm VM. - -Examples of possible use-cases for this library are not limited to the following: - -- implementing smart-contract runtimes that use wasm for contract code -- executing a wasm substrate runtime inside of a wasm parachain - -License: Apache-2.0 \ No newline at end of file diff --git a/primitives/sandbox/src/embedded_executor.rs b/primitives/sandbox/src/embedded_executor.rs deleted file mode 100644 index 115c3192f3d89..0000000000000 --- a/primitives/sandbox/src/embedded_executor.rs +++ /dev/null @@ -1,478 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! An embedded WASM executor utilizing `wasmi`. - -use alloc::string::String; - -use wasmi::{ - memory_units::Pages, Externals, FuncInstance, FuncRef, GlobalDescriptor, GlobalRef, - ImportResolver, MemoryDescriptor, MemoryInstance, MemoryRef, Module, ModuleInstance, ModuleRef, - RuntimeArgs, RuntimeValue, Signature, TableDescriptor, TableRef, Trap, -}; - -use sp_std::{ - borrow::ToOwned, collections::btree_map::BTreeMap, fmt, marker::PhantomData, prelude::*, -}; - -use crate::{Error, HostError, HostFuncType, ReturnValue, Value, TARGET}; - -/// The linear memory used by the sandbox. -#[derive(Clone)] -pub struct Memory { - memref: MemoryRef, -} - -impl super::SandboxMemory for Memory { - fn new(initial: u32, maximum: Option) -> Result { - Ok(Memory { - memref: MemoryInstance::alloc( - Pages(initial as usize), - maximum.map(|m| Pages(m as usize)), - ) - .map_err(|_| Error::Module)?, - }) - } - - fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error> { - self.memref.get_into(ptr, buf).map_err(|_| Error::OutOfBounds)?; - Ok(()) - } - - fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error> { - self.memref.set(ptr, value).map_err(|_| Error::OutOfBounds)?; - Ok(()) - } -} - -struct HostFuncIndex(usize); - -struct DefinedHostFunctions { - funcs: Vec>, -} - -impl Clone for DefinedHostFunctions { - fn clone(&self) -> DefinedHostFunctions { - DefinedHostFunctions { funcs: self.funcs.clone() } - } -} - -impl DefinedHostFunctions { - fn new() -> DefinedHostFunctions { - DefinedHostFunctions { funcs: Vec::new() } - } - - fn define(&mut self, f: HostFuncType) -> HostFuncIndex { - let idx = self.funcs.len(); - self.funcs.push(f); - HostFuncIndex(idx) - } -} - -#[derive(Debug)] -struct DummyHostError; - -impl fmt::Display for DummyHostError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "DummyHostError") - } -} - -impl wasmi::HostError for DummyHostError {} - -struct GuestExternals<'a, T: 'a> { - state: &'a mut T, - defined_host_functions: &'a DefinedHostFunctions, -} - -impl<'a, T> Externals for GuestExternals<'a, T> { - fn invoke_index( - &mut self, - index: usize, - args: RuntimeArgs, - ) -> Result, Trap> { - let args = args.as_ref().iter().cloned().map(to_interface).collect::>(); - - let result = (self.defined_host_functions.funcs[index])(self.state, &args); - match result { - Ok(value) => Ok(match value { - ReturnValue::Value(v) => Some(to_wasmi(v)), - ReturnValue::Unit => None, - }), - Err(HostError) => Err(Trap::host(DummyHostError)), - } - } -} - -enum ExternVal { - HostFunc(HostFuncIndex), - Memory(Memory), -} - -/// A builder for the environment of the sandboxed WASM module. -pub struct EnvironmentDefinitionBuilder { - map: BTreeMap<(Vec, Vec), ExternVal>, - defined_host_functions: DefinedHostFunctions, -} - -impl super::SandboxEnvironmentBuilder for EnvironmentDefinitionBuilder { - fn new() -> EnvironmentDefinitionBuilder { - EnvironmentDefinitionBuilder { - map: BTreeMap::new(), - defined_host_functions: DefinedHostFunctions::new(), - } - } - - fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) - where - N1: Into>, - N2: Into>, - { - let idx = self.defined_host_functions.define(f); - self.map.insert((module.into(), field.into()), ExternVal::HostFunc(idx)); - } - - fn add_memory(&mut self, module: N1, field: N2, mem: Memory) - where - N1: Into>, - N2: Into>, - { - self.map.insert((module.into(), field.into()), ExternVal::Memory(mem)); - } -} - -impl ImportResolver for EnvironmentDefinitionBuilder { - fn resolve_func( - &self, - module_name: &str, - field_name: &str, - signature: &Signature, - ) -> Result { - let key = (module_name.as_bytes().to_owned(), field_name.as_bytes().to_owned()); - let externval = self.map.get(&key).ok_or_else(|| { - log::debug!(target: TARGET, "Export {}:{} not found", module_name, field_name); - wasmi::Error::Instantiation(String::new()) - })?; - let host_func_idx = match *externval { - ExternVal::HostFunc(ref idx) => idx, - _ => { - log::debug!( - target: TARGET, - "Export {}:{} is not a host func", - module_name, - field_name, - ); - return Err(wasmi::Error::Instantiation(String::new())) - }, - }; - Ok(FuncInstance::alloc_host(signature.clone(), host_func_idx.0)) - } - - fn resolve_global( - &self, - _module_name: &str, - _field_name: &str, - _global_type: &GlobalDescriptor, - ) -> Result { - log::debug!(target: TARGET, "Importing globals is not supported yet"); - Err(wasmi::Error::Instantiation(String::new())) - } - - fn resolve_memory( - &self, - module_name: &str, - field_name: &str, - _memory_type: &MemoryDescriptor, - ) -> Result { - let key = (module_name.as_bytes().to_owned(), field_name.as_bytes().to_owned()); - let externval = self.map.get(&key).ok_or_else(|| { - log::debug!(target: TARGET, "Export {}:{} not found", module_name, field_name); - wasmi::Error::Instantiation(String::new()) - })?; - let memory = match *externval { - ExternVal::Memory(ref m) => m, - _ => { - log::debug!( - target: TARGET, - "Export {}:{} is not a memory", - module_name, - field_name, - ); - return Err(wasmi::Error::Instantiation(String::new())) - }, - }; - Ok(memory.memref.clone()) - } - - fn resolve_table( - &self, - _module_name: &str, - _field_name: &str, - _table_type: &TableDescriptor, - ) -> Result { - log::debug!("Importing tables is not supported yet"); - Err(wasmi::Error::Instantiation(String::new())) - } -} - -/// Sandboxed instance of a WASM module. -pub struct Instance { - instance: ModuleRef, - defined_host_functions: DefinedHostFunctions, - _marker: PhantomData, -} - -impl super::SandboxInstance for Instance { - type Memory = Memory; - type EnvironmentBuilder = EnvironmentDefinitionBuilder; - - fn new( - code: &[u8], - env_def_builder: &EnvironmentDefinitionBuilder, - state: &mut T, - ) -> Result, Error> { - let module = Module::from_buffer(code).map_err(|_| Error::Module)?; - let not_started_instance = - ModuleInstance::new(&module, env_def_builder).map_err(|_| Error::Module)?; - - let defined_host_functions = env_def_builder.defined_host_functions.clone(); - let instance = { - let mut externals = - GuestExternals { state, defined_host_functions: &defined_host_functions }; - let instance = - not_started_instance.run_start(&mut externals).map_err(|_| Error::Execution)?; - instance - }; - - Ok(Instance { instance, defined_host_functions, _marker: PhantomData:: }) - } - - fn invoke(&mut self, name: &str, args: &[Value], state: &mut T) -> Result { - let args = args.iter().cloned().map(to_wasmi).collect::>(); - - let mut externals = - GuestExternals { state, defined_host_functions: &self.defined_host_functions }; - let result = self.instance.invoke_export(name, &args, &mut externals); - - match result { - Ok(None) => Ok(ReturnValue::Unit), - Ok(Some(val)) => Ok(ReturnValue::Value(to_interface(val))), - Err(_err) => Err(Error::Execution), - } - } - - fn get_global_val(&self, name: &str) -> Option { - let global = self.instance.export_by_name(name)?.as_global()?.get(); - - Some(to_interface(global)) - } -} - -/// Convert the substrate value type to the wasmi value type. -fn to_wasmi(value: Value) -> RuntimeValue { - match value { - Value::I32(val) => RuntimeValue::I32(val), - Value::I64(val) => RuntimeValue::I64(val), - Value::F32(val) => RuntimeValue::F32(val.into()), - Value::F64(val) => RuntimeValue::F64(val.into()), - } -} - -/// Convert the wasmi value type to the substrate value type. -fn to_interface(value: RuntimeValue) -> Value { - match value { - RuntimeValue::I32(val) => Value::I32(val), - RuntimeValue::I64(val) => Value::I64(val), - RuntimeValue::F32(val) => Value::F32(val.into()), - RuntimeValue::F64(val) => Value::F64(val.into()), - } -} - -#[cfg(test)] -mod tests { - use super::{EnvironmentDefinitionBuilder, Instance}; - use crate::{Error, HostError, ReturnValue, SandboxEnvironmentBuilder, SandboxInstance, Value}; - use assert_matches::assert_matches; - - fn execute_sandboxed(code: &[u8], args: &[Value]) -> Result { - struct State { - counter: u32, - } - - fn env_assert(_e: &mut State, args: &[Value]) -> Result { - if args.len() != 1 { - return Err(HostError) - } - let condition = args[0].as_i32().ok_or_else(|| HostError)?; - if condition != 0 { - Ok(ReturnValue::Unit) - } else { - Err(HostError) - } - } - fn env_inc_counter(e: &mut State, args: &[Value]) -> Result { - if args.len() != 1 { - return Err(HostError) - } - let inc_by = args[0].as_i32().ok_or_else(|| HostError)?; - e.counter += inc_by as u32; - Ok(ReturnValue::Value(Value::I32(e.counter as i32))) - } - /// Function that takes one argument of any type and returns that value. - fn env_polymorphic_id(_e: &mut State, args: &[Value]) -> Result { - if args.len() != 1 { - return Err(HostError) - } - Ok(ReturnValue::Value(args[0])) - } - - let mut state = State { counter: 0 }; - - let mut env_builder = EnvironmentDefinitionBuilder::new(); - env_builder.add_host_func("env", "assert", env_assert); - env_builder.add_host_func("env", "inc_counter", env_inc_counter); - env_builder.add_host_func("env", "polymorphic_id", env_polymorphic_id); - - let mut instance = Instance::new(code, &env_builder, &mut state)?; - let result = instance.invoke("call", args, &mut state); - - result.map_err(|_| HostError) - } - - #[test] - fn invoke_args() { - let code = wat::parse_str( - r#" - (module - (import "env" "assert" (func $assert (param i32))) - - (func (export "call") (param $x i32) (param $y i64) - ;; assert that $x = 0x12345678 - (call $assert - (i32.eq - (get_local $x) - (i32.const 0x12345678) - ) - ) - - (call $assert - (i64.eq - (get_local $y) - (i64.const 0x1234567887654321) - ) - ) - ) - ) - "#, - ) - .unwrap(); - - let result = - execute_sandboxed(&code, &[Value::I32(0x12345678), Value::I64(0x1234567887654321)]); - assert!(result.is_ok()); - } - - #[test] - fn return_value() { - let code = wat::parse_str( - r#" - (module - (func (export "call") (param $x i32) (result i32) - (i32.add - (get_local $x) - (i32.const 1) - ) - ) - ) - "#, - ) - .unwrap(); - - let return_val = execute_sandboxed(&code, &[Value::I32(0x1336)]).unwrap(); - assert_eq!(return_val, ReturnValue::Value(Value::I32(0x1337))); - } - - #[test] - fn signatures_dont_matter() { - let code = wat::parse_str( - r#" - (module - (import "env" "polymorphic_id" (func $id_i32 (param i32) (result i32))) - (import "env" "polymorphic_id" (func $id_i64 (param i64) (result i64))) - (import "env" "assert" (func $assert (param i32))) - - (func (export "call") - ;; assert that we can actually call the "same" function with different - ;; signatures. - (call $assert - (i32.eq - (call $id_i32 - (i32.const 0x012345678) - ) - (i32.const 0x012345678) - ) - ) - (call $assert - (i64.eq - (call $id_i64 - (i64.const 0x0123456789abcdef) - ) - (i64.const 0x0123456789abcdef) - ) - ) - ) - ) - "#, - ) - .unwrap(); - - let return_val = execute_sandboxed(&code, &[]).unwrap(); - assert_eq!(return_val, ReturnValue::Unit); - } - - #[test] - fn cant_return_unmatching_type() { - fn env_returns_i32(_e: &mut (), _args: &[Value]) -> Result { - Ok(ReturnValue::Value(Value::I32(42))) - } - - let mut env_builder = EnvironmentDefinitionBuilder::new(); - env_builder.add_host_func("env", "returns_i32", env_returns_i32); - - let code = wat::parse_str( - r#" - (module - ;; It's actually returns i32, but imported as if it returned i64 - (import "env" "returns_i32" (func $returns_i32 (result i64))) - - (func (export "call") - (drop - (call $returns_i32) - ) - ) - ) - "#, - ) - .unwrap(); - - // It succeeds since we are able to import functions with types we want. - let mut instance = Instance::new(&code, &env_builder, &mut ()).unwrap(); - - // But this fails since we imported a function that returns i32 as if it returned i64. - assert_matches!(instance.invoke("call", &[], &mut ()), Err(Error::Execution)); - } -} diff --git a/primitives/sandbox/src/env.rs b/primitives/sandbox/src/env.rs deleted file mode 100644 index 94b1c5e467a9c..0000000000000 --- a/primitives/sandbox/src/env.rs +++ /dev/null @@ -1,120 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Definition of a sandbox environment. - -use codec::{Decode, Encode}; - -use sp_core::RuntimeDebug; -use sp_std::vec::Vec; - -/// Error error that can be returned from host function. -#[derive(Encode, Decode, RuntimeDebug)] -pub struct HostError; - -/// Describes an entity to define or import into the environment. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] -pub enum ExternEntity { - /// Function that is specified by an index in a default table of - /// a module that creates the sandbox. - #[codec(index = 1)] - Function(u32), - - /// Linear memory that is specified by some identifier returned by sandbox - /// module upon creation new sandboxed memory. - #[codec(index = 2)] - Memory(u32), -} - -/// An entry in a environment definition table. -/// -/// Each entry has a two-level name and description of an entity -/// being defined. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] -pub struct Entry { - /// Module name of which corresponding entity being defined. - pub module_name: Vec, - /// Field name in which corresponding entity being defined. - pub field_name: Vec, - /// External entity being defined. - pub entity: ExternEntity, -} - -/// Definition of runtime that could be used by sandboxed code. -#[derive(Clone, PartialEq, Eq, Encode, Decode, RuntimeDebug)] -pub struct EnvironmentDefinition { - /// Vector of all entries in the environment definition. - pub entries: Vec, -} - -/// Constant for specifying no limit when creating a sandboxed -/// memory instance. For FFI purposes. -pub const MEM_UNLIMITED: u32 = -1i32 as u32; - -/// No error happened. -/// -/// For FFI purposes. -pub const ERR_OK: u32 = 0; - -/// Validation or instantiation error occurred when creating new -/// sandboxed module instance. -/// -/// For FFI purposes. -pub const ERR_MODULE: u32 = -1i32 as u32; - -/// Out-of-bounds access attempted with memory or table. -/// -/// For FFI purposes. -pub const ERR_OUT_OF_BOUNDS: u32 = -2i32 as u32; - -/// Execution error occurred (typically trap). -/// -/// For FFI purposes. -pub const ERR_EXECUTION: u32 = -3i32 as u32; - -#[cfg(test)] -mod tests { - use super::*; - use codec::Codec; - use std::fmt; - - fn roundtrip(s: S) { - let encoded = s.encode(); - assert_eq!(S::decode(&mut &encoded[..]).unwrap(), s); - } - - #[test] - fn env_def_roundtrip() { - roundtrip(EnvironmentDefinition { entries: vec![] }); - - roundtrip(EnvironmentDefinition { - entries: vec![Entry { - module_name: b"kernel"[..].into(), - field_name: b"memory"[..].into(), - entity: ExternEntity::Memory(1337), - }], - }); - - roundtrip(EnvironmentDefinition { - entries: vec![Entry { - module_name: b"env"[..].into(), - field_name: b"abort"[..].into(), - entity: ExternEntity::Function(228), - }], - }); - } -} diff --git a/primitives/sandbox/src/host_executor.rs b/primitives/sandbox/src/host_executor.rs deleted file mode 100644 index e62c051262ca8..0000000000000 --- a/primitives/sandbox/src/host_executor.rs +++ /dev/null @@ -1,274 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! A WASM executor utilizing the sandbox runtime interface of the host. - -use codec::{Decode, Encode}; - -use sp_io::sandbox; -use sp_std::{marker, mem, prelude::*, rc::Rc, slice, vec}; - -use crate::{env, Error, HostFuncType, ReturnValue, Value}; - -mod ffi { - use super::HostFuncType; - use sp_std::mem; - - /// Index into the default table that points to a `HostFuncType`. - pub type HostFuncIndex = usize; - - /// Coerce `HostFuncIndex` to a callable host function pointer. - /// - /// # Safety - /// - /// This function should be only called with a `HostFuncIndex` that was previously registered - /// in the environment definition. Typically this should only - /// be called with an argument received in `dispatch_thunk`. - pub unsafe fn coerce_host_index_to_func(idx: HostFuncIndex) -> HostFuncType { - // We need to ensure that sizes of a callable function pointer and host function index is - // indeed equal. - // We can't use `static_assertions` create because it makes compiler panic, fallback to - // runtime assert. const_assert!(mem::size_of::() == - // mem::size_of::>()); - assert!(mem::size_of::() == mem::size_of::>()); - mem::transmute::>(idx) - } -} - -struct MemoryHandle { - memory_idx: u32, -} - -impl Drop for MemoryHandle { - fn drop(&mut self) { - sandbox::memory_teardown(self.memory_idx); - } -} - -/// The linear memory used by the sandbox. -#[derive(Clone)] -pub struct Memory { - // Handle to memory instance is wrapped to add reference-counting semantics - // to `Memory`. - handle: Rc, -} - -impl super::SandboxMemory for Memory { - fn new(initial: u32, maximum: Option) -> Result { - let maximum = if let Some(maximum) = maximum { maximum } else { env::MEM_UNLIMITED }; - - match sandbox::memory_new(initial, maximum) { - env::ERR_MODULE => Err(Error::Module), - memory_idx => Ok(Memory { handle: Rc::new(MemoryHandle { memory_idx }) }), - } - } - - fn get(&self, offset: u32, buf: &mut [u8]) -> Result<(), Error> { - let result = - sandbox::memory_get(self.handle.memory_idx, offset, buf.as_mut_ptr(), buf.len() as u32); - match result { - env::ERR_OK => Ok(()), - env::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds), - _ => unreachable!(), - } - } - - fn set(&self, offset: u32, val: &[u8]) -> Result<(), Error> { - let result = sandbox::memory_set( - self.handle.memory_idx, - offset, - val.as_ptr() as _, - val.len() as u32, - ); - match result { - env::ERR_OK => Ok(()), - env::ERR_OUT_OF_BOUNDS => Err(Error::OutOfBounds), - _ => unreachable!(), - } - } -} - -/// A builder for the environment of the sandboxed WASM module. -pub struct EnvironmentDefinitionBuilder { - env_def: env::EnvironmentDefinition, - retained_memories: Vec, - _marker: marker::PhantomData, -} - -impl EnvironmentDefinitionBuilder { - fn add_entry(&mut self, module: N1, field: N2, extern_entity: env::ExternEntity) - where - N1: Into>, - N2: Into>, - { - let entry = env::Entry { - module_name: module.into(), - field_name: field.into(), - entity: extern_entity, - }; - self.env_def.entries.push(entry); - } -} - -impl super::SandboxEnvironmentBuilder for EnvironmentDefinitionBuilder { - fn new() -> EnvironmentDefinitionBuilder { - EnvironmentDefinitionBuilder { - env_def: env::EnvironmentDefinition { entries: Vec::new() }, - retained_memories: Vec::new(), - _marker: marker::PhantomData::, - } - } - - fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) - where - N1: Into>, - N2: Into>, - { - let f = env::ExternEntity::Function(f as u32); - self.add_entry(module, field, f); - } - - fn add_memory(&mut self, module: N1, field: N2, mem: Memory) - where - N1: Into>, - N2: Into>, - { - // We need to retain memory to keep it alive while the EnvironmentDefinitionBuilder alive. - self.retained_memories.push(mem.clone()); - - let mem = env::ExternEntity::Memory(mem.handle.memory_idx as u32); - self.add_entry(module, field, mem); - } -} - -/// Sandboxed instance of a WASM module. -pub struct Instance { - instance_idx: u32, - _retained_memories: Vec, - _marker: marker::PhantomData, -} - -/// The primary responsibility of this thunk is to deserialize arguments and -/// call the original function, specified by the index. -extern "C" fn dispatch_thunk( - serialized_args_ptr: *const u8, - serialized_args_len: usize, - state: usize, - f: ffi::HostFuncIndex, -) -> u64 { - let serialized_args = unsafe { - if serialized_args_len == 0 { - &[] - } else { - slice::from_raw_parts(serialized_args_ptr, serialized_args_len) - } - }; - let args = Vec::::decode(&mut &serialized_args[..]).expect( - "serialized args should be provided by the runtime; - correctly serialized data should be deserializable; - qed", - ); - - unsafe { - // This should be safe since `coerce_host_index_to_func` is called with an argument - // received in an `dispatch_thunk` implementation, so `f` should point - // on a valid host function. - let f = ffi::coerce_host_index_to_func(f); - - // This should be safe since mutable reference to T is passed upon the invocation. - let state = &mut *(state as *mut T); - - // Pass control flow to the designated function. - let result = f(state, &args).encode(); - - // Leak the result vector and return the pointer to return data. - let result_ptr = result.as_ptr() as u64; - let result_len = result.len() as u64; - mem::forget(result); - - (result_ptr << 32) | result_len - } -} - -impl super::SandboxInstance for Instance { - type Memory = Memory; - type EnvironmentBuilder = EnvironmentDefinitionBuilder; - - fn new( - code: &[u8], - env_def_builder: &EnvironmentDefinitionBuilder, - state: &mut T, - ) -> Result, Error> { - let serialized_env_def: Vec = env_def_builder.env_def.encode(); - // It's very important to instantiate thunk with the right type. - let dispatch_thunk = dispatch_thunk::; - let result = sandbox::instantiate( - dispatch_thunk as u32, - code, - &serialized_env_def, - state as *const T as _, - ); - - let instance_idx = match result { - env::ERR_MODULE => return Err(Error::Module), - env::ERR_EXECUTION => return Err(Error::Execution), - instance_idx => instance_idx, - }; - - // We need to retain memories to keep them alive while the Instance is alive. - let retained_memories = env_def_builder.retained_memories.clone(); - Ok(Instance { - instance_idx, - _retained_memories: retained_memories, - _marker: marker::PhantomData::, - }) - } - - fn invoke(&mut self, name: &str, args: &[Value], state: &mut T) -> Result { - let serialized_args = args.to_vec().encode(); - let mut return_val = vec![0u8; ReturnValue::ENCODED_MAX_SIZE]; - - let result = sandbox::invoke( - self.instance_idx, - name, - &serialized_args, - return_val.as_mut_ptr() as _, - return_val.len() as u32, - state as *const T as _, - ); - - match result { - env::ERR_OK => { - let return_val = - ReturnValue::decode(&mut &return_val[..]).map_err(|_| Error::Execution)?; - Ok(return_val) - }, - env::ERR_EXECUTION => Err(Error::Execution), - _ => unreachable!(), - } - } - - fn get_global_val(&self, name: &str) -> Option { - sandbox::get_global_val(self.instance_idx, name) - } -} - -impl Drop for Instance { - fn drop(&mut self) { - sandbox::instance_teardown(self.instance_idx); - } -} diff --git a/primitives/sandbox/src/lib.rs b/primitives/sandbox/src/lib.rs deleted file mode 100644 index b6b4a5a97da8c..0000000000000 --- a/primitives/sandbox/src/lib.rs +++ /dev/null @@ -1,190 +0,0 @@ -// This file is part of Substrate. - -// Copyright (C) 2018-2022 Parity Technologies (UK) Ltd. -// SPDX-License-Identifier: Apache-2.0 - -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! This crate provides means to instantiate and execute wasm modules. -//! -//! It works even when the user of this library executes from -//! inside the wasm VM. In this case the same VM is used for execution -//! of both the sandbox owner and the sandboxed module, without compromising security -//! and without the performance penalty of full wasm emulation inside wasm. -//! -//! This is achieved by using bindings to the wasm VM, which are published by the host API. -//! This API is thin and consists of only a handful functions. It contains functions for -//! instantiating modules and executing them, but doesn't contain functions for inspecting the -//! module structure. The user of this library is supposed to read the wasm module. -//! -//! When this crate is used in the `std` environment all these functions are implemented by directly -//! calling the wasm VM. -//! -//! Examples of possible use-cases for this library are not limited to the following: -//! -//! - implementing smart-contract runtimes that use wasm for contract code -//! - executing a wasm substrate runtime inside of a wasm parachain - -#![warn(missing_docs)] -#![cfg_attr(not(feature = "std"), no_std)] - -extern crate alloc; - -pub mod embedded_executor; -pub mod env; -#[cfg(not(feature = "std"))] -pub mod host_executor; - -use sp_core::RuntimeDebug; -use sp_std::prelude::*; - -pub use sp_wasm_interface::{ReturnValue, Value}; - -#[cfg(not(all(feature = "wasmer-sandbox", not(feature = "std"))))] -pub use self::embedded_executor as default_executor; -pub use self::env::HostError; -#[cfg(all(feature = "wasmer-sandbox", not(feature = "std")))] -pub use self::host_executor as default_executor; - -/// The target used for logging. -const TARGET: &str = "runtime::sandbox"; - -/// Error that can occur while using this crate. -#[derive(RuntimeDebug)] -pub enum Error { - /// Module is not valid, couldn't be instantiated. - Module, - - /// Access to a memory or table was made with an address or an index which is out of bounds. - /// - /// Note that if wasm module makes an out-of-bounds access then trap will occur. - OutOfBounds, - - /// Failed to invoke the start function or an exported function for some reason. - Execution, -} - -impl From for HostError { - fn from(_e: Error) -> HostError { - HostError - } -} - -/// Function pointer for specifying functions by the -/// supervisor in [`EnvironmentDefinitionBuilder`]. -/// -/// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html -pub type HostFuncType = fn(&mut T, &[Value]) -> Result; - -/// Reference to a sandboxed linear memory, that -/// will be used by the guest module. -/// -/// The memory can't be directly accessed by supervisor, but only -/// through designated functions [`get`](SandboxMemory::get) and [`set`](SandboxMemory::set). -pub trait SandboxMemory: Sized + Clone { - /// Construct a new linear memory instance. - /// - /// The memory allocated with initial number of pages specified by `initial`. - /// Minimal possible value for `initial` is 0 and maximum possible is `65536`. - /// (Since maximum addressable memory is 232 = 4GiB = 65536 * 64KiB). - /// - /// It is possible to limit maximum number of pages this memory instance can have by specifying - /// `maximum`. If not specified, this memory instance would be able to allocate up to 4GiB. - /// - /// Allocated memory is always zeroed. - fn new(initial: u32, maximum: Option) -> Result; - - /// Read a memory area at the address `ptr` with the size of the provided slice `buf`. - /// - /// Returns `Err` if the range is out-of-bounds. - fn get(&self, ptr: u32, buf: &mut [u8]) -> Result<(), Error>; - - /// Write a memory area at the address `ptr` with contents of the provided slice `buf`. - /// - /// Returns `Err` if the range is out-of-bounds. - fn set(&self, ptr: u32, value: &[u8]) -> Result<(), Error>; -} - -/// Struct that can be used for defining an environment for a sandboxed module. -/// -/// The sandboxed module can access only the entities which were defined and passed -/// to the module at the instantiation time. -pub trait SandboxEnvironmentBuilder: Sized { - /// Construct a new `EnvironmentDefinitionBuilder`. - fn new() -> Self; - - /// Register a host function in this environment definition. - /// - /// NOTE that there is no constraints on type of this function. An instance - /// can import function passed here with any signature it wants. It can even import - /// the same function (i.e. with same `module` and `field`) several times. It's up to - /// the user code to check or constrain the types of signatures. - fn add_host_func(&mut self, module: N1, field: N2, f: HostFuncType) - where - N1: Into>, - N2: Into>; - - /// Register a memory in this environment definition. - fn add_memory(&mut self, module: N1, field: N2, mem: Memory) - where - N1: Into>, - N2: Into>; -} - -/// Sandboxed instance of a wasm module. -/// -/// This instance can be used for invoking exported functions. -pub trait SandboxInstance: Sized { - /// The memory type used for this sandbox. - type Memory: SandboxMemory; - - /// The environment builder used to construct this sandbox. - type EnvironmentBuilder: SandboxEnvironmentBuilder; - - /// Instantiate a module with the given [`EnvironmentDefinitionBuilder`]. It will - /// run the `start` function (if it is present in the module) with the given `state`. - /// - /// Returns `Err(Error::Module)` if this module can't be instantiated with the given - /// environment. If execution of `start` function generated a trap, then `Err(Error::Execution)` - /// will be returned. - /// - /// [`EnvironmentDefinitionBuilder`]: struct.EnvironmentDefinitionBuilder.html - fn new( - code: &[u8], - env_def_builder: &Self::EnvironmentBuilder, - state: &mut State, - ) -> Result; - - /// Invoke an exported function with the given name. - /// - /// # Errors - /// - /// Returns `Err(Error::Execution)` if: - /// - /// - An export function name isn't a proper utf8 byte sequence, - /// - This module doesn't have an exported function with the given name, - /// - If types of the arguments passed to the function doesn't match function signature then - /// trap occurs (as if the exported function was called via call_indirect), - /// - Trap occurred at the execution time. - fn invoke( - &mut self, - name: &str, - args: &[Value], - state: &mut State, - ) -> Result; - - /// Get the value from a global with the given `name`. - /// - /// Returns `Some(_)` if the global could be found. - fn get_global_val(&self, name: &str) -> Option; -} diff --git a/primitives/wasm-interface/src/lib.rs b/primitives/wasm-interface/src/lib.rs index 173e3241170fc..1ecff5a0ce91e 100644 --- a/primitives/wasm-interface/src/lib.rs +++ b/primitives/wasm-interface/src/lib.rs @@ -303,9 +303,6 @@ pub trait FunctionContext { fn allocate_memory(&mut self, size: WordSize) -> Result>; /// Deallocate a given memory instance. fn deallocate_memory(&mut self, ptr: Pointer) -> Result<()>; - /// Provides access to the sandbox. - fn sandbox(&mut self) -> &mut dyn Sandbox; - /// Registers a panic error message within the executor. /// /// This is meant to be used in situations where the runtime @@ -330,60 +327,6 @@ pub trait FunctionContext { fn register_panic_error_message(&mut self, message: &str); } -/// Sandbox memory identifier. -pub type MemoryId = u32; - -/// Something that provides access to the sandbox. -pub trait Sandbox { - /// Get sandbox memory from the `memory_id` instance at `offset` into the given buffer. - fn memory_get( - &mut self, - memory_id: MemoryId, - offset: WordSize, - buf_ptr: Pointer, - buf_len: WordSize, - ) -> Result; - /// Set sandbox memory from the given value. - fn memory_set( - &mut self, - memory_id: MemoryId, - offset: WordSize, - val_ptr: Pointer, - val_len: WordSize, - ) -> Result; - /// Delete a memory instance. - fn memory_teardown(&mut self, memory_id: MemoryId) -> Result<()>; - /// Create a new memory instance with the given `initial` size and the `maximum` size. - /// The size is given in wasm pages. - fn memory_new(&mut self, initial: u32, maximum: u32) -> Result; - /// Invoke an exported function by a name. - fn invoke( - &mut self, - instance_id: u32, - export_name: &str, - args: &[u8], - return_val: Pointer, - return_val_len: WordSize, - state: u32, - ) -> Result; - /// Delete a sandbox instance. - fn instance_teardown(&mut self, instance_id: u32) -> Result<()>; - /// Create a new sandbox instance. - fn instance_new( - &mut self, - dispatch_thunk_id: u32, - wasm: &[u8], - raw_env_def: &[u8], - state: u32, - ) -> Result; - - /// Get the value from a global with the given `name`. The sandbox is determined by the - /// given `instance_idx` instance. - /// - /// Returns `Some(_)` when the requested global variable could be found. - fn get_global_val(&self, instance_idx: u32, name: &str) -> Result>; -} - if_wasmtime_is_enabled! { /// A trait used to statically register host callbacks with the WASM executor, /// so that they call be called from within the runtime with minimal overhead. diff --git a/scripts/ci/gitlab/pipeline/test.yml b/scripts/ci/gitlab/pipeline/test.yml index c38eac45d7ba4..90b43100bcdb8 100644 --- a/scripts/ci/gitlab/pipeline/test.yml +++ b/scripts/ci/gitlab/pipeline/test.yml @@ -143,25 +143,11 @@ cargo-check-try-runtime: - time cargo check --locked --features try-runtime - rusty-cachier cache upload -cargo-check-wasmer-sandbox: - stage: test - # this is an artificial job dependency, for pipeline optimization using GitLab's DAGs - needs: - - job: cargo-check-try-runtime - artifacts: false - extends: - - .docker-env - - .test-refs - script: - - rusty-cachier snapshot create - - time cargo check --locked --features wasmer-sandbox - - rusty-cachier cache upload - test-deterministic-wasm: stage: test # this is an artificial job dependency, for pipeline optimization using GitLab's DAGs needs: - - job: cargo-check-wasmer-sandbox + - job: cargo-check-try-runtime artifacts: false extends: - .docker-env @@ -375,27 +361,6 @@ test-full-crypto-feature: - time cargo +nightly build --locked --verbose --no-default-features --features full_crypto - rusty-cachier cache upload -test-wasmer-sandbox: - stage: test - needs: - - job: cargo-check-wasmer-sandbox - artifacts: false - extends: - - .docker-env - - .test-refs-wasmer-sandbox - variables: - RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - RUST_BACKTRACE: 1 - WASM_BUILD_NO_COLOR: 1 - WASM_BUILD_RUSTFLAGS: "-Cdebug-assertions=y -Dwarnings" - CI_JOB_NAME: "test-wasmer-sandbox" - parallel: 3 - script: - - rusty-cachier snapshot create - - echo "Node index - ${CI_NODE_INDEX}. Total amount - ${CI_NODE_TOTAL}" - - time cargo nextest run --locked --release --features runtime-benchmarks,wasmer-sandbox,disable-ui-tests --partition count:${CI_NODE_INDEX}/${CI_NODE_TOTAL} - - if [ ${CI_NODE_INDEX} == 1 ]; then rusty-cachier cache upload; fi - check-rustdoc: stage: test variables: