diff --git a/.github/workflows/test-sys.yaml b/.github/workflows/test-sys.yaml index 5bf6c867877..84686ad4ac7 100644 --- a/.github/workflows/test-sys.yaml +++ b/.github/workflows/test-sys.yaml @@ -226,7 +226,7 @@ jobs: - name: Install Nightly Rust for Headless uses: actions-rs/toolchain@v1 with: - toolchain: 'nightly-2021-11-01' + toolchain: 'nightly-2022-02-08' target: ${{ matrix.target }} override: true components: "rust-src" diff --git a/Cargo.lock b/Cargo.lock index be39708b755..a6524b9d616 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -60,9 +60,9 @@ checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0" [[package]] name = "arbitrary" -version = "1.0.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510c76ecefdceada737ea728f4f9a84bd2e1ef29f1ba555e560940fe279954de" +checksum = "c38b6b6b79f671c25e1a3e785b7b82d7562ffc9cd3efdc98627e5668a2472490" dependencies = [ "derive_arbitrary", ] @@ -106,15 +106,15 @@ dependencies = [ [[package]] name = "autocfg" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" [[package]] name = "backtrace" -version = "0.3.63" +version = "0.3.64" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321629d8ba6513061f26707241fa9bc89524ff1cd7a915a97ef0c62c666ce1b6" +checksum = "5e121dee8023ce33ab248d9ce1493df03c3b38a659b240096fcbd7048ff9c31f" dependencies = [ "addr2line", "cc", @@ -185,9 +185,9 @@ dependencies = [ [[package]] name = "block-buffer" -version = "0.10.0" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95" +checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324" dependencies = [ "generic-array", ] @@ -309,9 +309,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa66045b9cb23c2e9c1520732030608b02ee07e5cfaa5a521ec15ded7fa24c90" +checksum = "4cc00842eed744b858222c4c9faf7243aafc6d33f92f96935263ef4d8a41ce21" dependencies = [ "glob", "libc", @@ -419,6 +419,19 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "245097e9a4535ee1e3e3931fcfcd55a796a44c643e8596ff6566d68f09b87bbc" +[[package]] +name = "corosensei" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab4b310cff9117ec16d05970743c20df3eaddafd461829f2758e76a8de2863a9" +dependencies = [ + "autocfg", + "cfg-if 1.0.0", + "libc", + "scopeguard", + "windows-sys", +] + [[package]] name = "cranelift-bforest" version = "0.76.0" @@ -443,7 +456,7 @@ dependencies = [ "log", "regalloc", "smallvec", - "target-lexicon 0.12.2", + "target-lexicon 0.12.3", ] [[package]] @@ -478,14 +491,14 @@ dependencies = [ "hashbrown 0.9.1", "log", "smallvec", - "target-lexicon 0.12.2", + "target-lexicon 0.12.3", ] [[package]] name = "crc32fast" -version = "1.3.1" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3" +checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" dependencies = [ "cfg-if 1.0.0", ] @@ -549,9 +562,9 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.6" +version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97242a70df9b89a65d0b6df3c4bf5b9ce03c5b7309019777fbde37e7537f8762" +checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9" dependencies = [ "cfg-if 1.0.0", "crossbeam-utils", @@ -562,9 +575,9 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120" +checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6" dependencies = [ "cfg-if 1.0.0", "lazy_static", @@ -572,9 +585,9 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d6b536309245c849479fba3da410962a43ed8e51c26b729208ec0ac2798d0" +checksum = "a4600d695eb3f6ce1cd44e6e291adceb2cc3ab12f20a33777ecd0bf6eba34e06" dependencies = [ "generic-array", ] @@ -654,9 +667,9 @@ dependencies = [ [[package]] name = "derive_arbitrary" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b24629208e87a2d8b396ff43b15c4afb0a69cea3fbbaa9ed9b92b7c02f0aed73" +checksum = "98e23c06c035dac87bd802d98f368df73a7f2cb05a66ffbd1f377e821fac4af9" dependencies = [ "proc-macro2", "quote", @@ -683,13 +696,12 @@ checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" [[package]] name = "digest" -version = "0.10.1" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b" +checksum = "8cb780dce4f9a8f5c087362b3a4595936b2019e7c8b30f2c3e9a7e94e6ae9837" dependencies = [ "block-buffer", "crypto-common", - "generic-array", "subtle", ] @@ -770,15 +782,6 @@ version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" -[[package]] -name = "encoding_rs" -version = "0.8.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dc8abb250ffdda33912550faa54c88ec8b998dec0b2c55ab224921ce11df" -dependencies = [ - "cfg-if 1.0.0", -] - [[package]] name = "enum-iterator" version = "0.7.0" @@ -889,18 +892,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "flate2" -version = "1.0.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f" -dependencies = [ - "cfg-if 1.0.0", - "crc32fast", - "libc", - "miniz_oxide", -] - [[package]] name = "float-cmp" version = "0.8.0" @@ -1206,9 +1197,9 @@ checksum = "884e2677b40cc8c339eaefcb701c32ef1fd2493d71118dc0ca4b6a736c93bd67" [[package]] name = "libc" -version = "0.2.115" +version = "0.2.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a8d982fa7a96a000f6ec4cfe966de9703eccde29750df2bb8949da91b0e818d" +checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4" [[package]] name = "libfuzzer-sys" @@ -1233,9 +1224,9 @@ dependencies = [ [[package]] name = "llvm-sys" -version = "120.2.2" +version = "120.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4897352ffc39e1b2b3f7078b632222939044b76d3a99d36666c1c47203c104cc" +checksum = "ce76f8393b7a607a906087666db398d872db739622e644e58552c198ccdfdf45" dependencies = [ "cc", "lazy_static", @@ -1246,9 +1237,9 @@ dependencies = [ [[package]] name = "lock_api" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109" +checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b" dependencies = [ "scopeguard", ] @@ -1309,9 +1300,9 @@ checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] name = "memmap2" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe3179b85e1fd8b14447cbebadb75e45a1002f541b925f0bfec366d56a81c56d" +checksum = "057a3db23999c867821a7a59feb06a578fcb03685e983dff90daf9e7d24ac08f" dependencies = [ "libc", ] @@ -1469,9 +1460,9 @@ checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "orbclient" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c976c5018e7f1db4359616d8b31ef8ae7d9649b11803c0b38fff67fd2999fc8" +checksum = "2d3aa1482d3a9cb7547932f54a20910090073e81b3b7b236277c91698a10f83e" dependencies = [ "libc", "raw-window-handle 0.3.4", @@ -1484,9 +1475,9 @@ dependencies = [ [[package]] name = "output_vt100" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53cdc5b785b7a58c5aad8216b3dfa114df64b0b06ae6e1501cef91df2fbdf8f9" +checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ "winapi", ] @@ -1625,9 +1616,9 @@ dependencies = [ [[package]] name = "pretty_assertions" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0cfe1b2403f172ba0f234e500906ee0a3e493fb81092dac23ebefe129301cc" +checksum = "76d5b548b725018ab5496482b45cb8bef21e9fed1858a6d674e3a8a0f0bb5d50" dependencies = [ "ansi_term", "ctor", @@ -1705,14 +1696,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.4" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", "rand_chacha", "rand_core", - "rand_hc", ] [[package]] @@ -1734,15 +1724,6 @@ dependencies = [ "getrandom", ] -[[package]] -name = "rand_hc" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7" -dependencies = [ - "rand_core", -] - [[package]] name = "raw-window-handle" version = "0.3.4" @@ -1940,7 +1921,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" dependencies = [ - "semver 1.0.4", + "semver 1.0.5", ] [[package]] @@ -1990,29 +1971,26 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" [[package]] name = "sdl2" -version = "0.34.5" +version = "0.35.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deecbc3fa9460acff5a1e563e05cb5f31bba0aa0c214bb49a43db8159176d54b" +checksum = "f7959277b623f1fb9e04aea73686c3ca52f01b2145f8ea16f4ff30d8b7623b1a" dependencies = [ "bitflags", "lazy_static", "libc", - "raw-window-handle 0.3.4", + "raw-window-handle 0.4.2", "sdl2-sys", ] [[package]] name = "sdl2-sys" -version = "0.34.5" +version = "0.35.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a29aa21f175b5a41a6e26da572d5e5d1ee5660d35f9f9d0913e8a802098f74" +checksum = "e3586be2cf6c0a8099a79a12b4084357aa9b3e0b0d7980e3b67aaf7a9d55f9f0" dependencies = [ - "cfg-if 0.1.10", + "cfg-if 1.0.0", "cmake", - "flate2", "libc", - "tar", - "unidiff", "version-compare", ] @@ -2042,9 +2020,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012" +checksum = "0486718e92ec9a68fbed73bb5ef687d71103b142595b406835649bebd33f72c7" [[package]] name = "semver-parser" @@ -2102,9 +2080,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.78" +version = "1.0.79" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" +checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95" dependencies = [ "itoa 1.0.1", "ryu", @@ -2292,17 +2270,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tar" -version = "0.4.38" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b55807c0344e1e6c04d7c965f5289c39a8d94ae23ed5c0b57aabac549f871c6" -dependencies = [ - "filetime", - "libc", - "xattr", -] - [[package]] name = "target-lexicon" version = "0.11.2" @@ -2311,9 +2278,9 @@ checksum = "422045212ea98508ae3d28025bc5aaa2bd4a9cdaecd442a08da2ee620ee9ea95" [[package]] name = "target-lexicon" -version = "0.12.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9bffcddbc2458fa3e6058414599e3c838a022abae82e5c67b4f7f80298d5bff" +checksum = "d7fa7e55043acb85fca6b3c01485a2eeb6b69c5d21002e273c79e465f43b7ac1" [[package]] name = "tempfile" @@ -2360,7 +2327,7 @@ name = "test-generator" version = "0.1.0" dependencies = [ "anyhow", - "target-lexicon 0.12.2", + "target-lexicon 0.12.3", ] [[package]] @@ -2484,9 +2451,9 @@ dependencies = [ [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" dependencies = [ "cfg-if 1.0.0", "log", @@ -2497,9 +2464,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716" dependencies = [ "proc-macro2", "quote", @@ -2508,18 +2475,19 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" dependencies = [ "lazy_static", + "valuable", ] [[package]] name = "tracing-subscriber" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5312f325fe3588e277415f5a6cca1f4ccad0f248c4cd5a4bd33032d7286abc22" +checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa" dependencies = [ "lazy_static", "matchers", @@ -2543,9 +2511,9 @@ dependencies = [ [[package]] name = "trybuild" -version = "1.0.54" +version = "1.0.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f04343ff86b62ca40bd5dca986aadf4f10c152a9ebe9a869e456b60fa85afd3f" +checksum = "2d60539445867cdd9680b2bfe2d0428f1814b7d5c9652f09d8d3eae9d19308db" dependencies = [ "glob", "once_cell", @@ -2593,9 +2561,9 @@ checksum = "56dee185309b50d1f11bfedef0fe6d036842e3fb77413abef29f8f8d1c5d4c1c" [[package]] name = "unicode-segmentation" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8895849a949e7845e06bd6dc1aa51731a103c42707010a5b591c0038fb73385b" +checksum = "7e8820f5d777f6224dc4be3632222971ac30164d4a258d595640799554ebfd99" [[package]] name = "unicode-width" @@ -2609,23 +2577,18 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" -[[package]] -name = "unidiff" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8a62719acf1933bfdbeb73a657ecd9ecece70b405125267dd549e2e2edc232c" -dependencies = [ - "encoding_rs", - "lazy_static", - "regex", -] - [[package]] name = "unix_mode" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35abed4630bb800f02451a7428205d1f37b8e125001471bfab259beee6a587ed" +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vec_map" version = "0.8.2" @@ -2634,9 +2597,9 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "version-compare" -version = "0.0.10" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d63556a25bae6ea31b52e640d7c41d1ab27faba4ccb600013837a3d0b3994ca1" +checksum = "fe88247b92c1df6b6de80ddc290f3976dbdf2f5f5d3fd049a9fb598c6dd5ca73" [[package]] name = "version_check" @@ -2804,7 +2767,7 @@ dependencies = [ "js-sys", "loupe", "more-asserts", - "target-lexicon 0.12.2", + "target-lexicon 0.12.3", "tempfile", "thiserror", "wasm-bindgen", @@ -2931,7 +2894,7 @@ dependencies = [ "serde", "serde_bytes", "smallvec", - "target-lexicon 0.12.2", + "target-lexicon 0.12.3", "thiserror", "wasmer-types", "wasmer-vm", @@ -2952,7 +2915,7 @@ dependencies = [ "more-asserts", "rayon", "smallvec", - "target-lexicon 0.12.2", + "target-lexicon 0.12.3", "tracing", "wasmer-compiler", "wasmer-types", @@ -2974,9 +2937,9 @@ dependencies = [ "rayon", "regex", "rustc_version 0.4.0", - "semver 1.0.4", + "semver 1.0.5", "smallvec", - "target-lexicon 0.12.2", + "target-lexicon 0.12.3", "wasmer-compiler", "wasmer-types", "wasmer-vm", @@ -2995,7 +2958,7 @@ dependencies = [ "more-asserts", "rayon", "smallvec", - "target-lexicon 0.12.2", + "target-lexicon 0.12.3", "wasmer-compiler", "wasmer-types", "wasmer-vm", @@ -3039,7 +3002,7 @@ dependencies = [ "rustc-demangle", "serde", "serde_bytes", - "target-lexicon 0.12.2", + "target-lexicon 0.12.3", "thiserror", "wasmer-compiler", "wasmer-types", @@ -3184,14 +3147,17 @@ dependencies = [ "backtrace", "cc", "cfg-if 1.0.0", + "corosensei", "enum-iterator", "indexmap", "libc", "loupe", + "mach", "memoffset", "more-asserts", "region", "rkyv", + "scopeguard", "serde", "thiserror", "wasmer-types", @@ -3487,23 +3453,57 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] -name = "x11-dl" -version = "2.19.1" +name = "windows-sys" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" +checksum = "43dbb096663629518eb1dfa72d80243ca5a6aca764cae62a2df70af760a9be75" dependencies = [ - "lazy_static", - "libc", - "pkg-config", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_msvc", ] [[package]] -name = "xattr" -version = "0.2.2" +name = "windows_aarch64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd761fd3eb9ab8cc1ed81e56e567f02dd82c4c837e48ac3b2181b9ffc5060807" + +[[package]] +name = "windows_i686_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cab0cf703a96bab2dc0c02c0fa748491294bf9b7feb27e1f4f96340f208ada0e" + +[[package]] +name = "windows_i686_msvc" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "244c3741f4240ef46274860397c7c74e50eb23624996930e484c16679633a54c" +checksum = "8cfdbe89cc9ad7ce618ba34abc34bbb6c36d99e96cae2245b7943cd75ee773d0" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4dd9b0c0e9ece7bb22e84d70d01b71c6d6248b81a3c60d11869451b4cb24784" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff1e4aa646495048ec7f3ffddc411e1d829c026a2ec62b39da15c1055e406eaa" + +[[package]] +name = "x11-dl" +version = "2.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea26926b4ce81a6f5d9d0f3a0bc401e5a37c6ae14a1bfaa8ff6099ca80038c59" dependencies = [ + "lazy_static", "libc", + "pkg-config", ] [[package]] diff --git a/lib/api/src/sys/externals/function.rs b/lib/api/src/sys/externals/function.rs index 3146c823854..8271b646a75 100644 --- a/lib/api/src/sys/externals/function.rs +++ b/lib/api/src/sys/externals/function.rs @@ -15,7 +15,7 @@ use std::fmt; use std::sync::Arc; use wasmer_engine::{Export, ExportFunction, ExportFunctionMetadata}; use wasmer_vm::{ - raise_user_trap, resume_panic, wasmer_call_trampoline, ImportInitializerFuncPtr, + on_host_stack, raise_user_trap, resume_panic, wasmer_call_trampoline, ImportInitializerFuncPtr, VMCallerCheckedAnyfunc, VMDynamicFunctionContext, VMFuncRef, VMFunction, VMFunctionBody, VMFunctionEnvironment, VMFunctionKind, VMTrampoline, }; @@ -803,32 +803,34 @@ impl VMDynamicFunctionCall for VMDynamicFunctionContext values_vec: *mut i128, ) { use std::panic::{self, AssertUnwindSafe}; - let result = panic::catch_unwind(AssertUnwindSafe(|| { - let func_ty = self.ctx.function_type(); - let mut args = Vec::with_capacity(func_ty.params().len()); - let store = self.ctx.store(); - for (i, ty) in func_ty.params().iter().enumerate() { - args.push(Val::read_value_from(store, values_vec.add(i), *ty)); - } - let returns = self.ctx.call(&args)?; - - // We need to dynamically check that the returns - // match the expected types, as well as expected length. - let return_types = returns.iter().map(|ret| ret.ty()).collect::>(); - if return_types != func_ty.results() { - return Err(RuntimeError::new(format!( - "Dynamic function returned wrong signature. Expected {:?} but got {:?}", - func_ty.results(), - return_types - ))); - } - for (i, ret) in returns.iter().enumerate() { - ret.write_value_to(values_vec.add(i)); - } - Ok(()) - })); // We get extern ref drops at the end of this block that we don't need. - // By preventing extern ref incs in the code above we can save the work of - // incrementing and decrementing. However the logic as-is is correct. + let result = on_host_stack(|| { + panic::catch_unwind(AssertUnwindSafe(|| { + let func_ty = self.ctx.function_type(); + let mut args = Vec::with_capacity(func_ty.params().len()); + let store = self.ctx.store(); + for (i, ty) in func_ty.params().iter().enumerate() { + args.push(Val::read_value_from(store, values_vec.add(i), *ty)); + } + let returns = self.ctx.call(&args)?; + + // We need to dynamically check that the returns + // match the expected types, as well as expected length. + let return_types = returns.iter().map(|ret| ret.ty()).collect::>(); + if return_types != func_ty.results() { + return Err(RuntimeError::new(format!( + "Dynamic function returned wrong signature. Expected {:?} but got {:?}", + func_ty.results(), + return_types + ))); + } + for (i, ret) in returns.iter().enumerate() { + ret.write_value_to(values_vec.add(i)); + } + Ok(()) + })) // We get extern ref drops at the end of this block that we don't need. + // By preventing extern ref incs in the code above we can save the work of + // incrementing and decrementing. However the logic as-is is correct. + }); match result { Ok(Ok(())) => {} @@ -846,6 +848,7 @@ mod inner { use std::error::Error; use std::marker::PhantomData; use std::panic::{self, AssertUnwindSafe}; + use wasmer_vm::on_host_stack; #[cfg(feature = "experimental-reference-types-extern-ref")] pub use wasmer_types::{ExternRef, VMExternRef}; @@ -1309,9 +1312,11 @@ mod inner { Func: Fn( $( $x ),* ) -> RetsAsResult + 'static { let func: &Func = unsafe { &*(&() as *const () as *const Func) }; - let result = panic::catch_unwind(AssertUnwindSafe(|| { - func( $( FromToNativeWasmType::from_native($x) ),* ).into_result() - })); + let result = on_host_stack(|| { + panic::catch_unwind(AssertUnwindSafe(|| { + func( $( FromToNativeWasmType::from_native($x) ),* ).into_result() + })) + }); match result { Ok(Ok(result)) => return result.into_c_struct(), @@ -1353,9 +1358,11 @@ mod inner { { let func: &Func = unsafe { &*(&() as *const () as *const Func) }; - let result = panic::catch_unwind(AssertUnwindSafe(|| { - func(env, $( FromToNativeWasmType::from_native($x) ),* ).into_result() - })); + let result = on_host_stack(|| { + panic::catch_unwind(AssertUnwindSafe(|| { + func(env, $( FromToNativeWasmType::from_native($x) ),* ).into_result() + })) + }); match result { Ok(Ok(result)) => return result.into_c_struct(), diff --git a/lib/api/src/sys/store.rs b/lib/api/src/sys/store.rs index 29b81e92cc7..6f301a3c76a 100644 --- a/lib/api/src/sys/store.rs +++ b/lib/api/src/sys/store.rs @@ -1,11 +1,10 @@ use crate::sys::tunables::BaseTunables; use loupe::MemoryUsage; -use std::any::Any; use std::fmt; use std::sync::{Arc, RwLock}; #[cfg(all(feature = "compiler", feature = "engine"))] use wasmer_compiler::CompilerConfig; -use wasmer_engine::{is_wasm_pc, Engine, Tunables}; +use wasmer_engine::{Engine, Tunables}; use wasmer_vm::{init_traps, TrapHandler, TrapHandlerFn}; /// The store represents all global state that can be manipulated by @@ -48,7 +47,7 @@ impl Store { { // Make sure the signal handlers are installed. // This is required for handling traps. - init_traps(is_wasm_pc); + init_traps(); Self { engine: engine.cloned(), @@ -82,11 +81,6 @@ impl PartialEq for Store { } unsafe impl TrapHandler for Store { - #[inline] - fn as_any(&self) -> &dyn Any { - self - } - fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool { if let Some(handler) = *&self.trap_handler.read().unwrap().as_ref() { call(handler) diff --git a/lib/compiler-cranelift/src/compiler.rs b/lib/compiler-cranelift/src/compiler.rs index a08ab57668f..a13d3c5b0e2 100644 --- a/lib/compiler-cranelift/src/compiler.rs +++ b/lib/compiler-cranelift/src/compiler.rs @@ -21,7 +21,6 @@ use gimli::write::{Address, EhFrame, FrameTable}; use loupe::MemoryUsage; use rayon::prelude::{IntoParallelRefIterator, ParallelIterator}; use std::sync::Arc; -use target_lexicon::{Architecture, OperatingSystem}; use wasmer_compiler::CompileError; use wasmer_compiler::{CallingConvention, ModuleTranslationState, Target}; use wasmer_compiler::{ @@ -30,13 +29,8 @@ use wasmer_compiler::{ FunctionBodyData, MiddlewareBinaryReader, ModuleMiddleware, ModuleMiddlewareChain, SectionIndex, }; -use wasmer_compiler::{ - CustomSection, CustomSectionProtection, Relocation, RelocationKind, RelocationTarget, - SectionBody, -}; use wasmer_types::entity::{EntityRef, PrimaryMap}; use wasmer_types::{FunctionIndex, LocalFunctionIndex, SignatureIndex}; -use wasmer_vm::libcalls::LibCall; /// A compiler that compiles a WebAssembly module with Cranelift, translating the Wasm to Cranelift IR, /// optimizing it and then translating to assembly. @@ -109,35 +103,6 @@ impl Compiler for CraneliftCompiler { let mut custom_sections = PrimaryMap::new(); - let probestack_trampoline_relocation_target = if target.triple().operating_system - == OperatingSystem::Linux - && target.triple().architecture == Architecture::X86_64 - { - let probestack_trampoline = CustomSection { - protection: CustomSectionProtection::ReadExecute, - // We create a jump to an absolute 64bits address - // with an indrect jump immediatly followed but the absolute address - // JMP [IP+0] FF 25 00 00 00 00 - // 64bits ADDR 00 00 00 00 00 00 00 00 preset to 0 until the relocation takes place - bytes: SectionBody::new_with_vec(vec![ - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, - ]), - relocations: vec![Relocation { - kind: RelocationKind::Abs8, - reloc_target: RelocationTarget::LibCall(LibCall::Probestack), - // 6 is the size of the jmp instruction. The relocated address must follow - offset: 6, - addend: 0, - }], - }; - custom_sections.push(probestack_trampoline); - - Some(SectionIndex::new(custom_sections.len() - 1)) - } else { - None - }; - let (functions, fdes): (Vec, Vec<_>) = function_body_inputs .iter() .collect::)>>() @@ -174,8 +139,7 @@ impl Compiler for CraneliftCompiler { )?; let mut code_buf: Vec = Vec::new(); - let mut reloc_sink = - RelocSink::new(&module, func_index, probestack_trampoline_relocation_target); + let mut reloc_sink = RelocSink::new(&module, func_index); let mut trap_sink = TrapSink::new(); let mut stackmap_sink = binemit::NullStackMapSink {}; context diff --git a/lib/compiler-cranelift/src/sink.rs b/lib/compiler-cranelift/src/sink.rs index 9640e2614f0..dfc803002d0 100644 --- a/lib/compiler-cranelift/src/sink.rs +++ b/lib/compiler-cranelift/src/sink.rs @@ -2,11 +2,9 @@ use crate::translator::{irlibcall_to_libcall, irreloc_to_relocationkind}; use cranelift_codegen::binemit; -use cranelift_codegen::ir::LibCall; use cranelift_codegen::ir::{self, ExternalName}; use cranelift_entity::EntityRef as CraneliftEntityRef; use wasmer_compiler::{JumpTable, Relocation, RelocationTarget, TrapInformation}; -use wasmer_compiler::{RelocationKind, SectionIndex}; use wasmer_types::entity::EntityRef; use wasmer_types::{FunctionIndex, LocalFunctionIndex, ModuleInfo}; use wasmer_vm::TrapCode; @@ -20,9 +18,6 @@ pub(crate) struct RelocSink<'a> { /// Relocations recorded for the function. pub func_relocs: Vec, - - /// The section where the probestack trampoline call is located - pub probestack_trampoline_relocation_target: Option, } impl<'a> binemit::RelocSink for RelocSink<'a> { @@ -42,21 +37,7 @@ impl<'a> binemit::RelocSink for RelocSink<'a> { .expect("The provided function should be local"), ) } else if let ExternalName::LibCall(libcall) = *name { - match (libcall, self.probestack_trampoline_relocation_target) { - (LibCall::Probestack, Some(probestack_trampoline_relocation_target)) => { - self.func_relocs.push(Relocation { - kind: RelocationKind::X86CallPCRel4, - reloc_target: RelocationTarget::CustomSection( - probestack_trampoline_relocation_target, - ), - offset: offset, - addend: addend, - }); - // Skip the default path - return; - } - _ => RelocationTarget::LibCall(irlibcall_to_libcall(libcall)), - } + RelocationTarget::LibCall(irlibcall_to_libcall(libcall)) } else { panic!("unrecognized external name") }; @@ -93,11 +74,7 @@ impl<'a> binemit::RelocSink for RelocSink<'a> { impl<'a> RelocSink<'a> { /// Return a new `RelocSink` instance. - pub fn new( - module: &'a ModuleInfo, - func_index: FunctionIndex, - probestack_trampoline_relocation_target: Option, - ) -> Self { + pub fn new(module: &'a ModuleInfo, func_index: FunctionIndex) -> Self { let local_func_index = module .local_func_index(func_index) .expect("The provided function should be local"); @@ -105,7 +82,6 @@ impl<'a> RelocSink<'a> { module, local_func_index, func_relocs: Vec::new(), - probestack_trampoline_relocation_target, } } } diff --git a/lib/compiler-llvm/src/object_file.rs b/lib/compiler-llvm/src/object_file.rs index fff49d258cb..794cd6a52f7 100644 --- a/lib/compiler-llvm/src/object_file.rs +++ b/lib/compiler-llvm/src/object_file.rs @@ -96,7 +96,6 @@ where libcalls.insert("wasmer_vm_memory32_init".to_string(), LibCall::Memory32Init); libcalls.insert("wasmer_vm_data_drop".to_string(), LibCall::DataDrop); libcalls.insert("wasmer_vm_raise_trap".to_string(), LibCall::RaiseTrap); - libcalls.insert("wasmer_vm_probestack".to_string(), LibCall::Probestack); let elf = object::File::parse(contents).map_err(map_object_err)?; diff --git a/lib/compiler-llvm/src/translator/intrinsics.rs b/lib/compiler-llvm/src/translator/intrinsics.rs index 48f176f45a6..916c741c161 100644 --- a/lib/compiler-llvm/src/translator/intrinsics.rs +++ b/lib/compiler-llvm/src/translator/intrinsics.rs @@ -703,7 +703,7 @@ impl<'ctx> Intrinsics<'ctx> { ), readonly: context .create_enum_attribute(Attribute::get_named_enum_kind_id("readonly"), 0), - stack_probe: context.create_string_attribute("probe-stack", "wasmer_vm_probestack"), + stack_probe: context.create_string_attribute("probe-stack", "inline-asm"), void_ty, i1_ty, diff --git a/lib/compiler-singlepass/src/codegen.rs b/lib/compiler-singlepass/src/codegen.rs index 2e68a8fc0d9..3f28a3be932 100644 --- a/lib/compiler-singlepass/src/codegen.rs +++ b/lib/compiler-singlepass/src/codegen.rs @@ -510,6 +510,18 @@ impl<'a, M: Machine> FuncGen<'a, M> { // Allocate save area, without actually writing to it. static_area_size = self.machine.round_stack_adjust(static_area_size); + + // Stack probe. + // + // `rep stosq` writes data from low address to high address and may skip the stack guard page. + // so here we probe it explicitly when needed. + for i in (sig.params().len()..n) + .step_by(NATIVE_PAGE_SIZE / 8) + .skip(1) + { + self.machine.zero_location(Size::S64, locations[i]); + } + self.machine.adjust_stack(static_area_size as _); // Save callee-saved registers. @@ -586,17 +598,6 @@ impl<'a, M: Machine> FuncGen<'a, M> { Location::GPR(self.machine.get_vmctx_reg()), ); - // Stack probe. - // - // `rep stosq` writes data from low address to high address and may skip the stack guard page. - // so here we probe it explicitly when needed. - for i in (sig.params().len()..n) - .step_by(NATIVE_PAGE_SIZE / 8) - .skip(1) - { - self.machine.zero_location(Size::S64, locations[i]); - } - // Initialize all normal locals to zero. let mut init_stack_loc_cnt = 0; let mut last_stack_loc = Location::Memory(self.machine.local_pointer(), i32::MAX); diff --git a/lib/engine-universal/src/trampoline.rs b/lib/engine-universal/src/trampoline.rs index e90a7c92f98..7c4c62b9729 100644 --- a/lib/engine-universal/src/trampoline.rs +++ b/lib/engine-universal/src/trampoline.rs @@ -23,7 +23,7 @@ const AARCH64_TRAMPOLINE: [u8; 16] = [ // JMP [RIP + 2] FF 25 02 00 00 00 [00 00] // 64-bit ADDR 00 00 00 00 00 00 00 00 const X86_64_TRAMPOLINE: [u8; 16] = [ - 0xff, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xff, 0x25, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, ]; fn make_trampoline( diff --git a/lib/engine/src/artifact.rs b/lib/engine/src/artifact.rs index 0a5c5df1797..9b29f43fb40 100644 --- a/lib/engine/src/artifact.rs +++ b/lib/engine/src/artifact.rs @@ -176,7 +176,7 @@ pub trait Artifact: Send + Sync + Upcastable + MemoryUsage { /// See [`InstanceHandle::finish_instantiation`]. unsafe fn finish_instantiation( &self, - trap_handler: &dyn TrapHandler, + trap_handler: &(dyn TrapHandler + 'static), handle: &InstanceHandle, ) -> Result<(), InstantiationError> { let data_initializers = self diff --git a/lib/engine/src/trap/frame_info.rs b/lib/engine/src/trap/frame_info.rs index 9b7e4df95c5..451fa3f3505 100644 --- a/lib/engine/src/trap/frame_info.rs +++ b/lib/engine/src/trap/frame_info.rs @@ -43,14 +43,6 @@ pub struct GlobalFrameInfo { ranges: BTreeMap, } -/// Returns whether the `pc`, according to globally registered information, -/// is a wasm trap or not. -pub fn is_wasm_pc(pc: usize) -> bool { - let frame_info = FRAME_INFO.read().unwrap(); - let module_info = frame_info.module_info(pc); - module_info.is_some() -} - /// An RAII structure used to unregister a module's frame information when the /// module is destroyed. #[derive(MemoryUsage)] diff --git a/lib/engine/src/trap/mod.rs b/lib/engine/src/trap/mod.rs index ca7b548c690..d1eac6b420b 100644 --- a/lib/engine/src/trap/mod.rs +++ b/lib/engine/src/trap/mod.rs @@ -2,6 +2,6 @@ mod error; mod frame_info; pub use error::RuntimeError; pub use frame_info::{ - is_wasm_pc, register as register_frame_info, FrameInfo, FunctionExtent, - GlobalFrameInfoRegistration, FRAME_INFO, + register as register_frame_info, FrameInfo, FunctionExtent, GlobalFrameInfoRegistration, + FRAME_INFO, }; diff --git a/lib/vfs/src/host_fs.rs b/lib/vfs/src/host_fs.rs index 19475768e51..961f6685be0 100644 --- a/lib/vfs/src/host_fs.rs +++ b/lib/vfs/src/host_fs.rs @@ -205,6 +205,7 @@ pub struct File { #[cfg_attr(feature = "enable-serde", serde(skip_serializing))] pub inner: fs::File, pub host_path: PathBuf, + #[cfg(feature = "enable-serde")] flags: u16, } @@ -303,24 +304,25 @@ impl File { /// creates a new host file from a `std::fs::File` and a path pub fn new(file: fs::File, host_path: PathBuf, read: bool, write: bool, append: bool) -> Self { - let mut flags = 0; + let mut _flags = 0; if read { - flags |= Self::READ; + _flags |= Self::READ; } if write { - flags |= Self::WRITE; + _flags |= Self::WRITE; } if append { - flags |= Self::APPEND; + _flags |= Self::APPEND; } Self { inner: file, host_path, - flags, + #[cfg(feature = "enable-serde")] + _flags, } } diff --git a/lib/vm/Cargo.toml b/lib/vm/Cargo.toml index 84bd4b2493e..ef5a60b8788 100644 --- a/lib/vm/Cargo.toml +++ b/lib/vm/Cargo.toml @@ -24,6 +24,11 @@ serde = { version = "1.0", features = ["derive", "rc"] } rkyv = { version = "0.7.20", optional = true } loupe = { version = "0.1", features = ["enable-indexmap"] } enum-iterator = "0.7.0" +corosensei = { version = "0.1.2" } +scopeguard = "1.1.0" + +[target.'cfg(target_vendor = "apple")'.dependencies] +mach = "0.3.2" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["winbase", "memoryapi", "errhandlingapi"] } diff --git a/lib/vm/build.rs b/lib/vm/build.rs deleted file mode 100644 index 278eb570db7..00000000000 --- a/lib/vm/build.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! Runtime build script compiles C code using setjmp for trap handling. - -use std::env; - -fn main() { - println!("cargo:rerun-if-changed=src/trap/handlers.c"); - - cc::Build::new() - .warnings(true) - .define( - &format!( - "CFG_TARGET_OS_{}", - env::var("CARGO_CFG_TARGET_OS").unwrap().to_uppercase() - ), - None, - ) - .file("src/trap/handlers.c") - .compile("handlers"); -} diff --git a/lib/vm/src/instance/mod.rs b/lib/vm/src/instance/mod.rs index 0db1147af84..4bac14e3a8f 100644 --- a/lib/vm/src/instance/mod.rs +++ b/lib/vm/src/instance/mod.rs @@ -395,7 +395,10 @@ impl Instance { } /// Invoke the WebAssembly start function of the instance, if one is present. - fn invoke_start_function(&self, trap_handler: &dyn TrapHandler) -> Result<(), Trap> { + fn invoke_start_function( + &self, + trap_handler: &(dyn TrapHandler + 'static), + ) -> Result<(), Trap> { let start_index = match self.module.start_function { Some(idx) => idx, None => return Ok(()), @@ -1015,7 +1018,7 @@ impl InstanceHandle { /// Only safe to call immediately after instantiation. pub unsafe fn finish_instantiation( &self, - trap_handler: &dyn TrapHandler, + trap_handler: &(dyn TrapHandler + 'static), data_initializers: &[DataInitializer<'_>], ) -> Result<(), Trap> { let instance = self.instance().as_ref(); diff --git a/lib/vm/src/libcalls.rs b/lib/vm/src/libcalls.rs index 89e7ffd5e25..83149f6ac12 100644 --- a/lib/vm/src/libcalls.rs +++ b/lib/vm/src/libcalls.rs @@ -42,7 +42,7 @@ use crate::probestack::PROBESTACK; use crate::table::{RawTableElement, TableElement}; use crate::trap::{raise_lib_trap, Trap, TrapCode}; use crate::vmcontext::VMContext; -use crate::VMExternRef; +use crate::{on_host_stack, VMExternRef}; use enum_iterator::IntoEnumIterator; use loupe::MemoryUsage; #[cfg(feature = "enable-rkyv")] @@ -155,13 +155,15 @@ pub unsafe extern "C" fn wasmer_vm_memory32_grow( delta: u32, memory_index: u32, ) -> u32 { - let instance = (&*vmctx).instance(); - let memory_index = LocalMemoryIndex::from_u32(memory_index); + on_host_stack(|| { + let instance = (&*vmctx).instance(); + let memory_index = LocalMemoryIndex::from_u32(memory_index); - instance - .memory_grow(memory_index, delta) - .map(|pages| pages.0) - .unwrap_or(u32::max_value()) + instance + .memory_grow(memory_index, delta) + .map(|pages| pages.0) + .unwrap_or(u32::max_value()) + }) } /// Implementation of memory.grow for imported 32-bit memories. @@ -175,13 +177,15 @@ pub unsafe extern "C" fn wasmer_vm_imported_memory32_grow( delta: u32, memory_index: u32, ) -> u32 { - let instance = (&*vmctx).instance(); - let memory_index = MemoryIndex::from_u32(memory_index); + on_host_stack(|| { + let instance = (&*vmctx).instance(); + let memory_index = MemoryIndex::from_u32(memory_index); - instance - .imported_memory_grow(memory_index, delta) - .map(|pages| pages.0) - .unwrap_or(u32::max_value()) + instance + .imported_memory_grow(memory_index, delta) + .map(|pages| pages.0) + .unwrap_or(u32::max_value()) + }) } /// Implementation of memory.size for locally-defined 32-bit memories. @@ -440,18 +444,20 @@ pub unsafe extern "C" fn wasmer_vm_table_grow( delta: u32, table_index: u32, ) -> u32 { - let instance = (&*vmctx).instance(); - let table_index = LocalTableIndex::from_u32(table_index); + on_host_stack(|| { + let instance = (&*vmctx).instance(); + let table_index = LocalTableIndex::from_u32(table_index); - let init_value = match instance.get_local_table(table_index).ty().ty { - Type::ExternRef => TableElement::ExternRef(init_value.extern_ref.into()), - Type::FuncRef => TableElement::FuncRef(init_value.func_ref), - _ => panic!("Unrecognized table type: does not contain references"), - }; + let init_value = match instance.get_local_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(init_value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(init_value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; - instance - .table_grow(table_index, delta, init_value) - .unwrap_or(u32::max_value()) + instance + .table_grow(table_index, delta, init_value) + .unwrap_or(u32::max_value()) + }) } /// Implementation of `table.grow` for imported tables. @@ -466,17 +472,19 @@ pub unsafe extern "C" fn wasmer_vm_imported_table_grow( delta: u32, table_index: u32, ) -> u32 { - let instance = (&*vmctx).instance(); - let table_index = TableIndex::from_u32(table_index); - let init_value = match instance.get_table(table_index).ty().ty { - Type::ExternRef => TableElement::ExternRef(init_value.extern_ref.into()), - Type::FuncRef => TableElement::FuncRef(init_value.func_ref), - _ => panic!("Unrecognized table type: does not contain references"), - }; + on_host_stack(|| { + let instance = (&*vmctx).instance(); + let table_index = TableIndex::from_u32(table_index); + let init_value = match instance.get_table(table_index).ty().ty { + Type::ExternRef => TableElement::ExternRef(init_value.extern_ref.into()), + Type::FuncRef => TableElement::FuncRef(init_value.func_ref), + _ => panic!("Unrecognized table type: does not contain references"), + }; - instance - .imported_table_grow(table_index, delta, init_value) - .unwrap_or(u32::max_value()) + instance + .imported_table_grow(table_index, delta, init_value) + .unwrap_or(u32::max_value()) + }) } /// Implementation of `func.ref`. @@ -517,7 +525,7 @@ pub unsafe extern "C" fn wasmer_vm_externref_inc(externref: VMExternRef) { /// and other serious memory bugs may occur. #[no_mangle] pub unsafe extern "C" fn wasmer_vm_externref_dec(mut externref: VMExternRef) { - externref.ref_drop() + on_host_stack(|| externref.ref_drop()) } /// Implementation of `elem.drop`. @@ -527,9 +535,11 @@ pub unsafe extern "C" fn wasmer_vm_externref_dec(mut externref: VMExternRef) { /// `vmctx` must be dereferenceable. #[no_mangle] pub unsafe extern "C" fn wasmer_vm_elem_drop(vmctx: *mut VMContext, elem_index: u32) { - let elem_index = ElemIndex::from_u32(elem_index); - let instance = (&*vmctx).instance(); - instance.elem_drop(elem_index); + on_host_stack(|| { + let elem_index = ElemIndex::from_u32(elem_index); + let instance = (&*vmctx).instance(); + instance.elem_drop(elem_index); + }) } /// Implementation of `memory.copy` for locally defined memories. @@ -656,9 +666,11 @@ pub unsafe extern "C" fn wasmer_vm_memory32_init( /// `vmctx` must be dereferenceable. #[no_mangle] pub unsafe extern "C" fn wasmer_vm_data_drop(vmctx: *mut VMContext, data_index: u32) { - let data_index = DataIndex::from_u32(data_index); - let instance = (&*vmctx).instance(); - instance.data_drop(data_index) + on_host_stack(|| { + let data_index = DataIndex::from_u32(data_index); + let instance = (&*vmctx).instance(); + instance.data_drop(data_index) + }) } /// Implementation for raising a trap diff --git a/lib/vm/src/trap/handlers.c b/lib/vm/src/trap/handlers.c deleted file mode 100644 index 3273353acc2..00000000000 --- a/lib/vm/src/trap/handlers.c +++ /dev/null @@ -1,66 +0,0 @@ -// This file contains partial code from other sources. -// Attributions: https://github.com/wasmerio/wasmer/blob/master/ATTRIBUTIONS.md - -#include -#include -#if defined(CFG_TARGET_OS_MACOS) -#include -#include -#include -#endif -// Note that `sigsetjmp` and `siglongjmp` are used here where possible to -// explicitly pass a 0 argument to `sigsetjmp` that we don't need to preserve -// the process signal mask. This should make this call a bit faster b/c it -// doesn't need to touch the kernel signal handling routines. -// In case of macOS, stackoverflow -#if defined(CFG_TARGET_OS_WINDOWS) -// On Windows, default setjmp/longjmp sequence will try to unwind the stack -// it's fine most of the time, but not for JIT'd code that may not respect stack ordring -// Using a special setjmp here, with NULL as second parameter to disable that behaviour -// and have a regular simple setjmp/longjmp sequence -#ifdef __MINGW32__ -// MINGW64 doesn't expose the __intrinsic_setjmp function, but a similar _setjump instead -#define platform_setjmp(buf) _setjmp(buf, NULL) -#else -#define platform_setjmp(buf) __intrinsic_setjmp(buf, NULL) -#endif -#define platform_longjmp(buf, arg) longjmp(buf, arg) -#define platform_jmp_buf jmp_buf -#elif defined(CFG_TARGET_OS_MACOS) -// TODO: This is not the most performant, since it adds overhead when calling functions -// https://github.com/wasmerio/wasmer/issues/2562 -#define platform_setjmp(buf) sigsetjmp(buf, 1) -#define platform_longjmp(buf, arg) siglongjmp(buf, arg) -#define platform_jmp_buf sigjmp_buf -#else -#define platform_setjmp(buf) sigsetjmp(buf, 0) -#define platform_longjmp(buf, arg) siglongjmp(buf, arg) -#define platform_jmp_buf sigjmp_buf -#endif - -int wasmer_register_setjmp( - void **buf_storage, - void (*body)(void*), - void *payload) { - #if 0 && defined(CFG_TARGET_OS_MACOS) - // Enable this block to ba able to debug Segfault with lldb - // This will mask the EXC_BAD_ACCESS from lldb... - static int allow_bad_access = 0; - if(!allow_bad_access) { - allow_bad_access = 1; - task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, MACH_PORT_NULL, EXCEPTION_DEFAULT, 0); - } - #endif - platform_jmp_buf buf; - if (platform_setjmp(buf) != 0) { - return 0; - } - *buf_storage = &buf; - body(payload); - return 1; -} - -void wasmer_unwind(void *JmpBuf) { - platform_jmp_buf *buf = (platform_jmp_buf*) JmpBuf; - platform_longjmp(*buf, 1); -} diff --git a/lib/vm/src/trap/mod.rs b/lib/vm/src/trap/mod.rs index 26b6c2a5321..6d406f56a79 100644 --- a/lib/vm/src/trap/mod.rs +++ b/lib/vm/src/trap/mod.rs @@ -8,7 +8,7 @@ mod traphandlers; pub use trapcode::TrapCode; pub use traphandlers::{ - catch_traps, catch_traps_with_result, raise_lib_trap, raise_user_trap, wasmer_call_trampoline, - TlsRestore, Trap, TrapHandler, TrapHandlerFn, + catch_traps, on_host_stack, raise_lib_trap, raise_user_trap, wasmer_call_trampoline, Trap, + TrapHandler, TrapHandlerFn, }; pub use traphandlers::{init_traps, resume_panic}; diff --git a/lib/vm/src/trap/traphandlers.rs b/lib/vm/src/trap/traphandlers.rs index 79660134fac..a7376a832f4 100644 --- a/lib/vm/src/trap/traphandlers.rs +++ b/lib/vm/src/trap/traphandlers.rs @@ -7,14 +7,19 @@ use super::trapcode::TrapCode; use crate::vmcontext::{VMFunctionBody, VMFunctionEnvironment, VMTrampoline}; use backtrace::Backtrace; +use corosensei::trap::{CoroutineTrapHandler, TrapHandlerRegs}; +use corosensei::{CoroutineResult, ScopedCoroutine, Yielder}; +use scopeguard::defer; use std::any::Any; -use std::cell::{Cell, UnsafeCell}; +use std::cell::Cell; use std::error::Error; use std::io; -use std::mem::{self, MaybeUninit}; -use std::ptr; +use std::mem; +#[cfg(unix)] +use std::mem::MaybeUninit; +use std::ptr::{self, NonNull}; +use std::sync::atomic::{compiler_fence, AtomicPtr, Ordering}; use std::sync::Once; -pub use tls::TlsRestore; cfg_if::cfg_if! { if #[cfg(unix)] { @@ -26,13 +31,17 @@ cfg_if::cfg_if! { } } -extern "C" { - fn wasmer_register_setjmp( - jmp_buf: *mut *const u8, - callback: extern "C" fn(*mut u8), - payload: *mut u8, - ) -> i32; - fn wasmer_unwind(jmp_buf: *const u8) -> !; +/// A package of functionality needed by `catch_traps` to figure out what to do +/// when handling a trap. +/// +/// Note that this is an `unsafe` trait at least because it's being run in the +/// context of a synchronous signal handler, so it needs to be careful to not +/// access too much state in answering these queries. +pub unsafe trait TrapHandler { + /// Uses `call` to call a custom signal handler, if one is specified. + /// + /// Returns `true` if `call` returns true, otherwise returns `false`. + fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool; } cfg_if::cfg_if! { @@ -85,31 +94,43 @@ cfg_if::cfg_if! { if cfg!(target_arch = "arm") || cfg!(target_vendor = "apple") { register(&mut PREV_SIGBUS, libc::SIGBUS); } - } - #[cfg(target_vendor = "apple")] - unsafe fn thread_stack() -> (usize, usize) { - let this_thread = libc::pthread_self(); - let stackaddr = libc::pthread_get_stackaddr_np(this_thread); - let stacksize = libc::pthread_get_stacksize_np(this_thread); - (stackaddr as usize - stacksize, stacksize) - } + // This is necessary to support debugging under LLDB on Darwin. + // For more details see https://github.com/mono/mono/commit/8e75f5a28e6537e56ad70bf870b86e22539c2fb7 + #[cfg(target_vendor = "apple")] + { + use mach::exception_types::*; + use mach::kern_return::*; + use mach::port::*; + use mach::thread_status::*; + use mach::traps::*; + use mach::mach_types::*; + + extern "C" { + fn task_set_exception_ports( + task: task_t, + exception_mask: exception_mask_t, + new_port: mach_port_t, + behavior: exception_behavior_t, + new_flavor: thread_state_flavor_t, + ) -> kern_return_t; + } - #[cfg(not(target_vendor = "apple"))] - unsafe fn thread_stack() -> (usize, usize) { - let this_thread = libc::pthread_self(); - let mut thread_attrs: libc::pthread_attr_t = mem::zeroed(); - let mut stackaddr: *mut libc::c_void = ptr::null_mut(); - let mut stacksize: libc::size_t = 0; - #[cfg(not(target_os = "freebsd"))] - let ok = libc::pthread_getattr_np(this_thread, &mut thread_attrs); - #[cfg(target_os = "freebsd")] - let ok = libc::pthread_attr_get_np(this_thread, &mut thread_attrs); - if ok == 0 { - libc::pthread_attr_getstack(&thread_attrs, &mut stackaddr, &mut stacksize); - libc::pthread_attr_destroy(&mut thread_attrs); + #[allow(non_snake_case)] + #[cfg(target_arch = "x86_64")] + let MACHINE_THREAD_STATE = x86_THREAD_STATE64; + #[allow(non_snake_case)] + #[cfg(target_arch = "aarch64")] + let MACHINE_THREAD_STATE = 6; + + task_set_exception_ports( + mach_task_self(), + EXC_MASK_BAD_ACCESS | EXC_MASK_ARITHMETIC, + MACH_PORT_NULL, + EXCEPTION_STATE_IDENTITY as exception_behavior_t, + MACHINE_THREAD_STATE, + ); } - (stackaddr as usize, stacksize) } unsafe extern "C" fn trap_handler( @@ -124,56 +145,23 @@ cfg_if::cfg_if! { libc::SIGILL => &PREV_SIGILL, _ => panic!("unknown signal: {}", signum), }; - // We try to get the Code trap associated to this signal - let maybe_signal_trap = match signum { + // We try to get the fault address associated to this signal + let maybe_fault_address = match signum { libc::SIGSEGV | libc::SIGBUS => { - let addr = (*siginfo).si_addr() as usize; - let (stackaddr, stacksize) = thread_stack(); - // The stack and its guard page covers the - // range [stackaddr - guard pages .. stackaddr + stacksize). - // We assume the guard page is 1 page, and pages are 4KiB (or 16KiB in Apple Silicon) - if stackaddr - region::page::size() <= addr && addr < stackaddr + stacksize { - Some(TrapCode::StackOverflow) - } else { - Some(TrapCode::HeapAccessOutOfBounds) - } + Some((*siginfo).si_addr() as usize) } _ => None, }; - let handled = tls::with(|info| { - // If no wasm code is executing, we don't handle this as a wasm - // trap. - let info = match info { - Some(info) => info, - None => return false, - }; - - // If we hit an exception while handling a previous trap, that's - // quite bad, so bail out and let the system handle this - // recursive segfault. - // - // Otherwise flag ourselves as handling a trap, do the trap - // handling, and reset our trap handling flag. Then we figure - // out what to do based on the result of the trap handling. - let jmp_buf = info.handle_trap( - get_pc(context), - false, - maybe_signal_trap, - |handler| handler(signum, siginfo, context), - ); - // Figure out what to do based on the result of this handling of - // the trap. Note that our sentinel value of 1 means that the - // exception was handled by a custom exception handler, so we - // keep executing. - if jmp_buf.is_null() { - false - } else if jmp_buf as usize == 1 { - true - } else { - wasmer_unwind(jmp_buf) - } - }); + let ucontext = &mut *(context as *mut libc::ucontext_t); + let (pc, sp) = get_pc_sp(ucontext); + let handled = TrapHandlerContext::handle_trap( + pc, + sp, + maybe_fault_address, + |regs| update_context(ucontext, regs), + |handler| handler(signum, siginfo, context), + ); if handled { return; @@ -194,104 +182,177 @@ cfg_if::cfg_if! { usize, extern "C" fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void), >(previous.sa_sigaction)(signum, siginfo, context) - } else if previous.sa_sigaction == libc::SIG_DFL || - previous.sa_sigaction == libc::SIG_IGN + } else if previous.sa_sigaction == libc::SIG_DFL { libc::sigaction(signum, previous, ptr::null_mut()); - } else { + } else if previous.sa_sigaction != libc::SIG_IGN { mem::transmute::( previous.sa_sigaction )(signum) } } - unsafe fn get_pc(cx: *mut libc::c_void) -> *const u8 { + unsafe fn get_pc_sp(context: &libc::ucontext_t) -> (usize, usize) { + let (pc, sp); cfg_if::cfg_if! { - if #[cfg(all(target_os = "linux", target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.gregs[libc::REG_RIP as usize] as *const u8 - } else if #[cfg(all(target_os = "linux", target_arch = "x86"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.gregs[libc::REG_EIP as usize] as *const u8 - } else if #[cfg(all(target_os = "android", target_arch = "x86"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.gregs[libc::REG_EIP as usize] as *const u8 - } else if #[cfg(all(target_os = "linux", target_arch = "aarch64"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.pc as *const u8 - } else if #[cfg(all(target_os = "android", target_arch = "aarch64"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.pc as *const u8 + if #[cfg(all( + any(target_os = "linux", target_os = "android"), + target_arch = "x86_64", + ))] { + pc = context.uc_mcontext.gregs[libc::REG_RIP as usize] as usize; + sp = context.uc_mcontext.gregs[libc::REG_RSP as usize] as usize; + } else if #[cfg(all( + any(target_os = "linux", target_os = "android"), + target_arch = "x86", + ))] { + pc = context.uc_mcontext.gregs[libc::REG_EIP as usize] as usize; + sp = context.uc_mcontext.gregs[libc::REG_ESP as usize] as usize; + } else if #[cfg(all(target_os = "freebsd", target_arch = "x86"))] { + pc = context.uc_mcontext.mc_rip as usize; + sp = context.uc_mcontext.mc_rsp as usize; } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); - (*cx.uc_mcontext).__ss.__rip as *const u8 + pc = (*context.uc_mcontext).__ss.__rip as usize; + sp = (*context.uc_mcontext).__ss.__rsp as usize; + } else if #[cfg(all( + any(target_os = "linux", target_os = "android"), + target_arch = "aarch64", + ))] { + pc = context.uc_mcontext.pc as usize; + sp = context.uc_mcontext.sp as usize; + } else if #[cfg(all( + any(target_os = "linux", target_os = "android"), + target_arch = "arm", + ))] { + pc = context.uc_mcontext.arm_pc as usize; + sp = context.uc_mcontext.arm_sp as usize; + } else if #[cfg(all( + any(target_os = "linux", target_os = "android"), + any(target_arch = "riscv64", target_arch = "riscv32"), + ))] { + pc = context.uc_mcontext.__gregs[libc::REG_PC] as usize; + sp = context.uc_mcontext.__gregs[libc::REG_SP] as usize; } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] { - use std::mem; - // TODO: This should be integrated into rust/libc - // Related issue: https://github.com/rust-lang/libc/issues/1977 - #[allow(non_camel_case_types)] - pub struct __darwin_arm_thread_state64 { - pub __x: [u64; 29], /* General purpose registers x0-x28 */ - pub __fp: u64, /* Frame pointer x29 */ - pub __lr: u64, /* Link register x30 */ - pub __sp: u64, /* Stack pointer x31 */ - pub __pc: u64, /* Program counter */ - pub __cpsr: u32, /* Current program status register */ - pub __pad: u32, /* Same size for 32-bit or 64-bit clients */ - } + pc = (*context.uc_mcontext).__ss.__pc as usize; + sp = (*context.uc_mcontext).__ss.__sp as usize; + } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] { + pc = context.uc_mcontext.mc_gpregs.gp_elr as usize; + sp = context.uc_mcontext.mc_gpregs.gp_sp as usize; + } else { + compile_error!("Unsupported platform"); + } + }; + (pc, sp) + } - let cx = &*(cx as *const libc::ucontext_t); - let uc_mcontext = mem::transmute::<_, *const __darwin_arm_thread_state64>(&(*cx.uc_mcontext).__ss); - (*uc_mcontext).__pc as *const u8 + unsafe fn update_context(context: &mut libc::ucontext_t, regs: TrapHandlerRegs) { + cfg_if::cfg_if! { + if #[cfg(all( + any(target_os = "linux", target_os = "android"), + target_arch = "x86_64", + ))] { + let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs; + context.uc_mcontext.gregs[libc::REG_RIP as usize] = rip as i64; + context.uc_mcontext.gregs[libc::REG_RSP as usize] = rsp as i64; + context.uc_mcontext.gregs[libc::REG_RBP as usize] = rbp as i64; + context.uc_mcontext.gregs[libc::REG_RDI as usize] = rdi as i64; + context.uc_mcontext.gregs[libc::REG_RSI as usize] = rsi as i64; + } else if #[cfg(all( + any(target_os = "linux", target_os = "android"), + target_arch = "x86", + ))] { + let TrapHandlerRegs { eip, esp, ebp, ecx, edx } = regs; + context.uc_mcontext.gregs[libc::REG_EIP as usize] = eip as i32; + context.uc_mcontext.gregs[libc::REG_ESP as usize] = esp as i32; + context.uc_mcontext.gregs[libc::REG_EBP as usize] = ebp as i32; + context.uc_mcontext.gregs[libc::REG_ECX as usize] = ecx as i32; + context.uc_mcontext.gregs[libc::REG_EDX as usize] = edx as i32; + } else if #[cfg(all(target_vendor = "apple", target_arch = "x86_64"))] { + let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs; + (*context.uc_mcontext).__ss.__rip = rip; + (*context.uc_mcontext).__ss.__rsp = rsp; + (*context.uc_mcontext).__ss.__rbp = rbp; + (*context.uc_mcontext).__ss.__rdi = rdi; + (*context.uc_mcontext).__ss.__rsi = rsi; } else if #[cfg(all(target_os = "freebsd", target_arch = "x86_64"))] { - let cx = &*(cx as *const libc::ucontext_t); - cx.uc_mcontext.mc_rip as *const u8 - } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] { - #[repr(align(16))] - #[allow(non_camel_case_types)] - pub struct gpregs { - pub gp_x: [libc::register_t; 30], - pub gp_lr: libc::register_t, - pub gp_sp: libc::register_t, - pub gp_elr: libc::register_t, - pub gp_spsr: u32, - pub gp_pad: libc::c_int, - }; - #[repr(align(16))] - #[allow(non_camel_case_types)] - pub struct fpregs { - pub fp_q: [u128; 32], - pub fp_sr: u32, - pub fp_cr: u32, - pub fp_flags: libc::c_int, - pub fp_pad: libc::c_int, - }; - #[repr(align(16))] - #[allow(non_camel_case_types)] - pub struct mcontext_t { - pub mc_gpregs: gpregs, - pub mc_fpregs: fpregs, - pub mc_flags: libc::c_int, - pub mc_pad: libc::c_int, - pub mc_spare: [u64; 8], - }; - #[repr(align(16))] - #[allow(non_camel_case_types)] - pub struct ucontext_t { - pub uc_sigmask: libc::sigset_t, - pub uc_mcontext: mcontext_t, - pub uc_link: *mut ucontext_t, - pub uc_stack: libc::stack_t, - pub uc_flags: libc::c_int, - __spare__: [libc::c_int; 4], + let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs; + context.uc_mcontext.mc_rip = rip as libc::register_t; + context.uc_mcontext.mc_rsp = rsp as libc::register_t; + context.uc_mcontext.mc_rbp = rbp as libc::register_t; + context.uc_mcontext.mc_rdi = rdi as libc::register_t; + context.uc_mcontext.mc_rsi = rsi as libc::register_t; + } else if #[cfg(all( + any(target_os = "linux", target_os = "android"), + target_arch = "aarch64", + ))] { + let TrapHandlerRegs { pc, sp, x0, x1, x29, lr } = regs; + context.uc_mcontext.pc = pc; + context.uc_mcontext.sp = sp; + context.uc_mcontext.regs[0] = x0; + context.uc_mcontext.regs[1] = x1; + context.uc_mcontext.regs[29] = x29; + context.uc_mcontext.regs[30] = lr; + } else if #[cfg(all( + any(target_os = "linux", target_os = "android"), + target_arch = "arm", + ))] { + let TrapHandlerRegs { + pc, + r0, + r1, + r7, + r11, + r13, + r14, + cpsr_thumb, + cpsr_endian, + } = regs; + context.uc_mcontext.arm_pc = pc; + context.uc_mcontext.arm_r0 = r0; + context.uc_mcontext.arm_r1 = r1; + context.uc_mcontext.arm_r7 = r7; + context.uc_mcontext.arm_fp = r11; + context.uc_mcontext.arm_sp = r13; + context.uc_mcontext.arm_lr = r14; + if cpsr_thumb { + context.uc_mcontext.arm_cpsr |= 0x20; + } else { + context.uc_mcontext.arm_cpsr &= !0x20; } - - let cx = &*(cx as *const ucontext_t); - cx.uc_mcontext.mc_gpregs.gp_elr as *const u8 + if cpsr_endian { + context.uc_mcontext.arm_cpsr |= 0x200; + } else { + context.uc_mcontext.arm_cpsr &= !0x200; + } + } else if #[cfg(all( + any(target_os = "linux", target_os = "android"), + any(target_arch = "riscv64", target_arch = "riscv32"), + ))] { + let TrapHandlerRegs { pc, ra, sp, a0, a1, s0 } = regs; + context.uc_mcontext.__gregs[libc::REG_PC] = pc as libc::c_ulong; + context.uc_mcontext.__gregs[libc::REG_RA] = ra as libc::c_ulong; + context.uc_mcontext.__gregs[libc::REG_SP] = sp as libc::c_ulong; + context.uc_mcontext.__gregs[libc::REG_A0] = a0 as libc::c_ulong; + context.uc_mcontext.__gregs[libc::REG_A0 + 1] = a1 as libc::c_ulong; + context.uc_mcontext.__gregs[libc::REG_S0] = s0 as libc::c_ulong; + } else if #[cfg(all(target_vendor = "apple", target_arch = "aarch64"))] { + let TrapHandlerRegs { pc, sp, x0, x1, x29, lr } = regs; + (*context.uc_mcontext).__ss.__pc = pc; + (*context.uc_mcontext).__ss.__sp = sp; + (*context.uc_mcontext).__ss.__x[0] = x0; + (*context.uc_mcontext).__ss.__x[1] = x1; + (*context.uc_mcontext).__ss.__fp = x29; + (*context.uc_mcontext).__ss.__lr = lr; + } else if #[cfg(all(target_os = "freebsd", target_arch = "aarch64"))] { + context.uc_mcontext.mc_gpregs.gp_pc = pc as libc::register_t; + context.uc_mcontext.mc_gpregs.gp_sp = sp as libc::register_t; + context.uc_mcontext.mc_gpregs.gp_x[0] = x0 as libc::register_t; + context.uc_mcontext.mc_gpregs.gp_x[1] = x1 as libc::register_t; + context.uc_mcontext.mc_gpregs.gp_x[29] = x29 as libc::register_t; + context.uc_mcontext.mc_gpregs.gp_x[30] = lr as libc::register_t; } else { - compile_error!("unsupported platform"); + compile_error!("Unsupported platform"); } - } + }; } } else if #[cfg(target_os = "windows")] { use winapi::um::errhandlingapi::*; @@ -336,45 +397,73 @@ cfg_if::cfg_if! { // return EXCEPTION_CONTINUE_SEARCH; // } + let context = &mut *(*exception_info).ContextRecord; + let (pc, sp) = get_pc_sp(context); + + // We try to get the fault address associated to this exception. + let maybe_fault_address = match record.ExceptionCode { + EXCEPTION_ACCESS_VIOLATION => Some(record.ExceptionInformation[1]), + EXCEPTION_STACK_OVERFLOW => Some(sp), + _ => None, + }; + // This is basically the same as the unix version above, only with a // few parameters tweaked here and there. - tls::with(|info| { - let info = match info { - Some(info) => info, - None => return EXCEPTION_CONTINUE_SEARCH, - }; - #[cfg(target_pointer_width = "32")] - let pc = (*(*exception_info).ContextRecord).Eip as *const u8; - - #[cfg(target_pointer_width = "64")] - let pc = (*(*exception_info).ContextRecord).Rip as *const u8; - - let jmp_buf = info.handle_trap( - pc, - record.ExceptionCode == EXCEPTION_STACK_OVERFLOW, - // TODO: fix the signal trap associated to memory access in Windows - None, - |handler| handler(exception_info), - ); - if jmp_buf.is_null() { - EXCEPTION_CONTINUE_SEARCH - } else if jmp_buf as usize == 1 { - EXCEPTION_CONTINUE_EXECUTION + let handled = TrapHandlerContext::handle_trap( + pc, + sp, + maybe_fault_address, + |regs| update_context(context, regs), + |handler| handler(exception_info), + ); + + if handled { + EXCEPTION_CONTINUE_EXECUTION + } else { + EXCEPTION_CONTINUE_SEARCH + } + } + + unsafe fn get_pc_sp(context: &CONTEXT) -> (usize, usize) { + let (pc, sp); + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86_64")] { + pc = context.Rip as usize; + sp = context.Rsp as usize; + } else if #[cfg(target_arch = "x86")] { + pc = context.Rip as usize; + sp = context.Rsp as usize; } else { - wasmer_unwind(jmp_buf) + compile_error!("Unsupported platform"); } - }) + }; + (pc, sp) + } + + unsafe fn update_context(context: &mut CONTEXT, regs: TrapHandlerRegs) { + cfg_if::cfg_if! { + if #[cfg(target_arch = "x86_64")] { + let TrapHandlerRegs { rip, rsp, rbp, rdi, rsi } = regs; + context.Rip = rip; + context.Rsp = rsp; + context.Rbp = rbp; + context.Rdi = rdi; + context.Rsi = rsi; + } else if #[cfg(target_arch = "x86")] { + let TrapHandlerRegs { eip, esp, ebp, ecx, edx } = regs; + context.Eip = eip; + context.Esp = esp; + context.Ebp = ebp; + context.Ecx = ecx; + context.Edx = edx; + } else { + compile_error!("Unsupported platform"); + } + }; } } } -/// Globally-set callback to determine whether a program counter is actually a -/// wasm trap. -/// -/// This is initialized during `init_traps` below. The definition lives within -/// `wasmer` currently. -static mut IS_WASM_PC: fn(usize) -> bool = |_| false; - /// This function is required to be called before any WebAssembly is entered. /// This will configure global state such as signal handlers to prepare the /// process to receive wasm traps. @@ -383,15 +472,9 @@ static mut IS_WASM_PC: fn(usize) -> bool = |_| false; /// WebAssembly but it must also be called once-per-thread that enters /// WebAssembly. Currently in wasmer's integration this function is called on /// creation of a `Store`. -/// -/// The `is_wasm_pc` argument is used when a trap happens to determine if a -/// program counter is the pc of an actual wasm trap or not. This is then used -/// to disambiguate faults that happen due to wasm and faults that happen due to -/// bugs in Rust or elsewhere. -pub fn init_traps(is_wasm_pc: fn(usize) -> bool) { +pub fn init_traps() { static INIT: Once = Once::new(); INIT.call_once(|| unsafe { - IS_WASM_PC = is_wasm_pc; platform_init(); }); } @@ -409,7 +492,7 @@ pub fn init_traps(is_wasm_pc: fn(usize) -> bool) { /// Additionally no Rust destructors may be on the stack. /// They will be skipped and not executed. pub unsafe fn raise_user_trap(data: Box) -> ! { - tls::with(|info| info.unwrap().unwind_with(UnwindReason::UserTrap(data))) + unwind_with(UnwindReason::UserTrap(data)) } /// Raises a trap from inside library code immediately. @@ -424,7 +507,7 @@ pub unsafe fn raise_user_trap(data: Box) -> ! { /// Additionally no Rust destructors may be on the stack. /// They will be skipped and not executed. pub unsafe fn raise_lib_trap(trap: Trap) -> ! { - tls::with(|info| info.unwrap().unwind_with(UnwindReason::LibTrap(trap))) + unwind_with(UnwindReason::LibTrap(trap)) } /// Carries a Rust panic across wasm code and resumes the panic on the other @@ -436,25 +519,9 @@ pub unsafe fn raise_lib_trap(trap: Trap) -> ! { /// have been previously called and not returned. Additionally no Rust destructors may be on the /// stack. They will be skipped and not executed. pub unsafe fn resume_panic(payload: Box) -> ! { - tls::with(|info| info.unwrap().unwind_with(UnwindReason::Panic(payload))) + unwind_with(UnwindReason::Panic(payload)) } -#[cfg(target_os = "windows")] -fn reset_guard_page() { - extern "C" { - fn _resetstkoflw() -> winapi::ctypes::c_int; - } - - // We need to restore guard page under stack to handle future stack overflows properly. - // https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/resetstkoflw?view=vs-2019 - if unsafe { _resetstkoflw() } == 0 { - panic!("failed to restore stack guard page"); - } -} - -#[cfg(not(target_os = "windows"))] -fn reset_guard_page() {} - /// Stores trace message with backtrace. #[derive(Debug)] pub enum Trap { @@ -540,7 +607,7 @@ impl Trap { /// Wildly unsafe because it calls raw function pointers and reads/writes raw /// function pointers. pub unsafe fn wasmer_call_trampoline( - trap_handler: &impl TrapHandler, + trap_handler: &(impl TrapHandler + 'static), vmctx: VMFunctionEnvironment, trampoline: VMTrampoline, callee: *const VMFunctionBody, @@ -557,74 +624,167 @@ pub unsafe fn wasmer_call_trampoline( /// returning them as a `Result`. /// /// Highly unsafe since `closure` won't have any dtors run. -pub unsafe fn catch_traps(trap_handler: &dyn TrapHandler, mut closure: F) -> Result<(), Trap> +pub unsafe fn catch_traps( + trap_handler: &(dyn TrapHandler + 'static), + closure: F, +) -> Result where - F: FnMut(), + F: FnOnce() -> R, { - return CallThreadState::new(trap_handler).with(|cx| { - wasmer_register_setjmp( - cx.jmp_buf.as_ptr(), - call_closure::, - &mut closure as *mut F as *mut u8, - ) - }); + // Ensure that per-thread initialization is done. + lazy_per_thread_init()?; - extern "C" fn call_closure(payload: *mut u8) - where - F: FnMut(), - { - unsafe { (*(payload as *mut F))() } - } + on_wasm_stack(trap_handler, closure).map_err(UnwindReason::to_trap) } -/// Catches any wasm traps that happen within the execution of `closure`, -/// returning them as a `Result`, with the closure contents. -/// -/// The main difference from this method and `catch_traps`, is that is able -/// to return the results from the closure. -/// -/// # Safety -/// -/// Check [`catch_traps`]. -pub unsafe fn catch_traps_with_result( - trap_handler: &dyn TrapHandler, - mut closure: F, -) -> Result -where - F: FnMut() -> R, -{ - let mut global_results = MaybeUninit::::uninit(); - catch_traps(trap_handler, || { - global_results.as_mut_ptr().write(closure()); - })?; - Ok(global_results.assume_init()) +// We need two separate thread-local variables here: +// - YIELDER is set within the new stack and is used to unwind back to the root +// of the stack from inside it. +// - TRAP_HANDLER is set from outside the new stack and is solely used from +// signal handlers. It must be atomic since it is used by signal handlers. +// +// We also do per-thread signal stack initialization on the first time +// TRAP_HANDLER is accessed. +thread_local! { + static YIELDER: Cell>>> = Cell::new(None); + static TRAP_HANDLER: AtomicPtr = AtomicPtr::new(ptr::null_mut()); } -/// Temporary state stored on the stack which is registered in the `tls` module -/// below for calls into wasm. -pub struct CallThreadState<'a> { - unwind: UnsafeCell>, - jmp_buf: Cell<*const u8>, - reset_guard_page: Cell, - prev: Cell, - trap_handler: &'a (dyn TrapHandler + 'a), - handling_trap: Cell, +/// Read-only information that is used by signal handlers to handle and recover +/// from traps. +struct TrapHandlerContext { + inner: *const u8, + handle_trap: + fn(*const u8, usize, usize, Option, &mut dyn FnMut(TrapHandlerRegs)) -> bool, + custom_trap: *const dyn TrapHandler, +} +struct TrapHandlerContextInner { + /// Information about the currently running coroutine. This is used to + /// reset execution to the root of the coroutine when a trap is handled. + coro_trap_handler: CoroutineTrapHandler>, } -/// A package of functionality needed by `catch_traps` to figure out what to do -/// when handling a trap. -/// -/// Note that this is an `unsafe` trait at least because it's being run in the -/// context of a synchronous signal handler, so it needs to be careful to not -/// access too much state in answering these queries. -pub unsafe trait TrapHandler { - /// Converts this object into an `Any` to dynamically check its type. - fn as_any(&self) -> &dyn Any; +impl TrapHandlerContext { + /// Runs the given function with a trap handler context. The previous + /// trap handler context is preserved and restored afterwards. + fn install( + custom_trap: &(dyn TrapHandler + 'static), + coro_trap_handler: CoroutineTrapHandler>, + f: impl FnOnce() -> R, + ) -> R { + // Type-erase the trap handler function so that it can be placed in TLS. + fn func( + ptr: *const u8, + pc: usize, + sp: usize, + maybe_fault_address: Option, + update_regs: &mut dyn FnMut(TrapHandlerRegs), + ) -> bool { + unsafe { + (*(ptr as *const TrapHandlerContextInner)).handle_trap( + pc, + sp, + maybe_fault_address, + update_regs, + ) + } + } + let inner = TrapHandlerContextInner { coro_trap_handler }; + let ctx = TrapHandlerContext { + inner: &inner as *const _ as *const u8, + handle_trap: func::, + custom_trap: custom_trap, + }; - /// Uses `call` to call a custom signal handler, if one is specified. - /// - /// Returns `true` if `call` returns true, otherwise returns `false`. - fn custom_trap_handler(&self, call: &dyn Fn(&TrapHandlerFn) -> bool) -> bool; + compiler_fence(Ordering::Release); + let prev = TRAP_HANDLER.with(|ptr| { + let prev = ptr.load(Ordering::Relaxed); + ptr.store( + &ctx as *const TrapHandlerContext as *mut TrapHandlerContext, + Ordering::Relaxed, + ); + prev + }); + + defer! { + TRAP_HANDLER.with(|ptr| ptr.store(prev, Ordering::Relaxed)); + compiler_fence(Ordering::Acquire); + } + + f() + } + + /// Attempts to handle the trap if it's a wasm trap. + unsafe fn handle_trap( + pc: usize, + sp: usize, + maybe_fault_address: Option, + mut update_regs: impl FnMut(TrapHandlerRegs), + call_handler: impl Fn(&TrapHandlerFn) -> bool, + ) -> bool { + let ptr = TRAP_HANDLER.with(|ptr| ptr.load(Ordering::Relaxed)); + if ptr.is_null() { + return false; + } + + let ctx = &*ptr; + + // Check if this trap is handled by a custom trap handler. + if (*ctx.custom_trap).custom_trap_handler(&call_handler) { + return true; + } + + (ctx.handle_trap)(ctx.inner, pc, sp, maybe_fault_address, &mut update_regs) + } +} + +impl TrapHandlerContextInner { + unsafe fn handle_trap( + &self, + pc: usize, + sp: usize, + maybe_fault_address: Option, + update_regs: &mut dyn FnMut(TrapHandlerRegs), + ) -> bool { + // Check if this trap occurred while executing on the Wasm stack. We can + // only recover from traps if that is the case. + if !self.coro_trap_handler.stack_ptr_in_bounds(sp) { + return false; + } + + let signal_trap = maybe_fault_address.map(|addr| { + if self.coro_trap_handler.stack_ptr_in_bounds(addr) { + TrapCode::StackOverflow + } else { + TrapCode::HeapAccessOutOfBounds + } + }); + + // Don't try to generate a backtrace for stack overflows: unwinding + // information is often not precise enough to properly describe what is + // happenning during a function prologue, which can lead the unwinder to + // read invalid memory addresses. + // + // See: https://github.com/rust-lang/backtrace-rs/pull/357 + let backtrace = if signal_trap == Some(TrapCode::StackOverflow) { + Backtrace::from(vec![]) + } else { + Backtrace::new_unresolved() + }; + + // Set up the register state for exception return to force the + // coroutine to return to its caller with UnwindReason::WasmTrap. + let unwind = UnwindReason::WasmTrap { + backtrace, + signal_trap, + pc, + }; + let regs = self + .coro_trap_handler + .setup_trap_handler(move || Err(unwind)); + update_regs(regs); + true + } } enum UnwindReason { @@ -642,266 +802,113 @@ enum UnwindReason { }, } -impl<'a> CallThreadState<'a> { - #[inline] - fn new(trap_handler: &'a (dyn TrapHandler + 'a)) -> CallThreadState<'a> { - Self { - unwind: UnsafeCell::new(MaybeUninit::uninit()), - jmp_buf: Cell::new(ptr::null()), - reset_guard_page: Cell::new(false), - prev: Cell::new(ptr::null()), - trap_handler, - handling_trap: Cell::new(false), - } - } - - fn with(self, closure: impl FnOnce(&CallThreadState) -> i32) -> Result<(), Trap> { - let ret = tls::set(&self, || closure(&self))?; - if ret != 0 { - return Ok(()); - } - // We will only reach this path if ret == 0. And that will - // only happen if a trap did happen. As such, it's safe to - // assume that the `unwind` field is already initialized - // at this moment. - match unsafe { (*self.unwind.get()).as_ptr().read() } { - UnwindReason::UserTrap(data) => Err(Trap::User(data)), - UnwindReason::LibTrap(trap) => Err(trap), +impl UnwindReason { + fn to_trap(self) -> Trap { + match self { + UnwindReason::UserTrap(data) => Trap::User(data), + UnwindReason::LibTrap(trap) => trap, UnwindReason::WasmTrap { backtrace, pc, signal_trap, - } => Err(Trap::wasm(pc, backtrace, signal_trap)), + } => Trap::wasm(pc, backtrace, signal_trap), UnwindReason::Panic(panic) => std::panic::resume_unwind(panic), } } - - fn unwind_with(&self, reason: UnwindReason) -> ! { - unsafe { - (*self.unwind.get()).as_mut_ptr().write(reason); - wasmer_unwind(self.jmp_buf.get()); - } - } - - /// Trap handler using our thread-local state. - /// - /// * `pc` - the program counter the trap happened at - /// * `reset_guard_page` - whether or not to reset the guard page, - /// currently Windows specific - /// * `call_handler` - a closure used to invoke the platform-specific - /// signal handler for each instance, if available. - /// - /// Attempts to handle the trap if it's a wasm trap. Returns a few - /// different things: - /// - /// * null - the trap didn't look like a wasm trap and should continue as a - /// trap - /// * 1 as a pointer - the trap was handled by a custom trap handler on an - /// instance, and the trap handler should quickly return. - /// * a different pointer - a jmp_buf buffer to longjmp to, meaning that - /// the wasm trap was succesfully handled. - fn handle_trap( - &self, - pc: *const u8, - reset_guard_page: bool, - signal_trap: Option, - call_handler: impl Fn(&TrapHandlerFn) -> bool, - ) -> *const u8 { - // If we hit a fault while handling a previous trap, that's quite bad, - // so bail out and let the system handle this recursive segfault. - // - // Otherwise flag ourselves as handling a trap, do the trap handling, - // and reset our trap handling flag. - if self.handling_trap.replace(true) { - return ptr::null(); - } - - // First up see if we have a custom trap handler, - // in which case run it. If anything handles the trap then we - // return that the trap was handled. - if self.trap_handler.custom_trap_handler(&call_handler) { - return 1 as *const _; - } - - // If this fault wasn't in wasm code, then it's not our problem - // except if it's a StackOverflow (see below) - if unsafe { !IS_WASM_PC(pc as _) } && signal_trap != Some(TrapCode::StackOverflow) { - return ptr::null(); - } - - // TODO: stack overflow can happen at any random time (i.e. in malloc() - // in memory.grow) and it's really hard to determine if the cause was - // stack overflow and if it happened in WebAssembly module. - // - // So, let's assume that any untrusted code called from WebAssembly - // doesn't trap. Then, if we have called some WebAssembly code, it - // means the trap is stack overflow. - if self.jmp_buf.get().is_null() { - self.handling_trap.set(false); - return ptr::null(); - } - let backtrace = Backtrace::new_unresolved(); - self.reset_guard_page.set(reset_guard_page); - unsafe { - (*self.unwind.get()) - .as_mut_ptr() - .write(UnwindReason::WasmTrap { - backtrace, - signal_trap, - pc: pc as usize, - }); - } - self.handling_trap.set(false); - self.jmp_buf.get() - } } -impl<'a> Drop for CallThreadState<'a> { - fn drop(&mut self) { - if self.reset_guard_page.get() { - reset_guard_page(); - } - } -} +unsafe fn unwind_with(reason: UnwindReason) -> ! { + let yielder = YIELDER + .with(|cell| cell.replace(None)) + .expect("not running on Wasm stack"); -// A private inner module for managing the TLS state that we require across -// calls in wasm. The WebAssembly code is called from C++ and then a trap may -// happen which requires us to read some contextual state to figure out what to -// do with the trap. This `tls` module is used to persist that information from -// the caller to the trap site. -mod tls { - use super::CallThreadState; - use crate::Trap; - use std::mem; - use std::ptr; - - pub use raw::Ptr; - - // An even *more* inner module for dealing with TLS. This actually has the - // thread local variable and has functions to access the variable. - // - // Note that this is specially done to fully encapsulate that the accessors - // for tls must not be inlined. Wasmer's async support will employ stack - // switching which can resume execution on different OS threads. This means - // that borrows of our TLS pointer must never live across accesses because - // otherwise the access may be split across two threads and cause unsafety. - // - // This also means that extra care is taken by the runtime to save/restore - // these TLS values when the runtime may have crossed threads. - mod raw { - use super::CallThreadState; - use crate::Trap; - use std::cell::Cell; - use std::ptr; - - pub type Ptr = *const CallThreadState<'static>; - - // The first entry here is the `Ptr` which is what's used as part of the - // public interface of this module. The second entry is a boolean which - // allows the runtime to perform per-thread initialization if necessary - // for handling traps (e.g. setting up ports on macOS and sigaltstack on - // Unix). - thread_local!(static PTR: Cell<(Ptr, bool)> = Cell::new((ptr::null(), false))); - - #[inline(never)] // see module docs for why this is here - pub fn replace(val: Ptr) -> Result { - PTR.with(|p| { - // When a new value is configured that means that we may be - // entering WebAssembly so check to see if this thread has - // performed per-thread initialization for traps. - let (prev, mut initialized) = p.get(); - if !initialized { - super::super::lazy_per_thread_init()?; - initialized = true; - } - p.set((val, initialized)); - Ok(prev) - }) - } + yielder.as_ref().suspend(reason); - #[inline(never)] // see module docs for why this is here - pub fn get() -> Ptr { - PTR.with(|p| p.get().0) - } - } + // on_wasm_stack will forcibly reset the coroutine stack after yielding. + unreachable!(); +} - /// Opaque state used to help control TLS state across stack switches for - /// async support. - pub struct TlsRestore(raw::Ptr); - - impl TlsRestore { - /// Takes the TLS state that is currently configured and returns a - /// token that is used to replace it later. - /// - /// # Safety - /// - /// This is not a safe operation since it's intended to only be used - /// with stack switching found with fibers and async wasmer. - pub unsafe fn take() -> Result { - // Our tls pointer must be set at this time, and it must not be - // null. We need to restore the previous pointer since we're - // removing ourselves from the call-stack, and in the process we - // null out our own previous field for safety in case it's - // accidentally used later. - let raw = raw::get(); - assert!(!raw.is_null()); - let prev = (*raw).prev.replace(ptr::null()); - raw::replace(prev)?; - Ok(TlsRestore(raw)) - } +/// Runs the given function on a separate stack so that its stack usage can be +/// bounded. Stack overflows and other traps can be caught and execution +/// returned to the root of the stack. +fn on_wasm_stack T, T>( + trap_handler: &(dyn TrapHandler + 'static), + f: F, +) -> Result { + // Create a coroutine with a new stack to run the function on. + let mut coro = ScopedCoroutine::new(move |yielder, ()| { + // Save the yielder to TLS so that it can be used later. + YIELDER.with(|cell| cell.set(Some(yielder.into()))); + + Ok(f()) + }); - /// Restores a previous tls state back into this thread's TLS. - /// - /// # Safety - /// - /// This is unsafe because it's intended to only be used within the - /// context of stack switching within wasmer. - pub unsafe fn replace(self) -> Result<(), super::Trap> { - // We need to configure our previous TLS pointer to whatever is in - // TLS at this time, and then we set the current state to ourselves. - let prev = raw::get(); - assert!((*self.0).prev.get().is_null()); - (*self.0).prev.set(prev); - raw::replace(self.0)?; - Ok(()) - } + // Ensure that YIELDER is reset on exit even if the coroutine panics, + defer! { + YIELDER.with(|cell| cell.set(None)); } - /// Configures thread local state such that for the duration of the - /// execution of `closure` any call to `with` will yield `ptr`, unless this - /// is recursively called again. - pub fn set(state: &CallThreadState<'_>, closure: impl FnOnce() -> R) -> Result { - struct Reset<'a, 'b>(&'a CallThreadState<'b>); - - impl Drop for Reset<'_, '_> { - #[inline] - fn drop(&mut self) { - raw::replace(self.0.prev.replace(ptr::null())) - .expect("tls should be previously initialized"); + // Set up metadata for the trap handler for the duration of the coroutine + // execution. This is restored to its previous value afterwards. + TrapHandlerContext::install(trap_handler, coro.trap_handler(), || { + match coro.resume(()) { + CoroutineResult::Yield(trap) => { + // This came from unwind_with which requires that there be only + // Wasm code on the stack. + unsafe { + coro.force_reset(); + } + Err(trap) } + CoroutineResult::Return(result) => result, } + }) +} - // Note that this extension of the lifetime to `'static` should be - // safe because we only ever access it below with an anonymous - // lifetime, meaning `'static` never leaks out of this module. - let ptr = unsafe { mem::transmute::<*const CallThreadState<'_>, _>(state) }; - let prev = raw::replace(ptr)?; - state.prev.set(prev); - let _reset = Reset(state); - Ok(closure()) +/// When executing on the Wasm stack, temporarily switch back to the host stack +/// to perform an operation that should not be constrainted by the Wasm stack +/// limits. +/// +/// This is particularly important since the usage of the Wasm stack is under +/// the control of untrusted code. Malicious code could artificially induce a +/// stack overflow in the middle of a sensitive host operations (e.g. growing +/// a memory) which would be hard to recover from. +pub fn on_host_stack T, T>(f: F) -> T { + // Reset YIEDER to None for the duration of this call to indicate that we + // are no longer on the Wasm stack. + let yielder_ptr = YIELDER.with(|cell| cell.replace(None)); + + // If we are already on the host stack, execute the function directly. This + // happens if a host function is called directly from the API. + let yielder = match yielder_ptr { + Some(ptr) => unsafe { ptr.as_ref() }, + None => return f(), + }; + + // Restore YIELDER upon exiting normally or unwinding. + defer! { + YIELDER.with(|cell| cell.set(yielder_ptr)); } - /// Returns the last pointer configured with `set` above. Panics if `set` - /// has not been previously called and not returned. - pub fn with(closure: impl FnOnce(Option<&CallThreadState<'_>>) -> R) -> R { - let p = raw::get(); - unsafe { closure(if p.is_null() { None } else { Some(&*p) }) } - } + // on_parent_stack requires the closure to be Send so that the Yielder + // cannot be called from the parent stack. This is not a problem for us + // since we don't expose the Yielder. + struct SendWrapper(T); + unsafe impl Send for SendWrapper {} + let wrapped = SendWrapper(f); + yielder.on_parent_stack(move || (wrapped.0)()) } -#[cfg(not(unix))] +#[cfg(windows)] pub fn lazy_per_thread_init() -> Result<(), Trap> { - // Unused on Windows + // We need additional space on the stack to handle stack overflow + // exceptions. Rust's initialization code sets this to 0x5000 but this + // seems to be insufficient in practice. + use winapi::um::processthreadsapi::SetThreadStackGuarantee; + if unsafe { SetThreadStackGuarantee(&mut 0x10000) } == 0 { + panic!("failed to set thread stack guarantee"); + } + Ok(()) } @@ -913,13 +920,12 @@ pub fn lazy_per_thread_init() -> Result<(), Trap> { /// page. #[cfg(unix)] pub fn lazy_per_thread_init() -> Result<(), Trap> { - use std::cell::RefCell; use std::ptr::null_mut; thread_local! { /// Thread-local state is lazy-initialized on the first time it's used, /// and dropped when the thread exits. - static TLS: RefCell = RefCell::new(Tls::None); + static TLS: Tls = unsafe { init_sigstack() }; } /// The size of the sigaltstack (not including the guard, which will be @@ -927,7 +933,7 @@ pub fn lazy_per_thread_init() -> Result<(), Trap> { const MIN_STACK_SIZE: usize = 16 * 4096; enum Tls { - None, + OOM, Allocated { mmap_ptr: *mut libc::c_void, mmap_size: usize, @@ -935,21 +941,14 @@ pub fn lazy_per_thread_init() -> Result<(), Trap> { BigEnough, } - return TLS.with(|slot| unsafe { - let mut slot = slot.borrow_mut(); - match *slot { - Tls::None => {} - // already checked - _ => return Ok(()), - } + unsafe fn init_sigstack() -> Tls { // Check to see if the existing sigaltstack, if it exists, is big // enough. If so we don't need to allocate our own. let mut old_stack = mem::zeroed(); let r = libc::sigaltstack(ptr::null(), &mut old_stack); assert_eq!(r, 0, "learning about sigaltstack failed"); if old_stack.ss_flags & libc::SS_DISABLE == 0 && old_stack.ss_size >= MIN_STACK_SIZE { - *slot = Tls::BigEnough; - return Ok(()); + return Tls::BigEnough; } // ... but failing that we need to allocate our own, so do all that @@ -967,7 +966,7 @@ pub fn lazy_per_thread_init() -> Result<(), Trap> { 0, ); if ptr == libc::MAP_FAILED { - return Err(Trap::oom()); + return Tls::OOM; } // Prepare the stack with readable/writable memory and then register it @@ -987,11 +986,20 @@ pub fn lazy_per_thread_init() -> Result<(), Trap> { let r = libc::sigaltstack(&new_stack, ptr::null_mut()); assert_eq!(r, 0, "registering new sigaltstack failed"); - *slot = Tls::Allocated { + Tls::Allocated { mmap_ptr: ptr, mmap_size: alloc_size, - }; - Ok(()) + } + } + + // Ensure TLS runs its initializer and return an error if it failed to + // set up a separate stack for signal handlers. + return TLS.with(|tls| { + if let Tls::OOM = tls { + Err(Trap::oom()) + } else { + Ok(()) + } }); impl Drop for Tls { diff --git a/rust-toolchain b/rust-toolchain index e01e6c121d0..791d8759b87 100644 --- a/rust-toolchain +++ b/rust-toolchain @@ -1 +1 @@ -1.56 +1.59 diff --git a/tests/compilers/traps.rs b/tests/compilers/traps.rs index 493090c7e9a..77b5d6e8560 100644 --- a/tests/compilers/traps.rs +++ b/tests/compilers/traps.rs @@ -135,13 +135,9 @@ fn test_trap_stack_overflow(config: crate::Config) -> Result<()> { let e = run_func.call(&[]).err().expect("error calling function"); - let trace = e.trace(); - assert!(trace.len() >= 32); - for i in 0..trace.len() { - assert_eq!(trace[i].module_name(), "rec_mod"); - assert_eq!(trace[i].func_index(), 0); - assert_eq!(trace[i].function_name(), Some("run")); - } + // We specifically don't check the stack trace here: stack traces after + // stack overflows are not generally possible due to unreliable unwinding + // information. assert!(e.message().contains("call stack exhausted")); Ok(()) diff --git a/tests/ignores.txt b/tests/ignores.txt index a2efd9dbb3a..a7b67a9a6d3 100644 --- a/tests/ignores.txt +++ b/tests/ignores.txt @@ -36,16 +36,10 @@ aarch64 traps::start_trap_pretty singlepass multi_value_imports::dylib # Singlepass doesn't support multivalue singlepass multi_value_imports::dynamic # Singlepass doesn't support multivalue -# TODO: We need to fix this in ARM. The issue is caused by libunwind overflowing -# the stack while creating the stacktrace. -# https://github.com/rust-lang/backtrace-rs/issues/356 -cranelift+aarch64 spec::skip_stack_guard_page # This is skipped for ARM, not fully fixed yet -llvm+aarch64 spec::skip_stack_guard_page # This is skipped for ARM, not fully fixed yet -singlepass+windows spec::skip_stack_guard_page # Needs investigation. -cranelift+windows spec::skip_stack_guard_page # Needs investigation. Issue: `STATUS_ACCESS_VIOLATION` trap happened -cranelift+macos spec::skip_stack_guard_page # Needs investigation. process didn't exit successfully: (signal: 6, SIGABRT: process abort signal) -llvm+macos spec::skip_stack_guard_page # Needs investigation. process didn't exit successfully: (signal: 6, SIGABRT: process abort signal) -dylib spec::skip_stack_guard_page # Missing trap information in dylibs +# Also neither LLVM nor Cranelift currently implement stack probing on AArch64. +# https://github.com/wasmerio/wasmer/issues/2808 +cranelift+aarch64 spec::skip_stack_guard_page +llvm+aarch64 spec::skip_stack_guard_page # Some SIMD opperations are not yet supported by Cranelift # Cranelift just added support for most of those recently, it might be easy to update