Files
android/libwvdrmengine/oemcrypto/test/fuzz_tests/README.md
Ian Benz 14c5d6ee5f Move internal fuzz target naming scheme to g3doc
Change-Id: I400b0a34c670673aba9dd347ec41060b4b23897a
2024-01-26 16:26:25 -08:00

141 lines
5.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# OEMCrypto fuzzing
ClusterFuzz and Google Cloud infrastructure continuously runs OEMCrypto fuzz
tests and reports crashes. To create a new automated fuzzing setup, refer to
[*ClusterFuzz setup*][1].
## Run fuzz tests locally
1. Build the fuzz tests:
```shell
$ cd <cdm_repo_path>
$ oemcrypto/test/fuzz_tests/build_oemcrypto_fuzztests
```
2. Run the fuzz test:
```shell
$ out/Default/<fuzz_test> [<corpus_dir>]
```
The corpus directory is optional and can either be a seed corpus from the
`corpus` subdirectory or be an empty directory. The corpus will be extended
with new inputs while the fuzz test is running.
## Triage crashes
To reproduce a crash locally for debugging:
1. Download the minimized testcase from the ClusterFuzz report.
2. Build the fuzz tests:
```shell
$ oemcrypto/test/fuzz_tests/build_oemcrypto_fuzztests
```
3. Debug the crash:
```shell
$ gdb --args <fuzz_test_path> -timeout=0 <testcase_path>
```
Example after substituting fuzz test and test case paths:
```shell
$ gdb --args out/Default/oemcrypto_opk_decrypt_cenc_fuzz -timeout=0 \
clusterfuzz-testcase-minimized-oemcrypto_v17_opk_decrypt_cenc_fuzz-6727459932078080
```
4. If reproducing the crash is unsuccessful, download the unminimized testcase
from the ClusterFuzz report and try again. If still unsuccessful, this may
indicate there is a persistent state issue with the fuzz test.
Once the root cause of the crash is identified, its severity and complexity
should be assessed. The [*SEI CERT C Coding Standard*][2] is a good resource for
risk assessment. The ClusterFuzz report will also provide input in the Security
field. For complex fixes with a longer timeline, ClusterFuzz may report
duplicate crashes with the same root cause.
## Write fuzz tests
While fuzzing has random elements, input data mutations are heavily influenced
by coverage feedback. Since discovering new control flow edges is a time
consuming process, input bytes should map to control flow edges in a simple,
predictable way. [`FuzzedDataProvider`][3], a class supplied with LLVMs
libFuzzer, can be used to easily split input data:
```cpp
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider fuzzed_data(data, size);
// One bit of input data maps to this control flow edge:
if (fuzzed_data.ConsumeBool()) {
// ...
}
// ...
}
```
Fuzzing API methods with complex, structured input, may benefit from a seed
corpus containing a representative set of starting inputs. Unfortunately,
`FuzzedDataProvider` is not suitable for fuzz tests utilizing a seed corpus
since there is no equivalent serialization functionality for generating the
corpus. OEMCrypto fuzz tests have previously used struct-based serialization,
but this is no longer recommended due to portability issues. Protocol Buffers or
another portable serialization format should be considered instead.
Fuzz tests must be deterministic to reproduce and debug a crash. A common
pitfall is not resetting the OEMCrypto API state between calls to
`LLVMFuzzerTestOneInput`. Fully terminating OEMCrypto between inputs is
preferred, but in some cases, it may be necessary to implement careful
optimizations to achieve acceptable performance. Candidates for optimization
typically have less than 1000 executions per second (exec/s).
`LLVMFuzzerInitialize` can be used for global initialization, but there is no
corresponding termination method.
A good starting example is [`oemcrypto_install_oem_private_key_fuzz.cc`][4].
Targets should be added to `oemcrypto_opk_fuzztests.gyp` and, if the fuzz test
applies to partner OEMCrypto implementations, `partner_oemcrypto_fuzztests.gyp`.
The infrastructure expects that the target name starts with *oemcrypto* and ends
with *fuzz*.
For additional information about writing fuzz tests, see
[*What makes a good fuzz target*][5].
## Generate corpus with OEMCrypto unit tests
1. Build the unit tests:
```shell
$ cd <cdm_repo_path>
$ export CDM_DIR=${PWD}
$ export PATH_TO_CDM_DIR=..
$ gyp --format=ninja --depth=${PWD} oemcrypto/oemcrypto_unittests.gyp
$ ninja -C out/Default
```
2. Run the unit tests with the `--generate_corpus` flag:
```shell
$ mkdir oemcrypto/test/fuzz_tests/corpus/<fuzz_test>_seed_corpus
$ out/Default/oemcrypto_unittests --generate_corpus --gtest_filter='-*Huge*'
```
3. The unit tests can generate many duplicate corpus files. To minimize the
corpus to only the subset of inputs that cover unique paths within the API:
```shell
$ oemcrypto/test/fuzz_tests/build_oemcrypto_fuzztests
$ mkdir /tmp/minimized_corpus
$ out/Default/<fuzz_test> -merge=1 /tmp/minimized_corpus <full_corpus_dir>
```
[1]: clusterfuzz_setup.md
[2]: https://wiki.sei.cmu.edu/confluence/display/c/SEI+CERT+C+Coding+Standard
[3]: https://github.com/llvm/llvm-project/blob/main/compiler-rt/include/fuzzer/FuzzedDataProvider.h
[4]: oemcrypto_install_oem_private_key_fuzz.cc
[5]: https://github.com/google/fuzzing/blob/master/docs/good-fuzz-target.md