Skip to content

Commit

Permalink
[rust] Document experimental Rust toolchain.
Browse files Browse the repository at this point in the history
Bug: 1069271
Change-Id: If50fc28b03b3df13534de62719a7564896488270
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3255785
Commit-Queue: Adrian Taylor <adetaylor@chromium.org>
Reviewed-by: danakj <danakj@chromium.org>
Cr-Commit-Position: refs/heads/main@{#937896}
  • Loading branch information
adetaylor authored and Chromium LUCI CQ committed Nov 3, 2021
1 parent dbe67cc commit 6c0a9da
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 1 deletion.
3 changes: 2 additions & 1 deletion docs/security/rule-of-2.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ are Java (on Android only) and JavaScript or WebAssembly (although we don't
currently use them in high-privilege processes like the browser). One can
imagine Swift on iOS or Kotlin on Android, too, although they are not currently
used in Chromium. (Some of us on Security Team aspire to get more of Chromium in
safer languages, but that's a long-term, heavy lift.)
safer languages, and you may be able to [help with our experiments](rust-toolchain.md).)

For an example of image processing, we have the pure-Java class
[BaseGifImage](https://cs.chromium.org/chromium/src/third_party/gif_player/src/jp/tomorrowkey/android/gifplayer/BaseGifImage.java?rcl=27febd503d1bab047d73df26db83184fff8d6620&l=27).
Expand All @@ -277,6 +277,7 @@ using trustworthy patterns can be used at high privilege to match on
untrustworthy input strings. This does not automatically turn the matched text
or captured groups into safe values.


## Safe Types

As discussed above in [Normalization](#normalization), there are some types that
Expand Down
167 changes: 167 additions & 0 deletions docs/security/rust-toolchain.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
# Experimental Rust toolchain

[TOC]

# Why?

Parsing untrustworthy data is a major source of security bugs, and it's therefore
against Chromium rules [to do it in the browser process](rule-of-2.md) unless
you can use a memory safe language.

For teams building browser process features which need to handle untrustworthy
data, they usually have to do the parsing in a utility process which incurs
a performance overhead and adds engineering complexity.

The Chrome security team is working to make a cross-platform memory safe language
available to Chromium developers. This document describes how to use that
language in Chromium. The language, at least for now, is Rust.

# Guidelines

Support for Rust in Chromium is experimental. We appreciate your help in these
experiments, but please remember that Rust is not supported for production use
cases.

So:

* any experiments must be reversible (you may have to write a C++ equivalent
in order to ship)
* Rust code must not affect production Chrome binaries nor be shipped to Chrome
users (we provide `#ifdef` and other facilities to make this easy) - so if
you put Rust code in Chrome, the sole purpose is to help experiment and provide
data for evaluation of future memory safe language options
* Rust is not yet available on all Chromium platforms (just Linux and Android
for now)
* Facilities and tooling in Rust are not as rich as other languages yet.

That said, if presence of Rust would make your feature easier, we are keen
for you to join in our experiments. Here's how. Please also let us know
your interest via `rust-dev@chromium.org`.

# GN support

Assume you want to add some Rust code to an existing C++ `source_set`.
Simply:

* `import("//build/rust/mixed_source_set.gni")`
* Replace `source_set` with `mixed_source_set`
* Add `rs_sources = [ "src/lib.rs" ]` (and likely `rs_cxx_bindings`, see below)
* Add your Rust code in `src/lib.rs`
* In your C++ code, make Rust calls based on the `#define` `RUST_ENABLED`.

In toolchains with Rust disabled, your `source_set` will continue to be a plain
C++ source set and absolutely nothing will change.

In toolchains with Rust, `RUST_ENABLED` will be defined and then you can
call into Rust code (again, see the section on C++/Rust interop bindings below).

## A note on source code naming

Within a mixed code source set, it's (currently) normal to have C/C++ code
in its main directory, whilst Rust code goes into a subdirectory called `src`
(and the main file is always called `lib.rs`.) This follows the practice of
other teams, but if you don't like it, that's fine: feel free to store your
`.rs` code alongside your `.cc` code, but specify also `rs_crate_root` in your
`mixed_source_set`.

## I'm not using a `source_set`

If your code isn't already a `source_set`, switching to a `mixed_source_set`
obviously isn't possible. Instead, you'll create a new Rust language target
- see `//build/rust/rust_source_set.gni`. C++ targets can simply depend on
this Rust target but with the suffix `_cpp_bindings` appended to the target
name:

```
deps = [ "//path/to/my_rust_target:my_rust_target_cpp_bindings" ]
```

If your Rust code calls back into C++, this is more complex in order to
avoid layering violations - look into `mutually_dependent_target` in
that `.gni` file.

# Unit tests

Rust supports unit tests within the primary source code files.
With either approach, you'll get a bonus `gn` target created called
`<your target name>_rs_unittests` which is an executable containing any Rust
unit tests in your code.

At present, there is no automatic integration of such unit tests into our
existing test infrastructure, but this is something we're working on.

# Third party dependencies

Adding Rust third party dependencies follows the same protocols
[as for C++ or other languages](../adding_to_third_party.md). But practically,
Rust libraries are almost always distributed as cargo "crates" which have
build scripts and metadata in `Cargo.toml` files.

The crate you need may already be listed in
`//third_party/rust/third_party.toml` - if so, just depend upon it like this:

```
deps = [ "//third_party/rust/cxx/v1:lib" ]
```

(Only those crates explicitly listed in `//third_party/rust/third_party.toml`
are visible to first-party code; other crates in `//third_party/rust` are
transitive dependencies).

If you need to add new Rust third-party dependencies, there are scripts and gn
templates to make it nearly automatic (except of course for review). Please
reach out to `rust-dev@chromium.org` for advice.

# C++/Rust interop

There are multiple different solutions for Rust/C++ interop. In this phase of our
experiments, we're supporting just one: [cxx, described in this excellent online book](https://cxx.rs).

To use this interop facility in Chromium:

* define your `#[cxx::bridge]` module in your `.rs` file
* in your `mixed_source_set`, add `rs_cxx_bindings = [ "src/lib.rs" ]`
* from your C++,

```
#ifdef RUST_ENABLED
#include "path/to/your/target/src/lib.rs.h`
#endif
```

You can now simply call functions and use types declared/defined in your CXX
bridge. A typical usage might be to pass a `const std::string&` or `rust::Slice<const uint8_t>`
from C++ into Rust and then return a struct with the parsed results.

If you need to call back into C++ from Rust, this is also supported -
`include!` directives within an `extern "C++"` section should work:

```
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
include!("path/to/my_cpp_header.h");
fn some_function_defined_in_cpp();
}
}
// Rust code calls ffi::some_function_defined_in_cpp()
```

Future work may expose existing C++ Chromium APIs to Rust with no need
to declare the interface in a `#[cxx::bridge]` module.

## Dependencies between Rust targets

If your `rust_source_set` exposes Rust APIs for other Rust targets in Chromium,
those targets should be able to depend directly on your `rust_source_set` target.

If you have a `mixed_source_set` or any other component which is intended for
both Rust _and_ C++ consumers, please reach out to `rust-dev@chromium.org`
with your use-case. (This _should_ be possible with the current gn rules but
layering here is fragile so we'd rather discuss it.)

# Example

To see an example of all this, look at `//build/rust/tests/test_variable_source_set`.

0 comments on commit 6c0a9da

Please sign in to comment.