1
    2
    3
    4
    5
    6
    7
    8
    9
   10
   11
   12
   13
   14
   15
   16
   17
   18
   19
   20
   21
   22
   23
   24
   25
   26
   27
   28
   29
   30
   31
   32
   33
   34
   35
   36
   37
   38
   39
   40
   41
   42
   43
   44
   45
   46
   47
   48
   49
   50
   51
   52
   53
   54
   55
   56
   57
   58
   59
   60
   61
   62
   63
   64
   65
   66
   67
   68
   69
   70
   71
   72
   73
   74
   75
   76
   77
   78
   79
   80
   81
   82
   83
   84
   85
   86
   87
   88
   89
   90
   91
   92
   93
   94
   95
   96
   97
   98
   99
  100
  101
  102
  103
  104
  105
  106
  107
  108
  109
  110
  111
  112
  113
  114
  115
  116
  117
  118
  119
  120
  121
  122
  123
  124
  125
  126
  127
  128
  129
  130
  131
  132
  133
  134
  135
  136
  137
  138
  139
  140
  141
  142
  143
  144
  145
  146
  147
  148
  149
  150
  151
  152
  153
  154
  155
  156
  157
  158
  159
  160
  161
  162
  163
  164
  165
  166
  167
  168
  169
  170
  171
  172
  173
  174
  175
  176
  177
  178
  179
  180
  181
  182
  183
  184
  185
  186
  187
  188
  189
  190
  191
  192
  193
  194
  195
  196
  197
  198
  199
  200
  201
  202
  203
  204
  205
  206
  207
  208
  209
  210
  211
  212
  213
  214
  215
  216
  217
  218
  219
  220
  221
  222
  223
  224
  225
  226
  227
  228
  229
  230
  231
  232
  233
  234
  235
  236
  237
  238
  239
  240
  241
  242
  243
  244
  245
  246
  247
  248
  249
  250
  251
  252
  253
  254
  255
  256
  257
  258
  259
  260
  261
  262
  263
  264
  265
  266
  267
  268
  269
  270
  271
  272
  273
  274
  275
  276
  277
  278
  279
  280
  281
  282
  283
  284
  285
  286
  287
  288
  289
  290
  291
  292
  293
  294
  295
  296
  297
  298
  299
  300
  301
  302
  303
  304
  305
  306
  307
  308
  309
  310
  311
  312
  313
  314
  315
  316
  317
  318
  319
  320
  321
  322
  323
  324
  325
  326
  327
  328
  329
  330
  331
  332
  333
  334
  335
  336
  337
  338
  339
  340
  341

docs / rust.md [blame]

# Rust in Chromium

[TOC]

# Why?

Handling untrustworthy data in non-trivial ways is a major source of security
bugs, and it's therefore against Chromium's security policies
[to do it in the Browser or Gpu process](../docs/security/rule-of-2.md) unless
you are working in a memory-safe language.

Rust provides a cross-platform memory-safe language so that all platforms can
handle untrustworthy data directly from a privileged process, without the
performance overheads and complexity of a utility process.

# Status

The Rust toolchain is enabled for and supports all platforms and development
environments that are supported by the Chromium project. The first milestone
to include full production-ready support was M119.

