aboutsummaryrefslogtreecommitdiffhomepage
path: root/docs/getting-started/new-project-guide/rust_lang.md
blob: 7d961ba416aafa6aa22f021b943c855ff095d1aa (plain)
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
---
layout: default
title: Integrating a Rust project
parent: Setting up a new project
grand_parent: Getting started
nav_order: 2
permalink: /getting-started/new-project-guide/rust-lang/
---

# Integrating a Rust project
{: .no_toc}

- TOC
{:toc}
---

The process of integrating a project written in Rust with OSS-Fuzz is very
similar to the general [Setting up a new project]({{ site.baseurl
}}/getting-started/new-project-guide/) process. The key specifics of integrating
a Rust project are outlined below.

## cargo-fuzz support

Rust integration with OSS-Fuzz is expected to use [`cargo
fuzz`](https://github.com/rust-fuzz/cargo-fuzz) to build fuzzers. The `cargo
fuzz` tool will build code with required compiler flags as well as link to the
correct libFuzzer on OSS-Fuzz itself. Note that using `cargo fuzz` also makes it
quite easy to run the fuzzers locally yourself if you get a failing test case!

## Project files

First you'll want to follow the [setup instructions for `cargo fuzz`
itself](https://rust-fuzz.github.io/book/). Afterwards your project should have:

* A top-level `fuzz` directory.
* A `fuzz/Cargo.toml` manifest which pulls in necessary dependencies to fuzz.
* Some `fuzz/fuzz_targets/*.rs` files which are the fuzz targets that will be
  compiled and run on OSS-Fuzz.

Note that you can customize this layout as well, but you'll need to edit some
the scripts below to integrate into OSS-Fuzz.

### project.yaml

The `language` attribute must be specified.

```yaml
language: rust
```

The only supported fuzzing engine and sanitizer are `libfuzzer` and `address`,
respectively.
[Example](https://github.com/google/oss-fuzz/blob/12ef3654b3e9adfd20b5a6afdde54819ba71493d/projects/serde_json/project.yaml#L3-L6)

```yaml
sanitizers:
  - address
fuzzing_engines:
  - libfuzzer
```

### Dockerfile

The OSS-Fuzz builder image has the latest nightly release of Rust as well as
`cargo fuzz` pre-installed and in `PATH`. In the `Dockerfile` for your project
all you'll need to do is fetch the latest copy of your code and install any
system dependencies necessary to build your project.
[Example](https://github.com/google/oss-fuzz/blob/12ef3654b3e9adfd20b5a6afdde54819ba71493d/projects/serde_json/Dockerfile#L18-L20)

```dockerfile
RUN git clone --depth 1 https://github.com/serde-rs/json json
```

### build.sh

Here it's expected that you'll build the fuzz targets for your project and then
copy the final binaries into the output directory.
[Example](https://github.com/google/oss-fuzz/blob/12ef3654b3e9adfd20b5a6afdde54819ba71493d/projects/serde_json/build.sh#L20):

```sh
cd $SRC/json
cargo fuzz build -O
cp fuzz/target/x86_64-unknown-linux-gnu/release/from_slice $OUT/
```

Note that you likely want to pass the `-O` flag to `cargo fuzz build` which
builds fuzzers in release mode. You may also want to pass the
`--debug-assertions` flag to enable more checks while fuzzing. In this example
the `from_slice` binary is the fuzz target.

With some bash-fu you can also automatically copy over all fuzz targets into
the output directory so when you add a fuzz target to your project it's
automatically integrated into OSS-Fuzz:

```sh
FUZZ_TARGET_OUTPUT_DIR=target/x86_64-unknown-linux-gnu/release
for f in fuzz/fuzz_targets/*.rs
do
    FUZZ_TARGET_NAME=$(basename ${f%.*})
    cp $FUZZ_TARGET_OUTPUT_DIR/$FUZZ_TARGET_NAME $OUT/
done
```

## Writing fuzzers using a test-style strategy

In Rust you will often have tests written in a way so they are only 
compiled into the final binary when build in test-mode. This is, achieved by
wrapping your test code in `cfg(test)`, e.g.
```rust
#[cfg(test)]
mod tests {
    use super::*;
    
    ...
```

Cargo-fuzz automatically enables the `fuzzing` feature, which means you can
follow a similar strategy to writing fuzzers as you do when writing tests.
Specifically, you can create modules wrapped in the `fuzzing` feature:
```rust
#[cfg(fuzzing)]
pub mod fuzz_logic {
    use super::*;

    ...
```
and then call the logic within `fuzz_logic` from your fuzzer. 

Furthermore, within your `.toml` files, you can then specify fuzzing-specific
depedencies by wrapping them as follows:
```
[target.'cfg(fuzzing)'.dependencies]
```
similar to how you wrap test-dependencies as follows:
```
[dev-dependencies]
```

Finally, you can also combine the testing logic you have and the fuzz logic. This
can be achieved simply by using 
```rust
#[cfg(any(test, fuzzing))]
```

A project that follows this structure is Linkerd2-proxy and the project files can be
seen [here](https://github.com/google/oss-fuzz/tree/master/projects/linkerd2-proxy).