
Update docs/rust.md and docs/adding_to_third_party.md to remove the 3P restriction. For an extended description of the new process and rationale for changing it, Googlers can see go/rethinking-rust-approvals-chrome. Change-Id: Ifb6acd08e11eafcbbbe7823803033d38bd384974 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5980554 Commit-Queue: David Adrian <dadrian@google.com> Reviewed-by: Nico Weber <thakis@chromium.org> Reviewed-by: Nasko Oskov <nasko@chromium.org> Cr-Commit-Position: refs/heads/main@{#1395613}
14 KiB
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 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, 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
.
For questions or help, reach out to rust-dev@chromium.org
or #rust
on the
Chromium Slack.
If you use VSCode, we have additional advice below.
Adding a third-party Rust library
Third-party libraries are pulled from 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 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, 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:
- Change directory to the root
src/
dir of Chromium. - 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 offoo
.vpython3 ./tools/crates/run_gnrt.py add foo@1.2.3
to add a specific version offoo
.- 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.
- 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 below for how to resolve this.
- (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"
- 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
- Verify if all new dependencies are already audited by running
cargo vet
Seerust-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
.
- 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
- Upload the CL. If there is any
unsafe
usage then Security experts will need to audit the "ub-risk" level. Seerust-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:
[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:
- Commit the plain new version of the crate to your local git branch
- Modify the crate as necessary
- Commit that modified version
- Use
git format-patch <unpatched version>
to generate the patch files - Add the patch files in a new, third, commit
- 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:
- Download the crate without applying patches:
vpython3 ./tools/crates/run_gnrt.py vendor --no-patches=<CRATE_NAME>
- Then recreate the patches as described in 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, 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
.
If you nevertheless need to manually update a crate to its latest minor
version, then follow the steps below:
- Change directory to the root
src/
dir of Chromium. - 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>
- 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
- 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.
- Generate the
BUILD.gn
filesvpython3 ./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
- 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 is
used to generate bindings between C++ and Rust.
See
//third_party/rust/serde_json_lenient/v0_1/wrapper/
and
//components/qr_code_generator
for examples.
Rust libraries should use the
rust_static_library
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 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 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)
).
Note that the standard library also includes a helpful
dbg!
macro which writes
everything about a variable to stderr
.
Logging may not yet work in component builds: crbug.com/374023535.
Tracing
TODO: crbug.com/377915495.
Strings
Prefer to use BString
and BStr
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
.
Using VSCode
- Ensure you're using the
rust-analyzer
extension for VSCode, rather than earlier forms of Rust support. - Run
gn
with the--export-rust-project
flag, such as:gn gen out/Release --export-rust-project
. ln -s out/Release/rust-project.json rust-project.json
- When you run VSCode, or any other IDE that uses
rust-analyzer 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. - 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:This assumes you are working with an output directory like{ // The rest of the settings... "rust-analyzer.cargo.extraEnv": { "PATH": "../../third_party/rust-toolchain/bin:$PATH", } }
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"