Rust can be used anywhere in the Chromium repository (not just `//third_party`)
subject to [current interop capabilities][interop-rust-doc], however it is
currently subject to a internal approval and FYI process. Googlers can view
go/chrome-rust for details. New usages of Rust are documented at
[`rust-fyi@chromium.org`](https://groups.google.com/a/chromium.org/g/rust-fyi).

For questions or help, reach out to `rust-dev@chromium.org` or `#rust` on the
[Chromium Slack](https://www.chromium.org/developers/slack/).

If you use VSCode, we have [additional advice below](#using-vscode).

# Adding a third-party Rust library

Third-party libraries are pulled from [crates.io](https://crates.io), but
Chromium does not use Cargo as a build system.

## Third-party review

All third-party crates need to go through third-party review. See
[//docs/adding_to_third_party.md](adding_to_third_party.md) for instructions on
how to have a library reviewed.

## Importing a crate from crates.io

The `//third_party/rust/chromium_crates_io/Cargo.toml` file defines the set of crates
depended on from first-party code. Any transitive dependencies will be found
from those listed there. The file is a [standard `Cargo.toml` file](
https://doc.rust-lang.org/cargo/reference/manifest.html), though the crate
itself is never built, it is only used to collect dependencies through the
`[dependencies]` section.

To use a third-party crate "bar" version 3 from first party code:
1. Change directory to the root `src/` dir of Chromium.
1. Add the crate to `//third_party/rust/chromium_crates_io/Cargo.toml`:
   * `vpython3 ./tools/crates/run_gnrt.py add foo` to add the latest version of `foo`.
   * `vpython3 ./tools/crates/run_gnrt.py add foo@1.2.3` to add a specific version of `foo`.
   * Or, directly through (nightly) cargo:
     `cargo run --release --manifest-path tools/crates/gnrt/Cargo.toml --target-dir out/gnrt add foo`
   * Or, edit the Cargo.toml by hand, finding the version you want from [crates.io](https://crates.io).
1. Download the crate's files:
   * `./tools/crates/run_gnrt.py vendor` to download the new crate.
   * Or, directly through (nightly) cargo:
     `cargo run --release --manifest-path tools/crates/gnrt/Cargo.toml --target-dir out/gnrt vendor`
   * This will also apply any patches in `//third_party/rust/chromium_crates_io/patches`
     for the crates. If a patch can not apply, the crate's download will be cancelled and
     an error will be printed. See [patching errors](#patching-errors) below for how to resolve
     this.
1. (optional) If the crate is only to be used by tests and tooling, then
   specify the `"test"` group in `//third_party/rust/chromium_crates_io/gnrt_config.toml`:
   ```
   [crate.foo]
   group = "test"
   ```
1. Generate the `BUILD.gn` file for the new crate:
   * `vpython3 ./tools/crates/run_gnrt.py gen`
   * Or, directly through (nightly) cargo:
     `cargo run --release --manifest-path tools/crates/gnrt/Cargo.toml --target-dir out/gnrt gen`
1. Verify if all new dependencies are already audited by running `cargo vet`
   See [`rust-unsafe.md#cargo-vet-policy`](rust-unsafe.md#cargo-vet-policy) for
   more details.  This boils down to:
   * `./tools/crates/run_cargo_vet.py check`
   * If `check` fails, then there are missing audits, which need to be added to
     `//third_party/rust/chromium_crates_io/supply-chain/audits.toml`.
1. Add the new files to git:
   * `git add -f third_party/rust/chromium_crates_io/vendor`.
     (The `-f` is important, as files may be skipped otherwise from a
     `.gitignore` inside the crate.)
   * `git add third_party/rust`
1. Upload the CL. If there is any `unsafe` usage then Security experts will need to
   audit the "ub-risk" level.  See
   [`rust-unsafe.md#code-review-policy`](rust-unsafe.md#code-review-policy) for
   more details.

### Cargo features

To enable a feature "spaceships" in the crate, change the entry in
`//third_party/rust/chromium_crates_io/Cargo.toml` to include the feature:
```toml
[dependencies]
bar = { version = "3", features = [ "spaceships" ] }
```

### Patching third-party crates

You may patch a crate in tree, but save any changes made into a diff file in
a `//third_party/rust/chromium_crates_io/patches/` directory for the crate.
The diff file should be generated by `git-format-patch` each new patch numbered
consecutively so that they can be applied in order. For example, these files
might exist if the "foo" crate was patched with a couple of changes:

```
//third_party/rust/chromium_crates_io/patches/foo/patches/0001-Edit-the-Cargo-toml.diff
//third_party/rust/chromium_crates_io/patches/foo/patches/0002-Other-changes.diff
```

The recommended procedure to create such patches is:

1. Commit the plain new version of the crate to your local git branch
2. Modify the crate as necessary
3. Commit that modified version
4. Use `git format-patch <unpatched version>` to generate the patch files
5. Add the patch files in a new, third, commit
6. Squash them, or rely on `git cl upload` doing so

#### Patching errors

If `gnrt vendor` fails to apply a patch for a crate, it will cancel the download of that
crate rather than leave it in a broken state. To recreate patches, first get a pristine
copy of the crate by using the `--no-patches` argument:

1. Download the crate without applying patches:
   * `vpython3 ./tools/crates/run_gnrt.py vendor --no-patches=<CRATE_NAME>`
2. Then recreate the patches as described in [Patching third-party crates](
   #patching-third_party-crates).

To verify the patches work, remove the vendored crate directory in
`//third_party/rust/chromium_crates_io/vendor/`, named after the crate name
and version. Then run the `vendor` action without `--no-patches` which will
download the crate and apply the patches:
   * `vpython3 ./tools/crates/run_gnrt.py vendor`

## Security

If a shipping library needs security review (has any `unsafe`), and the review
finds it's not satisfying the [rule of 2](../docs/security/rule-of-2.md), then
move it to the `"sandbox"` group in `//third_party/rust/chromium_crates_io/gnrt_config.toml`
to make it clear it can't be used in a privileged process:
```
[crate.foo]
group = "sandbox"
```

If a transitive dependency moves from `"safe"` to `"sandbox"` and causes
a dependency chain across the groups, it will break the `gnrt vendor` step.
You will need to fix the new crate so that it's deemed safe in unsafe review,
or move the other dependent crates out of `"safe"` as well by setting their
group in `gnrt_config.toml`.

# Updating existing third-party crates

Third-party crates will get updated semi-automatically through the process
described in
[`../tools/crates/create_update_cl.md`](../tools/crates/create_update_cl.md).
If you nevertheless need to manually update a crate to its latest minor
version, then follow the steps below:

1. Change directory to the root `src/` dir of Chromium.
1. Update the versions in `//third_party/rust/chromium_crates_io/Cargo.toml`.
   * `vpython3 ./tools/crates/run_gnrt.py update <crate name>`
   * Or, directly through (nightly) cargo:
     `cargo run --release --manifest-path tools/crates/gnrt/Cargo.toml --target-dir out/gnrt update <crate name>`
1. Download any updated crate's files:
   * `./tools/crates/run_gnrt.py vendor`
   * If you want to restrict the update to certain crates, add the crate names
     as arguments to `vendor`, like: `./tools/crates/run_gnrt.py vendor
     <crate-name>`
   * Or, directly through (nightly) cargo:
     `cargo run --release --manifest-path tools/crates/gnrt/Cargo.toml --target-dir out/gnrt vendor`
1. Add the downloaded files to git:
   * `git add -f third_party/rust/chromium_crates_io/vendor`
   * The `-f` is important, as files may be skipped otherwise from a
     `.gitignore` inside the crate.
1. Generate the `BUILD.gn` files
   * `vpython3 ./tools/crates/run_gnrt.py gen`
   * Or, directly through (nightly) cargo:
     `cargo run --release --manifest-path tools/crates/gnrt/Cargo.toml --target-dir out/gnrt gen`
1. Add the generated files to git:
   * `git add -f third_party/rust`

### Directory structure for third-party crates

The directory structure for a crate "foo" version 3.4.2 is:
```
//third_party/
    rust/
        foo/  (for the "foo" crate)
            v3/  (version 3.4.2 maps to the v3 epoch)
                BUILD.gn  (generated by gnrt gen)
                README.chromium  (generated by gnrt vendor)
        chromium_crates_io/
            vendor/
                foo-3.4.2  (crate sources downloaded from crates.io)
            patches/
                foo/  (patches for the "foo" crate)
                    0001-Edit-the-Cargo-toml.diff
                    0002-Other-changes.diff
            Cargo.toml
            Cargo.lock
            gnrt_config.toml
```

## Writing a wrapper for binding generation

Most Rust libraries will need a more C++-friendly API written on top of them in
order to generate C++ bindings to them. The wrapper library can be placed
in `//third_party/rust/<cratename>/<epoch>/wrapper` or at another single place
that all C++ goes through to access the library. The [CXX](https://cxx.rs) is
used to generate bindings between C++ and Rust.

See
[`//third_party/rust/serde_json_lenient/v0_1/wrapper/`](
https://source.chromium.org/chromium/chromium/src/+/main:third_party/rust/serde_json_lenient/v0_1/wrapper/)
and
[`//components/qr_code_generator`](
https://source.chromium.org/chromium/chromium/src/+/main:components/qr_code_generator/;l=1;drc=b185db5d502d4995627e09d62c6934590031a5f2)
for examples.

Rust libraries should use the
[`rust_static_library`](
https://source.chromium.org/chromium/chromium/src/+/main:build/rust/rust_static_library.gni)
GN template (not the built-in `rust_library`) to integrate properly into the
mixed-language Chromium build and get the correct compiler options applied to
them.

The [CXX](https://cxx.rs) tool is used for generating C++ bindings to Rust
code. Since it requires explicit declarations in Rust, an wrapper shim around a
pure Rust library is needed. Add these Rust shims that contain the CXX
`bridge` macro to the `cxx_bindings` GN variable in the `rust_static_library`
to have CXX generate a C++ header for that file. To include the C++ header
file, rooted in the `gen` output directory, use
```
#include "the/path/to/the/rust/file.rs.h"
```

# Logging

Use the [log](https://docs.rs/log) crate's macros in place of base `LOG`
macros from C++. They do the same things. The `debug!` macro maps to
`DLOG(INFO)`, the `info!` macro maps to `LOG(INFO)`, and `warn!` and `error!`
map to `LOG(WARNING)` and `LOG(ERROR)` respectively. The additional `trace!`
macro maps to `DLOG(INFO)` (but there is [WIP to map it to `DVLOG(INFO)`](
https://chromium-review.googlesource.com/c/chromium/src/+/5996820)).

Note that the standard library also includes a helpful
[`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro which writes
everything about a variable to `stderr`.

Logging may not yet work in component builds:
[crbug.com/374023535](https://crbug.com/374023535).

# Tracing

TODO: [crbug.com/377915495](https://crbug.com/377915495).

# Strings

Prefer to use [`BString`](https://docs.rs/bstr/latest/bstr/struct.BString.html)
and [`BStr`](https://docs.rs/bstr/latest/bstr/struct.BStr.html) to work with
strings in first-party code instead of `std::String` and `str`. These types do
not require the strings to be valid UTF-8, and avoid error handling or panic
crashes when working with strings from C++ and/or from the web. Because the
web is not UTF-8 encoded, many strings in Chromium are also not.

In cross-language bindings, `&[u8]` can be used to represent a string until
native support for `BStr` is available in our interop tooling. A `u8` slice
can be converted to `BStr` or treated as a string with
[`ByteSlice`](https://docs.rs/bstr/latest/bstr/trait.ByteSlice.html).

# Using VSCode

1. Ensure you're using the `rust-analyzer` extension for VSCode, rather than
   earlier forms of Rust support.
2. Run `gn` with the `--export-rust-project` flag, such as:
   `gn gen out/Release --export-rust-project`.
3. `ln -s out/Release/rust-project.json rust-project.json`
4. When you run VSCode, or any other IDE that uses
   [rust-analyzer](https://rust-analyzer.github.io/) it should detect the
   `rust-project.json` and use this to give you rich browsing, autocompletion,
   type annotations etc. for all the Rust within the Chromium codebase.
5. Point rust-analyzer to the rust toolchain in Chromium. Otherwise you will
   need to install Rustc in your system, and Chromium uses the nightly
   compiler, so you would need that to match. Add the following to
   `.vscode/settings.json` in the Chromium checkout:
   ```
   {
      // The rest of the settings...

      "rust-analyzer.cargo.extraEnv": {
        "PATH": "../../third_party/rust-toolchain/bin:$PATH",
      }
   }
   ```
   This assumes you are working with an output directory like `out/Debug` which
   has two levels; adjust the number of `..` in the path according to your own
   setup.

# Using cargo

If you are building a throwaway or experimental tool, you might like to use pure
`cargo` tooling rather than `gn` and `ninja`. Even then, you may choose
to restrict yourself to the toolchain and crates that are already approved for
use in Chromium.

Here's how.

```
export PATH_TO_CHROMIUM_SRC=~/chromium/src
mkdir my-rust-tool
cd my-rust-tool
mkdir .cargo
cat <<END > .cargo/config.toml
[source.crates-io]
replace-with = "vendored-sources"

[source.vendored-sources]
directory = "$PATH_TO_CHROMIUM_SRC/third_party/rust/chromium_crates_io/vendor"
END
$PATH_TO_CHROMIUM_SRC/third_party/rust-toolchain/bin/cargo init --offline
$PATH_TO_CHROMIUM_SRC/third_party/rust-toolchain/bin/cargo run --offline
```

Most `cargo` tooling works well with this setup; one exception is `cargo add`,
but you can still add dependencies manually to your `Cargo.toml`:

```
[dependencies]
log = "0.4"
```

[interop-rust-doc]: https://docs.google.com/document/d/1kvgaVMB_isELyDQ4nbMJYWrqrmL3UZI4tDxnyxy9RTE/edit?tab=t.0#heading=h.fpqr6hf3c3j0