diff --git a/.github/workflows/main.yaml b/.github/workflows/main.yaml new file mode 100644 index 0000000..73df599 --- /dev/null +++ b/.github/workflows/main.yaml @@ -0,0 +1,86 @@ +name: CI Fuzz +# Set an action secret called "CI_FUZZ_API_TOKEN" with an API token +# generated in CI Fuzz web interface. + +on: + workflow_dispatch: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +env: + # CI Sense gRPC URL. + FUZZING_SERVER_ADDRESS: grpc.code-intelligence.com:443 + # CI Sense HTTP URL. + WEB_APP_ADDRESS: https://app.code-intelligence.com + # Directory in which the repository will be cloned. + CHECKOUT_DIR: checkout-dir/ + CIFUZZ_DOWNLOAD_URL: "https://github.com/CodeIntelligenceTesting/cifuzz/releases/latest/download/cifuzz_installer_linux_amd64" + CIFUZZ_INSTALL_DIR: ./cifuzz + FUZZING_ARTIFACT: fuzzing-artifact.tar.gz +jobs: + fuzz_tests: + runs-on: ubuntu-latest + container: cifuzz/builder-zint:llvm-13 + steps: + - id: checkout + name: Checkout Repository + uses: actions/checkout@v2 + with: + path: ${{ env.CHECKOUT_DIR }} + - id: install-cifuzz + name: Install cifuzz + run: | + curl --fail --silent --show-error --location -o cifuzz_installer "$CIFUZZ_DOWNLOAD_URL" + chmod u+x cifuzz_installer + ./cifuzz_installer --install-dir $CIFUZZ_INSTALL_DIR + - id: install-depdendencies + name: Install Dependencies + run: | + apt-get update && apt-get install -y libssl-dev + - id: build-fuzzers + name: Build Fuzzers + run: | + export cifuzz_DIR="$GITHUB_WORKSPACE/$CIFUZZ_INSTALL_DIR/share/cmake" + cd $CHECKOUT_DIR/ + $GITHUB_WORKSPACE/$CIFUZZ_INSTALL_DIR/bin/cifuzz bundle \ + --commit $GITHUB_SHA \ + --branch $GITHUB_REF_NAME \ + --output $GITHUB_WORKSPACE/$CHECKOUT_DIR/$FUZZING_ARTIFACT + shell: "bash" + - id: start-fuzzing + name: Start Fuzzing + uses: CodeIntelligenceTesting/github-actions/start-fuzzing@v5 + with: + ci_fuzz_api_token: ${{ secrets.CI_FUZZ_API_TOKEN }} + fuzzing_server_address: ${{ env.FUZZING_SERVER_ADDRESS }} + fuzzing_artifact: ${{ env.CHECKOUT_DIR }}/${{ env.FUZZING_ARTIFACT }} + checkout_directory: ${{ env.CHECKOUT_DIR }} + project: "projects/prj-ho58fmjYVGuQ" + - id: monitor-fuzzing + name: Fuzzing + uses: CodeIntelligenceTesting/github-actions/monitor-fuzzing@v5 + with: + ci_fuzz_api_token: ${{ secrets.CI_FUZZ_API_TOKEN }} + test_collection_run: ${{ steps.start-fuzzing.outputs.test_collection_run }} + fuzzing_server_address: ${{ env.FUZZING_SERVER_ADDRESS }} + dashboard_address: ${{ env.WEB_APP_ADDRESS }} + - id: save-results + name: Save Fuzz Test Results + uses: CodeIntelligenceTesting/github-actions/save-results@v5 + if: ${{ success() || failure() }} + with: + ci_fuzz_api_token: ${{ secrets.CI_FUZZ_API_TOKEN }} + test_collection_run: ${{ steps.start-fuzzing.outputs.test_collection_run }} + fuzzing_server_address: ${{ env.FUZZING_SERVER_ADDRESS }} + dashboard_address: ${{ env.WEB_APP_ADDRESS }} + - id: upload-artifact + uses: actions/upload-artifact@v2 + if: ${{ (success() || failure()) }} + with: + name: ci_fuzz_results + path: | + findings.json + coverage.json + web_app_address.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 5dc9639..5df2020 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,5 +14,8 @@ set(CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake/external) enable_testing() include(googletest) +find_package(cifuzz NO_SYSTEM_ENVIRONMENT_PATH) +enable_fuzz_testing() + add_subdirectory(src/explore_me) -add_subdirectory(src/automotive) \ No newline at end of file +add_subdirectory(src/automotive) diff --git a/CMakeUserPresets.json b/CMakeUserPresets.json new file mode 100644 index 0000000..fad5c91 --- /dev/null +++ b/CMakeUserPresets.json @@ -0,0 +1,100 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "cifuzz (Coverage)", + "displayName": "cifuzz (Coverage)", + "binaryDir": "${sourceDir}/.cifuzz-build/replayer/gcov", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CIFUZZ_ENGINE": "replayer", + "CIFUZZ_SANITIZERS": "gcov", + "CIFUZZ_TESTING": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_RPATH_USE_ORIGIN": { + "type": "BOOL", + "value": "ON" + } + } + }, + { + "name": "cifuzz (Fuzzing)", + "displayName": "cifuzz (Fuzzing)", + "binaryDir": "${sourceDir}/.cifuzz-build/libfuzzer/address+undefined", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CIFUZZ_ENGINE": "libfuzzer", + "CIFUZZ_SANITIZERS": "address;undefined", + "CIFUZZ_TESTING": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_RPATH_USE_ORIGIN": { + "type": "BOOL", + "value": "ON" + } + }, + "environment": { + "CC": "clang", + "CXX": "clang++" + } + }, + { + "name": "cifuzz (Regression Test)", + "displayName": "cifuzz (Regression Test)", + "binaryDir": "${sourceDir}/.cifuzz-build/replayer/address+undefined", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo", + "CIFUZZ_ENGINE": "replayer", + "CIFUZZ_SANITIZERS": "address;undefined", + "CIFUZZ_TESTING": { + "type": "BOOL", + "value": "ON" + }, + "CMAKE_BUILD_RPATH_USE_ORIGIN": { + "type": "BOOL", + "value": "ON" + } + } + } + ], + "buildPresets": [ + { + "name": "cifuzz (Coverage)", + "displayName": "cifuzz (Coverage)", + "configurePreset": "cifuzz (Coverage)", + "configuration": "RelWithDebInfo" + }, + { + "name": "cifuzz (Fuzzing)", + "displayName": "cifuzz (Fuzzing)", + "configurePreset": "cifuzz (Fuzzing)", + "configuration": "RelWithDebInfo" + }, + { + "name": "cifuzz (Regression Test)", + "displayName": "cifuzz (Regression Test)", + "configurePreset": "cifuzz (Regression Test)", + "configuration": "RelWithDebInfo" + } + ], + "testPresets": [ + { + "name": "cifuzz (Regression Test)", + "displayName": "cifuzz (Regression Test)", + "configurePreset": "cifuzz (Regression Test)", + "filter": { + "include": { + "label": "^cifuzz_regression_test$" + } + } + } + ] +} diff --git a/cifuzz.yaml b/cifuzz.yaml new file mode 100644 index 0000000..3c10b0d --- /dev/null +++ b/cifuzz.yaml @@ -0,0 +1,46 @@ +## Configuration for a CI Fuzz project +## Generated on 2023-06-06 + +## The build system used to build this project. If not set, cifuzz tries +## to detect the build system automatically. +## Valid values: "bazel", "cmake", "maven", "gradle", "other". +#build-system: cmake + +## If the build system type is "other", this command is used by +## `cifuzz run` to build the fuzz test. +#build-command: "make my_fuzz_test" + +## Directories containing sample inputs for the code under test. +## See https://llvm.org/docs/LibFuzzer.html#corpus +#seed-corpus-dirs: +# - path/to/seed-corpus + +## A file containing input language keywords or other interesting byte +## sequences. +## See https://llvm.org/docs/LibFuzzer.html#dictionaries +#dict: path/to/dictionary.dct + +## Command-line arguments to pass to libFuzzer. +## See https://llvm.org/docs/LibFuzzer.html#options +engine-args: + - -use_value_profile=1 + +## Maximum time to run fuzz tests. The default is to run indefinitely. +#timeout: 30m + +## By default, fuzz tests are executed in a sandbox to prevent accidental +## damage to the system. Set to false to run fuzz tests unsandboxed. +## Only supported on Linux. +#use-sandbox: false + +## Set to true to print output of the `cifuzz run` command as JSON. +#print-json: true + +## Set to true to disable desktop notifications +#no-notifications: true + +## Set URL of the CI App +#server: https://app.code-intelligence.com + +## Set the project name on the CI App +#project: my-project-1a2b3c4d diff --git a/src/automotive/CMakeLists.txt b/src/automotive/CMakeLists.txt index 6fc7507..ba4b591 100644 --- a/src/automotive/CMakeLists.txt +++ b/src/automotive/CMakeLists.txt @@ -11,4 +11,13 @@ target_include_directories(automotive PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/gps ${CMAKE_CURRENT_SOURCE_DIR}/key_management ${CMAKE_CURRENT_SOURCE_DIR}/time -) \ No newline at end of file +) + +add_fuzz_test(automotive_fuzzer + fuzz_test.cpp + mocks.cpp +) + +target_link_libraries(automotive_fuzzer + automotive +) diff --git a/src/automotive/fuzz_test.cpp b/src/automotive/fuzz_test.cpp new file mode 100644 index 0000000..f3b62e1 --- /dev/null +++ b/src/automotive/fuzz_test.cpp @@ -0,0 +1,209 @@ + +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "crypto_1.h" +#include "crypto_2.h" +#include "gps_1.h" +#include "key_management_1.h" +#include "time_1.h" + +#ifdef __cplusplus +} +#endif + +void SetFDP(FuzzedDataProvider *fuzzed_data_provider); +FuzzedDataProvider *GetFDP(); +void ConsumeDataAndFillRestWithZeros(void *destination, size_t num_bytes); + +FUZZ_TEST_SETUP() {} + +FUZZ_TEST(const uint8_t *data, size_t size) { + + // Ensure a minimum data length + if (size < 100) + return; + + // Setup FuzzedDataProvider and initialize the mocklib + FuzzedDataProvider fdp(data, size); + SetFDP(&fdp); + + int number_of_functions = GetFDP()->ConsumeIntegralInRange(1, 100); + for (int i = 0; i < number_of_functions; i++) { + int function_id = GetFDP()->ConsumeIntegralInRange(0, 15); + switch (function_id) { + + case 0: { + crypto_get_state(); + break; + } + + case 1: { + get_destination_position(); + break; + } + + case 2: { + crypto_key key = {}; + ConsumeDataAndFillRestWithZeros(key.key, 64); + + crypto_verify_key(key); + break; + } + + case 3: { + current_time(); + break; + } + + case 4: { + crypto_nonce nonce_tmp = {}; + ConsumeDataAndFillRestWithZeros(nonce_tmp.nonce, 64); + nonce_tmp.time_of_creation = GetFDP()->ConsumeIntegral(); + crypto_nonce *nonce = &nonce_tmp; + + crypto_verify_nonce(nonce); + break; + } + + case 5: { + std::vector message_vec = GetFDP()->ConsumeBytes( + sizeof(uint8_t) * GetFDP()->ConsumeIntegral()); + const uint8_t *message = (const uint8_t *)message_vec.data(); + + // The parameter "len" seems to represent the length of a buffer/array. In + // this case, we usually don't want to provide fuzzer-generated lengths + // that differ from the actual length of the buffer. If you confirm that + // the parameter is a length parameter, you can get the length of the + // fuzzer-generated buffer as follows (replace "buffer" with the actual + // variable): + // int len = buffer.size(); + int len = message_vec.size(); + crypto_hmac hmac_tmp = {}; + ConsumeDataAndFillRestWithZeros(hmac_tmp.hmac, 64); + crypto_hmac *hmac = &hmac_tmp; + + crypto_verify_hmac(message, len, hmac); + break; + } + + case 6: { + crypto_nonce nonce = {}; + ConsumeDataAndFillRestWithZeros(nonce.nonce, 64); + nonce.time_of_creation = GetFDP()->ConsumeIntegral(); + + crypto_set_nonce(nonce); + break; + } + + case 7: { + std::vector message_vec = GetFDP()->ConsumeBytes( + sizeof(uint8_t) * GetFDP()->ConsumeIntegral()); + const uint8_t *message = (const uint8_t *)message_vec.data(); + + // The parameter "len" seems to represent the length of a buffer/array. In + // this case, we usually don't want to provide fuzzer-generated lengths + // that differ from the actual length of the buffer. If you confirm that + // the parameter is a length parameter, you can get the length of the + // fuzzer-generated buffer as follows (replace "buffer" with the actual + // variable): + // int len = buffer.size(); + int len = message_vec.size(); + crypto_hmac hmac_tmp = {}; + ConsumeDataAndFillRestWithZeros(hmac_tmp.hmac, 64); + crypto_hmac *hmac = &hmac_tmp; + + crypto_calculate_hmac(message, len, hmac); + break; + } + + case 8: { + GPS_position position = {}; + position.longitude_degree = GetFDP()->ConsumeIntegral(); + position.longitude_minute = GetFDP()->ConsumeIntegral(); + position.longitude_second = GetFDP()->ConsumeIntegral(); + position.latitude_degree = GetFDP()->ConsumeIntegral(); + position.latitude_minute = GetFDP()->ConsumeIntegral(); + position.latitude_second = GetFDP()->ConsumeIntegral(); + + set_destination_postition(position); + break; + } + + case 9: { + crypto_key key = {}; + ConsumeDataAndFillRestWithZeros(key.key, 64); + + crypto_set_key(key); + break; + } + + case 10: { + GPS_position position_tmp = {}; + position_tmp.longitude_degree = GetFDP()->ConsumeIntegral(); + position_tmp.longitude_minute = GetFDP()->ConsumeIntegral(); + position_tmp.longitude_second = GetFDP()->ConsumeIntegral(); + position_tmp.latitude_degree = GetFDP()->ConsumeIntegral(); + position_tmp.latitude_minute = GetFDP()->ConsumeIntegral(); + position_tmp.latitude_second = GetFDP()->ConsumeIntegral(); + GPS_position *position = &position_tmp; + + get_current_position(position); + break; + } + + case 11: { + init_crypto_module(); + break; + } + + case 12: { + std::vector key_vec = GetFDP()->ConsumeBytes( + sizeof(uint8_t) * GetFDP()->ConsumeIntegral()); + uint8_t *key = (uint8_t *)key_vec.data(); + + // The parameter "length" seems to represent the length of a buffer/array. + // In this case, we usually don't want to provide fuzzer-generated lengths + // that differ from the actual length of the buffer. If you confirm that + // the parameter is a length parameter, you can get the length of the + // fuzzer-generated buffer as follows (replace "buffer" with the actual + // variable): + // uint8_t length = buffer.size(); + uint8_t length = key_vec.size(); + + key_management_create_key(key, length); + break; + } + + case 13: { + std::vector nonce_vec = GetFDP()->ConsumeBytes( + sizeof(uint8_t) * GetFDP()->ConsumeIntegral()); + uint8_t *nonce = (uint8_t *)nonce_vec.data(); + + // The parameter "length" seems to represent the length of a buffer/array. + // In this case, we usually don't want to provide fuzzer-generated lengths + // that differ from the actual length of the buffer. If you confirm that + // the parameter is a length parameter, you can get the length of the + // fuzzer-generated buffer as follows (replace "buffer" with the actual + // variable): + // uint8_t length = buffer.size(); + uint8_t length = nonce_vec.size(); + + key_management_create_nonce(nonce, length); + break; + } + + case 14: { + crypto_init(); + break; + } + } + } +} diff --git a/src/automotive/mocks.cpp b/src/automotive/mocks.cpp new file mode 100644 index 0000000..7def44e --- /dev/null +++ b/src/automotive/mocks.cpp @@ -0,0 +1,83 @@ + +#include +#include +#include +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include "crypto_1.h" +#include "gps_1.h" +#include "key_management_1.h" +#include "time_1.h" + +#ifdef __cplusplus +} +#endif + +static FuzzedDataProvider *gFDP; + +// This function received the fuzzer generated data from the fuzz target. +// It needs to be called at the beginning of the LLVMFuzzerTestOneInput +// function. +void SetFDP(FuzzedDataProvider *fuzzed_data_provider) { + gFDP = fuzzed_data_provider; +} + +FuzzedDataProvider *GetFDP() { return gFDP; } + +// Wrapper function for FuzzedDataProvider.h +// Writes |num_bytes| of input data to the given destination pointer. If there +// is not enough data left, writes all remaining bytes and fills the rest with +// zeros. Return value is the number of bytes written. +void ConsumeDataAndFillRestWithZeros(void *destination, size_t num_bytes) { + if (destination != nullptr) { + size_t num_consumed_bytes = GetFDP()->ConsumeData(destination, num_bytes); + if (num_bytes > num_consumed_bytes) { + size_t num_zero_bytes = num_bytes - num_consumed_bytes; + std::memset((char *)destination + num_consumed_bytes, 0, num_zero_bytes); + } + } +} + +#ifdef __cplusplus +extern "C" { +#endif + +int driver_get_current_time() { + int cifuzz_var_0 = GetFDP()->ConsumeIntegral(); + return cifuzz_var_0; +} + +uint8_t GPS_driver_obtain_current_position(uint8_t *position_as_bytes, + uint8_t *hmac_as_bytes) { + unsigned int position_as_bytes_length = 12; + ConsumeDataAndFillRestWithZeros((void *)position_as_bytes, + position_as_bytes_length); + unsigned int hmac_as_bytes_length = 64; + ConsumeDataAndFillRestWithZeros((void *)hmac_as_bytes, hmac_as_bytes_length); + uint8_t cifuzz_var_1 = GetFDP()->ConsumeIntegral(); + return cifuzz_var_1; +} + +uint8_t HSM_get_random_byte() { + uint8_t cifuzz_var_2 = GetFDP()->ConsumeIntegral(); + return cifuzz_var_2; +} + +uint8_t third_party_library_calc_hmac(const uint8_t *message, int len, + const char *key, const char *nonce, + uint8_t *hmac) { + unsigned int hmac_length = 64; + ConsumeDataAndFillRestWithZeros((void *)hmac, hmac_length); + uint8_t cifuzz_var_3 = GetFDP()->ConsumeIntegral(); + return cifuzz_var_3; +} + +#ifdef __cplusplus +} +#endif diff --git a/src/explore_me/CMakeLists.txt b/src/explore_me/CMakeLists.txt index a3c3e04..9273a3d 100644 --- a/src/explore_me/CMakeLists.txt +++ b/src/explore_me/CMakeLists.txt @@ -34,4 +34,13 @@ foreach(TestType IN ITEMS ) add_test(explore_me.${TestType} ${TestType}_test) + + add_fuzz_test(${TestType}_fuzz_test + ${TestType}_test.cpp + ) + + target_link_libraries(${TestType}_fuzz_test + explore_me + ${GTEST_BOTH_LIBRARIES} + ) endforeach(TestType ) diff --git a/src/explore_me/complex_checks_test.cpp b/src/explore_me/complex_checks_test.cpp index 190e014..aea3746 100644 --- a/src/explore_me/complex_checks_test.cpp +++ b/src/explore_me/complex_checks_test.cpp @@ -11,3 +11,12 @@ TEST(ExploreComplexChecksTests, DeveloperTest) { TEST(ExploreComplexChecksTests, MaintainerTest) { EXPECT_NO_THROW(ExploreComplexChecks(20, -10, "Maintainer")); } + +FUZZ_TEST(const uint8_t *data, size_t size) { + FuzzedDataProvider fdp(data, size); + long a = fdp.ConsumeIntegral(); + long b = fdp.ConsumeIntegral(); + std::string c = fdp.ConsumeRemainingBytesAsString(); + + ExploreComplexChecks(a, b, c); +} diff --git a/src/explore_me/simple_checks_test.cpp b/src/explore_me/simple_checks_test.cpp index 2eec307..af2fcfa 100644 --- a/src/explore_me/simple_checks_test.cpp +++ b/src/explore_me/simple_checks_test.cpp @@ -11,3 +11,12 @@ TEST(ExploreSimpleChecksTests, DeveloperTest) { TEST(ExploreSimpleChecksTests, MaintainerTest) { EXPECT_NO_THROW(ExploreSimpleChecks(20, -10, "Maintainer")); } + +FUZZ_TEST(const uint8_t *data, size_t size) { + FuzzedDataProvider fdp(data, size); + int a = fdp.ConsumeIntegral(); + int b = fdp.ConsumeIntegral(); + std::string c = fdp.ConsumeRemainingBytesAsString(); + + ExploreSimpleChecks(a, b, c); +}