Avoid writing `ChannelManager` when hitting lnd bug 6039
[rust-lightning] / fuzz / README.md
1 # Fuzzing
2
3 Fuzz tests generate a ton of random parameter arguments to the program and then validate that none cause it to crash.
4
5 ## How does it work?
6
7 Typically, Travis CI will run `travis-fuzz.sh` on one of the environments the automated tests are configured for.
8 This is the most time-consuming component of the continuous integration workflow, so it is recommended that you detect
9 issues locally, and Travis merely acts as a sanity check. Fuzzing is further only effective with
10 a lot of CPU time, indicating that if crash scenarios are discovered on Travis with its low
11 runtime constraints, the crash is caused relatively easily.
12
13 ## How do I run fuzz tests locally?
14
15 You typically won't need to run the entire combination of different fuzzing tools. For local execution, `honggfuzz`
16 should be more than sufficient. 
17
18 ### Setup
19
20 To install `honggfuzz`, simply run
21
22 ```shell
23 cargo update
24 cargo install --force honggfuzz
25 ```
26
27 In some environments, you may want to pin the honggfuzz version to `0.5.52`:
28
29 ```shell
30 cargo update -p honggfuzz --precise "0.5.52"
31 cargo install --force honggfuzz --version "0.5.52"
32 ```
33
34 ### Execution
35
36 To run the Hongg fuzzer, do
37
38 ```shell
39 export CPU_COUNT=1 # replace as needed
40 export HFUZZ_BUILD_ARGS="--features honggfuzz_fuzz"
41 export HFUZZ_RUN_ARGS="-n $CPU_COUNT --exit_upon_crash"
42
43 export TARGET="msg_ping_target" # replace with the target to be fuzzed
44 cargo hfuzz run $TARGET
45 ```
46
47 (Or, for a prettier output, replace the last line with `cargo --color always hfuzz run $TARGET`.)
48
49 To see a list of available fuzzing targets, run:
50
51 ```shell
52 ls ./src/bin/
53 ```
54
55 ## A fuzz test failed on Travis, what do I do?
56
57 You're trying to create a PR, but need to find the underlying cause of that pesky fuzz failure blocking the merge?
58
59 Worry not, for this is easily traced.
60
61 If your Travis output log looks like this:
62
63 ```
64 Size:639 (i,b,hw,ed,ip,cmp): 0/0/0/0/0/1, Tot:0/0/0/2036/5/28604
65 Seen a crash. Terminating all fuzzing threads
66
67 … # a lot of lines in between
68
69 <0x0000555555565559> [func:UNKNOWN file: line:0 module:/home/travis/build/rust-bitcoin/rust-lightning/fuzz/hfuzz_target/x86_64-unknown-linux-gnu/release/full_stack_target]
70 <0x0000000000000000> [func:UNKNOWN file: line:0 module:UNKNOWN]
71 =====================================================================
72 2d3136383734090101010101010101010101010101010101010101010101
73 010101010100040101010101010101010101010103010101010100010101
74 0069d07c319a4961
75 The command "if [ "$(rustup show | grep default | grep stable)" != "" ]; then cd fuzz && cargo test --verbose && ./travis-fuzz.sh; fi" exited with 1.
76 ```
77
78 Note that the penultimate stack trace line ends in `release/full_stack_target]`. That indicates that
79 the failing target was `full_stack`. To reproduce the error locally, simply copy the hex, 
80 and run the following from the `fuzz` directory:
81
82 ```shell
83 export TARGET="full_stack" # adjust for your output
84 export HEX="2d3136383734090101010101010101010101010101010101010101010101\
85 010101010100040101010101010101010101010103010101010100010101\
86 0069d07c319a4961" # adjust for your output
87
88 mkdir -p ./test_cases/$TARGET
89 echo $HEX | xxd -r -p > ./test_cases/$TARGET/any_filename_works
90
91 export RUST_BACKTRACE=1
92 export RUSTFLAGS="--cfg=fuzzing"
93 cargo test
94 ```
95
96 Note that if the fuzz test failed locally, moving the offending run's trace 
97 to the `test_cases` folder should also do the trick; simply replace the `echo $HEX |` line above
98 with (the trace file name is of course a bit longer than in the example):
99
100 ```shell
101 mv hfuzz_workspace/fuzz_target/SIGABRT.PC.7ffff7e21ce1.STACK.[…].fuzz ./test_cases/$TARGET/
102 ```
103
104 This will reproduce the failing fuzz input and yield a usable stack trace.
105
106
107 ## How do I add a new fuzz test?
108
109 1. The easiest approach is to take one of the files in `fuzz/src/`, such as 
110 `process_network_graph.rs`, and duplicate it, renaming the new file to something more 
111 suitable. For the sake of example, let's call the new fuzz target we're creating 
112 `my_fuzzy_experiment`.
113
114 2. In the newly created file `fuzz/src/my_fuzzy_experiment.rs`, run a string substitution
115 of `process_network_graph` to `my_fuzzy_experiment`, such that the three methods in the
116 file are `do_test`, `my_fuzzy_experiment_test`, and `my_fuzzy_experiment_run`.
117
118 3. Adjust the body (not the signature!) of `do_test` as necessary for the new fuzz test.
119
120 4. In `fuzz/src/bin/gen_target.sh`, add a line reading `GEN_TEST my_fuzzy_experiment` to the 
121 first group of `GEN_TEST` lines (starting in line 9).
122
123 5. If your test relies on a new local crate, add that crate as a dependency to `fuzz/Cargo.toml`.
124
125 6. In `fuzz/src/lib.rs`, add the line `pub mod my_fuzzy_experiment`. Additionally, if 
126 you added a new crate dependency, add the `extern crate […]` import line.
127
128 7. Run `fuzz/src/bin/gen_target.sh`.
129
130 8. There is no step eight: happy fuzzing!