diff --git a/.gitattributes b/.gitattributes index 41cd9941435..05028087a74 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,6 +3,8 @@ *.png binary *.jpg binary +src/crypto/test/cbor_fuzz_corpus/* binary + *.h linguist-language=C++ *.cpp linguist-language=C++ diff --git a/.github/workflows/long-test.yml b/.github/workflows/long-test.yml index e9629da6c91..6f2b7bdb68b 100644 --- a/.github/workflows/long-test.yml +++ b/.github/workflows/long-test.yml @@ -190,6 +190,18 @@ jobs: cd build ./tests.sh --output-on-failure --timeout 1600 -LE "benchmark" + - name: "Run CBOR fuzz test" + run: | + set -o pipefail + set -ex + mkdir -p build_fuzz + cd build_fuzz + rm -f CMakeCache.txt + cmake -GNinja -DFUZZING=ON -DSAN=ON -DUSE_LIBCXX=ON -DUSE_SNMALLOC=OFF .. + ninja cbor_fuzz_test + mkdir -p /tmp/cbor_fuzz_live + ./cbor_fuzz_test /tmp/cbor_fuzz_live ../src/crypto/test/cbor_fuzz_corpus -max_total_time=60 + - name: "Upload logs" if: success() || failure() uses: actions/upload-artifact@v7 diff --git a/CMakeLists.txt b/CMakeLists.txt index 84da67ac8af..eb1a6aca5ae 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -677,6 +677,15 @@ if(BUILD_TESTS) target_include_directories(cbor_test PRIVATE ${CCFCRYPTO_INC}) target_link_libraries(cbor_test PRIVATE ccfcrypto) + if(FUZZING) + add_fuzz_test( + cbor_fuzz_test + ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/cbor.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/test/cbor_fuzz.cpp + ) + target_link_libraries(cbor_fuzz_test PRIVATE evercbor) + endif() + add_unit_test( sharing_test ${CMAKE_CURRENT_SOURCE_DIR}/src/crypto/test/secret_sharing.cpp diff --git a/cmake/common.cmake b/cmake/common.cmake index 98cb90e703d..15b2e0a87c4 100644 --- a/cmake/common.cmake +++ b/cmake/common.cmake @@ -52,6 +52,23 @@ function(add_unit_test name) add_san_test_properties(${name}) endfunction() +# Fuzz test wrapper (requires -DFUZZING=ON -DUSE_LIBCXX=ON) +function(add_fuzz_test name) + if(NOT USE_LIBCXX) + message( + FATAL_ERROR + "Fuzz targets require USE_LIBCXX=ON to avoid UBSAN false positives from libstdc++ shared_ptr" + ) + endif() + + add_executable(${name} ${CCF_DIR}/src/enclave/thread_local.cpp ${ARGN}) + target_compile_options(${name} PRIVATE ${COMPILE_LIBCXX} -fsanitize=fuzzer) + target_link_options(${name} PRIVATE -fsanitize=fuzzer) + target_include_directories(${name} PRIVATE src ${CCFCRYPTO_INC}) + target_link_libraries(${name} PRIVATE ${LINK_LIBCXX} -pthread) + add_san(${name}) +endfunction() + # Test binary wrapper function(add_test_bin name) add_executable(${name} ${CCF_DIR}/src/enclave/thread_local.cpp ${ARGN}) diff --git a/cmake/preproject.cmake b/cmake/preproject.cmake index 330eee587b0..bd20fdb337c 100644 --- a/cmake/preproject.cmake +++ b/cmake/preproject.cmake @@ -57,6 +57,11 @@ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) endif() option(TSAN "Enable Thread Sanitizers" OFF) +option(FUZZING "Enable libFuzzer fuzz testing" OFF) + +if(FUZZING AND TSAN) + message(FATAL_ERROR "FUZZING and TSAN cannot be enabled together") +endif() option(COLORED_OUTPUT "Always produce ANSI-colored output." ON) diff --git a/src/crypto/test/cbor_fuzz.cpp b/src/crypto/test/cbor_fuzz.cpp new file mode 100644 index 00000000000..fde45e0bab9 --- /dev/null +++ b/src/crypto/test/cbor_fuzz.cpp @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the Apache 2.0 License. + +#include "crypto/cbor.h" + +#include +#include +#include + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) +{ + ccf::cbor::Value value; + try + { + value = ccf::cbor::parse({data, size}); + } + catch (const ccf::cbor::CBORDecodeError&) + { + return 0; + } + + // If parse succeeded, exercise serialization round-trip and string + // rendering. Any failure here is a real bug — let the fuzzer surface it. + std::ignore = ccf::cbor::to_string(value); + auto serialized = ccf::cbor::serialize(value); + auto reparsed = ccf::cbor::parse(serialized); + auto reserialized = ccf::cbor::serialize(reparsed); + + if (serialized != reserialized) + { + __builtin_trap(); + } + + return 0; +} diff --git a/src/crypto/test/cbor_fuzz_corpus/array123 b/src/crypto/test/cbor_fuzz_corpus/array123 new file mode 100644 index 00000000000..4cdf6ce18a8 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/array123 @@ -0,0 +1 @@ +ƒ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/array_of_maps b/src/crypto/test/cbor_fuzz_corpus/array_of_maps new file mode 100644 index 00000000000..c3c8fd6e58b --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/array_of_maps @@ -0,0 +1 @@ +ƒ¡ax¡ay¡az \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/array_with_map b/src/crypto/test/cbor_fuzz_corpus/array_with_map new file mode 100644 index 00000000000..b4f56d78ccc --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/array_with_map @@ -0,0 +1 @@ +ƒ¡aa \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/bstr_hello b/src/crypto/test/cbor_fuzz_corpus/bstr_hello new file mode 100644 index 00000000000..e29f6100305 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/bstr_hello @@ -0,0 +1 @@ +Ehello \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/complex_array_map b/src/crypto/test/cbor_fuzz_corpus/complex_array_map new file mode 100644 index 00000000000..babbd2c030f --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/complex_array_map @@ -0,0 +1 @@ +ƒõ£ecount*elabeleitemsfactiveõö \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/cose_receipt b/src/crypto/test/cbor_fuzz_corpus/cose_receipt new file mode 100644 index 00000000000..729ab75d1a1 Binary files /dev/null and b/src/crypto/test/cbor_fuzz_corpus/cose_receipt differ diff --git a/src/crypto/test/cbor_fuzz_corpus/cose_sign1_detached b/src/crypto/test/cbor_fuzz_corpus/cose_sign1_detached new file mode 100644 index 00000000000..630e57ec8df Binary files /dev/null and b/src/crypto/test/cbor_fuzz_corpus/cose_sign1_detached differ diff --git a/src/crypto/test/cbor_fuzz_corpus/cose_sign1_flat b/src/crypto/test/cbor_fuzz_corpus/cose_sign1_flat new file mode 100644 index 00000000000..2bf42cd38f8 Binary files /dev/null and b/src/crypto/test/cbor_fuzz_corpus/cose_sign1_flat differ diff --git a/src/crypto/test/cbor_fuzz_corpus/cose_sign1_nested b/src/crypto/test/cbor_fuzz_corpus/cose_sign1_nested new file mode 100644 index 00000000000..b13c1bcd511 Binary files /dev/null and b/src/crypto/test/cbor_fuzz_corpus/cose_sign1_nested differ diff --git a/src/crypto/test/cbor_fuzz_corpus/deeply_nested_array b/src/crypto/test/cbor_fuzz_corpus/deeply_nested_array new file mode 100644 index 00000000000..47ac41671d5 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/deeply_nested_array @@ -0,0 +1 @@ +„‚‚‚ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/empty b/src/crypto/test/cbor_fuzz_corpus/empty new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/crypto/test/cbor_fuzz_corpus/empty_array b/src/crypto/test/cbor_fuzz_corpus/empty_array new file mode 100644 index 00000000000..5416677bc7d --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/empty_array @@ -0,0 +1 @@ +€ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/empty_bytes b/src/crypto/test/cbor_fuzz_corpus/empty_bytes new file mode 100644 index 00000000000..b516b2c489f --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/empty_bytes @@ -0,0 +1 @@ +@ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/empty_map b/src/crypto/test/cbor_fuzz_corpus/empty_map new file mode 100644 index 00000000000..eea1bf0c31f --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/empty_map @@ -0,0 +1 @@ +  \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/empty_string b/src/crypto/test/cbor_fuzz_corpus/empty_string new file mode 100644 index 00000000000..64845fb7679 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/empty_string @@ -0,0 +1 @@ +` \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/header_map_footer b/src/crypto/test/cbor_fuzz_corpus/header_map_footer new file mode 100644 index 00000000000..1f13ce2d5fe --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/header_map_footer @@ -0,0 +1 @@ +ƒfheader¢bid{dnamedtestffooter \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/int64_max b/src/crypto/test/cbor_fuzz_corpus/int64_max new file mode 100644 index 00000000000..32c451a3f95 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/int64_max @@ -0,0 +1 @@ +ÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/int64_min b/src/crypto/test/cbor_fuzz_corpus/int64_min new file mode 100644 index 00000000000..62bd9681dc3 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/int64_min @@ -0,0 +1 @@ +;ÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/int64_overflow b/src/crypto/test/cbor_fuzz_corpus/int64_overflow new file mode 100644 index 00000000000..bd19232503e Binary files /dev/null and b/src/crypto/test/cbor_fuzz_corpus/int64_overflow differ diff --git a/src/crypto/test/cbor_fuzz_corpus/int_widths b/src/crypto/test/cbor_fuzz_corpus/int_widths new file mode 100644 index 00000000000..626802657fd Binary files /dev/null and b/src/crypto/test/cbor_fuzz_corpus/int_widths differ diff --git a/src/crypto/test/cbor_fuzz_corpus/map1234 b/src/crypto/test/cbor_fuzz_corpus/map1234 new file mode 100644 index 00000000000..abc169a8cc5 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/map1234 @@ -0,0 +1 @@ +¢ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/map_bool_vals b/src/crypto/test/cbor_fuzz_corpus/map_bool_vals new file mode 100644 index 00000000000..59fc7561ca8 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/map_bool_vals @@ -0,0 +1 @@ +£genabledõhdisabledôgunknownö \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/map_multi_arrays b/src/crypto/test/cbor_fuzz_corpus/map_multi_arrays new file mode 100644 index 00000000000..f480825ba53 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/map_multi_arrays @@ -0,0 +1 @@ +£aa‚ab‚ac‚ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/map_neg_keys b/src/crypto/test/cbor_fuzz_corpus/map_neg_keys new file mode 100644 index 00000000000..e75caab7b54 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/map_neg_keys @@ -0,0 +1 @@ +¢ iminus one)iminus ten \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/map_str_str b/src/crypto/test/cbor_fuzz_corpus/map_str_str new file mode 100644 index 00000000000..67e2365942f --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/map_str_str @@ -0,0 +1 @@ +¢axaycfoocbar \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/map_with_array b/src/crypto/test/cbor_fuzz_corpus/map_with_array new file mode 100644 index 00000000000..13087c6c56e --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/map_with_array @@ -0,0 +1 @@ +¡eitemsƒ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/mixed_array b/src/crypto/test/cbor_fuzz_corpus/mixed_array new file mode 100644 index 00000000000..6977ce88f85 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/mixed_array @@ -0,0 +1 @@ +…ctwoA3õö \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/negint1 b/src/crypto/test/cbor_fuzz_corpus/negint1 new file mode 100644 index 00000000000..0519ecba6ea --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/negint1 @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/nested_arrays b/src/crypto/test/cbor_fuzz_corpus/nested_arrays new file mode 100644 index 00000000000..ab67b0db0b1 --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/nested_arrays @@ -0,0 +1 @@ +ƒ‚‚‚ \ No newline at end of file diff --git a/src/crypto/test/cbor_fuzz_corpus/nested_tags b/src/crypto/test/cbor_fuzz_corpus/nested_tags new file mode 100644 index 00000000000..b2822f0b43c --- /dev/null +++ b/src/crypto/test/cbor_fuzz_corpus/nested_tags @@ -0,0 +1 @@ +Ù#2Ù#