1# Coding guidelines 2 3For the most part, Wasmtime and Cranelift follow common Rust conventions and 4[pull request] (PR) workflows, though we do have a few additional things to 5be aware of. 6 7[pull request]: https://help.github.com/articles/about-pull-requests/ 8 9### `rustfmt` 10 11All PRs must be formatted according to rustfmt, and this is checked in the 12continuous integration tests. You can format code locally with: 13 14```console 15cargo fmt 16``` 17 18at the root of the repository. You can find [more information about rustfmt 19online](https://github.com/rust-lang/rustfmt) too, such as how to configure 20your editor. 21 22### Compiler Warnings and Lints 23 24Wasmtime promotes all compiler warnings to errors in CI, meaning that the `main` 25branch will never have compiler warnings for the version of Rust that's being 26tested on CI. Compiler warnings change over time, however, so it's not always 27guaranteed that Wasmtime will build with zero warnings given an arbitrary 28version of Rust. If you encounter compiler warnings on your version of Rust 29please feel free to send a PR fixing them. 30 31During local development, however, compiler warnings are simply warnings and the 32build and tests can still succeed despite the presence of warnings. This can be 33useful because warnings are often quite prevalent in the middle of a 34refactoring, for example. By the time you make a PR, though, we'll require that 35all warnings are resolved or otherwise CI will fail and the PR cannot land. 36 37Compiler lints are controlled through the `[workspace.lints.rust]` table in the 38`Cargo.toml` at the root of the Wasmtime repository. A few allow-by-default 39lints are enabled such as `trivial_numeric_casts`, and you're welcome to enable 40more lints as applicable. Lints can additionally be enabled on a per-crate basis 41such as placing this in a `src/lib.rs` file: 42 43```rust 44#![warn(trivial_numeric_casts)] 45``` 46 47Using `warn` here will allow local development to continue while still causing 48CI to promote this warning to an error. 49 50### Clippy 51 52All PRs are gated on `cargo clippy` passing for all workspace crates and 53targets. All clippy lints, however, are allow-by-default and thus disabled. The 54Wasmtime project selectively enables Clippy lints on an opt-in basis. Lints can 55be controlled for the entire workspace via `[workspace.lints.clippy]`: 56 57```toml 58[workspace.lints.clippy] 59# ... 60manual_strip = 'warn' 61``` 62 63or on a per-crate or module basis by using attributes: 64 65```rust 66#![warn(clippy::manual_strip)] 67``` 68 69In Wasmtime we've found that the default set of Clippy lints is too noisy to 70productively use other Clippy lints, hence the allow-by-default behavior. 71Despite this though there are numerous useful Clippy lints which are desired for 72all crates or in some cases for a single crate or module. Wasmtime encourages 73contributors to enable Clippy lints they find useful through workspace or 74per-crate configuration. 75 76Like compiler warnings in the above section all Clippy warnings are turned into 77errors in CI. This means that `cargo clippy` should always produce no warnings 78on Wasmtime's `main` branch if you're using the same compiler version that CI 79does (typically current stable Rust). This means, however, that if you enable a 80new Clippy lint for the workspace you'll be required to fix the lint for all 81crates in the workspace to land the PR in CI. 82 83Clippy can be run locally with: 84 85```console 86cargo clippy --workspace --all-targets 87``` 88 89Contributors are welcome to enable new lints and send PRs for this. Feel free to 90reach out if you're not sure about a lint as well. 91 92### Minimum Supported `rustc` Version (MSRV) 93 94Wasmtime and Cranelift support the latest three stable releases of Rust. This 95means that if the latest version of Rust is 1.72.0 then Wasmtime supports Rust 961.70.0, 1.71.0, and 1.72.0. CI will test by default with 1.72.0 and there will 97be one job running the full test suite on Linux x86\_64 on 1.70.0. 98 99Some of the CI jobs depend on nightly Rust, for example to run rustdoc with 100nightly features, however these use pinned versions in CI that are updated 101periodically and the general repository does not depend on nightly features. 102 103Updating Wasmtime's MSRV is done by editing the `rust-version` field in the 104workspace root's `Cargo.toml` 105 106Note that this policy is subject to change over time (notably it might be 107extended to include more rustc versions). Current Wasmtime users don't require a 108larger MSRV window to justify the maintenance needed to have a larger window. If 109your use case requires a larger MSRV range though please feel free to contact 110maintainers to raise your use case (e.g. an issue, in a Wasmtime meeting, on 111Zulip, etc). 112 113### Dependencies of Wasmtime 114 115Wasmtime and Cranelift have a higher threshold than default for adding 116dependencies to the project. All dependencies are required to be "vetted" 117through the [`cargo vet` tool](https://mozilla.github.io/cargo-vet/). This is 118checked on CI and will run on all modifications to `Cargo.lock`. 119 120A "vet" for Wasmtime is not a meticulous code review of a dependency for 121correctness but rather it is a statement that the crate does not contain 122malicious code and is safe for us to run during development and (optionally) 123users to run when they run Wasmtime themselves. Wasmtime's vet entries are used 124by other organizations which means that this isn't simply for our own personal 125use. Wasmtime additionally uses vet entries from other organizations as well 126which means we don't have to vet everything ourselves. 127 128New vet entries are required to be made by trusted contributors to Wasmtime. 129This is all configured in the `supply-chain` folder of Wasmtime. These files 130generally aren't hand-edited though and are instead managed through the `cargo 131vet` tool itself. Note that our `supply-chain/audits.toml` additionally contains 132entries which indicates that authors are trusted as opposed to vets of 133individual crates. This lowers the burden of updating version of a crate from a 134trusted author. 135 136When put together this means that contributions to Wasmtime and Cranelift which 137update existing dependencies or add new dependencies will not be mergeable by 138default (CI will fail). This is expected from our project's configuration and 139this situation will be handled one of a few ways: 140 141Note that this process is not in place to prevent new dependencies or prevent 142updates, but rather it ensures that development of Wasmtime is done with a 143trusted set of code that has been reviewed by trusted parties. We welcome 144dependency updates and new functionality, so please don't be too alarmed when 145contributing and seeing a failure of `cargo vet` on CI! 146 147### `cargo vet` for Contributors 148 149If you're a contributor to Wasmtime and you've landed on this documentation, 150hello and thanks for your contribution! Here's some guidelines for changing the 151set of dependencies in Wasmtime: 152 153* If a new dependency is being added it might be worth trying to slim down 154 what's required or avoiding the dependency altogether. Avoiding new 155 dependencies is best when reasonable, but it is not always reasonable to do 156 so. This is left to the judgement of the author and reviewer. 157 158* When updating dependencies this should be done for a specific purpose relevant 159 to the PR-at-hand. For example if the PR implements a new feature then the 160 dependency update should be required for the new feature. Otherwise it's best 161 to leave dependency updates to their own PRs. It's ok to update dependencies 162 "just for the update" but we prefer to have that as separate PRs. 163 164Dependency additions or updates require action on behalf of project maintainers 165so we ask that you don't run `cargo vet` yourself or update the `supply-chain` 166folder yourself. Instead a maintainer will review your PR and perform the `cargo 167vet` entries themselves. Reviewers will typically make a separate pull request 168to add `cargo vet` entries and once that lands yours will be added to the queue. 169 170### `cargo vet` for Maintainers 171 172Maintainers of Wasmtime are required to explicitly vet and approve all 173dependency updates and modifications to Wasmtime. This means that when reviewing 174a PR you should ensure that contributors are not modifying the `supply-chain` 175directory themselves outside of commits authored by other maintainers. Otherwise 176though to add vet entries this is done through one of a few methods: 177 178* For a PR where maintainers themselves are modifying dependencies the `cargo 179 vet` entries can be included inline with the PR itself by the author. The 180 reviewer knows that the author of the PR is themself a maintainer. 181 182* PRs that "just update dependencies" are ok to have at any time. You can do 183 this in preparation for a future feature or for a future contributor. This 184 more-or-less is the same as the previous categories. 185 186* For contributors who should not add vet entries themselves maintainers should 187 review the PR and add vet entries either in a separate PR or as part of the 188 contributor's PR itself. As a separate PR you'll check out the branch, run 189 `cargo vet`, then rebase away the contributor's commits and push your `cargo 190 vet` commit alone to merge. For pushing directly to the contributor's own PR 191 be sure to read the notes below. 192 193Note for the last case it's important to ensure that if you push directly to a 194contributor's PR any future updates pushed by the contributor either contain or 195don't overwrite your vet entries. Also verify that if the PR branch is rebased 196or force-pushed, the details of your previously pushed vetting remain the same: 197e.g., versions were not bumped and descriptive reasons remain the same. If 198pushing a vetting commit to a contributor's PR and also asking for more changes, 199request that the contributor make the requested fixes in an additional commit 200rather than force-pushing a rewritten history, so your existing vetting commit 201remains untouched. These guidelines make it easier to verify no tampering has 202occurred. 203 204### Policy for adding `cargo vet` entries 205 206For maintainers this is intended to document the project's policy on adding 207`cargo vet` entries. The goal of this policy is to not make dependency updates 208so onerous that they never happen while still achieving much of the intended 209benefit of `cargo vet` in protection against supply-chain style attacks. 210 211* For dependencies **that receive at least 10,000 downloads a day** on crates.io 212 it's ok to add an entry to `exemptions` in `supply-chain/config.toml`. This 213 does not require careful review or review at all of these dependencies. The 214 assumption here is that a supply chain attack against a popular crate is 215 statistically likely to be discovered relatively quickly. Changes to `main` in 216 Wasmtime take at least 2 weeks to be released due to our release process, so 217 the assumption is that popular crates that are victim of a supply chain attack 218 would be discovered during this time. This policy additionally greatly helps 219 when updating dependencies on popular crates that are common to see without 220 increasing the burden too much on maintainers. 221 222* For other dependencies a manual vet is required. The `cargo vet` tool will 223 assist in adding a vet by pointing you towards the source code, as published 224 on crates.io, to be browsed online. Manual review should be done to ensure 225 that "nothing nefarious" is happening. For example `unsafe` should be 226 inspected as well as use of ambient system capabilities such as `std::fs`, 227 `std::net`, or `std::process`, and build scripts. Note that you're not 228 reviewing for correctness, instead only for whether a supply-chain attack 229 appears to be present. 230 231This policy intends to strike a rough balance between usability and security. 232It's always recommended to add vet entries where possible, but the first bullet 233above can be used to update an `exemptions` entry or add a new entry. Note that 234when the "popular threshold" is used **do not add a vet entry** because the 235crate is, in fact, not vetted. This is required to go through an 236`[[exemptions]]` entry. 237 238### Crate Organization 239 240The Wasmtime repository is a bit of a monorepo with lots of crates internally 241within it. The Wasmtime project and `wasmtime` crate also consists of a variety 242of crates intended for various purposes. As such not all crates are treated 243exactly the same and so there are some rough guidelines here about adding new 244crates to the repository and where to place/name them: 245 246* Wasmtime-related crates live in `crates/foo/Cargo.toml` where the crate name 247 is typically `wasmtime-foo` or `wasmtime-internal-foo`. 248 249* Cranelift-related crates live in `cranelift/foo/Cargo.toml` where the crate is 250 named `cranelift-foo`. 251 252* Some projects such as Winch, Pulley, and Wiggle are exceptions to the above 253 rules and live in `winch/*`, `pulley/*` and `crates/wiggle/*`. 254 255* Some crates are "internal" to Wasmtime. This means that they only exist for 256 crate organization purposes (such as optional dependencies, or code 257 organization). These crates are not intended for public consumption and are 258 intended for exclusively being used by the `wasmtime` crate, for example, or 259 other public crates. These crates should be named `wasmtime-internal-foo` and 260 live in `crates/foo`. The `[workspace.dependencies]` directive in `Cargo.toml` 261 at the root of the repository should rename it to `wasmtime-foo` in 262 workspace-local usage, meaning that the "internal" part is only relevant on 263 crates.io. 264 265### Adding Crates 266 267Adding a new crate to the Wasmtime workspace takes a bit of care. Wasmtime uses 268crates.io trusted publishing meaning that all crates are published from CI in a 269specific workflow. This means that crates must exist on crates.io prior to their 270first publication from the Wasmtime workspace and be configured for trusted 271publishing. 272 273The process for adding a new crate to the workspace looks like: 274 2751. In a PR a new crate is added and this documentation probably isn't read to 276 start out with. 2772. CI will fail in the "verify-publish" job because this crate doesn't exist on 278 crates.io. 2793. The PR author should publish a placeholder crate to crates.io. 2804. The PR author should go to "Settings" on crates.io, click on "Add" under 281 "Trusted Publishing", and enter the following: 282 fields: 283 * Publisher: `GitHub` 284 * Repository Owner: `bytecodealliance` 285 * Repository name: `wasmtime` 286 * Workflow filename: `publish-to-cratesio.yml` 287 * Environment name: `publish` 2885. The PR author should then check the box for requiring all publishes to use 289 the trusted publishing workflow. 2906. The PR author should invite the `wasmtime-publish` user to this crate. 2917. A Wasmtime maintainer, with access to the BA 1password vault, will log in to 292 crates.io as the `wasmtime-publish` user to accept the invite. Wasmtime 293 maintainers should double-check all of the settings and remove the original 294 owner of the crate so just `wasmtime-publish` owns the crate. 295 296This ensures that when publication time rolls around the crate is already 297reserved on GitHub and the publication workflow will succeed. After the initial 298publication the crate is managed by Wasmtime maintainers. 299 300### Use of `unsafe` 301 302Wasmtime is a project that contains `unsafe` Rust code. Wasmtime is also used in 303security-critical contexts which means that it's extra-important that this 304`unsafe` code is correct. The purpose of this section is to outline guidelines 305and guidance for how to use `unsafe` in Wasmtime. 306 307Ideally Wasmtime would have no `unsafe` code. For large components of Wasmtime 308this is already true, for these components have little to no `unsafe` code: 309 310* Cranelift - compiling WebAssembly modules. 311* Winch - compiling WebAssembly modules. 312* Wasmparser - validating WebAssembly. 313* `wasmtime-wasi` / `wasmtime-wasi-http` - implementation of WASI. 314 315Without `unsafe` the likelihood of a security bug is greatly reduced with the 316riskiest possibility being a DoS vector through a panic, generally considered a 317low-severity issue. Inevitably though due to the nature of Wasmtime it's 318effectively impossible to 100% remove `unsafe` code. The question then becomes 319what is the right balance and how to work with `unsafe`? 320 321Some `unsafe` blocks are effectively impossible to remove. For example somewhere 322in Wasmtime we're going to take the output of Cranelift and turn it into a 323function pointer to calling it. In doing so the correctness of the `unsafe` 324block relies on the correctness of Cranelift as well as the translation from 325WebAssembly to Cranelift. This is a fundamental property of the Wasmtime project 326and thus can't really be mitigated. 327 328Other `unsafe` blocks, however, ideally will be self-contained and isolated to a 329small portion of Wasmtime. For this code Wasmtime tries to follow these 330guidelines: 331 3321. Users of the public API of the `wasmtime` crate should never need `unsafe`. 333 The API of `wasmtime` should be sound and safe no matter how its combined 334 with other safe Rust code. While `unsafe` additions are allowed they should 335 be very clearly documented with a precise contract of what exactly is unsafe 336 and what must be upheld by the caller. For example `Module::deserialize` 337 clearly documents that it could allow arbitrary code execution and thus it's 338 not safe to pass in arbitrary bytes, but previously serialized bytes are 339 always safe to pass in. 340 3412. Declaring a function as `unsafe` should be accompanied with clear 342 documentation on the function declaration indicating why the function is 343 `unsafe`. This should clearly indicate all the contracts that need to be 344 upheld by callers for the invocation to be safe. There is no way to verify 345 that the documentation is correct but this is a useful flag to reviewers and 346 readers alike to be more vigilant around such functions. 347 3483. An `unsafe` block within a function should be accompanied with a preceding 349 comment explaining why it's safe to have this block. It should be possible to 350 verify this comment with local reasoning, for example considering little code 351 outside of the current function or module. This means that it should be 352 almost trivial to connect the contracts required on the callee function (why 353 the `unsafe` block is there in the first place) to the surrounding code. This 354 can include the current function being `unsafe` (effectively "forwarding" the 355 contract of the callee) or via local reasoning. 356 3574. Implementation of a feature within Wasmtime should not result in excessive 358 amounts of `unsafe` functions or usage of `unsafe` functions. The goal here 359 is that if two possible designs for a feature are being weighed it's not 360 required to favor one with zero unsafe vs one with just a little unsafe, but 361 one with a little unsafe should be favored over one that is entirely unsafe. 362 An example of this is Wasmtime's implementation of the GC proposal with a 363 sandboxed heap where the data on the heap is never trusted. This comes at a 364 minor theoretical performance loss on the host but has the benefit of all 365 functions within the implementation are all safe. These sorts of design 366 tradeoffs are not really possible to codify in stone, but the general 367 guideline is to try to favor safer implementations so long as the 368 hypothetical sacrifice in performance isn't too great. 369 370It should be noted that Wasmtime is a relatively large and old codebase and thus 371does not perfectly follow these guidelines for preexisting code. Code not 372following these guidelines is considered technical debt that must be paid down 373at one point. Wasmtime tries to [keep track of known issues][unsafe-code-tag] to 374burn down this list over time. New features to Wasmtime are allowed to add to 375this list, but it should be clear how to burn down the list in time for any new 376entries added. 377 378[unsafe-code-tag]: https://github.com/bytecodealliance/wasmtime/labels/wasmtime%3Aunsafe-code 379