Commit b3cca34b authored by WuEcho's avatar WuEcho

add code

parents
Pipeline #636 failed with stages
# Changelog
## [0.5.0] - 2019-07-25
- New module: `remapstart`. [[#107](https://github.com/wasmx/wasm-chisel/pull/107)]
- New module: `dropsection`. [[#72](https://github.com/wasmx/wasm-chisel/pull/72)]
- New module: `fromwat`. [[#100](https://github.com/wasmx/wasm-chisel/pull/100)]
- New module: `snip` (for using wasm-snip). [[#123](https://github.com/wasmx/wasm-chisel/pull/123)]
- New module: `dropnames`. [[#126](https://github.com/wasmx/wasm-chisel/pull/126)]
- New module: `checkfloats`. [[#118](https://github.com/wasmx/wasm-chisel/pull/118)]
- Refactored `verifyimpotrs` and `remapimports`. [[#6](https://github.com/wasmx/wasm-chisel/pull/66)]
- Added support for `eth2` / `debug` and `bignum` imports. [[#112](https://github.com/wasmx/wasm-chisel/pull/112)]
- Better handling for the names section.
- Removed Rust and many Clippy warnings.
## [0.4.0] - 2019-06-04
- New module: `repack`.
- Fixed: [[#97](https://github.com/wasmx/wasm-chisel/pull/97)]
Code generated by `deployer` now uses proper import signature.
- Improved internal APIs regarding error handling and testing.
## [0.3.0] - 2019-05-23
- New module: `trimstart`.
- Fixed: [[#84](https://github.com/wasmx/wasm-chisel/pull/84)
[#91](https://github.com/wasmx/wasm-chisel/pull/91)]
Fixed import list for `ewasm` in `remapimports` and `verifyimports`.
- Fixed: [[#85](https://github.com/wasmx/wasm-chisel/pull/85)]
Code generated by `deployer` now properly allocates memory.
## [0.2.0] - 2019-03-26
- New modules: `deployer`, `verifyexports` and `verifyimports`.
- Introduced new CLI frontend supporting a YAML configuration.
- Internal API improvements.
- Updated dependency: `pwasm` to 0.35.
- Moved on to Rust 2018 edition.
## [0.1.0] - 2018-11-15
- Initial release.
- Modules: `checkstartfunc`, `remapimports` and `trimexports`. All of
them with the `ewasm` profile.
[0.5.0]: https://github.com/wasmx/wasm-chisel/compare/v0.4.0...master
[0.4.0]: https://github.com/wasmx/wasm-chisel/releases/tag/v0.4.0
[0.3.0]: https://github.com/wasmx/wasm-chisel/releases/tag/v0.3.0
[0.2.0]: https://github.com/wasmx/wasm-chisel/releases/tag/v0.2.0
[0.1.0]: https://github.com/wasmx/wasm-chisel/releases/tag/v0.1.0
[Cable]: https://github.com/ethereum/cable
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "addr2line"
version = "0.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb"
dependencies = [
"gimli",
]
[[package]]
name = "adler"
version = "1.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
[[package]]
name = "aho-corasick"
version = "1.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ea5d730647d4fadd988536d06fecce94b7b4f2a7efdae548f1cf4b63205518ab"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "backtrace"
version = "0.3.69"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837"
dependencies = [
"addr2line",
"cc",
"cfg-if",
"libc",
"miniz_oxide",
"object",
"rustc-demangle",
]
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "byteorder"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "cc"
version = "1.0.83"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
dependencies = [
"libc",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "chisel"
version = "0.5.0"
dependencies = [
"clap",
"libchisel",
"parity-wasm",
"serde",
"serde_derive",
"serde_yaml",
]
[[package]]
name = "clap"
version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "cmake"
version = "0.1.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a31c789563b815f77f4250caee12365734369f942439b7defd71e18a48197130"
dependencies = [
"cc",
]
[[package]]
name = "crossbeam-deque"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
dependencies = [
"cfg-if",
"crossbeam-epoch",
"crossbeam-utils",
]
[[package]]
name = "crossbeam-epoch"
version = "0.9.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7"
dependencies = [
"autocfg",
"cfg-if",
"crossbeam-utils",
"memoffset",
"scopeguard",
]
[[package]]
name = "crossbeam-utils"
version = "0.8.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294"
dependencies = [
"cfg-if",
]
[[package]]
name = "either"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "failure"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86"
dependencies = [
"backtrace",
"failure_derive",
]
[[package]]
name = "failure_derive"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4"
dependencies = [
"proc-macro2 1.0.68",
"quote 1.0.33",
"syn 1.0.109",
"synstructure",
]
[[package]]
name = "gimli"
version = "0.28.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
[[package]]
name = "glob"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb"
[[package]]
name = "hashbrown"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
[[package]]
name = "heck"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d621efb26863f0e9924c6ac577e8275e5e6b77455db64ffa6c65c904e9e132c"
dependencies = [
"unicode-segmentation",
]
[[package]]
name = "hermit-abi"
version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
[[package]]
name = "id-arena"
version = "2.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a2bc672d1148e28034f176e01fffebb08b35768468cc954630da77a1449005"
dependencies = [
"rayon",
]
[[package]]
name = "indexmap"
version = "1.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99"
dependencies = [
"autocfg",
"hashbrown",
]
[[package]]
name = "itoa"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38"
[[package]]
name = "leb128"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67"
[[package]]
name = "libc"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libchisel"
version = "0.5.0"
dependencies = [
"byteorder",
"failure",
"parity-wasm",
"rustc-hex",
"wabt",
"wasm-snip",
]
[[package]]
name = "linked-hash-map"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0717cef1bc8b636c6e1c1bbdefc09e6322da8a9321966e8928ef80d20f7f770f"
[[package]]
name = "log"
version = "0.4.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f"
[[package]]
name = "memchr"
version = "2.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
[[package]]
name = "memoffset"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c"
dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
dependencies = [
"adler",
]
[[package]]
name = "object"
version = "0.32.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
dependencies = [
"memchr",
]
[[package]]
name = "parity-wasm"
version = "0.35.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e1e076c4e01399b6cd0793a8df42f90bba3ae424671ef421d1608a943155d93"
dependencies = [
"byteorder",
]
[[package]]
name = "proc-macro2"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf3d2011ab5c909338f7887f4fc896d35932e29146c12c8d01da6b22a80ba759"
dependencies = [
"unicode-xid 0.1.0",
]
[[package]]
name = "proc-macro2"
version = "1.0.68"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b1106fec09662ec6dd98ccac0f81cef56984d0b49f75c92d8cbad76e20c005c"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "0.6.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ce23b6b870e8f94f81fb0a363d65d86675884b34a09043c81e5562f11c1f8e1"
dependencies = [
"proc-macro2 0.4.30",
]
[[package]]
name = "quote"
version = "1.0.33"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae"
dependencies = [
"proc-macro2 1.0.68",
]
[[package]]
name = "rayon"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1"
dependencies = [
"either",
"rayon-core",
]
[[package]]
name = "rayon-core"
version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed"
dependencies = [
"crossbeam-deque",
"crossbeam-utils",
]
[[package]]
name = "regex"
version = "1.9.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebee201405406dbf528b8b672104ae6d6d63e6d118cb10e4d51abbc7b58044ff"
dependencies = [
"aho-corasick",
"memchr",
"regex-automata",
"regex-syntax",
]
[[package]]
name = "regex-automata"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59b23e92ee4318893fa3fe3e6fb365258efbfe6ac6ab30f090cdcbb7aa37efa9"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da"
[[package]]
name = "rustc-demangle"
version = "0.1.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
[[package]]
name = "rustc-hex"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ceb8ce7a5e520de349e1fa172baeba4a9e8d5ef06c47471863530bc4972ee1e"
[[package]]
name = "ryu"
version = "1.0.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741"
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "serde"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf9e0fcba69a370eed61bcf2b728575f726b50b55cba78064753d708ddc7549e"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.188"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2"
dependencies = [
"proc-macro2 1.0.68",
"quote 1.0.33",
"syn 2.0.38",
]
[[package]]
name = "serde_json"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6b420ce6e3d8bd882e9b243c6eed35dbc9a6110c9769e74b584e0d68d1f20c65"
dependencies = [
"itoa",
"ryu",
"serde",
]
[[package]]
name = "serde_yaml"
version = "0.8.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "578a7433b776b56a35785ed5ce9a7e777ac0598aac5a6dd1b4b18a307c7fc71b"
dependencies = [
"indexmap",
"ryu",
"serde",
"yaml-rust",
]
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "syn"
version = "0.15.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ca4b3b69a77cbe1ffc9e198781b7acb0c7365a883670e8f1c1bc66fba79a5c5"
dependencies = [
"proc-macro2 0.4.30",
"quote 0.6.13",
"unicode-xid 0.1.0",
]
[[package]]
name = "syn"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2 1.0.68",
"quote 1.0.33",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.38"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e96b79aaa137db8f61e26363a0c9b47d8b4ec75da28b7d1d614c2303e232408b"
dependencies = [
"proc-macro2 1.0.68",
"quote 1.0.33",
"unicode-ident",
]
[[package]]
name = "synstructure"
version = "0.12.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f"
dependencies = [
"proc-macro2 1.0.68",
"quote 1.0.33",
"syn 1.0.109",
"unicode-xid 0.2.4",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "unicode-ident"
version = "1.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
[[package]]
name = "unicode-segmentation"
version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36"
[[package]]
name = "unicode-width"
version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
[[package]]
name = "unicode-xid"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc"
[[package]]
name = "unicode-xid"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "wabt"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef65670cf56c774835522fcb43fe9ddb2ff42bc3fa61d1112cf19209dbb2243b"
dependencies = [
"serde",
"serde_derive",
"serde_json",
"wabt-sys",
]
[[package]]
name = "wabt-sys"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b064c81821100adb4b71923cecfc67fef083db21c3bbd454b0162c7ffe63eeaa"
dependencies = [
"cc",
"cmake",
"glob",
]
[[package]]
name = "walrus"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b751c638c5c86d92af28a3a68ce879b719c7e1cad75c66a3377ce386b9d705f"
dependencies = [
"failure",
"id-arena",
"leb128",
"log",
"rayon",
"walrus-macro",
"wasmparser",
]
[[package]]
name = "walrus-macro"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30dcc194dbffb8025ca1b42a92f8c33ac28b1025cd771f0d884f89508b5fb094"
dependencies = [
"heck",
"proc-macro2 0.4.30",
"quote 0.6.13",
"syn 0.15.44",
]
[[package]]
name = "wasm-snip"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57f589a17c710fe57174b989325dd863c4521070b765fe2e82d2ace6a50f1515"
dependencies = [
"clap",
"failure",
"rayon",
"regex",
"walrus",
]
[[package]]
name = "wasmparser"
version = "0.30.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "566a9eefa2267a1a32af59807326e84191cdff41c3fc2efda0a790d821615b31"
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "yaml-rust"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56c1936c4cc7a1c9ab21a1ebb602eb942ba868cbd44a99cb7cdc5892335e1c85"
dependencies = [
"linked-hash-map",
]
[workspace]
members = [
"chisel",
"libchisel"
]
\ No newline at end of file
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
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.
# wasm-chisel
![Build](https://circleci.com/gh/wasmx/wasm-chisel.svg?style=shield&circle-token=:circle-token)
![Version](https://img.shields.io/crates/v/chisel.svg)
Some useful utilities to transform WebAssembly binaries, most importantly for WebAssembly used in a deterministic / blockchain context,
such as with [ewasm].
## Library
### remapimports
Provide a list of imports (with namespace and name) and replace them with a new set of namespace and name pairs.
This can be very useful together with compilers, which do not support the specification of a namespace in imports yet. As of writing mid-2018,
that includes pretty much every compiler (one exception is AssemblyScript).
It supports the same presets as `verifyimports`.
### trimexports
Removes all exports, but the ones specified.
This comes with some presets:
- `ewasm`: keeps `main` and exported memory
- `pwasm`: keeps `_call`
### trimstartfunc
Remove start function.
This comes with the following preset:
- `ewasm`: removes `start` function if present
### verifyimports
Verifies that the module's imports are compliant with the provided import interface.
Can be set to require the existence of the entire import set, or just the validity of existing imports with matching identifiers.
Can be set to allow or prohibit unlisted additional imports.
The following presets are provided:
- `ewasm`: Verifies the ewasm [EEI](https://github.com/ewasm/design/blob/master/eth_interface.md). Disallows unlisted imports, and does not require that the entire interface be imported.
- `debug`: Debug utilities for ewasm.
- `bignum`: Big-number library for ewasm.
- `eth2`: Verifies imports according to [Scout](https://github.com/ewasm/scout).
### verifyexports
Verifies that the module's exports are compliant with the provided export interface.
Can be set to allow or prohibit unlisted additional exports.
The following presets are provided:
- `ewasm`: Verifies that the `main` function and `memory` is exported. Disallows any unlisted exports.
### dropsection
Removes selected sections from the module.
### deployer
Wraps module into an ewasm-compatible constructor. It has two presets:
- `memory`: wrap the module as a pre-defined memory section
- `customsection`: include the module as a custom section
### repack
Re-serializes the module. It will drop any unknown (custom) sections.
### remapstart
If there is a start section, export it as `main` (replacing any pre-existing `main` export) and remove the start section
### snip
Wraps [wasm-snip](https://github.com/rustwasm/wasm-snip/) and turns on removing Rust formatting and debugging from wasm.
### dropnames
Drops the NamesSection if present.
## CLI
`chisel` is available as a command line tool.
It uses features implemented in the library as well in [wasm-gc] and [wasm-utils]. It comes with a configuration file `chisel.yml`.
`chisel run`: searches for `chisel.yml` in the current directory, if not specified otherwise using the flag `-c`. Runs the modules specified in the configuration, outputs a new file if any changes were made by translator or creator modules, and prints a brief report of each module's results.
## Configuration file
The configuration file starts with a ruleset entry, where the name can be anything. Inside the ruleset are its options.
The only required field is `file`, which specifies the path to the Wasm binary to be chiseled.
Optionally, one may also specified an output file through the `output` option.
It is important to note that the configuration parsing will not work if all the rules are prepended with a hyphen. Please avoid this until the configuration parser is generalized.
```yaml
ewasm:
file: "target/wasm32-unknown-unknown/release/sentinel.wasm"
output: "out.wasm"
remapimports:
preset: "ewasm"
```
## sentinel.rs
TBA
## Changelog
A [changelog is available](CHANGELOG.md).
## Maintainers
* Alex Beregszaszi [@axic]
* Jake Lang [@jakelang]
## License
[Apache 2.0](LICENSE).
[@axic]: https://github.com/axic
[@jakelang]: https://github.com/jakelang
[ewasm]: http://github.com/ewasm
[wasm-gc]: https://github.com/alexcrichton/wasm-gc
[wasm-utils]: https://github.com/paritytech/wasm-utils
[package]
name = "chisel"
description = "Some useful utilities to transform WebAssembly binaries, most importantly for WebAssembly used in a deterministic / blockchain context"
version = "0.5.0"
authors = ["Alex Beregszaszi <alex@rtfs.hu>", "Jake Lang <jak3lang@gmail.com>"]
license = "Apache-2.0"
repository = "https://github.com/wasmx/wasm-chisel"
homepage = "https://github.com/wasmx/wasm-chisel"
categories = ["webassembly", "development tools"]
keywords = ["webassembly", "wasm", "blockchain", "ethereum"]
edition = "2018"
[dependencies]
libchisel = { path = "../libchisel", version = "0.5.0" }
parity-wasm = "^0.35.6"
clap = "2.32.0"
serde = "1.0.80"
serde_derive = "1.0.80"
serde_yaml = "0.8.7"
extern crate libchisel;
extern crate parity_wasm;
#[macro_use]
extern crate clap;
extern crate serde;
extern crate serde_derive;
extern crate serde_yaml;
use std::fs::{read, read_to_string};
use std::process;
use libchisel::{
checkstartfunc::*, deployer::*, dropsection::*, remapimports::*, remapstart::*, repack::*,
snip::*, trimexports::*, trimstartfunc::*, verifyexports::*, verifyimports::*,
};
use clap::{App, Arg, ArgMatches, SubCommand};
use libchisel::*;
use parity_wasm::elements::{deserialize_buffer, serialize_to_file, Module, Serialize};
use serde_yaml::Value;
// Error messages
static ERR_NO_SUBCOMMAND: &'static str = "No subcommand provided.";
static ERR_FAILED_OPEN_CONFIG: &'static str = "Failed to open configuration file.";
static ERR_FAILED_OPEN_BINARY: &'static str = "Failed to open wasm binary.";
static ERR_FAILED_PARSE_CONFIG: &'static str = "Failed to parse configuration file.";
static ERR_CONFIG_INVALID: &'static str = "Config is invalid.";
static ERR_CONFIG_MISSING_FILE: &'static str = "Config missing file path to chisel.";
static ERR_INPUT_FILE_TYPE_MISMATCH: &'static str = "Entry 'file' does not map to a string.";
static ERR_MODULE_TYPE_MISMATCH: &'static str =
"A module configuration does not point to a key-value map. Perhaps an option field is missing?";
static ERR_PRESET_TYPE_MISMATCH: &'static str =
"A field 'preset' belonging to a module is not a string";
static ERR_DESERIALIZE_MODULE: &'static str = "Failed to deserialize the wasm binary.";
static ERR_MISSING_PRESET: &'static str = "Module configuration missing preset.";
// Other constants
static DEFAULT_CONFIG_PATH: &'static str = "chisel.yml";
/// Chisel configuration structure. Contains a file to chisel and a list of modules configurations.
struct ChiselContext {
ruleset_name: String,
file: String,
// Output file. If a ModuleTranslator or ModuleCreator is invoked, resorts to a default.
outfile: Option<String>,
modules: Vec<ModuleContext>,
}
struct ModuleContext {
module_name: String,
preset: String,
}
/// Helper to get a field from a config mapping. Assumes that the Value is a Mapping.
fn get_field(yaml: &Value, key: &str) -> Result<String, &'static str> {
if let Some(path) = yaml
.as_mapping()
.unwrap()
.get(&Value::String(String::from(key)))
{
if path.is_string() {
Ok(String::from(path.as_str().unwrap()))
} else {
Err(ERR_INPUT_FILE_TYPE_MISMATCH)
}
} else {
Err(ERR_CONFIG_MISSING_FILE)
}
}
impl ChiselContext {
fn from_ruleset(ruleset: &Value) -> Result<Vec<Self>, &'static str> {
if let Value::Mapping(rules) = ruleset {
let mut ret: Vec<ChiselContext> = vec![];
for (name, config) in rules.iter().filter(|(left, right)| match (left, right) {
(Value::String(_s), Value::Mapping(_m)) => true,
_ => false,
}) {
let filepath = get_field(config, "file")?;
let outfilepath = if let Ok(out) = get_field(config, "output") {
Some(out)
} else {
None
};
// Parse all valid module entries. Unwrap is ok here because we
// established earlier that config is a Mapping.
let mut config_clone = config.as_mapping().unwrap().clone();
// Remove "file" and "output" so we don't interpret it as a module.
// TODO: use mappings to avoid the need for this
config_clone.remove(&Value::String(String::from("file")));
config_clone.remove(&Value::String(String::from("output")));
let mut module_confs: Vec<ModuleContext> = vec![];
let mut config_itr = config_clone.iter();
// Read modules while there are still modules left.
while let Some(module) = config_itr.next() {
module_confs.push(ModuleContext::from_yaml(module)?);
}
ret.push(ChiselContext {
ruleset_name: name.as_str().unwrap().into(),
file: filepath,
outfile: outfilepath,
modules: module_confs,
});
}
Ok(ret)
} else {
Err(ERR_CONFIG_INVALID)
}
}
fn name(&self) -> &String {
&self.ruleset_name
}
fn file(&self) -> &String {
&self.file
}
fn outfile(&self) -> &Option<String> {
&self.outfile
}
fn get_modules(&self) -> &Vec<ModuleContext> {
&self.modules
}
}
impl ModuleContext {
fn from_yaml(yaml: (&Value, &Value)) -> Result<Self, &'static str> {
match yaml {
(Value::String(name), Value::Mapping(flags)) => Ok(ModuleContext {
module_name: name.clone(),
preset: if let Some(pset) = flags.get(&Value::String(String::from("preset"))) {
// Check that the value to which "preset" resolves is a String. If not, return an error.
if pset.is_string() {
String::from(pset.as_str().unwrap())
} else {
return Err(ERR_PRESET_TYPE_MISMATCH);
}
} else {
return Err(ERR_MISSING_PRESET);
},
}),
_ => Err(ERR_MODULE_TYPE_MISMATCH),
}
}
fn fields(&self) -> (&String, &String) {
(&self.module_name, &self.preset)
}
}
fn err_exit(msg: &str) -> ! {
println!("{}: {}", crate_name!(), msg);
process::exit(-1);
}
fn yaml_configure(yaml: &str) -> Result<Vec<ChiselContext>, &'static str> {
if let Ok(rulesets) = serde_yaml::from_str::<Value>(yaml) {
ChiselContext::from_ruleset(&rulesets)
} else {
Err(ERR_FAILED_PARSE_CONFIG)
}
}
/// Helper that tries both translation methods in the case that a module cannot implement one of them.
fn translate_module<T>(module: &mut Module, translator: &T) -> Result<bool, &'static str>
where
T: ModuleTranslator,
{
// NOTE: The module must return an Err (in the case of failure) without mutating the module or nasty stuff happens.
if let Ok(ret) = translator.translate_inplace(module) {
Ok(ret)
} else if let Ok(new_module) = translator.translate(module) {
if new_module.is_some() {
*module = new_module.unwrap();
Ok(true)
} else {
Ok(false)
}
} else {
Err("Module translation failed")
}
}
fn execute_module(context: &ModuleContext, module: &mut Module) -> bool {
let (conf_name, conf_preset) = context.fields();
let preset = conf_preset.clone();
let mut is_translator = false; // Flag representing if the module is a translator
let name = conf_name.as_str();
let ret = match name {
"verifyexports" => {
if let Ok(chisel) = VerifyExports::with_preset(&preset) {
Ok(chisel.validate(module).unwrap_or(false))
} else {
Err("verifyexports: Invalid preset")
}
}
"verifyimports" => {
if let Ok(chisel) = VerifyImports::with_preset(&preset) {
Ok(chisel.validate(module).unwrap_or(false))
} else {
Err("verifyimports: Invalid preset")
}
}
"checkstartfunc" => {
// NOTE: checkstartfunc takes a bool for configuration. false by default for now.
let chisel = CheckStartFunc::new(false);
let ret = chisel.validate(module).unwrap_or(false);
Ok(ret)
}
"trimexports" => {
is_translator = true;
if let Ok(chisel) = TrimExports::with_preset(&preset) {
translate_module(module, &chisel)
} else {
Err("trimexports: Invalid preset")
}
}
"trimstartfunc" => {
is_translator = true;
if let Ok(chisel) = TrimStartFunc::with_preset(&preset) {
translate_module(module, &chisel)
} else {
Err("trimstartfunc: Invalid preset")
}
}
"remapimports" => {
is_translator = true;
if let Ok(chisel) = RemapImports::with_preset(&preset) {
translate_module(module, &chisel)
} else {
Err("remapimports: Invalid preset")
}
}
"remapstart" => {
is_translator = true;
if let Ok(chisel) = RemapStart::with_preset(&preset) {
translate_module(module, &chisel)
} else {
Err("remapimports: Invalid preset")
}
}
"deployer" => {
is_translator = true;
let mut payload = Vec::new();
module.clone().serialize(&mut payload).unwrap(); // This should not fail, but perhaps check anyway?
if let Ok(chisel) = Deployer::with_preset(&preset, &payload) {
let new_module = chisel.create().unwrap();
*module = new_module;
Ok(true)
} else {
Err("deployer: Invalid preset")
}
}
"repack" => {
is_translator = true;
translate_module(module, &Repack::new())
}
"snip" => {
is_translator = true;
translate_module(module, &Snip::new())
}
"dropnames" => {
is_translator = true;
translate_module(module, &DropSection::NamesSection)
}
_ => Err("Module Not Found"),
};
let module_status_msg = if let Ok(result) = ret {
match (result, is_translator) {
(true, true) => "Translated",
(true, false) => "OK",
(false, true) => "Already OK; not translated",
(false, false) => "Malformed",
}
} else {
ret.unwrap_err()
};
println!("\t{}: {}", name, module_status_msg);
if let Ok(result) = ret {
if !result && is_translator {
true
} else {
result
}
} else {
false
}
}
fn chisel_execute(context: &ChiselContext) -> Result<bool, &'static str> {
if let Ok(buffer) = read(context.file()) {
if let Ok(module) = deserialize_buffer::<Module>(&buffer) {
// If we do not parse the NamesSection here, parity-wasm will drop it at serialisation
// It is useful to have this for a number of optimisation passes, including binaryenopt and snip
// TODO: better error handling
let mut module = module.parse_names().expect("Failed to parse NamesSection");
let original = module.clone();
println!("Ruleset {}:", context.name());
let chisel_results = context
.get_modules()
.iter()
.map(|ctx| execute_module(ctx, &mut module))
.fold(true, |b, e| e & b);
// If the module was mutated, serialize to file.
if original != module {
if let Some(path) = context.outfile() {
println!("Writing to file: {}", path);
serialize_to_file(path, module).unwrap();
} else {
println!("No output file specified; writing in place");
serialize_to_file(context.file(), module).unwrap();
}
}
Ok(chisel_results)
} else {
Err(ERR_DESERIALIZE_MODULE)
}
} else {
Err(ERR_FAILED_OPEN_BINARY)
}
}
fn chisel_subcommand_run(args: &ArgMatches) -> i32 {
let config_path = args.value_of("CONFIG").unwrap_or(DEFAULT_CONFIG_PATH);
if let Ok(conf) = read_to_string(config_path) {
match yaml_configure(&conf) {
Ok(ctxs) => {
let result_final = ctxs.iter().fold(0, |acc, ctx| match chisel_execute(&ctx) {
Ok(result) => acc + if result { 0 } else { 1 }, // Add the number of module failures to exit code.
Err(msg) => err_exit(msg),
});
return result_final;
}
Err(msg) => err_exit(msg),
};
} else {
err_exit(ERR_FAILED_OPEN_CONFIG);
}
}
pub fn main() {
let cli_matches = App::new("chisel")
.version(crate_version!())
.about(crate_description!())
.subcommand(
SubCommand::with_name("run")
.about("Runs chisel with the closest configuration file.")
.arg(
Arg::with_name("CONFIG")
.short("c")
.long("config")
.help("Sets a custom configuration file")
.value_name("CONF_FILE")
.takes_value(true),
),
)
.get_matches();
match cli_matches.subcommand() {
("run", Some(subcmd_matches)) => process::exit(chisel_subcommand_run(subcmd_matches)),
_ => err_exit(ERR_NO_SUBCOMMAND),
};
}
[package]
name = "libchisel"
description = "Some useful utilities to transform WebAssembly binaries, most importantly for WebAssembly used in a deterministic / blockchain context"
version = "0.5.0"
authors = ["Alex Beregszaszi <alex@rtfs.hu>", "Jake Lang <jak3lang@gmail.com>"]
license = "Apache-2.0"
repository = "https://github.com/wasmx/wasm-chisel"
homepage = "https://github.com/wasmx/wasm-chisel"
categories = ["webassembly", "development tools"]
keywords = ["webassembly", "wasm", "blockchain", "ethereum"]
edition = "2018"
[dependencies]
parity-wasm = "^0.35.6"
rustc-hex = "1.0"
byteorder = "1.2.4"
failure = "0.1.5"
wabt = { version = "0.8.0", optional = true }
wasm-snip = "0.2.0"
[features]
default = []
use parity_wasm::elements::{Instruction, Module};
use super::{ModuleError, ModuleValidator};
/// Struct on which ModuleValidator is implemented.
pub struct CheckFloat {}
impl CheckFloat {
pub fn new() -> Self {
CheckFloat {}
}
}
impl ModuleValidator for CheckFloat {
fn validate(&self, module: &Module) -> Result<bool, ModuleError> {
let code_section = module.code_section();
if code_section.is_none() {
return Err(ModuleError::NotFound);
}
for function in code_section.unwrap().bodies() {
for instruction in function.code().elements() {
match instruction {
Instruction::F32Eq
| Instruction::F32Ne
| Instruction::F32Lt
| Instruction::F32Gt
| Instruction::F32Le
| Instruction::F32Ge
| Instruction::F32Abs
| Instruction::F32Neg
| Instruction::F32Ceil
| Instruction::F32Floor
| Instruction::F32Trunc
| Instruction::F32Nearest
| Instruction::F32Sqrt
| Instruction::F32Add
| Instruction::F32Sub
| Instruction::F32Mul
| Instruction::F32Div
| Instruction::F32Min
| Instruction::F32Max
| Instruction::F32Copysign
| Instruction::I32TruncSF32
| Instruction::I32TruncUF32
| Instruction::I64TruncSF32
| Instruction::I64TruncUF32
| Instruction::F32ConvertSI32
| Instruction::F32ConvertUI32
| Instruction::F32ConvertSI64
| Instruction::F32ConvertUI64
| Instruction::F32DemoteF64
| Instruction::F64PromoteF32
| Instruction::I32ReinterpretF32
| Instruction::F32ReinterpretI32
| Instruction::F32x4Splat
| Instruction::F32x4Eq
| Instruction::F32x4Ne
| Instruction::F32x4Lt
| Instruction::F32x4Le
| Instruction::F32x4Gt
| Instruction::F32x4Ge
| Instruction::F32x4Neg
| Instruction::F32x4Abs
| Instruction::F32x4Min
| Instruction::F32x4Max
| Instruction::F32x4Add
| Instruction::F32x4Sub
| Instruction::F32x4Div
| Instruction::F32x4Mul
| Instruction::F32x4Sqrt
| Instruction::F32x4ConvertSI32x4
| Instruction::F32x4ConvertUI32x4
| Instruction::I32x4TruncSF32x4Sat
| Instruction::I32x4TruncUF32x4Sat
| Instruction::F64Eq
| Instruction::F64Ne
| Instruction::F64Lt
| Instruction::F64Gt
| Instruction::F64Le
| Instruction::F64Ge
| Instruction::F64Abs
| Instruction::F64Neg
| Instruction::F64Ceil
| Instruction::F64Floor
| Instruction::F64Trunc
| Instruction::F64Nearest
| Instruction::F64Sqrt
| Instruction::F64Add
| Instruction::F64Sub
| Instruction::F64Mul
| Instruction::F64Div
| Instruction::F64Min
| Instruction::F64Max
| Instruction::F64Copysign
| Instruction::I32TruncSF64
| Instruction::I32TruncUF64
| Instruction::I64TruncSF64
| Instruction::I64TruncUF64
| Instruction::F64ConvertSI32
| Instruction::F64ConvertUI32
| Instruction::F64ConvertSI64
| Instruction::F64ConvertUI64
| Instruction::I64ReinterpretF64
| Instruction::F64ReinterpretI64
| Instruction::F64x2Splat
| Instruction::F64x2Eq
| Instruction::F64x2Ne
| Instruction::F64x2Lt
| Instruction::F64x2Le
| Instruction::F64x2Gt
| Instruction::F64x2Ge
| Instruction::F64x2Neg
| Instruction::F64x2Abs
| Instruction::F64x2Min
| Instruction::F64x2Max
| Instruction::F64x2Add
| Instruction::F64x2Sub
| Instruction::F64x2Div
| Instruction::F64x2Mul
| Instruction::F64x2Sqrt
| Instruction::F64x2ConvertSI64x2
| Instruction::F64x2ConvertUI64x2
| Instruction::I64x2TruncSF64x2Sat
| Instruction::I64x2TruncUF64x2Sat
| Instruction::F32x4ExtractLane(_)
| Instruction::F32x4ReplaceLane(_)
| Instruction::F64x2ExtractLane(_)
| Instruction::F64x2ReplaceLane(_)
| Instruction::F32Const(_)
| Instruction::F32Load(_, _)
| Instruction::F32Store(_, _)
| Instruction::F64Const(_)
| Instruction::F64Load(_, _)
| Instruction::F64Store(_, _) => {
return Ok(false);
}
_ => {}
}
}
}
Ok(true)
}
}
#[cfg(test)]
mod tests {
use parity_wasm::builder;
use parity_wasm::elements::deserialize_buffer;
use super::*;
#[test]
fn add_i32_no_fp() {
// (module
// (func $add (param $lhs i32) (param $rhs i32) (result i32)
// get_local $lhs
// get_local $rhs
// i32.add)
// (export "add" (func $add))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7f,
0x7f, 0x01, 0x7f, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64,
0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x6a, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckFloat::new();
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn add_f32_fp() {
// (module
// (func $add (param $lhs f32) (param $rhs f32) (result f32)
// get_local $lhs
// get_local $rhs
// f32.add)
// (export "add" (func $add))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7d,
0x7d, 0x01, 0x7d, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64,
0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0x92, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckFloat::new();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn add_f64_fp() {
// (module
// (func $add (param $lhs f64) (param $rhs f64) (result f64)
// get_local $lhs
// get_local $rhs
// f64.add)
// (export "add" (func $add))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x01, 0x60, 0x02, 0x7c,
0x7c, 0x01, 0x7c, 0x03, 0x02, 0x01, 0x00, 0x07, 0x07, 0x01, 0x03, 0x61, 0x64, 0x64,
0x00, 0x00, 0x0a, 0x09, 0x01, 0x07, 0x00, 0x20, 0x00, 0x20, 0x01, 0xa0, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckFloat::new();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn no_code_section() {
let module = builder::module().build();
let checker = CheckFloat::new();
let result = checker.validate(&module);
assert_eq!(true, result.is_err());
assert_eq!(result.err().unwrap(), ModuleError::NotFound)
}
}
use super::{ModuleError, ModuleValidator};
use parity_wasm::elements::Module;
/// Struct on which ModuleValidator is implemented.
pub struct CheckStartFunc {
start_required: bool,
}
impl CheckStartFunc {
pub fn new(is_start_required: bool) -> Self {
CheckStartFunc {
start_required: is_start_required,
}
}
}
impl ModuleValidator for CheckStartFunc {
fn validate(&self, module: &Module) -> Result<bool, ModuleError> {
Ok(module.start_section().is_some() == self.start_required)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::elements::deserialize_buffer;
#[test]
fn start_required_good() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x08, 0x01, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckStartFunc::new(true);
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn start_required_bad() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x08, 0x01, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckStartFunc::new(false);
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn start_not_required_good() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckStartFunc::new(false);
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn start_not_required_bad() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = CheckStartFunc::new(true);
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
}
use std::collections::HashSet;
use parity_wasm::elements::{FuncBody, Instruction, Module};
/// A function dependency graph is represented as a list of "edges", or pairs of function indices
/// (a, b) where a calls b.
/// An edge, where the function at the left index calls the function at the right
/// index.
#[derive(PartialEq, Eq, Hash)]
pub struct Edge(u32, u32);
/// Container struct for the function dependency graph.
pub struct DepGraph {
edges: HashSet<Edge>,
}
/// Private interface for managing the function dependency graph
pub trait DepGraphManager {
/// Recursive graph builder. Requires import section length in order to resolve the correct
/// function body.
fn probe(&mut self, idx: u32, imports_len: u32, bodies: &[FuncBody]);
fn add_edge(&mut self, dep: Edge) -> bool;
}
/// Public interface for building function dependency graphs.
pub trait DepGraphBuilder: DepGraphManager {
/// Builds the dependency graph.
fn build(module: &Module, entry_idx: u32) -> Result<Self, ()>
where
Self: std::marker::Sized;
}
impl DepGraph {
pub fn new() -> Self {
DepGraph {
edges: HashSet::new(),
}
}
#[cfg(test)]
pub fn edgecount(&self) -> usize {
self.edges.len()
}
// TODO: better access methods
}
impl DepGraphManager for DepGraph {
/// Recursively searches function bodies for calls to other functions and adds edges
/// accordingly.
fn probe(&mut self, idx: u32, imports_len: u32, bodies: &[FuncBody]) {
// If the function is an import, then just backtrack.
if idx < imports_len {
return;
}
// Overflow case handled by the previous early return condition.
let code_idx: usize = (idx - imports_len) as usize;
assert!((code_idx) < bodies.len());
let func_body = &bodies[code_idx];
for instr in func_body.code().elements().iter() {
if let Instruction::Call(call_idx) = instr {
if self.add_edge(Edge::from((idx, *call_idx))) {
self.probe(*call_idx, imports_len, bodies);
} else {
// If the edge already exists then begin backtracking.
return;
}
}
// TODO: Support for call_indirect
}
}
/// Simply inserts an edge into the graph. Returns false if it was duplicate.
fn add_edge(&mut self, dep: Edge) -> bool {
self.edges.insert(dep)
}
}
impl DepGraphBuilder for DepGraph {
fn build(module: &Module, entry_idx: u32) -> Result<Self, ()> {
if let Some(code_section) = module.code_section() {
let mut ret = DepGraph::new();
let imports_len: u32 = if let Some(section) = module.import_section() {
section.entries().len() as u32
} else {
0
};
ret.probe(entry_idx, imports_len, &code_section.bodies());
Ok(ret)
} else {
Err(())
}
}
}
impl From<(u32, u32)> for Edge {
fn from(tuple: (u32, u32)) -> Self {
Edge(tuple.0, tuple.1)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::elements::deserialize_buffer;
#[test]
fn one_dep_main() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main
// (call $otherfunc)
// )
// (func $otherfunc)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x03, 0x02, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04,
0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02,
0x00, 0x0a, 0x09, 0x02, 0x04, 0x00, 0x10, 0x01, 0x0b, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let g = DepGraph::build(&module, 0).unwrap();
assert!(g.edgecount() == 1);
}
#[test]
fn dep_chain2_main() {
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main
// (call $otherfunc)
// )
// (func $otherfunc
// (call $otherfunc1)
// )
// (func $otherfunc1)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x04, 0x03, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02,
0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79,
0x02, 0x00, 0x0a, 0x0e, 0x03, 0x04, 0x00, 0x10, 0x01, 0x0b, 0x04, 0x00, 0x10, 0x02,
0x0b, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let g = DepGraph::build(&module, 0).unwrap();
assert!(g.edgecount() == 2);
}
#[test]
fn mutual_recursion() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main
// (call $otherfunc)
// )
// (func $otherfunc
// (call $main)
// )
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x03, 0x02, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04,
0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02,
0x00, 0x0a, 0x0b, 0x02, 0x04, 0x00, 0x10, 0x01, 0x0b, 0x04, 0x00, 0x10, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let g = DepGraph::build(&module, 0).unwrap();
assert!(g.edgecount() == 2);
}
#[test]
fn main_calls_self_recursion() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main
// (call $main)
// )
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04, 0x6d,
0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00,
0x0a, 0x06, 0x01, 0x04, 0x00, 0x10, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let g = DepGraph::build(&module, 0).unwrap();
assert!(g.edgecount() == 1);
}
#[test]
fn arbitrary_graph() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main
// (call $main_child1)
// (call $main_child2)
// )
//
// (func $main_child1
// (call $child1_child1)
// (call $child1_child2)
// (call $child1_child3)
// )
//
// (func $main_child2)
//
// (func $child1_child1
// (call $main_child1)
// )
//
// (func $child1_child2
// (call $child1_child2)
// )
//
// (func $child1_child3)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x07, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01,
0x07, 0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d,
0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x21, 0x06, 0x06, 0x00, 0x10, 0x01, 0x10, 0x02,
0x0b, 0x08, 0x00, 0x10, 0x03, 0x10, 0x04, 0x10, 0x05, 0x0b, 0x02, 0x00, 0x0b, 0x04,
0x00, 0x10, 0x01, 0x0b, 0x04, 0x00, 0x10, 0x04, 0x0b, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let g = DepGraph::build(&module, 0).unwrap();
assert!(g.edgecount() == 7);
}
#[test]
fn arbitary_graph_2() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main
// (call $main_child1)
// (call $main_child2)
// )
//
// (func $main_child1
// (call $child1_child1)
// (call $child1_child2)
// (call $child1_child3)
// )
//
// (func $main_child2
// (call $child1_child2)
// (call $child1_child3)
// )
//
// (func $child1_child1
// (call $main_child1)
// )
//
// (func $child1_child2
// (call $child1_child2)
// )
//
// (func $child1_child3
// (call $main_child2)
// )
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x07, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01,
0x07, 0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d,
0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x27, 0x06, 0x06, 0x00, 0x10, 0x01, 0x10, 0x02,
0x0b, 0x08, 0x00, 0x10, 0x03, 0x10, 0x04, 0x10, 0x05, 0x0b, 0x06, 0x00, 0x10, 0x04,
0x10, 0x05, 0x0b, 0x04, 0x00, 0x10, 0x01, 0x0b, 0x04, 0x00, 0x10, 0x04, 0x0b, 0x04,
0x00, 0x10, 0x02, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let g = DepGraph::build(&module, 0).unwrap();
assert!(g.edgecount() == 10);
}
#[test]
fn arbitrary_graph_with_imports() {
// wast:
// (module
// (import "ethereum" "useGas" (func $useGas (param i64)))
// (import "ethereum" "getBlockGasLimit" (func $getBlockGasLimit (result i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
//
// (func $main
// (call $main_child1)
// (call $main_child2)
// )
//
// (func $main_child1
// (call $child1_child1)
// (call $child1_child2)
// (call $child1_child3)
// )
//
// (func $main_child2
// (call $child1_child2)
// (i64.store (i32.const 0) (call $getBlockGasLimit))
// (call $child1_child3)
// )
//
// (func $child1_child1
// (call $useGas (i64.const 420))
// (i64.store (i32.const 0) (call $getBlockGasLimit))
// (call $main_child1)
// )
//
// (func $child1_child2
// (call $useGas (i64.const 1337))
// (call $child1_child2)
// )
//
// (func $child1_child3
// (i64.store (i32.const 0) (call $getBlockGasLimit))
// (call $main_child2)
// )
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0c, 0x03, 0x60, 0x01, 0x7e,
0x00, 0x60, 0x00, 0x01, 0x7e, 0x60, 0x00, 0x00, 0x02, 0x2f, 0x02, 0x08, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x06, 0x75, 0x73, 0x65, 0x47, 0x61, 0x73, 0x00,
0x00, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x10, 0x67, 0x65, 0x74,
0x42, 0x6c, 0x6f, 0x63, 0x6b, 0x47, 0x61, 0x73, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x00,
0x01, 0x03, 0x07, 0x06, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x05, 0x03, 0x01, 0x00,
0x01, 0x07, 0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x02, 0x06, 0x6d, 0x65,
0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x46, 0x06, 0x06, 0x00, 0x10, 0x03, 0x10,
0x04, 0x0b, 0x08, 0x00, 0x10, 0x05, 0x10, 0x06, 0x10, 0x07, 0x0b, 0x0d, 0x00, 0x10,
0x06, 0x41, 0x00, 0x10, 0x01, 0x37, 0x03, 0x00, 0x10, 0x07, 0x0b, 0x10, 0x00, 0x42,
0xa4, 0x03, 0x10, 0x00, 0x41, 0x00, 0x10, 0x01, 0x37, 0x03, 0x00, 0x10, 0x03, 0x0b,
0x09, 0x00, 0x42, 0xb9, 0x0a, 0x10, 0x00, 0x10, 0x06, 0x0b, 0x0b, 0x00, 0x41, 0x00,
0x10, 0x01, 0x37, 0x03, 0x00, 0x10, 0x04, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let g = DepGraph::build(&module, 2).unwrap();
assert!(g.edgecount() == 15);
}
}
use super::{ModuleCreator, ModuleError};
use parity_wasm::builder;
use parity_wasm::elements::{CustomSection, Module};
use byteorder::{LittleEndian, WriteBytesExt};
/// Enum on which ModuleCreator is implemented.
pub enum Deployer<'a> {
Memory(&'a [u8]),
CustomSection(&'a [u8]),
}
// FIXME: Bring ModulePreset API in line with the other with_preset methods so a ModulePreset impl
// can be written
impl<'a> Deployer<'a> {
pub fn with_preset(preset: &str, payload: &'a [u8]) -> Result<Self, ()> {
match preset {
"memory" => Ok(Deployer::Memory(payload)),
"customsection" => Ok(Deployer::CustomSection(payload)),
_ => Err(()),
}
}
}
/*
(module
(import "ethereum" "getCodeSize" (func $getCodeSize (result i32)))
(import "ethereum" "codeCopy" (func $codeCopy (param i32 i32 i32)))
(import "ethereum" "finish" (func $finish (param i32 i32)))
(memory 1)
(export "memory" (memory 0))
(export "main" (func $main))
(func $main
;; load total code size
(local $size i32)
(local $payload_offset i32)
(local $payload_size i32)
(set_local $size (call $getCodeSize))
;; copy entire thing into memory at offset 0
(call $codeCopy (i32.const 0) (i32.const 0) (get_local $size))
;; retrieve payload size (the last 4 bytes treated as a little endian 32 bit number)
(set_local $payload_size (i32.load (i32.sub (get_local $size) (i32.const 4))))
;; start offset is calculated as $size - 4 - $payload_size
(set_local $payload_offset (i32.sub (i32.sub (get_local $size) (i32.const 4)) (get_local $payload_size)))
;; return the payload
(call $finish (get_local $payload_offset) (get_local $payload_size))
)
)
*/
fn deployer_code() -> Vec<u8> {
vec![
0, 97, 115, 109, 1, 0, 0, 0, 1, 19, 4, 96, 0, 1, 127, 96, 3, 127, 127, 127, 0, 96, 2, 127,
127, 0, 96, 0, 0, 2, 62, 3, 8, 101, 116, 104, 101, 114, 101, 117, 109, 11, 103, 101, 116,
67, 111, 100, 101, 83, 105, 122, 101, 0, 0, 8, 101, 116, 104, 101, 114, 101, 117, 109, 8,
99, 111, 100, 101, 67, 111, 112, 121, 0, 1, 8, 101, 116, 104, 101, 114, 101, 117, 109, 6,
102, 105, 110, 105, 115, 104, 0, 2, 3, 2, 1, 3, 5, 3, 1, 0, 1, 7, 17, 2, 6, 109, 101, 109,
111, 114, 121, 2, 0, 4, 109, 97, 105, 110, 0, 3, 10, 44, 1, 42, 1, 3, 127, 16, 0, 33, 0,
65, 0, 65, 0, 32, 0, 16, 1, 32, 0, 65, 4, 107, 40, 2, 0, 33, 2, 32, 0, 65, 4, 107, 32, 2,
107, 33, 1, 32, 1, 32, 2, 16, 2, 11,
]
}
/// Returns a module which contains the deployable bytecode as a custom section.
fn create_custom_deployer(payload: &[u8]) -> Result<Module, ModuleError> {
// The standard deployer code, which expects a 32 bit little endian as the trailing content
// immediately following the payload, placed in a custom section.
let code = deployer_code();
// This is the pre-written deployer code.
let mut module: Module = parity_wasm::deserialize_buffer(&code)?;
// Re-write memory to pre-allocate enough for code size
let memory_initial = (payload.len() as u32 / 65536) + 1;
let mem_type = parity_wasm::elements::MemoryType::new(memory_initial, None, false);
module
.memory_section_mut()
// This would be an internal error (.e.g the the deployer code above has no memory section)
.expect("failed to get memory section")
.entries_mut()[0] = mem_type;
// Prepare payload (append length).
let mut custom_payload = payload.to_vec();
custom_payload.write_i32::<LittleEndian>(payload.len() as i32)?;
// Prepare and append custom section.
let custom = CustomSection::new("deployer".to_string(), custom_payload);
module
.sections_mut()
.push(parity_wasm::elements::Section::Custom(custom));
Ok(module)
}
/// Returns a module which contains the deployable bytecode as a data segment.
#[rustfmt::skip]
fn create_memory_deployer(payload: &[u8]) -> Module {
// Instructions calling finish(0, payload_len)
let instructions = vec![
parity_wasm::elements::Instruction::I32Const(0),
parity_wasm::elements::Instruction::I32Const(payload.len() as i32),
parity_wasm::elements::Instruction::Call(0),
parity_wasm::elements::Instruction::End,
];
let memory_initial = (payload.len() as u32 / 65536) + 1;
builder::module()
// Create a func/type for the ethereum::finish
.function()
.signature()
.param().i32()
.param().i32()
.build()
.build()
.import()
.module("ethereum")
.field("finish")
.external()
.func(0)
.build()
// Create the "main fucntion"
.function()
// Empty signature `(func)`
.signature().build()
.body()
.with_instructions(parity_wasm::elements::Instructions::new(instructions))
.build()
.build()
// Export the "main" function.
.export()
.field("main")
.internal()
.func(2)
.build()
// Add default memory section
.memory()
.with_min(memory_initial)
.build()
// Export memory
.export()
.field("memory")
.internal()
.memory(0)
.build()
// Add data section with payload
.data()
.offset(parity_wasm::elements::Instruction::I32Const(0))
.value(payload.to_vec())
.build()
.build()
}
impl<'a> ModuleCreator for Deployer<'a> {
fn create(&self) -> Result<Module, ModuleError> {
let output = match self {
Deployer::Memory(payload) => create_memory_deployer(&payload),
Deployer::CustomSection(payload) => create_custom_deployer(&payload)?,
};
Ok(output)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm;
use rustc_hex::FromHex;
#[test]
fn zero_payload() {
let payload = vec![];
let module = Deployer::with_preset("customsection", &payload)
.unwrap()
.create()
.unwrap();
let expected = FromHex::from_hex(
"
0061736d010000000113046000017f60037f7f7f0060027f7f00600000023e0308
657468657265756d0b676574436f646553697a65000008657468657265756d0863
6f6465436f7079000108657468657265756d0666696e6973680002030201030503
010001071102066d656d6f72790200046d61696e00030a2c012a01037f10002100
4100410020001001200041046b2802002102200041046b20026b21012001200210
020b
000d086465706c6f79657200000000
",
)
.unwrap();
let output = parity_wasm::serialize(module).expect("Failed to serialize");
assert_eq!(output, expected);
}
#[test]
fn nonzero_payload() {
let payload = FromHex::from_hex("80ff007faa550011").unwrap();
let module = Deployer::with_preset("customsection", &payload)
.unwrap()
.create()
.unwrap();
let expected = FromHex::from_hex(
"
0061736d010000000113046000017f60037f7f7f0060027f7f00600000023e0308
657468657265756d0b676574436f646553697a65000008657468657265756d0863
6f6465436f7079000108657468657265756d0666696e6973680002030201030503
010001071102066d656d6f72790200046d61696e00030a2c012a01037f10002100
4100410020001001200041046b2802002102200041046b20026b21012001200210
020b
0015086465706c6f79657280ff007faa55001108000000
",
)
.unwrap();
let output = parity_wasm::serialize(module).expect("Failed to serialize");
assert_eq!(output, expected);
}
#[test]
fn big_payload() {
let payload = [0; 632232];
let module = Deployer::with_preset("customsection", &payload)
.unwrap()
.create()
.unwrap();
let memory_initial = module.memory_section().unwrap().entries()[0]
.limits()
.initial();
assert_eq!(memory_initial, 10);
}
#[test]
fn memory_zero_payload() {
let payload = vec![];
let module = Deployer::with_preset("memory", &payload)
.unwrap()
.create()
.unwrap();
let expected = FromHex::from_hex(
"
0061736d0100000001090260027f7f0060000002130108657468657265756d0666
696e697368000003030200010503010001071102046d61696e0002066d656d6f72
7902000a0d0202000b08004100410010000b0b06010041000b00
",
)
.unwrap();
let output = parity_wasm::serialize(module).expect("Failed to serialize");
assert_eq!(output, expected);
}
#[test]
fn memory_nonzero_payload() {
let payload = FromHex::from_hex("80ff007faa550011").unwrap();
let module = Deployer::with_preset("memory", &payload)
.unwrap()
.create()
.unwrap();
let expected = FromHex::from_hex(
"
0061736d0100000001090260027f7f0060000002130108657468657265756d0666
696e697368000003030200010503010001071102046d61696e0002066d656d6f72
7902000a0d0202000b08004100410810000b0b0e010041000b0880ff007faa5500
11
",
)
.unwrap();
let output = parity_wasm::serialize(module).expect("Failed to serialize");
assert_eq!(output, expected);
}
#[test]
fn memory_big_payload() {
let payload = [0; 632232];
let module = Deployer::with_preset("memory", &payload)
.unwrap()
.create()
.unwrap();
let memory_initial = module.memory_section().unwrap().entries()[0]
.limits()
.initial();
assert_eq!(memory_initial, 10);
}
}
use super::{ModuleError, ModuleTranslator};
use parity_wasm::elements::*;
/// Enum on which ModuleTranslator is implemented.
pub enum DropSection<'a> {
NamesSection,
/// Name of the custom section.
CustomSectionByName(&'a String),
/// Index of the custom section.
CustomSectionByIndex(usize),
/// Index of the unknown section.
UnknownSectionByIndex(usize),
}
fn names_section_index_for(module: &Module) -> Option<usize> {
module.sections().iter().position(|e| {
match e {
// The default case, when the section was not parsed by parity-wasm
Section::Custom(_section) => _section.name() == "name",
// This is the case, when the section was parsed by parity-wasm
Section::Name(_) => true,
_ => false,
}
})
}
fn custom_section_index_for(module: &Module, name: &str) -> Option<usize> {
module.sections().iter().position(|e| match e {
Section::Custom(_section) => _section.name() == name,
_ => false,
})
}
impl<'a> DropSection<'a> {
fn find_index(&self, module: &Module) -> Option<usize> {
match &self {
DropSection::NamesSection => names_section_index_for(module),
DropSection::CustomSectionByName(name) => custom_section_index_for(module, &name),
DropSection::CustomSectionByIndex(index) => Some(*index),
DropSection::UnknownSectionByIndex(index) => Some(*index),
}
}
fn drop_section(&self, module: &mut Module) -> Result<bool, ModuleError> {
let index = self.find_index(&module);
if index.is_none() {
return Ok(false);
}
let index = index.unwrap();
let sections = module.sections_mut();
if index >= sections.len() {
return Ok(false);
}
sections.remove(index);
Ok(true)
}
}
impl<'a> ModuleTranslator for DropSection<'a> {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
Ok(self.drop_section(module)?)
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
let mut ret = module.clone();
if self.drop_section(&mut ret)? {
Ok(Some(ret))
} else {
Ok(None)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::builder;
#[test]
fn keep_intact() {
let mut module = builder::module().build();
let name = "empty".to_string();
let dropper = DropSection::CustomSectionByName(&name);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, false);
}
#[test]
fn keep_intact_custom_section() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let name = "empty".to_string();
let dropper = DropSection::CustomSectionByName(&name);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, false);
}
#[test]
fn remove_custom_section() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let name = "test".to_string();
let dropper = DropSection::CustomSectionByName(&name);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, true);
}
#[test]
fn remove_custom_section_by_index() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let dropper = DropSection::CustomSectionByIndex(0);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, true);
}
#[test]
fn remove_oob_custom_section_by_index() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let dropper = DropSection::CustomSectionByIndex(1);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, false);
}
#[test]
fn remove_custom_unknown_by_index() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let dropper = DropSection::UnknownSectionByIndex(0);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, true);
}
#[test]
fn remove_oob_unknown_section_by_index() {
let mut module = builder::module()
.with_section(Section::Custom(CustomSection::new(
"test".to_string(),
vec![],
)))
.build();
let dropper = DropSection::UnknownSectionByIndex(1);
let did_change = dropper.translate_inplace(&mut module).unwrap();
assert_eq!(did_change, false);
}
}
use super::{ModuleCreator, ModuleError};
use crate::utils::*;
use parity_wasm::elements::{deserialize_buffer, Module};
use wabt::Wat2Wasm;
/// Struct on which ModuleCreator is implemented.
pub struct FromWat<'a> {
filename: &'a str,
}
impl<'a> FromWat<'a> {
pub fn new(filename: &'a str) -> Result<Self, ()> {
Ok(FromWat { filename: filename })
}
}
fn load_file(filename: &str) -> Result<Vec<u8>, ModuleError> {
use std::fs::File;
use std::io::prelude::*;
let mut file = File::open(filename)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
Ok(buf)
}
impl From<wabt::Error> for ModuleError {
fn from(error: wabt::Error) -> Self {
use std::error::Error;
ModuleError::Custom(error.description().to_string())
}
}
fn convert_wat(input: &[u8]) -> Result<Module, ModuleError> {
// TODO: turn on relocatable(true)?
let module = Wat2Wasm::new()
.canonicalize_lebs(true)
.write_debug_names(true)
.convert(&input)?;
let module = deserialize_buffer::<Module>(module.as_ref())?;
Ok(module)
}
impl<'a> ModuleCreator for FromWat<'a> {
fn create(&self) -> Result<Module, ModuleError> {
let input = load_file(&self.filename)?;
convert_wat(&input)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::elements::*;
#[test]
fn smoke() {
let expectation: Vec<u8> = vec![
0, 97, 115, 109, 1, 0, 0, 0, 0, 8, 4, 110, 97, 109, 101, 2, 1, 0,
];
let module = convert_wat(&r#"(module)"#.as_bytes()).unwrap();
let result = serialize::<Module>(module).unwrap();
assert_eq!(result, expectation);
}
#[test]
fn simple_function() {
let expectation: Vec<u8> = vec![
0, 97, 115, 109, 1, 0, 0, 0, 1, 9, 2, 96, 2, 127, 127, 0, 96, 0, 0, 2, 19, 1, 8, 101,
116, 104, 101, 114, 101, 117, 109, 6, 102, 105, 110, 105, 115, 104, 0, 0, 3, 2, 1, 1,
7, 8, 1, 4, 109, 97, 105, 110, 0, 1, 10, 10, 1, 8, 0, 65, 0, 65, 0, 16, 0, 11, 0, 27,
4, 110, 97, 109, 101, 1, 9, 1, 0, 6, 102, 105, 110, 105, 115, 104, 2, 9, 2, 0, 2, 0, 0,
1, 0, 1, 0,
];
let module = convert_wat(
&r#"
(module
(import "ethereum" "finish" (func $finish (param i32 i32)))
(func (export "main")
(call $finish (i32.const 0) (i32.const 0))
)
)
"#
.as_bytes(),
)
.unwrap();
let result = serialize::<Module>(module).unwrap();
assert_eq!(result, expectation);
}
}
use super::ModulePreset;
use parity_wasm::elements::{FunctionType, ValueType};
pub struct ImportList<'a>(Vec<ImportType<'a>>);
/// Enum internally representing a type of import.
#[derive(Clone)]
pub enum ImportType<'a> {
Function(&'a str, &'a str, FunctionType),
Global(&'a str, &'a str),
Memory(&'a str, &'a str),
Table(&'a str, &'a str),
}
impl<'a> ImportType<'a> {
pub fn module(&self) -> &'a str {
// FIXME: Is there a way to shorten this expression?
match self {
ImportType::Function(module, _, _) => module,
ImportType::Global(module, _)
| ImportType::Memory(module, _)
| ImportType::Table(module, _) => module,
}
}
pub fn field(&self) -> &'a str {
// FIXME: Is there a way to shorten this expression?
match self {
ImportType::Function(_, field, _) => field,
ImportType::Global(_, field)
| ImportType::Memory(_, field)
| ImportType::Table(_, field) => field,
}
}
pub fn signature(&self) -> Result<&FunctionType, ()> {
match self {
ImportType::Function(_, _, sig) => Ok(&sig),
_ => Err(()),
}
}
}
impl<'a> ImportList<'a> {
pub fn new() -> Self {
ImportList(Vec::new())
}
pub fn entries(&'a self) -> &'a Vec<ImportType<'a>> {
&self.0
}
pub fn entries_mut(&'a mut self) -> &'a mut Vec<ImportType<'a>> {
&mut self.0
}
pub fn into_inner(self) -> Vec<ImportType<'a>> {
self.0
}
pub fn concatenate(&mut self, other: ImportList<'a>) {
let mut to_append = other.into_inner();
self.0.append(&mut to_append);
}
pub fn with_entries(entries: Vec<ImportType<'a>>) -> Self {
ImportList(entries)
}
pub fn lookup_by_field(&self, name: &str) -> Option<&ImportType> {
let entries = self.entries();
for import in entries {
if import.field() == name {
return Some(&import);
}
}
None
}
}
impl<'a> ModulePreset for ImportList<'a> {
fn with_preset(preset: &str) -> Result<Self, ()>
where
Self: Sized,
{
match preset {
"ewasm" => Ok(ImportList(vec![
ImportType::Function(
"ethereum",
"useGas",
FunctionType::new(vec![ValueType::I64], None),
),
ImportType::Function(
"ethereum",
"getGasLeft",
FunctionType::new(vec![], Some(ValueType::I64)),
),
ImportType::Function(
"ethereum",
"getAddress",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"getExternalBalance",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"getBlockHash",
FunctionType::new(vec![ValueType::I64, ValueType::I32], Some(ValueType::I32)),
),
ImportType::Function(
"ethereum",
"call",
FunctionType::new(
vec![
ValueType::I64,
ValueType::I32,
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
Some(ValueType::I32),
),
),
ImportType::Function(
"ethereum",
"callCode",
FunctionType::new(
vec![
ValueType::I64,
ValueType::I32,
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
Some(ValueType::I32),
),
),
ImportType::Function(
"ethereum",
"callDelegate",
FunctionType::new(
vec![
ValueType::I64,
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
Some(ValueType::I32),
),
),
ImportType::Function(
"ethereum",
"callStatic",
FunctionType::new(
vec![
ValueType::I64,
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
Some(ValueType::I32),
),
),
ImportType::Function(
"ethereum",
"create",
FunctionType::new(
vec![
ValueType::I64,
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
Some(ValueType::I32),
),
),
ImportType::Function(
"ethereum",
"callDataCopy",
FunctionType::new(vec![ValueType::I32, ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"getCallDataSize",
FunctionType::new(vec![], Some(ValueType::I32)),
),
ImportType::Function(
"ethereum",
"getCodeSize",
FunctionType::new(vec![], Some(ValueType::I32)),
),
ImportType::Function(
"ethereum",
"getExternalCodeSize",
FunctionType::new(vec![ValueType::I32], Some(ValueType::I32)),
),
ImportType::Function(
"ethereum",
"externalCodeCopy",
FunctionType::new(
vec![
ValueType::I32,
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
None,
),
),
ImportType::Function(
"ethereum",
"codeCopy",
FunctionType::new(vec![ValueType::I32, ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"getCaller",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"getCallValue",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"getBlockDifficulty",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"getBlockCoinbase",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"getBlockNumber",
FunctionType::new(vec![], Some(ValueType::I64)),
),
ImportType::Function(
"ethereum",
"getBlockGasLimit",
FunctionType::new(vec![], Some(ValueType::I64)),
),
ImportType::Function(
"ethereum",
"getBlockTimestamp",
FunctionType::new(vec![], Some(ValueType::I64)),
),
ImportType::Function(
"ethereum",
"getTxGasPrice",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"getTxOrigin",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"storageStore",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"storageLoad",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
),
// add by echo >>>>>>>>>>>>>>>>>>>>>>>>>>>
ImportType::Function(
"ethereum",
"storageStore2",
FunctionType::new(vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"storageLoad2",
FunctionType::new(vec![ValueType::I32, ValueType::I32, ValueType::I32, ValueType::I32], None),
),
// add by echo <<<<<<<<<<<<<<<<<<<<<<<<<<<
ImportType::Function(
"ethereum",
"log",
FunctionType::new(
vec![
ValueType::I32,
ValueType::I32,
ValueType::I32,
ValueType::I32,
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
None,
),
),
ImportType::Function(
"ethereum",
"getReturnDataSize",
FunctionType::new(vec![], Some(ValueType::I32)),
),
ImportType::Function(
"ethereum",
"returnDataCopy",
FunctionType::new(vec![ValueType::I32, ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"finish",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"revert",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"ethereum",
"selfDestruct",
FunctionType::new(vec![ValueType::I32], None),
),
])),
"eth2" => Ok(ImportList(vec![
ImportType::Function(
"eth2",
"loadPreStateRoot",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"eth2",
"blockDataSize",
FunctionType::new(vec![], Some(ValueType::I32)),
),
ImportType::Function(
"eth2",
"blockDataCopy",
FunctionType::new(vec![ValueType::I32, ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"eth2",
"savePostStateRoot",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"eth2",
"pushNewDeposit",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
),
])),
"debug" => Ok(ImportList(vec![
ImportType::Function(
"debug",
"print32",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"debug",
"print64",
FunctionType::new(vec![ValueType::I64], None),
),
ImportType::Function(
"debug",
"printMem",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"debug",
"printMemHex",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"debug",
"printStorage",
FunctionType::new(vec![ValueType::I32], None),
),
ImportType::Function(
"debug",
"printStorageHex",
FunctionType::new(vec![ValueType::I32], None),
),
])),
"bignum" => Ok(ImportList(vec![
ImportType::Function(
"bignum",
"mul256",
FunctionType::new(vec![ValueType::I32, ValueType::I32, ValueType::I32], None),
),
ImportType::Function(
"bignum",
"umulmod256",
FunctionType::new(
vec![
ValueType::I32,
ValueType::I32,
ValueType::I32,
ValueType::I32,
],
None,
),
),
])),
_ => Err(()),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn lookup_by_field_ewasm_good() {
let list = ImportList::with_preset("ewasm").unwrap();
assert!(list.lookup_by_field("useGas").is_some());
}
#[test]
fn lookup_by_field_ewasm_not_found() {
let list = ImportList::with_preset("ewasm").unwrap();
assert!(list.lookup_by_field("foo").is_none());
}
}
extern crate byteorder;
extern crate parity_wasm;
extern crate rustc_hex;
use parity_wasm::elements::Module;
pub mod imports;
pub mod checkfloat;
pub mod checkstartfunc;
pub mod deployer;
pub mod dropsection;
#[cfg(feature = "wabt")]
pub mod fromwat;
pub mod remapimports;
pub mod remapstart;
pub mod repack;
pub mod snip;
pub mod trimexports;
pub mod trimstartfunc;
pub mod verifyexports;
pub mod verifyimports;
mod depgraph;
mod utils;
use std::{error, fmt};
#[derive(Eq, PartialEq, Debug)]
pub enum ModuleError {
NotSupported,
NotFound,
Custom(String),
}
pub trait ModuleCreator {
/// Returns new module.
fn create(&self) -> Result<Module, ModuleError>;
}
pub trait ModuleTranslator {
/// Translates module. Returns new module or none if nothing was modified. Can fail with ModuleError::NotSupported.
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError>;
/// Translates module in-place. Returns true if the module was modified. Can fail with ModuleError::NotSupported.
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError>;
}
pub trait ModuleValidator {
/// Validates module. Returns true if it is valid or false if invalid.
fn validate(&self, module: &Module) -> Result<bool, ModuleError>;
}
pub trait ModulePreset {
fn with_preset(preset: &str) -> Result<Self, ()>
where
Self: std::marker::Sized;
}
impl From<String> for ModuleError {
fn from(error: String) -> Self {
ModuleError::Custom(error)
}
}
impl From<std::io::Error> for ModuleError {
fn from(error: std::io::Error) -> Self {
use std::error::Error;
ModuleError::Custom(error.description().to_string())
}
}
impl fmt::Display for ModuleError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
f,
"{}",
match self {
ModuleError::NotSupported => "Method unsupported",
ModuleError::NotFound => "Not found",
ModuleError::Custom(msg) => msg,
}
)
}
}
impl error::Error for ModuleError {
fn description(&self) -> &str {
match self {
ModuleError::NotSupported => "Method unsupported",
ModuleError::NotFound => "Not found",
ModuleError::Custom(msg) => msg,
}
}
fn cause(&self) -> Option<&error::Error> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::error::Error;
struct SampleModule {}
impl ModuleCreator for SampleModule {
fn create(&self) -> Result<Module, ModuleError> {
Ok(Module::default())
}
}
impl ModuleTranslator for SampleModule {
fn translate(&self, _module: &Module) -> Result<Option<Module>, ModuleError> {
Ok(Some(Module::default()))
}
fn translate_inplace(&self, _module: &mut Module) -> Result<bool, ModuleError> {
Ok(true)
}
}
impl ModuleValidator for SampleModule {
fn validate(&self, _module: &Module) -> Result<bool, ModuleError> {
Ok(true)
}
}
#[test]
fn creator_succeeds() {
let creator = SampleModule {};
let result = creator.create();
assert!(result.is_ok());
}
#[test]
fn translator_succeeds() {
let translator = SampleModule {};
let result = translator.translate(&Module::default());
assert!(result.is_ok());
}
#[test]
fn translator_inplace_succeeds() {
let translator = SampleModule {};
let result = translator.translate_inplace(&mut Module::default());
assert!(result.is_ok());
}
#[test]
fn validator_succeeds() {
let validator = SampleModule {};
let result = validator.validate(&Module::default());
assert!(result.is_ok());
}
#[test]
fn from_error() {
let err: ModuleError = "custom message".to_string().into();
assert_eq!(err, ModuleError::Custom("custom message".to_string()));
}
#[test]
fn fmt_good() {
// Add new tests for each enum variant here as they are implemented.
let fmt_result_unsupported = format!("{}", ModuleError::NotSupported);
assert_eq!("Method unsupported", fmt_result_unsupported);
let fmt_result_custom = format!("{}", ModuleError::Custom("foo".to_string()));
assert_eq!("foo", fmt_result_custom);
}
#[test]
fn error_good() {
// Add new tests for each enum variant here as they are implemented.
let err_unsupported = ModuleError::NotSupported;
let err_description_unsupported = err_unsupported.description();
assert_eq!("Method unsupported", err_description_unsupported);
let err_custom = ModuleError::Custom("bar".to_string());
let err_description_custom = err_custom.description();
assert_eq!("bar", err_description_custom);
}
}
use super::{imports::ImportList, ModuleError, ModulePreset, ModuleTranslator};
use parity_wasm::elements::*;
pub struct RemapImports<'a> {
/// A list of import sets to remap.
interfaces: Vec<ImportInterface<'a>>,
}
/// A pair containing a list of imports for RemapImports to remap against, and an optional string with which all
/// imports are expected to be prefixed.
pub struct ImportInterface<'a>(ImportList<'a>, Option<&'a str>);
impl<'a> ModulePreset for RemapImports<'a> {
fn with_preset(preset: &str) -> Result<Self, ()> {
let mut interface_set: Vec<ImportInterface> = Vec::new();
// Accept a comma-separated list of presets.
let presets: String = preset
.chars()
.filter(|c| *c != '_' && *c != ' ' && *c != '\n' && *c != '\t')
.collect();
for preset_individual in presets.split(',') {
match preset_individual {
"ewasm" => interface_set.push(ImportInterface::new(
ImportList::with_preset("ewasm").expect("Missing ewasm preset"),
Some("ethereum_"),
)),
"eth2" => interface_set.push(ImportInterface::new(
ImportList::with_preset("eth2").expect("Missing eth2 preset"),
Some("eth2_"),
)),
"debug" => interface_set.push(ImportInterface::new(
ImportList::with_preset("debug").expect("Missing debug preset"),
Some("debug_"),
)),
"bignum" => interface_set.push(ImportInterface::new(
ImportList::with_preset("bignum").expect("Missing bignum preset"),
Some("bignum_"),
)),
_ => return Err(()),
}
}
Ok(RemapImports {
interfaces: interface_set,
})
}
}
impl<'a> ModuleTranslator for RemapImports<'a> {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
let mut was_mutated = false;
if let Some(section) = module.import_section_mut() {
for interface in self.interfaces.iter() {
*section = ImportSection::with_entries(
section
.entries()
.iter()
.map(|e| self.remap_from_list(e, &mut was_mutated, interface))
.collect(),
);
}
}
Ok(was_mutated)
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
let mut new_module = module.clone();
let mut was_mutated = false;
if let Some(section) = new_module.import_section_mut() {
// Iterate over entries and remap if needed.
for interface in self.interfaces.iter() {
*section = ImportSection::with_entries(
section
.entries()
.iter()
.map(|e| self.remap_from_list(e, &mut was_mutated, interface))
.collect(),
);
}
}
if was_mutated {
Ok(Some(new_module))
} else {
Ok(None)
}
}
}
impl<'a> ImportInterface<'a> {
pub fn new(imports: ImportList<'a>, prefix: Option<&'a str>) -> Self {
ImportInterface(imports, prefix)
}
pub fn prefix(&self) -> Option<&str> {
self.1
}
pub fn imports(&self) -> &ImportList<'a> {
&self.0
}
}
impl<'a> RemapImports<'a> {
fn new(interfaces: Vec<ImportInterface<'a>>) -> Self {
RemapImports {
interfaces: interfaces,
}
}
/// Takes an import entry and returns either the same entry or a remapped version if it exists.
/// Sets the mutation flag if was remapped.
fn remap_from_list(
&self,
entry: &ImportEntry,
mutflag: &mut bool,
interface: &ImportInterface,
) -> ImportEntry {
match interface.prefix() {
Some(prefix) => {
let prefix_len = prefix.len();
if entry.field().len() > prefix_len && prefix == &entry.field()[..prefix_len] {
// Look for a matching remappable import and mutate if found.
if let Some(import) = interface
.imports()
.lookup_by_field(&entry.field()[prefix_len..])
{
*mutflag = true;
return ImportEntry::new(
import.module().into(),
import.field().into(),
entry.external().clone(),
);
}
}
entry.clone()
}
None => {
if let Some(import) = interface.imports().lookup_by_field(&entry.field()) {
*mutflag = true;
ImportEntry::new(
import.module().into(),
import.field().into(),
entry.external().clone(),
)
} else {
entry.clone()
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::verifyimports::*;
use crate::{ModulePreset, ModuleTranslator, ModuleValidator};
use parity_wasm;
use rustc_hex::FromHex;
#[test]
fn smoke_test() {
let input = FromHex::from_hex(
"
0061736d0100000001050160017e0002170103656e760f65746865726575
6d5f7573654761730000
",
)
.unwrap();
let mut module = parity_wasm::deserialize_buffer(&input).expect("failed");
let did_change = RemapImports::with_preset("ewasm")
.unwrap()
.translate_inplace(&mut module)
.unwrap();
let output = parity_wasm::serialize(module).expect("failed");
let expected = FromHex::from_hex(
"
0061736d0100000001050160017e0002130108657468657265756d067573
654761730000
",
)
.unwrap();
assert_eq!(output, expected);
assert!(did_change);
}
#[test]
fn remap_did_mutate() {
// wast:
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
//
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x60, 0x01, 0x7e,
0x00, 0x60, 0x00, 0x00, 0x02, 0x17, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x0f, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x47, 0x61, 0x73, 0x00,
0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04,
0x6d, 0x61, 0x69, 0x6e, 0x00, 0x01, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02,
0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = parity_wasm::deserialize_buffer(&wasm).unwrap();
let new = RemapImports::with_preset("ewasm")
.unwrap()
.translate(&module)
.expect("Module internal error");
assert!(new.is_some());
}
#[test]
fn remap_did_mutate_verify() {
// wast:
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
//
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x60, 0x01, 0x7e,
0x00, 0x60, 0x00, 0x00, 0x02, 0x17, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x0f, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5f, 0x75, 0x73, 0x65, 0x47, 0x61, 0x73, 0x00,
0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04,
0x6d, 0x61, 0x69, 0x6e, 0x00, 0x01, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02,
0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = parity_wasm::deserialize_buffer(&wasm).unwrap();
let new = RemapImports::with_preset("ewasm")
.unwrap()
.translate(&module)
.expect("Module internal error");
assert!(new.is_some());
let verified = VerifyImports::with_preset("ewasm")
.unwrap()
.validate(&new.unwrap())
.unwrap();
assert_eq!(verified, true);
}
#[test]
fn remap_did_mutate_verify_explicit_type_section() {
// wast:
// (module
// (type (;0;) (func (result i64)))
// (import "env" "ethereum_getGasLeft" (func (;0;) (type 0)))
// (memory 1)
// (func $main)
// (export "main" (func $main))
// (export "memory" (memory 0))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x60, 0x00, 0x01,
0x7e, 0x60, 0x00, 0x00, 0x02, 0x1b, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x13, 0x65, 0x74,
0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x5f, 0x67, 0x65, 0x74, 0x47, 0x61, 0x73, 0x4c,
0x65, 0x66, 0x74, 0x00, 0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01,
0x07, 0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x01, 0x06, 0x6d, 0x65, 0x6d,
0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = parity_wasm::deserialize_buffer(&wasm).unwrap();
let new = RemapImports::with_preset("ewasm")
.unwrap()
.translate(&module)
.expect("Module internal error");
assert!(new.is_some());
let verified = VerifyImports::with_preset("ewasm")
.unwrap()
.validate(&new.unwrap())
.unwrap();
assert_eq!(verified, true);
}
#[test]
fn remap_mutated_multiple_interfaces() {
// wast:
// (module
// (type (;0;) (func (result i64)))
// (type (;1;) (func (param i32 i32 i32)))
// (type (;2;) (func (param i32)))
// (import "env" "ethereum_getGasLeft" (func (;0;) (type 0)))
// (import "env" "bignum_mul256" (func (;1;) (type 1)))
// (import "env" "debug_printStorage" (func (;2;) (type 2)))
// (memory 1)
// (func $main)
// (export "main" (func $main))
// (export "memory" (memory 0))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x12, 0x04, 0x60, 0x00, 0x01,
0x7e, 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x00, 0x00,
0x02, 0x48, 0x03, 0x03, 0x65, 0x6e, 0x76, 0x13, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
0x75, 0x6d, 0x5f, 0x67, 0x65, 0x74, 0x47, 0x61, 0x73, 0x4c, 0x65, 0x66, 0x74, 0x00,
0x00, 0x03, 0x65, 0x6e, 0x76, 0x0d, 0x62, 0x69, 0x67, 0x6e, 0x75, 0x6d, 0x5f, 0x6d,
0x75, 0x6c, 0x32, 0x35, 0x36, 0x00, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x12, 0x64, 0x65,
0x62, 0x75, 0x67, 0x5f, 0x70, 0x72, 0x69, 0x6e, 0x74, 0x53, 0x74, 0x6f, 0x72, 0x61,
0x67, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x03, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07,
0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x03, 0x06, 0x6d, 0x65, 0x6d, 0x6f,
0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = parity_wasm::deserialize_buffer(&wasm).unwrap();
let new = RemapImports::with_preset("ewasm, bignum, debug")
.unwrap()
.translate(&module)
.expect("Module internal error")
.expect("Module was not mutated");
let verifier = VerifyImports::with_preset("ewasm, bignum, debug").unwrap();
assert_eq!(verifier.validate(&new), Ok(true));
}
#[test]
fn no_prefix() {
// wast:
// (module
// (type (;0;) (func (result i64)))
// (type (;1;) (func (param i32 i32 i32)))
// (type (;2;) (func (param i32)))
// (import "env" "getGasLeft" (func (;0;) (type 0)))
// (import "env" "mul256" (func (;1;) (type 1)))
// (import "env" "printStorage" (func (;2;) (type 2)))
// (memory 1)
// (func $main)
// (export "main" (func $main))
// (export "memory" (memory 0))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x12, 0x04, 0x60, 0x00, 0x01,
0x7e, 0x60, 0x03, 0x7f, 0x7f, 0x7f, 0x00, 0x60, 0x01, 0x7f, 0x00, 0x60, 0x00, 0x00,
0x02, 0x32, 0x03, 0x03, 0x65, 0x6e, 0x76, 0x0a, 0x67, 0x65, 0x74, 0x47, 0x61, 0x73,
0x4c, 0x65, 0x66, 0x74, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x06, 0x6d, 0x75, 0x6c,
0x32, 0x35, 0x36, 0x00, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x0c, 0x70, 0x72, 0x69, 0x6e,
0x74, 0x53, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x00, 0x02, 0x03, 0x02, 0x01, 0x03,
0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00,
0x03, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02,
0x00, 0x0b,
];
let module = parity_wasm::deserialize_buffer(&wasm).unwrap();
let interfaces_noprefix = vec![
ImportInterface::new(ImportList::with_preset("ewasm").unwrap(), None),
ImportInterface::new(ImportList::with_preset("bignum").unwrap(), None),
ImportInterface::new(ImportList::with_preset("debug").unwrap(), None),
];
let new = RemapImports::new(interfaces_noprefix)
.translate(&module)
.expect("Module internal error")
.expect("Module was not mutated");
let verifier = VerifyImports::with_preset("ewasm, bignum, debug").unwrap();
assert_eq!(verifier.validate(&new), Ok(true));
}
}
use parity_wasm::elements::*;
use super::{ModuleError, ModulePreset, ModuleTranslator};
pub struct RemapStart;
impl ModulePreset for RemapStart {
fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
// TODO refactor this later
"ewasm" => Ok(RemapStart {}),
_ => Err(()),
}
}
}
impl ModuleTranslator for RemapStart {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
Ok(remap_start(module))
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
let mut ret = module.clone();
if remap_start(&mut ret) {
Ok(Some(ret))
} else {
Ok(None)
}
}
}
// NOTE: This seems to be exported properly in later versions of parity-wasm.
// TODO: When updated, use the proper method instead.
fn section_order(s: &Section) -> u8 {
match s {
Section::Custom(_) => 0x00,
Section::Unparsed { .. } => 0x00,
Section::Type(_) => 0x1,
Section::Import(_) => 0x2,
Section::Function(_) => 0x3,
Section::Table(_) => 0x4,
Section::Memory(_) => 0x5,
Section::Global(_) => 0x6,
Section::Export(_) => 0x7,
Section::Start(_) => 0x8,
Section::Element(_) => 0x9,
Section::Code(_) => 0x0a,
Section::Data(_) => 0x0b,
Section::Name(_) => 0x00,
Section::Reloc(_) => 0x00,
}
}
/// Replace an exported function with another function, or export if unexported.
fn remap_or_export_main(module: &mut Module, export_name: &str, func_idx: u32) {
let new_func_export = ExportEntry::new(export_name.to_string(), Internal::Function(func_idx));
if let Some(export_section) = module.export_section_mut() {
let export_section = export_section.entries_mut();
// If we find an export named `export_name`, replace it. Otherwise, append an entry to the
// section with the supplied func index.
if let Some(main_export_loc) = export_section
.iter_mut()
.position(|e| e.field() == export_name)
{
export_section[main_export_loc] = new_func_export;
} else {
export_section.push(new_func_export);
}
} else {
let sections = module.sections_mut();
let new_export_section =
Section::Export(ExportSection::with_entries(vec![new_func_export]));
// If we can find a section that is supposed to be after exports, insert the new section at its position (shifts other elements to the right).
// Otherwise, append it at the end.
// NOTE: Assumes that the ordering of sections is otherwise correct.
if let Some(exports_loc) = sections.iter().position(|sec| section_order(&sec) > 0x7) {
sections.insert(exports_loc, new_export_section);
} else {
sections.push(new_export_section);
}
}
}
fn remap_start(module: &mut Module) -> bool {
if let Some(start_func_idx) = module.start_section() {
// Look for an export "main". If found, replace it with an export of the function to
// which the start section points.
remap_or_export_main(module, "main", start_func_idx);
// Remove the start section, leaving the "main" export as the entry point.
module.clear_start_section();
true
} else {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::SerializationHelpers;
use crate::{ModulePreset, ModuleTranslator};
use rustc_hex::FromHex;
#[test]
fn remapstart_mutation() {
//wat:
//(module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main2)
// (func $main)
// (start $main2)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060
000002170103656e760f657468657265756d5f75736547617300000303020101050301000107110
2046d61696e0001066d656d6f727902000801020a070202000b02000b0020046e616d65010e0201
046d61696e02056d61696e320209030001000001000200",
)
.unwrap();
let mut module = Module::from_slice(&wasm);
module = module.parse_names().unwrap();
assert!(module.names_section().is_some());
let start_idx = module
.start_section()
.expect("Module missing start function");
let new = RemapStart::with_preset("ewasm")
.unwrap()
.translate(&module)
.expect("Module internal error")
.expect("new module not returned");
assert!(
new.start_section().is_none(),
"start section wasn't removed"
);
assert!(new
.export_section()
.expect("Module missing export section")
.entries()
.iter()
.find(|e| e.field() == String::from("main")
&& *e.internal() == Internal::Function(start_idx))
.is_some());
}
#[test]
fn remapstart_no_mutation() {
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060
000002170103656e760f657468657265756d5f757365476173000003020101050301000
1071102046d61696e0001066d656d6f727902000a040102000b",
)
.unwrap();
let module = Module::from_slice(&wasm);
let new = RemapStart::with_preset("ewasm")
.unwrap()
.translate(&module)
.expect("Module internal error");
assert!(new.is_none());
}
#[test]
fn remapstart_inplace_mutation() {
//wat:
//(module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main2)
// (func $main)
// (start $main2)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060
000002170103656e760f657468657265756d5f75736547617300000303020101050301000107110
2046d61696e0001066d656d6f727902000801020a070202000b02000b0020046e616d65010e0201
046d61696e02056d61696e320209030001000001000200",
)
.unwrap();
let mut module = Module::from_slice(&wasm);
module = module.parse_names().unwrap();
assert!(module.names_section().is_some());
let res = RemapStart::with_preset("ewasm")
.unwrap()
.translate_inplace(&mut module)
.unwrap();
assert!(res, "module was not modified");
assert!(
module.start_section().is_none(),
"start section wasn't removed"
);
}
#[test]
fn remapstart_inplace_no_mutation() {
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060
000002170103656e760f657468657265756d5f75736547617300000302010105030100010711020
46d61696e0001066d656d6f727902000a040102000b",
)
.unwrap();
let mut module = Module::from_slice(&wasm);
let res = RemapStart::with_preset("ewasm")
.unwrap()
.translate_inplace(&mut module)
.unwrap();
assert!(!res, "module was modified");
}
#[test]
fn remapstart_mutation_no_exports() {
//wat:
//(module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (func $main2)
// (func $main)
// (start $main2)
//)
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060000002170103656e760f657468657265756d5f7573654761730000030302010105030100010801010a070202000b02000b",
)
.unwrap();
let mut module = Module::from_slice(&wasm);
let res = RemapStart::with_preset("ewasm")
.unwrap()
.translate_inplace(&mut module)
.unwrap();
assert!(res, "module was not modified");
assert!(
module.export_section().is_some(),
"export section does not exist"
);
}
#[test]
fn export_section_exists_but_no_main() {
// wat:
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (start $main)
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060000002170103656e760f657468657265756d5f7573654761730000030201010503010001070a01066d656d6f727902000801010a040102000b"
).unwrap();
let mut module = Module::from_slice(&wasm);
let remapper = RemapStart::with_preset("ewasm").expect("Can't fail");
let res = remapper.translate_inplace(&mut module);
assert!(res.is_ok());
let mutated = res.unwrap();
assert_eq!(mutated, true);
assert!(module.export_section().is_some());
assert!(module.start_section().is_none());
assert!(module
.export_section()
.unwrap()
.entries()
.iter()
.find(|e| e.field() == "main")
.is_some());
}
}
use super::{ModuleError, ModuleTranslator};
use parity_wasm::builder;
use parity_wasm::elements::*;
pub struct Repack;
impl Repack {
pub fn new() -> Self {
Repack {}
}
}
impl ModuleTranslator for Repack {
fn translate_inplace(&self, _module: &mut Module) -> Result<bool, ModuleError> {
Err(ModuleError::NotSupported)
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
// TODO: check in names section is carried over.
let module = module.clone();
let module = builder::from_module(module).build();
Ok(Some(module))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::*;
use parity_wasm::builder;
use rustc_hex::FromHex;
#[test]
fn smoke_test() {
let module = Module::default();
let repack = Repack::new();
assert_eq!(module, repack.translate(&module).unwrap().unwrap());
}
#[test]
fn basic_sections_only() {
let module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("main")
.internal()
.func(0)
.build()
.export()
.field("memory")
.internal()
.memory(0)
.build()
.build();
let repack = Repack::new();
assert_eq!(module, repack.translate(&module).unwrap().unwrap());
}
#[test]
fn custom_section() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("main")
.internal()
.func(0)
.build()
.export()
.field("memory")
.internal()
.memory(0)
.build()
.build();
let custom = CustomSection::new("test".to_string(), vec![42u8; 16]);
module
.sections_mut()
.push(parity_wasm::elements::Section::Custom(custom));
let repack = Repack::new();
assert_ne!(module, repack.translate(&module).unwrap().unwrap());
}
#[test]
fn names_section() {
let input = FromHex::from_hex(
"0061736d010000000104016000000303020000070801046d61696e00010a
0a020300010b040010000b0014046e616d65010d0200047465737401046d
61696e",
)
.unwrap();
let module = Module::from_slice(&input);
// Forcefully parse names section here.
let module = module
.parse_names()
.expect("parsing the names section failed");
assert_eq!(module.names_section().is_some(), true);
let repack = Repack::new();
// Repack drops names section too.
let output = repack.translate(&module).unwrap().unwrap();
assert_eq!(output.has_names_section(), false);
}
}
use super::{ModuleError, ModuleTranslator};
use parity_wasm::elements::Module;
#[derive(Clone)]
pub struct Snip(wasm_snip::Options);
impl Snip {
pub fn new() -> Self {
let mut options = wasm_snip::Options::default();
// TODO: expose these as options
options.snip_rust_fmt_code = true;
options.snip_rust_panicking_code = true;
options.skip_producers_section = true;
Snip { 0: options }
}
}
impl From<failure::Error> for ModuleError {
fn from(error: failure::Error) -> Self {
ModuleError::Custom(error.to_string())
}
}
impl ModuleTranslator for Snip {
fn translate_inplace(&self, _module: &mut Module) -> Result<bool, ModuleError> {
Err(ModuleError::NotSupported)
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
// TODO: there must be a better way to accomplish this.
let serialized = parity_wasm::elements::serialize::<Module>(module.clone())?;
// TODO: improve wasm-snip API...
let mut options = self.0.clone();
options.input = wasm_snip::Input::Buffer(serialized);
let ret = wasm_snip::snip(options)?;
let output = ret.emit_wasm()?;
let output = parity_wasm::elements::deserialize_buffer::<Module>(&output[..])?;
Ok(Some(output))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::utils::SerializationHelpers;
use rustc_hex::FromHex;
#[test]
fn smoke_test() {
// (module
// (import "env" "ethereum_useGas" (func (param i64)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main
// (call $std::panicking::rust_panic_with_hook::h12b7239ed4348eae)
// (call $core::fmt::write::h9f284ae8e8e9b94a)
// )
// (func $std::panicking::rust_panic_with_hook::h12b7239ed4348eae)
// (func $core::fmt::write::h9f284ae8e8e9b94a)
// )
let wasm: Vec<u8> = FromHex::from_hex(
"0061736d0100000001080260017e0060000002170103656e760f65746865
7265756d5f75736547617300000304030101010503010001071102046d61
696e0001066d656d6f727902000a10030600100210030b0300010b030001
0b007f046e616d650178040011696d706f72742466756e6374696f6e2430
01046d61696e02377374643a3a70616e69636b696e673a3a727573745f70
616e69635f776974685f686f6f6b3a3a6831326237323339656434333438
6561650323636f72653a3a666d743a3a77726974653a3a68396632383461
65386538653962393461",
)
.unwrap();
let module = Module::from_slice(&wasm);
let module = Snip::new().translate(&module);
let module = module
.expect("translation to be succesful")
.expect("new module to be returned");
assert!(module.to_vec().len() < wasm.len());
}
}
use super::{ModuleError, ModulePreset, ModuleTranslator};
use parity_wasm::elements::*;
/// Struct containing a list of valid exports.
struct ExportWhitelist {
pub entries: Vec<ExportEntry>,
}
/// Wrapper struct implementing ModuleTranslator.
/// Removes any exports that are noncompliant with a specified interface.
pub struct TrimExports {
whitelist: ExportWhitelist,
}
/// Helper that compares the enum variant of two WebAssembly exports.
fn cmp_internal_variant(a: &Internal, b: &Internal) -> bool {
std::mem::discriminant(a) == std::mem::discriminant(b)
}
impl ModulePreset for ExportWhitelist {
fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
"ewasm" => Ok(ExportWhitelist {
entries: vec![
//NOTE: function signatures are not checked yet
ExportEntry::new("main".to_string(), Internal::Function(0)),
ExportEntry::new("memory".to_string(), Internal::Memory(0)),
],
}),
"pwasm" => Ok(ExportWhitelist {
entries: vec![ExportEntry::new("_call".to_string(), Internal::Function(0))],
}),
_ => Err(()),
}
}
}
impl ExportWhitelist {
/// Constructs an empty whitelist. Mostly useless.
fn new() -> Self {
ExportWhitelist {
entries: Vec::new(),
}
}
/// Looks up a given export entry in the whitelist and returns true if it is valid.
fn lookup(&self, export: &ExportEntry) -> bool {
self.entries
.iter()
.find(|matched_export| {
export.field() == matched_export.field()
&& cmp_internal_variant(export.internal(), matched_export.internal())
})
.is_some()
}
}
impl TrimExports {
/// Constructs an empty `trimexports` context.
pub fn new() -> Self {
TrimExports {
whitelist: ExportWhitelist::new(),
}
}
/// Takes a given preset string and constructs a context with the
/// corresponding whitelist.
pub fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
"ewasm" => Ok(TrimExports {
whitelist: ExportWhitelist::with_preset("ewasm").unwrap(),
}),
"pwasm" => Ok(TrimExports {
whitelist: ExportWhitelist::with_preset("pwasm").unwrap(),
}),
_ => Err(()),
}
}
/// Iterates over the export section, if there is one, and removes
/// unnecessary entries.
fn trim_exports(&self, module: &mut Module) -> bool {
if let Some(section) = module.export_section_mut() {
let new_section = ExportSection::with_entries(
section
.entries()
.iter()
.cloned()
.filter(|entry| self.whitelist.lookup(entry))
.collect(),
);
if new_section.entries().len() < section.entries().len() {
*section = new_section;
return true;
}
false
} else {
false
}
}
}
impl ModuleTranslator for TrimExports {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
Ok(self.trim_exports(module))
}
fn translate(&self, module: &Module) -> Result<Option<Module>, ModuleError> {
let mut ret = module.clone();
let modified = self.trim_exports(&mut ret);
if modified {
return Ok(Some(ret));
}
Ok(None)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::builder;
// Smoke tests
#[test]
fn builder_all_exports_good_ewasm() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("main")
.internal()
.func(0)
.build()
.export()
.field("memory")
.internal()
.memory(0)
.build()
.build();
let trimmer = TrimExports::with_preset("ewasm").unwrap();
let did_change = trimmer.translate_inplace(&mut module).unwrap();
assert_eq!(false, did_change);
}
#[test]
fn builder_one_wrong_mem_export_ewasm() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("main")
.internal()
.func(0)
.build()
.export()
.field("memory")
.internal()
.memory(0)
.build()
.export()
.field("foo")
.internal()
.memory(0)
.build()
.build();
let trimmer = TrimExports::with_preset("ewasm").unwrap();
let did_change = trimmer.translate_inplace(&mut module).unwrap();
assert_eq!(true, did_change);
}
#[test]
fn builder_no_export_ewasm() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.build();
let trimmer = TrimExports::with_preset("ewasm").unwrap();
let did_change = trimmer.translate_inplace(&mut module).unwrap();
assert_eq!(false, did_change);
}
#[test]
fn builder_all_exports_good_pwasm() {
let mut module = builder::module()
.function()
.signature()
.build()
.body()
.build()
.build()
.export()
.field("_call")
.internal()
.func(0)
.build()
.build();
let trimmer = TrimExports::with_preset("pwasm").unwrap();
let did_change = trimmer.translate_inplace(&mut module).unwrap();
assert_eq!(false, did_change);
}
}
use super::{ModuleError, ModulePreset, ModuleTranslator};
use parity_wasm::elements::*;
pub struct TrimStartFunc;
impl TrimStartFunc {
fn trim_startfunc(&self, module: &mut Module) -> bool {
if let Some(_start_section) = module.start_section() {
module.clear_start_section();
true
} else {
false
}
}
}
impl ModulePreset for TrimStartFunc {
fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
"ewasm" => Ok(TrimStartFunc {}),
_ => Err(()),
}
}
}
impl ModuleTranslator for TrimStartFunc {
fn translate_inplace(&self, module: &mut Module) -> Result<bool, ModuleError> {
Ok(self.trim_startfunc(module))
}
fn translate(&self, _module: &Module) -> Result<Option<Module>, ModuleError> {
Err(ModuleError::NotSupported)
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::elements::deserialize_buffer;
#[test]
fn start_removed() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x08, 0x01, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let mut module = deserialize_buffer::<Module>(&wasm).unwrap();
let trimmer = TrimStartFunc::with_preset("ewasm").unwrap();
trimmer.translate_inplace(&mut module).unwrap();
let result = serialize::<Module>(module).unwrap();
let expect: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
assert_eq!(expect, result);
}
#[test]
fn start_not_removed() {
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x07, 0x08, 0x01, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let mut module = deserialize_buffer::<Module>(&wasm).unwrap();
let trimmer = TrimStartFunc::with_preset("ewasm").unwrap();
trimmer.translate_inplace(&mut module).unwrap();
let result = serialize::<Module>(module).unwrap();
// result is equal to initial wasm (not changed)
assert_eq!(result, wasm);
}
}
//! These are helpers to be used internally.
use super::ModuleError;
use parity_wasm::elements::{deserialize_buffer, serialize, Module, Section};
pub trait HasNamesSection {
/// Returns true if the module has a NamesSection.
fn has_names_section(&self) -> bool;
}
impl HasNamesSection for Module {
fn has_names_section(&self) -> bool {
// TODO: move names_section_index_for helper from dropsection and consider upstreaming it
self.sections()
.iter()
.position(|e| {
match e {
// The default case, when the section was not parsed by parity-wasm
Section::Custom(_section) => _section.name() == "name",
// This is the case, when the section was parsed by parity-wasm
Section::Name(_) => true,
_ => false,
}
})
.is_some()
}
}
pub trait SerializationHelpers {
/// Deserialize bytecode to a Module.
fn from_slice(input: &[u8]) -> Module;
/// Serialize Module to bytecode. Serialization consumes the input.
fn to_vec(self) -> Vec<u8>;
}
impl SerializationHelpers for Module {
fn from_slice(input: &[u8]) -> Self {
deserialize_buffer::<Module>(&input).expect("invalid Wasm bytecode")
}
fn to_vec(self) -> Vec<u8> {
serialize::<Module>(self).expect("invalid Wasm module")
}
}
impl From<parity_wasm::SerializationError> for ModuleError {
fn from(a: parity_wasm::SerializationError) -> Self {
use std::error::Error;
ModuleError::Custom(a.description().to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
use rustc_hex::FromHex;
#[test]
fn module_roundtrip() {
let input = FromHex::from_hex(
"0061736d01000000010401600000030201000405017001010105030100100619
037f01418080c0000b7f00418080c0000b7f00418080c0000b072503066d656d
6f727902000b5f5f686561705f6261736503010a5f5f646174615f656e640302
0a040102000b",
)
.unwrap();
let module = Module::from_slice(&input);
let output = module.to_vec();
assert_eq!(input, output);
}
#[test]
fn bytecode_has_names_section() {
let input = FromHex::from_hex(
"0061736d010000000104016000000303020000070801046d61696e00010a
0a020300010b040010000b0014046e616d65010d0200047465737401046d
61696e",
)
.unwrap();
let module = Module::from_slice(&input);
assert_eq!(module.has_names_section(), true);
}
#[test]
fn bytecode_has_no_names_section() {
let input = FromHex::from_hex(
"0061736d010000000104016000000303020000070801046d61696e00010a
0a020300010b040010000b",
)
.unwrap();
let module = Module::from_slice(&input);
assert_eq!(module.has_names_section(), false);
}
fn try_serialize(module: Module) -> Result<Vec<u8>, ModuleError> {
Ok(serialize::<Module>(module)?)
}
fn try_deserialize(input: &[u8]) -> Result<Module, ModuleError> {
Ok(deserialize_buffer::<Module>(&input)?)
}
#[test]
fn test_serialize_error() {
// The failure case.
let module = parity_wasm::builder::module()
.export()
.field("invalid")
.internal()
.func(15)
.build()
.build();
// Shouldn't this one fail due to the invalid function reference?
assert_eq!(try_serialize(module).is_ok(), true);
// The success case.
assert_eq!(try_serialize(Module::default()).is_ok(), true)
}
#[test]
fn test_deserialize_error() {
// The failure case.
assert_eq!(try_deserialize(&[0u8; 0]).is_ok(), false);
// The success case.
let input = FromHex::from_hex(
"0061736d01000000010401600000030201000405017001010105030100100619
037f01418080c0000b7f00418080c0000b7f00418080c0000b072503066d656d
6f727902000b5f5f686561705f6261736503010a5f5f646174615f656e640302
0a040102000b",
)
.unwrap();
assert_eq!(try_deserialize(&input).is_ok(), true)
}
}
use super::{ModuleError, ModulePreset, ModuleValidator};
use parity_wasm::elements::{
ExportSection, External, FunctionSection, FunctionType, ImportSection, Internal, Module, Type,
};
/// Enum representing a type of export and any extra data to check.
pub enum ExportType<'a> {
Function(&'a str, FunctionType),
Global(&'a str),
Memory(&'a str),
Table(&'a str),
}
/// Trait over ExportType that lets a caller check if it is exported in a given module.
trait IsExported {
fn is_exported(&self, module: &Module) -> bool;
}
/// Struct on which ModuleValidator is implemented.
pub struct VerifyExports<'a> {
entries: Vec<ExportType<'a>>,
allow_unlisted: bool,
}
impl<'a> ModulePreset for VerifyExports<'a> {
fn with_preset(preset: &str) -> Result<Self, ()> {
match preset {
"ewasm" => Ok(VerifyExports {
entries: vec![
ExportType::Function("main", FunctionType::default()),
ExportType::Memory("memory"),
],
allow_unlisted: false,
}),
_ => Err(()),
}
}
}
impl<'a> ModuleValidator for VerifyExports<'a> {
fn validate(&self, module: &Module) -> Result<bool, ModuleError> {
// FIXME: This validating algorithm runs in O(n^2). Needs to be optimized
let required_exports_not_found = self
.entries
.iter()
.map(|e| e.is_exported(module))
.find(|e| *e == false)
.is_some();
if required_exports_not_found {
return Ok(false);
}
let module_export_count = if let Some(section) = module.export_section() {
section.entries().len()
} else {
0
};
if self.entries.len() != module_export_count {
Ok(self.allow_unlisted)
} else {
Ok(true)
}
}
}
impl<'a> IsExported for ExportType<'a> {
fn is_exported(&self, module: &Module) -> bool {
if let Some(section) = module.export_section() {
match self {
ExportType::Function(field, sig) => has_func_export(module, field, sig),
ExportType::Global(field) => has_global_export(section, field),
ExportType::Memory(field) => has_memory_export(section, field),
ExportType::Table(field) => has_table_export(section, field),
}
} else {
false
}
}
}
// NOTE: has_*_export is implemented with repeating code because you can't implement a trait for
// enum variants, as they are not types. Furthermore, having one helper for non-func exports would
// be ugly because information about the export type must still be passed down to check that an
// export is of the correct kind.
/// Checks if a global is exported with the given name.
fn has_global_export(section: &ExportSection, field: &str) -> bool {
if let Some(ref export) = section.entries().iter().find(|e| e.field() == field) {
match export.internal() {
Internal::Global(_index) => true,
_ => false,
}
} else {
false
}
}
/// Checks if a memory is exported with the given name.
fn has_memory_export(section: &ExportSection, field: &str) -> bool {
if let Some(ref export) = section.entries().iter().find(|e| e.field() == field) {
match export.internal() {
Internal::Memory(_index) => true,
_ => false,
}
} else {
false
}
}
/// Checks if a table is exported with the given name.
fn has_table_export(section: &ExportSection, field: &str) -> bool {
if let Some(ref export) = section.entries().iter().find(|e| e.field() == field) {
match export.internal() {
Internal::Table(_index) => true,
_ => false,
}
} else {
false
}
}
// NOTE: this is kind of hacked on. It works, but a refactor would make it more in line with the other
// helpers.
/// Checks if a function is exported with the given name.
fn has_func_export(module: &Module, field: &str, sig: &FunctionType) -> bool {
if let Some(section) = module.export_section() {
match func_export_index_by_name(section, field) {
Some(index) => {
if let Some(resolved) = func_sig_by_index(module, index) {
*sig == *resolved
} else {
false
}
}
None => false,
}
} else {
false
}
}
/// Resolves a function's signature from its internal index.
fn func_sig_by_index(module: &Module, index: u32) -> Option<&FunctionType> {
if let Some(func_section) = module.function_section() {
match (module.type_section(), module.import_section()) {
// If we have function imports in the section, subtract them from the function's index
// because their signatures are not in the types section.
(Some(type_section), Some(import_section)) => match type_section.types()[func_type_ref(
&func_section,
index - func_import_section_len(import_section),
)] {
Type::Function(ref ret) => Some(ret),
_ => None,
},
// If no function imports are present, no need to subtract them.
(Some(type_section), None) => {
match type_section.types()[func_type_ref(&func_section, index)] {
Type::Function(ref ret) => Some(ret),
_ => None,
}
}
(None, Some(_import_section)) => None,
(None, None) => None,
}
} else {
None
}
}
/// Returns the internal reference to a function's type signature.
fn func_type_ref(funcs: &FunctionSection, func_index: u32) -> usize {
funcs.entries()[func_index as usize].type_ref() as usize
}
/// Returns the number of functions in the function section that are imported.
fn func_import_section_len(imports: &ImportSection) -> u32 {
imports
.entries()
.iter()
.filter(|e| match e.external() {
External::Function(_) => true,
_ => false,
})
.count() as u32
}
/// Resolves a function export's index by name. Can be trivially adjusted for
/// all types of exports.
fn func_export_index_by_name(exports: &ExportSection, field_str: &str) -> Option<u32> {
if let Some(entry) = exports.entries().iter().find(|e| e.field() == field_str) {
match entry.internal() {
Internal::Function(index) => Some(*index),
_ => None,
}
} else {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::elements::deserialize_buffer;
#[test]
fn no_exports() {
// wast:
// (module)
let wasm: Vec<u8> = vec![0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn all_exports_good_ewasm() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04, 0x6d,
0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn main_export_returns_i32_ewasm() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main (result i32)
// (i32.const 0)
// )
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x00, 0x01,
0x7f, 0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04,
0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02,
0x00, 0x0a, 0x06, 0x01, 0x04, 0x00, 0x41, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn main_export_takes_param_i32_ewasm() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main (param i32))
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x01, 0x7f,
0x00, 0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04,
0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02,
0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn missing_mem_export_ewasm() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x01, 0x7f,
0x00, 0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x08, 0x01, 0x04,
0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn missing_main_export_ewasm() {
// wast:
// (module
// (memory 1)
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x05, 0x01, 0x60, 0x01, 0x7f,
0x00, 0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x0a, 0x01, 0x06,
0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn mem_export_points_to_main_ewasm() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (func $main))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x06, 0x6d,
0x65, 0x6d, 0x6f, 0x72, 0x79, 0x00, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn main_export_points_to_mem_ewasm() {
// wast:
// (module
// (memory 1)
// (export "main" (memory 0))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x06, 0x6d,
0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x02, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn main_export_spelled_wrong_ewasm() {
// wast:
// (module
// (memory 1)
// (export "man" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x10, 0x02, 0x06, 0x6d,
0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x03, 0x6d, 0x61, 0x6e, 0x00, 0x00, 0x0a,
0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn extra_export_disallowed_ewasm() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (export "foo" (func $main))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x1a, 0x03, 0x06, 0x6d,
0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x06, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00,
0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn extra_export_allowed_ewasm() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (export "foo" (func $main))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x1a, 0x03, 0x06, 0x6d,
0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x00,
0x06, 0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x00, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00,
0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyExports {
entries: vec![
ExportType::Function("main", FunctionType::default()),
ExportType::Memory("memory"),
],
allow_unlisted: true,
};
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
}
use super::{
imports::{ImportList, ImportType},
ModuleError, ModulePreset, ModuleValidator,
};
use parity_wasm::elements::{External, FunctionType, ImportSection, Module, Type};
/// Enum representing the state of an import in a module.
#[derive(PartialEq)]
pub enum ImportStatus {
Good,
NotFound,
Malformed,
}
/// Trait over ImportType that lets a caller check if it is imported in a given module, and
/// verifies its type signature is correct.
trait IsImported {
fn is_imported(&self, module: &Module) -> bool;
}
/// Trait over ImportType that checks an import's type signature in the case that it is imported.
trait ImportCheck {
fn check(&self, module: &Module) -> ImportStatus;
}
/// Struct on which ModuleValidator is implemented.
pub struct VerifyImports<'a> {
/// List of function signatures to check.
list: ImportList<'a>,
/// Option to require the presence of all listed imports in the module. When false, only the
/// validity of existing imports on the list is checked.
require_all: bool,
/// Option to allow imports that are not listed in `entries`.
allow_unlisted: bool,
}
impl<'a> ModulePreset for VerifyImports<'a> {
fn with_preset(preset: &str) -> Result<Self, ()> {
let mut import_set = ImportList::new();
let presets: String = preset
.chars()
.filter(|c| *c != ' ' && *c != '_' && *c != '\n' && *c != '\t')
.collect();
for preset_individual in presets.split(',') {
let to_append = ImportList::with_preset(preset_individual).expect("Invalid preset");
import_set.concatenate(to_append);
}
Ok(VerifyImports {
list: import_set,
require_all: false, //FIXME: How should require_all and allow_unlisted be handled in the case of multiple presets?
allow_unlisted: false,
})
}
}
// Utility functions used in tests to get more coverage
#[cfg(test)]
impl<'a> VerifyImports<'a> {
pub fn set_require_all(&mut self, arg: bool) {
self.require_all = arg;
}
pub fn set_allow_unlisted(&mut self, arg: bool) {
self.allow_unlisted = arg;
}
}
impl<'a> ModuleValidator for VerifyImports<'a> {
fn validate(&self, module: &Module) -> Result<bool, ModuleError> {
let import_section_len = if let Some(section) = module.import_section() {
section.entries().len()
} else {
0
};
Ok(match (self.require_all, self.allow_unlisted) {
// Check that all listed imports exist and are correct.
(true, true) => self
.list
.entries()
.iter()
.map(|e| e.is_imported(module))
.find(|e| *e == false)
.is_none(),
// Check that all listed imports exist, are correct, and are the only imports in the
// module.
(true, false) => {
self.list
.entries()
.iter()
.map(|e| e.is_imported(module))
.find(|e| *e == false)
.is_none()
&& (self.list.entries().len() == import_section_len)
}
// Check that the imports which are both listed and imported are of correct type.
(false, true) => self
.list
.entries()
.iter()
.map(|e| e.check(module))
.find(|e| *e == ImportStatus::Malformed)
.is_none(),
(false, false) => {
// Check that all existent imports are listed and correct.
let checklist: Vec<ImportStatus> = self
.list
.entries()
.iter()
.map(|e| e.check(module))
.collect();
let valid_entries_count = checklist
.iter()
.filter(|e| **e == ImportStatus::Good)
.count();
// Proof: If the number of valid entries is equal to the number of existing entries, all
// entries are valid.
//
// If an import entry is valid, it exists; If the number of existent imports is
// equal to the number of valid imports, all existent imports are valid; If all
// existent imports are valid, there are no existent invalid imports; qed
valid_entries_count == import_section_len
}
})
}
}
impl<'a> IsImported for ImportType<'a> {
fn is_imported(&self, module: &Module) -> bool {
if let Some(section) = module.import_section() {
match self {
ImportType::Function(namespace, field, sig) => {
has_func_import(module, namespace, field, sig)
}
ImportType::Global(namespace, field) => {
has_global_import(section, namespace, field)
}
ImportType::Memory(namespace, field) => {
has_memory_import(section, namespace, field)
}
ImportType::Table(namespace, field) => has_table_import(section, namespace, field),
}
} else {
false
}
}
}
impl<'a> ImportCheck for ImportType<'a> {
fn check(&self, module: &Module) -> ImportStatus {
// Destructure self here so that it is easier to manipulate individual fields later.
let (module_str, field_str, func_sig) = match self {
ImportType::Function(namespace, field, sig) => (namespace, field, Some(sig)),
ImportType::Global(namespace, field) => (namespace, field, None),
ImportType::Memory(namespace, field) => (namespace, field, None),
ImportType::Table(namespace, field) => (namespace, field, None),
};
if let Some(section) = module.import_section() {
// Find an entry that matches self. If the name matches, check the namespace and/or
// signature.
if let Some(entry) = section
.entries()
.iter()
.find(|e| e.field() == *field_str && *module_str == e.module())
{
match entry.external() {
External::Function(idx) => {
let sig = func_sig.expect("Function entry missing signature!");
if *sig == imported_func_sig_by_index(module, *idx as usize) {
ImportStatus::Good
} else {
ImportStatus::Malformed
}
}
// NOTE: There may be a better way to do mappings between enum variants.
// Just check import variant here.
External::Global(_idx) => {
if let ImportType::Global(_n, _f) = self {
ImportStatus::Good
} else {
ImportStatus::Malformed
}
}
External::Memory(_idx) => {
if let ImportType::Memory(_n, _f) = self {
ImportStatus::Good
} else {
ImportStatus::Malformed
}
}
External::Table(_idx) => {
if let ImportType::Table(_n, _f) = self {
ImportStatus::Good
} else {
ImportStatus::Malformed
}
}
}
} else {
ImportStatus::NotFound
}
} else {
ImportStatus::NotFound
}
}
}
fn has_global_import(section: &ImportSection, namespace: &str, field: &str) -> bool {
if let Some(import) = section
.entries()
.iter()
.find(|e| e.module() == namespace && e.field() == field)
{
match import.external() {
External::Global(_globaltype) => true,
_ => false,
}
} else {
false
}
}
fn has_memory_import(section: &ImportSection, namespace: &str, field: &str) -> bool {
if let Some(import) = section
.entries()
.iter()
.find(|e| e.module() == namespace && e.field() == field)
{
match import.external() {
External::Memory(_memorytype) => true,
_ => false,
}
} else {
false
}
}
fn has_table_import(section: &ImportSection, namespace: &str, field: &str) -> bool {
if let Some(import) = section
.entries()
.iter()
.find(|e| e.module() == namespace && e.field() == field)
{
match import.external() {
External::Table(_tabletype) => true,
_ => false,
}
} else {
false
}
}
fn has_func_import(module: &Module, namespace: &str, field: &str, sig: &FunctionType) -> bool {
if let Some(section) = module.import_section() {
if let Some(import) = section
.entries()
.iter()
.find(|e| e.module() == namespace && e.field() == field)
{
match import.external() {
External::Function(index) => {
imported_func_sig_by_index(module, *index as usize) == *sig
}
_ => false,
}
} else {
false
}
} else {
false
}
}
/// Resolves an imported function's signature from its callable index.
pub fn imported_func_sig_by_index(module: &Module, index: usize) -> FunctionType {
module.import_section().expect("No function section found");
let type_section = module.type_section().expect("No type section found");
match type_section.types()[index] {
Type::Function(ref func_type) => func_type.clone(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use parity_wasm::elements::{deserialize_buffer, ValueType};
#[test]
fn no_imports_ok_ewasm() {
// wast:
// (module
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x04, 0x01, 0x60, 0x00, 0x00,
0x03, 0x02, 0x01, 0x00, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04, 0x6d,
0x61, 0x69, 0x6e, 0x00, 0x00, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn one_import_ok_ewasm() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32 i32)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x19, 0x01, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x00, 0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07,
0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x01, 0x06, 0x6d, 0x65, 0x6d, 0x6f,
0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn one_import_bad_sig_ewasm() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x08, 0x02, 0x60, 0x01, 0x7f,
0x00, 0x60, 0x00, 0x00, 0x02, 0x19, 0x01, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65,
0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x00, 0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11,
0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x01, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72,
0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn one_import_bad_namespace_ewasm() {
// wast:
// (module
// (import "env" "storageStore" (func $storageStore (param i32 i32)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x14, 0x01, 0x03, 0x65, 0x6e, 0x76, 0x0c, 0x73,
0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x00, 0x00, 0x03,
0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04, 0x6d, 0x61,
0x69, 0x6e, 0x00, 0x01, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a,
0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn one_import_bad_field_ewasm() {
// wast:
// (module
// (import "ethereum" "stoageStore" (func $storageStore (param i32 i32)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x18, 0x01, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0b, 0x73, 0x74, 0x6f, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f, 0x72,
0x65, 0x00, 0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11,
0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x01, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72,
0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn state_test_case_good_ewasm() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32 i32)))
// (memory 1)
// (data (i32.const 0) "\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") ;; Path
// (data (i32.const 32) "\cd\ab\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00") ;; Value
// (export "memory" (memory 0))
// (export "main" (func $main))
// (func $main
// ;; Write to storage
// (call $storageStore (i32.const 0) (i32.const 32))
// )
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x19, 0x01, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x00, 0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07,
0x11, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6d, 0x61,
0x69, 0x6e, 0x00, 0x01, 0x0a, 0x0a, 0x01, 0x08, 0x00, 0x41, 0x00, 0x41, 0x20, 0x10,
0x00, 0x0b, 0x0b, 0x4b, 0x02, 0x00, 0x41, 0x00, 0x0b, 0x20, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x41, 0x20, 0x0b, 0x20, 0xcd, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn unlisted_import_eth_namespace_ewasm() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32 i32)))
// (import "ethereum" "foo" (func $foo))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x2b, 0x02, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x00, 0x00, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x06,
0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x00, 0x01, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03,
0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x02, 0x06,
0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn unlisted_import_eth_namespace_good_ewasm() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32 i32)))
// (import "ethereum" "foo" (func $foo))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x2b, 0x02, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x00, 0x00, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x06,
0x66, 0x6f, 0x6f, 0x62, 0x61, 0x72, 0x00, 0x01, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03,
0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x02, 0x06,
0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let mut checker = VerifyImports::with_preset("ewasm").unwrap();
// Allow unlisted, just for this test case
checker.set_allow_unlisted(true);
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn one_import_but_all_required_ewasm() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32 i32)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x19, 0x01, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x00, 0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07,
0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x01, 0x06, 0x6d, 0x65, 0x6d, 0x6f,
0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let mut checker = VerifyImports::with_preset("ewasm").unwrap();
// Require all, just for this test case
checker.set_require_all(true);
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn all_required_imports() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32 i32)))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x19, 0x01, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x00, 0x00, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07,
0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x01, 0x06, 0x6d, 0x65, 0x6d, 0x6f,
0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports {
list: ImportList::with_entries(vec![ImportType::Function(
"ethereum",
"storageStore",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
)]),
require_all: true,
allow_unlisted: false,
};
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn all_required_imports_but_one_unlisted_same_namespace_ok() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32 i32)))
// (import "ethereum" "foo" (func $foo))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x28, 0x02, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x00, 0x00, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x03,
0x66, 0x6f, 0x6f, 0x00, 0x01, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01,
0x07, 0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x02, 0x06, 0x6d, 0x65, 0x6d,
0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports {
list: ImportList::with_entries(vec![ImportType::Function(
"ethereum",
"storageStore",
FunctionType::new(vec![ValueType::I32, ValueType::I32], None),
)]),
allow_unlisted: true,
require_all: true,
};
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
#[test]
fn all_required_imports_but_one_unlisted_same_namespace() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32 i32)))
// (import "ethereum" "foo" (func $foo))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x28, 0x02, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x00, 0x00, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x03,
0x66, 0x6f, 0x6f, 0x00, 0x01, 0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01,
0x07, 0x11, 0x02, 0x04, 0x6d, 0x61, 0x69, 0x6e, 0x00, 0x02, 0x06, 0x6d, 0x65, 0x6d,
0x6f, 0x72, 0x79, 0x02, 0x00, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports {
list: ImportList::with_preset("ewasm").unwrap(),
allow_unlisted: false,
require_all: true,
};
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn all_required_imports_but_one_unlisted_diff_namespace() {
// wast:
// (module
// (import "ethereum" "storageStore" (func $storageStore (param i32 i32)))
// (import "env" "foo" (func $foo))
// (memory 1)
// (export "main" (func $main))
// (export "memory" (memory 0))
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x09, 0x02, 0x60, 0x02, 0x7f,
0x7f, 0x00, 0x60, 0x00, 0x00, 0x02, 0x23, 0x02, 0x08, 0x65, 0x74, 0x68, 0x65, 0x72,
0x65, 0x75, 0x6d, 0x0c, 0x73, 0x74, 0x6f, 0x72, 0x61, 0x67, 0x65, 0x53, 0x74, 0x6f,
0x72, 0x65, 0x00, 0x00, 0x03, 0x65, 0x6e, 0x76, 0x03, 0x66, 0x6f, 0x6f, 0x00, 0x01,
0x03, 0x02, 0x01, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07, 0x11, 0x02, 0x04, 0x6d,
0x61, 0x69, 0x6e, 0x00, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00,
0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports {
list: ImportList::with_preset("ewasm").unwrap(),
allow_unlisted: false,
require_all: true,
};
let result = checker.validate(&module).unwrap();
assert_eq!(false, result);
}
#[test]
fn verify_with_dynamic_dispatch_before_imports_good() {
// NOTE: This is important for binaries utilizing dynamic dispatch.
// For example, rustc will place a type for vtable lookups before the imports in the type
// section, causing OOB.
// wast:
// (module
// (type (;0;) (func (param i32 i32 i32) (result i32))) ; this is the problem
// (type (;1;) (func (param i64)))
//
// (import "ethereum" "useGas" (func $useGas (type 1)))
//
// (memory 1)
// (export "memory" (memory 0))
// (export "main" (func $main))
//
// (func $main)
// )
let wasm: Vec<u8> = vec![
0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00, 0x01, 0x0f, 0x03, 0x60, 0x03, 0x7f,
0x7f, 0x7f, 0x01, 0x7f, 0x60, 0x01, 0x7e, 0x00, 0x60, 0x00, 0x00, 0x02, 0x13, 0x01,
0x08, 0x65, 0x74, 0x68, 0x65, 0x72, 0x65, 0x75, 0x6d, 0x06, 0x75, 0x73, 0x65, 0x47,
0x61, 0x73, 0x00, 0x01, 0x03, 0x02, 0x01, 0x02, 0x05, 0x03, 0x01, 0x00, 0x01, 0x07,
0x11, 0x02, 0x06, 0x6d, 0x65, 0x6d, 0x6f, 0x72, 0x79, 0x02, 0x00, 0x04, 0x6d, 0x61,
0x69, 0x6e, 0x00, 0x01, 0x0a, 0x04, 0x01, 0x02, 0x00, 0x0b,
];
let module = deserialize_buffer::<Module>(&wasm).unwrap();
let checker = VerifyImports::with_preset("ewasm").unwrap();
let result = checker.validate(&module).unwrap();
assert_eq!(true, result);
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment