5.0 KiB
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.
Run fuzz tests locally
-
Build the fuzz tests:
$ cd <cdm_repo_path> $ oemcrypto/test/fuzz_tests/build_oemcrypto_fuzztests -
Run the fuzz test:
$ out/Default/<fuzz_test> [<corpus_dir>]The corpus directory is optional and can either be a seed corpus from the
corpussubdirectory 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:
-
Download the minimized testcase from the ClusterFuzz report.
-
Build the fuzz tests:
$ oemcrypto/test/fuzz_tests/build_oemcrypto_fuzztests -
Debug the crash:
$ gdb --args <fuzz_test_path> -timeout=0 <testcase_path>Example after substituting fuzz test and test case paths:
$ gdb --args out/Default/oemcrypto_opk_decrypt_cenc_fuzz -timeout=0 \ clusterfuzz-testcase-minimized-oemcrypto_v17_opk_decrypt_cenc_fuzz-6727459932078080 -
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 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, a class supplied with LLVM’s
libFuzzer, can be used to easily split input data:
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.
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.
Generate corpus with OEMCrypto unit tests
-
Build the unit tests:
$ 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 -
Run the unit tests with the
--generate_corpusflag:$ mkdir oemcrypto/test/fuzz_tests/corpus/<fuzz_test>_seed_corpus $ out/Default/oemcrypto_unittests --generate_corpus --gtest_filter='-*Huge*' -
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:
$ oemcrypto/test/fuzz_tests/build_oemcrypto_fuzztests $ mkdir /tmp/minimized_corpus $ out/Default/<fuzz_test> -merge=1 /tmp/minimized_corpus <full_corpus_dir>