From 203416dfdfc205826879938d223c1d3169b3af6a Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Tue, 12 Aug 2025 19:56:11 -0400 Subject: [PATCH 01/15] Initial use of fuzztest submodule --- .gitmodules | 4 ++++ CMakeLists.txt | 5 +++++ deps/fuzztest | 1 + 3 files changed, 10 insertions(+) create mode 160000 deps/fuzztest diff --git a/.gitmodules b/.gitmodules index 5ac9aba0..9a7a3b7b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,3 +10,7 @@ path = deps/QCBOR url = https://github.com/laurencelundblade/QCBOR.git branch = master +[submodule "deps/fuzztest"] + path = deps/fuzztest + url = https://github.com/google/fuzztest.git + branch = main diff --git a/CMakeLists.txt b/CMakeLists.txt index 2a56abdc..8b27dcaa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ option(BUILD_DOCS_MAN "Enable manpage building" OFF) option(BUILD_TESTING "Enable building unit tests" ON) option(TEST_MEMCHECK "Enable test runtime memory checking" ON) option(TEST_COVERAGE "Enable test runtime coverage logging" ON) +option(TEST_FUZZ "Enable fuzz testing" OFF) option(BUILD_PACKAGE "Enable building package outputs" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -162,6 +163,10 @@ if(BUILD_TESTING) BASE_DIRECTORY "${PROJECT_SOURCE_DIR}" ) endif(TEST_COVERAGE) + if(TEST_FUZZ) + add_subdirectory(deps/fuzztest) + fuzztest_setup_fuzzing_flags() + endif(TEST_FUZZ) include(CTest) set(CMAKE_CTEST_ARGUMENTS diff --git a/deps/fuzztest b/deps/fuzztest new file mode 160000 index 00000000..5132f07a --- /dev/null +++ b/deps/fuzztest @@ -0,0 +1 @@ +Subproject commit 5132f07a75689c04ee6827839e748ce48cdc1f25 From eb57fbfd1479d11cb12543baa06fd53c6edf831f Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 08:23:16 -0400 Subject: [PATCH 02/15] Working fuzztest example but issue with the library itself --- CMakeLists.txt | 14 ++--- deps/fuzztest | 2 +- resources/apply_format.sh | 2 +- src/BPSecLib_Private.h | 11 +++- src/BPSecLib_Public.h | 12 +++- src/BSLConfig.h.in | 34 ++++++------ src/backend/HostInterface.c | 2 +- src/backend/PolicyProvider.c | 2 +- src/mock_bpa/agent.c | 2 +- src/mock_bpa/agent.h | 2 +- src/mock_bpa/decode.c | 2 +- src/mock_bpa/eid.h | 2 +- test/CMakeLists.txt | 14 +++++ test/fuzz_mock_bpa.cpp | 104 +++++++++++++++++++++++++++++++++++ test/test_MockBPA_Codecs.c | 5 +- 15 files changed, 172 insertions(+), 38 deletions(-) create mode 100644 test/fuzz_mock_bpa.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b27dcaa..8aa89b95 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,7 +60,7 @@ STRING(REPLACE "-" "." GIT_TAG_MOD ${GIT_TAG_MOD}) message(STATUS "Using version marking ${GIT_TAG_VERS} - ${GIT_TAG_MOD}") project(bsl - LANGUAGES C + LANGUAGES C CXX VERSION ${GIT_TAG_VERS} ) @@ -76,21 +76,19 @@ add_definitions( # Generic warn/error options add_compile_options( -Wall - -Wextra - -Wpedantic - -Werror + $<$:-Wextra> + $<$:-Wpedantic> + $<$:-Werror> -Wshadow -Wpointer-arith -Wstrict-prototypes -Wmissing-prototypes -Wredundant-decls -Wcast-align - -Wformat=2 -Wswitch-enum + -Wformat=2 -fno-strict-aliasing -Werror=format-security -fno-common -Wstrict-aliasing=2 # -Wconversion -) -add_compile_options( -ffunction-sections -fdata-sections -fno-omit-frame-pointer - $<$:-fno-exceptions> + $<$:-Wswitch-enum> ) if(CMAKE_C_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0") diff --git a/deps/fuzztest b/deps/fuzztest index 5132f07a..bdb32e6a 160000 --- a/deps/fuzztest +++ b/deps/fuzztest @@ -1 +1 @@ -Subproject commit 5132f07a75689c04ee6827839e748ce48cdc1f25 +Subproject commit bdb32e6a121600e53b5fc97fdf12c61ccfce941b diff --git a/resources/apply_format.sh b/resources/apply_format.sh index 00eb5158..acdf50e1 100755 --- a/resources/apply_format.sh +++ b/resources/apply_format.sh @@ -38,7 +38,7 @@ if [[ "$#" -ne 0 ]] then ARGS="$@" else - ARGS=$(find src test -iname '*.h' -o -iname '*.c') + ARGS=$(find src test -iname '*.h' -o -iname '*.c' -o -iname '*.cpp') fi clang-format --style=file -i $ARGS diff --git a/src/BPSecLib_Private.h b/src/BPSecLib_Private.h index d9a27484..05cd0edd 100644 --- a/src/BPSecLib_Private.h +++ b/src/BPSecLib_Private.h @@ -47,6 +47,10 @@ #include "BPSecLib_Public.h" +#ifdef __cplusplus +extern "C" { +#endif + /** @brief Catalog of error code * * @note BSL error codes are negative, such that a caller can check `if (BSL_MyFunc(...) < 0)` for errors. @@ -553,7 +557,7 @@ int BSL_BundleCtx_GetBundleMetadata(const BSL_BundleRef_t *bundle, BSL_PrimaryBl * @param[out] result_count Contains the number of elements put into the array * @return 0 on success, negative on error */ -int BSL_BundleCtx_GetBlockIds(const BSL_BundleRef_t *bundle, size_t array_count, uint64_t block_ids_array[array_count], +int BSL_BundleCtx_GetBlockIds(const BSL_BundleRef_t *bundle, size_t array_count, uint64_t *block_ids_array, size_t *result_count); /** @brief Returns information about the bundle Canonical block @@ -1328,4 +1332,9 @@ struct BSL_SecCtxDesc_s BSL_SecCtx_Execute_f execute; }; +#ifdef __cplusplus +} // extern C +#endif + #endif /* BSL_BPSECLIB_PRIVATE_H_ */ + diff --git a/src/BPSecLib_Public.h b/src/BPSecLib_Public.h index 0e17f762..2d3d9fa2 100644 --- a/src/BPSecLib_Public.h +++ b/src/BPSecLib_Public.h @@ -37,6 +37,10 @@ #include "BSLConfig.h" +#ifdef __cplusplus +extern "C" { +#endif + /// This annotation on a function requires the caller to capture and inspect the return value. #if defined(__GNUC__) || defined(__clang__) #define BSL_REQUIRE_CHECK __attribute__((warn_unused_result)) @@ -193,8 +197,8 @@ typedef struct int (*bundle_metadata_fn)(const BSL_BundleRef_t *bundle_ref, BSL_PrimaryBlock_t *result_primary_block); /// @brief Host BPA function to populate a pre-allocated array with canonical block IDs - int (*bundle_get_block_ids)(const BSL_BundleRef_t *bundle_ref, size_t array_count, - uint64_t array_block_ids[array_count], size_t *result_count); + int (*bundle_get_block_ids)(const BSL_BundleRef_t *bundle_ref, size_t array_count, uint64_t *array_block_ids, + size_t *result_count); /// @brief Host BPA function to populate a Canonical Block struct for a given block number. int (*block_metadata_fn)(const BSL_BundleRef_t *bundle_ref, uint64_t block_num, BSL_CanonicalBlock_t *result_block); @@ -321,4 +325,8 @@ BSL_REQUIRE_CHECK int BSL_API_ApplySecurity(const BSL_LibCtx_t *bsl, BSL_SecurityResponseSet_t *response_output, BSL_BundleRef_t *bundle, const BSL_SecurityActionSet_t *policy_actions); +#ifdef __cplusplus +} // extern C +#endif + #endif /* BSL_BPSECLIB_PUBLIC_H_ */ diff --git a/src/BSLConfig.h.in b/src/BSLConfig.h.in index a69bc0f2..0bdb33c1 100644 --- a/src/BSLConfig.h.in +++ b/src/BSLConfig.h.in @@ -32,6 +32,10 @@ extern "C" { #endif +/** Part of POSIX.1-2008 + */ +#cmakedefine HAVE_CLOCK_GETTIME + /** Compile-time library version based on SemVer-2.0.0 conventions. * @sa Use bsl_version() to get run-time version. */ @@ -73,35 +77,33 @@ const char * bsl_version(void); #define BSL_CALLOC calloc #endif /* BSL_CALLOC */ +/** Force the use of M_ prefixed macros for M*LIB + */ +//#define M_USE_SMALL_NAME 0 + +#undef M_MEMORY_ALLOC /** Define to override value/struct allocation. * See m-core.h for details. */ -#undef M_MEMORY_ALLOC -#define M_MEMORY_ALLOC(type) BSL_MALLOC(sizeof(type)); +#define M_MEMORY_ALLOC(type) ((type *) BSL_MALLOC(sizeof(type))) + +#undef M_MEMORY_DEL /** Define to override value/struct deallocation. * See m-core.h for details. */ -#undef M_MEMORY_DEL -#define M_MEMORY_DEL(ptr) BSL_FREE(ptr); +#define M_MEMORY_DEL(ptr) BSL_FREE(ptr) +#undef M_MEMORY_REALLOC /** Define to override array allocation. * See m-core.h for details. */ -#undef M_MEMORY_REALLOC -#define M_MEMORY_REALLOC(type, ptr, n) (M_UNLIKELY((n) > SIZE_MAX / sizeof(type)) ? NULL : BSL_REALLOC((ptr), (n)*sizeof (type))) +#define M_MEMORY_REALLOC(type, ptr, n) (M_UNLIKELY((n) > SIZE_MAX / sizeof(type)) ? (type *) NULL : (type *) BSL_REALLOC((ptr), (n)*sizeof (type))) + +#undef M_MEMORY_FREE /** Define to override array deallocation. * See m-core.h for details. */ -#undef M_MEMORY_FREE -#define M_MEMORY_FREE(ptr) BSL_FREE(ptr); - -/** Whether this was built for RTEMS/LEON (instead of Ubuntu/x86) - */ -#cmakedefine RTEMS_BSP - -/** Part of POSIX.1-2008 - */ -#cmakedefine HAVE_CLOCK_GETTIME +#define M_MEMORY_FREE(ptr) BSL_FREE(ptr) #ifdef __cplusplus } // extern C diff --git a/src/backend/HostInterface.c b/src/backend/HostInterface.c index c5493d0d..90ca5fbb 100644 --- a/src/backend/HostInterface.c +++ b/src/backend/HostInterface.c @@ -79,7 +79,7 @@ int BSL_BundleCtx_GetBlockMetadata(const BSL_BundleRef_t *bundle, uint64_t block return (result == 0) ? BSL_SUCCESS : BSL_ERR_HOST_CALLBACK_FAILED; } -int BSL_BundleCtx_GetBlockIds(const BSL_BundleRef_t *bundle, size_t array_count, uint64_t block_ids_array[array_count], +int BSL_BundleCtx_GetBlockIds(const BSL_BundleRef_t *bundle, size_t array_count, uint64_t *block_ids_array, size_t *result_count) { CHK_ARG_NONNULL(bundle); diff --git a/src/backend/PolicyProvider.c b/src/backend/PolicyProvider.c index 82a43489..b725ad3f 100644 --- a/src/backend/PolicyProvider.c +++ b/src/backend/PolicyProvider.c @@ -49,4 +49,4 @@ int BSL_PolicyRegistry_FinalizeActions(const BSL_LibCtx_t *bsl, const BSL_Securi CHK_ARG_NONNULL(bundle); CHK_PRECONDITION(bsl->policy_registry.finalize_fn != NULL); return bsl->policy_registry.finalize_fn(bsl->policy_registry.user_data, policy_actions, bundle, response_output); -} \ No newline at end of file +} diff --git a/src/mock_bpa/agent.c b/src/mock_bpa/agent.c index 95e94d8d..075f3d8c 100644 --- a/src/mock_bpa/agent.c +++ b/src/mock_bpa/agent.c @@ -80,7 +80,7 @@ int MockBPA_GetBundleMetadata(const BSL_BundleRef_t *bundle_ref, BSL_PrimaryBloc } int MockBPA_GetBlockNums(const BSL_BundleRef_t *bundle_ref, size_t block_id_array_capacity, - uint64_t block_id_array_result[block_id_array_capacity], size_t *result_count) + uint64_t *block_id_array_result, size_t *result_count) { if (!bundle_ref || !bundle_ref->data || block_id_array_capacity == 0 || !block_id_array_result || !result_count) { diff --git a/src/mock_bpa/agent.h b/src/mock_bpa/agent.h index e22240c1..78589844 100644 --- a/src/mock_bpa/agent.h +++ b/src/mock_bpa/agent.h @@ -86,7 +86,7 @@ int MockBPA_Bundle_Deinit(MockBPA_Bundle_t *bundle_ref); int MockBPA_GetBundleMetadata(const BSL_BundleRef_t *bundle_ref, BSL_PrimaryBlock_t *result_primary_block); int MockBPA_GetBlockNums(const BSL_BundleRef_t *bundle_ref, size_t block_id_array_capacity, - uint64_t block_id_array_result[block_id_array_capacity], size_t *result_count); + uint64_t *block_id_array_result, size_t *result_count); int MockBPA_GetBlockMetadata(const BSL_BundleRef_t *bundle_ref, uint64_t block_num, BSL_CanonicalBlock_t *result_canonical_block); int MockBPA_ReallocBTSD(BSL_BundleRef_t *bundle_ref, uint64_t block_num, size_t bytesize); diff --git a/src/mock_bpa/decode.c b/src/mock_bpa/decode.c index 3ea49275..ee3685c7 100644 --- a/src/mock_bpa/decode.c +++ b/src/mock_bpa/decode.c @@ -96,7 +96,7 @@ int bsl_mock_decode_eid(QCBORDecodeContext *dec, BSL_HostEID_t *eid) const size_t begin = QCBORDecode_Tell(dec); QCBORDecode_VGetNextConsume(dec, &decitem); const size_t end = QCBORDecode_Tell(dec); - if (end > begin) + if ((begin != UINT32_MAX) && (end != UINT32_MAX) && (end > begin)) { BSL_Data_t *raw = &(obj->ssp.as_raw); assert(raw != NULL); diff --git a/src/mock_bpa/eid.h b/src/mock_bpa/eid.h index fd7de0c7..517e2e34 100644 --- a/src/mock_bpa/eid.h +++ b/src/mock_bpa/eid.h @@ -35,7 +35,7 @@ extern "C" { #endif -int MockBPA_GetEid(const void *user_data, BSL_HostEID_t *result_eid _U_); +int MockBPA_GetEid(const void *user_data, BSL_HostEID_t *result_eid); /// Scheme-specific part for IPN scheme typedef struct diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 23e65872..d1db4c96 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -87,3 +87,17 @@ target_link_libraries(test_DefaultSecurityContext PUBLIC ${LIB_ORDERED}) # Exercises the MockBPA using the publicly exposed BSL front end add_unity_test(SOURCE test_PublicInterfaceImpl.c) target_link_libraries(test_PublicInterfaceImpl PUBLIC ${LIB_ORDERED}) + +if(TEST_FUZZ) + add_executable( + fuzz_mock_bpa + fuzz_mock_bpa.cpp + ) + target_link_libraries(fuzz_mock_bpa PUBLIC ${LIB_ORDERED}) + link_fuzztest(fuzz_mock_bpa) + + add_test( + NAME fuzz_mock_bpa + COMMAND ${TEST_EXEC_PREFIX} "${CMAKE_CURRENT_BINARY_DIR}/fuzz_mock_bpa" + ) +endif(TEST_FUZZ) diff --git a/test/fuzz_mock_bpa.cpp b/test/fuzz_mock_bpa.cpp new file mode 100644 index 00000000..e7c4373e --- /dev/null +++ b/test/fuzz_mock_bpa.cpp @@ -0,0 +1,104 @@ +#include +#include +#include +#include + +template +class Contains { +public: + Contains(const Member &mem) : _mem(mem){} + + template + bool operator()(const Container &ctr) const + { + return ctr.count(_mem) > 0; + } + +private: + Member _mem; +}; + +class TestMockBPA : public ::testing::Test { +protected: + static void SetUpTestSuite() + { + BSL_openlog(); + bsl_mock_bpa_init(); + } + + static void TearDownTestSuite() + { + bsl_mock_bpa_deinit(); + BSL_closelog(); + } +}; + +using Bytes = std::vector; + +class FuzzMockBPA { +public: + FuzzMockBPA() + { + BSL_openlog(); + bsl_mock_bpa_init(); + } + + ~FuzzMockBPA() + { + bsl_mock_bpa_deinit(); + BSL_closelog(); + } + + void loopbackEID(const Bytes &in_data) + { + BSL_HostEID_t eid; + BSL_HostEID_Init(&eid); + ASSERT_NE(eid.handle, nullptr); + { + QCBORDecodeContext decoder; + QCBORDecode_Init(&decoder, (UsefulBufC) { in_data.data(), in_data.size() }, QCBOR_DECODE_MODE_NORMAL); + if (bsl_mock_decode_eid(&decoder, &eid)) + { + BSL_HostEID_Deinit(&eid); + return; + } + std::set valid_decode = { QCBOR_SUCCESS, QCBOR_ERR_EXTRA_BYTES }; + int res = QCBORDecode_Finish(&decoder);; + if (valid_decode.count(res) == 0) + { + BSL_HostEID_Deinit(&eid); + return; + } + } + + BSL_Data_t out_data; + BSL_Data_Init(&out_data); + { + QCBOREncodeContext encoder; + size_t needlen; + + QCBOREncode_Init(&encoder, SizeCalculateUsefulBuf); + ASSERT_EQ(0, bsl_mock_encode_eid(&encoder, &eid)) << "bsl_mock_encode_eid() failed"; + ASSERT_EQ(QCBOR_SUCCESS, QCBOREncode_FinishGetSize(&encoder, &needlen)); + + ASSERT_EQ(0, BSL_Data_Resize(&out_data, needlen)); + QCBOREncode_Init(&encoder, (UsefulBuf) { out_data.ptr, out_data.len }); + ASSERT_EQ(0, bsl_mock_encode_eid(&encoder, &eid)) << "bsl_mock_encode_eid() failed"; + + UsefulBufC out; + ASSERT_EQ(QCBOR_SUCCESS, QCBOREncode_Finish(&encoder, &out)); + } + + // TEST_ASSERT_EQUAL_MEMORY(in_data.ptr, out_data.ptr, in_data.len); + + BSL_Data_Deinit(&out_data); + BSL_HostEID_Deinit(&eid); + } + + std::vector>> seedsEID() + { + return { std::make_tuple({0x82, 0x02, 0x82, 0x01, 0x02 } ) }; + } +}; + +FUZZ_TEST_F(FuzzMockBPA, loopbackEID).WithSeeds(&FuzzMockBPA::seedsEID); diff --git a/test/test_MockBPA_Codecs.c b/test/test_MockBPA_Codecs.c index 44b32585..be8cd3fc 100644 --- a/test/test_MockBPA_Codecs.c +++ b/test/test_MockBPA_Codecs.c @@ -342,9 +342,9 @@ void test_bsl_loopback_eid(const char *hexdata) TEST_ASSERT_EQUAL_INT_MESSAGE(0, BSL_TestUtils_DecodeBase16(&in_data, in_text), "BSL_TestUtils_DecodeBase16() failed"); - BSL_HostEID_t eid = { 0 }; + BSL_HostEID_t eid; BSL_HostEID_Init(&eid); - TEST_ASSERT_TRUE(eid.handle != NULL); + TEST_ASSERT_NOT_NULL(eid.handle); { QCBORDecodeContext decoder; QCBORDecode_Init(&decoder, (UsefulBufC) { in_data.ptr, in_data.len }, QCBOR_DECODE_MODE_NORMAL); @@ -354,7 +354,6 @@ void test_bsl_loopback_eid(const char *hexdata) BSL_Data_t out_data; BSL_Data_Init(&out_data); - TEST_ASSERT_TRUE(eid.handle != NULL); { QCBOREncodeContext encoder; size_t needlen; From 52f395f5fff550a3981cdb7a45f37669e2ce0e38 Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 11:40:22 -0400 Subject: [PATCH 03/15] Working fuzzing on EIDs --- .github/workflows/fuzzing.yaml | 104 ++++++++++++++++++ CMakeLists.txt | 30 +++-- cmake/Findlibfuzzer.cmake | 64 +++++++++++ test/CMakeLists.txt | 19 +--- test/bsl_test_utils.h | 8 ++ test/fuzz_mock_bpa.cpp | 104 ------------------ .../dtn-shortservice | 1 + .../ipn-2elem-max | 1 + .../ipn-2elem-small | 1 + .../ipn-3elem-max | 1 + .../ipn-3elem-small | 2 + .../private-struct | Bin 0 -> 19 bytes test/fuzz_mock_bpa_eid_cbor-corpus/reserved | Bin 0 -> 3 bytes test/fuzz_mock_bpa_eid_cbor.cpp | 96 ++++++++++++++++ 14 files changed, 298 insertions(+), 133 deletions(-) create mode 100644 .github/workflows/fuzzing.yaml create mode 100644 cmake/Findlibfuzzer.cmake delete mode 100644 test/fuzz_mock_bpa.cpp create mode 100644 test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice create mode 100644 test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max create mode 100644 test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small create mode 100644 test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max create mode 100644 test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small create mode 100644 test/fuzz_mock_bpa_eid_cbor-corpus/private-struct create mode 100644 test/fuzz_mock_bpa_eid_cbor-corpus/reserved create mode 100644 test/fuzz_mock_bpa_eid_cbor.cpp diff --git a/.github/workflows/fuzzing.yaml b/.github/workflows/fuzzing.yaml new file mode 100644 index 00000000..b6ce9f44 --- /dev/null +++ b/.github/workflows/fuzzing.yaml @@ -0,0 +1,104 @@ +## +## Copyright (c) 2025 The Johns Hopkins University Applied Physics +## Laboratory LLC. +## +## This file is part of the Bundle Protocol Security Library (BSL). +## +## Licensed under the Apache License, Version 2.0 (the "License"); +## you may not use this file except in compliance with the License. +## You may obtain a copy of the License at +## http://www.apache.org/licenses/LICENSE-2.0 +## Unless required by applicable law or agreed to in writing, software +## distributed under the License is distributed on an "AS IS" BASIS, +## WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +## See the License for the specific language governing permissions and +## limitations under the License. +## +## This work was performed for the Jet Propulsion Laboratory, California +## Institute of Technology, sponsored by the United States Government under +## the prime contract 80NM0018D0004 between the Caltech and NASA under +## subcontract 1700763. +## +name: Build and run fuzzing tools +on: + schedule: + - cron: '0 0 * * 0' + push: + branches: + - main + - 'apl-fy[0-9][0-9]' + pull_request: {} # any target + +jobs: + fuzzing: + strategy: + fail-fast: false + matrix: + os: ['ubuntu-24.04', 'centos-9'] + compiler: ['clang'] + name: Unit Test (${{matrix.os}} ${{matrix.compiler}}) + runs-on: ${{ matrix.os == 'centos-9' && 'ubuntu-24.04' || matrix.os }} + container: ${{ matrix.os == 'centos-9' && 'quay.io/centos/centos:stream9' || null }} + permissions: + contents: read + actions: write + env: + CC: ${{matrix.compiler=='clang' && 'clang' || 'gcc'}} + CXX: ${{matrix.compiler=='clang' && 'clang++' || 'g++'}} + steps: + - name: Set up OS + if: startsWith(matrix.os, 'ubuntu') + run: | + sudo rm /var/lib/man-db/auto-update + sudo apt-get update + sudo apt-get install -y \ + cmake ninja-build \ + ruby pkg-config ccache patch \ + ${{matrix.compiler=='clang' && 'clang llvm' || 'gcc g++'}} \ + libssl-dev libjansson-dev \ + valgrind gcovr xmlstarlet + - name: Set up OS + if: startsWith(matrix.os, 'centos') + run: | + dnf install -y epel-release + crb enable + dnf install -y \ + git rsync \ + cmake ninja-build \ + ruby pkg-config ccache patch \ + ${{matrix.compiler=='clang' && 'clang llvm' || 'gcc gcc-c++'}} \ + openssl-devel jansson-devel \ + valgrind xmlstarlet python3-pip + pip3 install gcovr + - name: Checkout repository + uses: actions/checkout@v4 + with: + fetch-depth: 0 + submodules: recursive + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + create-symlink: true + - name: Dependencies + run: ./build.sh deps + - name: Prep + run: > + ./build.sh prep + -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_TESTING=OFF -DBUILD_FUZZING=ON + - name: Build + run: ./build.sh + - name: Install + run: ./build.sh install + - name: Test + run: ./build.sh check + - name: Collect coverage + run: ./build.sh coverage + - name: Archive coverage + uses: actions/upload-artifact@v4 + with: + name: ${{github.job}}-${{matrix.os}}-${{matrix.compiler}}-coverage + path: build/default/coverage* + - name: Report coverage + run: | + SRC_COV_PERC=$(xmlstarlet sel -t -v 'floor(/coverage/packages/package[@name="src"]/@line-rate * 100)' -n build/default/coverage-xml.xml) + echo "Source coverage: ${SRC_COV_PERC}%" >> $GITHUB_STEP_SUMMARY diff --git a/CMakeLists.txt b/CMakeLists.txt index 8aa89b95..10f24e06 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,7 +28,7 @@ option(BUILD_DOCS_MAN "Enable manpage building" OFF) option(BUILD_TESTING "Enable building unit tests" ON) option(TEST_MEMCHECK "Enable test runtime memory checking" ON) option(TEST_COVERAGE "Enable test runtime coverage logging" ON) -option(TEST_FUZZ "Enable fuzz testing" OFF) +option(BUILD_FUZZING "Enable building fuzzing executables" OFF) option(BUILD_PACKAGE "Enable building package outputs" OFF) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") @@ -161,29 +161,27 @@ if(BUILD_TESTING) BASE_DIRECTORY "${PROJECT_SOURCE_DIR}" ) endif(TEST_COVERAGE) - if(TEST_FUZZ) - add_subdirectory(deps/fuzztest) - fuzztest_setup_fuzzing_flags() - endif(TEST_FUZZ) - - include(CTest) - set(CMAKE_CTEST_ARGUMENTS - --output-junit testresults.xml - --output-on-failure - ) endif(BUILD_TESTING) +if(BUILD_FUZZING) + find_package(libfuzzer) +endif(BUILD_FUZZING) + +include(CTest) +set(CMAKE_CTEST_ARGUMENTS + --output-junit testresults.xml + --output-on-failure +) + # Install config used by tall targets include(GNUInstallDirs) if(BUILD_LIB) add_subdirectory(src) endif(BUILD_LIB) -if(BUILD_TESTING) - message(STATUS "Building tests") - set(TEST_INSTALL_PREFIX "${CMAKE_INSTALL_LIBEXECDIR}/${PROJECT_NAME}") - add_subdirectory(test) -endif(BUILD_TESTING) + +set(TEST_INSTALL_PREFIX "${CMAKE_INSTALL_LIBEXECDIR}/${PROJECT_NAME}") +add_subdirectory(test) add_subdirectory(docs) if(BUILD_PACKAGE) diff --git a/cmake/Findlibfuzzer.cmake b/cmake/Findlibfuzzer.cmake new file mode 100644 index 00000000..8fa13167 --- /dev/null +++ b/cmake/Findlibfuzzer.cmake @@ -0,0 +1,64 @@ +# Defines the function add_fuzz_test() for generating libFuzzer executables. +# +# The variable TEST_INSTALL_PREFIX can be set to have the unit tests installed +# by cmake under that executable prefix path. +# + +if(NOT CMAKE_C_COMPILER_ID MATCHES "Clang" + OR NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang") + message(FATAL "Can only fuzz with clang compiler") +endif() + +function(add_fuzz_test) + set(options OPTIONAL ) + set(oneValueArgs TARGET MAIN_NAME RUNS_COUNT) + set(multiValueArgs SOURCE) + cmake_parse_arguments( + FUZZTEST "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} + ) + + list(GET FUZZTEST_SOURCE 0 MAIN_SOURCE) + get_filename_component(BASENAME ${MAIN_SOURCE} NAME_WE) + get_filename_component(ABSOLUTE_SOURCE ${MAIN_SOURCE} ABSOLUTE) + + if(NOT FUZZTEST_TARGET) + set(FUZZTEST_TARGET "${BASENAME}") + endif() + if(NOT FUZZTEST_RUNS_COUNT) + set(FUZZTEST_RUNS_COUNT "1000000") + endif() + + message(STATUS "Adding fuzz test ${FUZZTEST_TARGET} from ${ABSOLUTE_SOURCE}") + + add_executable(${BASENAME} ${FUZZTEST_SOURCE}) + target_compile_options( + ${BASENAME} + PRIVATE + -g + -fsanitize=fuzzer,address + ) + target_link_options( + ${BASENAME} + PRIVATE + -fsanitize=fuzzer,address + ) + + add_test( + NAME ${BASENAME} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} + COMMAND + "${CMAKE_CURRENT_BINARY_DIR}/${BASENAME}" + "${BASENAME}-corpus" + -runs=${FUZZTEST_RUNS_COUNT} + -detect_leaks=1 + ) + + if(TEST_INSTALL_PREFIX) + install( + TARGETS ${BASENAME} + RUNTIME + DESTINATION ${TEST_INSTALL_PREFIX} + COMPONENT test + ) + endif(TEST_INSTALL_PREFIX) +endfunction() diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d1db4c96..1f4a2221 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -50,6 +50,7 @@ install( set(LIB_ORDERED bsl_front bsl_dynamic bsl_mock_bpa bsl_test_utils) +if(BUILD_TESTING) add_unity_test(SOURCE test_MockBPA_text_util.c) target_link_libraries(test_MockBPA_text_util PUBLIC bsl_mock_bpa) @@ -87,17 +88,9 @@ target_link_libraries(test_DefaultSecurityContext PUBLIC ${LIB_ORDERED}) # Exercises the MockBPA using the publicly exposed BSL front end add_unity_test(SOURCE test_PublicInterfaceImpl.c) target_link_libraries(test_PublicInterfaceImpl PUBLIC ${LIB_ORDERED}) +endif(BUILD_TESTING) -if(TEST_FUZZ) - add_executable( - fuzz_mock_bpa - fuzz_mock_bpa.cpp - ) - target_link_libraries(fuzz_mock_bpa PUBLIC ${LIB_ORDERED}) - link_fuzztest(fuzz_mock_bpa) - - add_test( - NAME fuzz_mock_bpa - COMMAND ${TEST_EXEC_PREFIX} "${CMAKE_CURRENT_BINARY_DIR}/fuzz_mock_bpa" - ) -endif(TEST_FUZZ) +if(BUILD_FUZZING) + add_fuzz_test(SOURCE fuzz_mock_bpa_eid_cbor.cpp) + target_link_libraries(fuzz_mock_bpa_eid_cbor PUBLIC ${LIB_ORDERED}) +endif(BUILD_FUZZING) diff --git a/test/bsl_test_utils.h b/test/bsl_test_utils.h index d057b850..8c60000c 100644 --- a/test/bsl_test_utils.h +++ b/test/bsl_test_utils.h @@ -31,6 +31,10 @@ #include #include +#ifdef __cplusplus +extern "C" { +#endif + /// @brief Key ID for the Appendix A1 key in OpenSSL #define RFC9173_EXAMPLE_A1_KEY "9100" @@ -339,4 +343,8 @@ int rfc9173_byte_gen_fn_a2_kek(unsigned char *buf, int len); int rfc9173_byte_gen_fn_a2_cek(unsigned char *buf, int len); int rfc9173_byte_gen_fn_a4(unsigned char *buf, int len); +#ifdef __cplusplus +} // extern "C" +#endif + #endif diff --git a/test/fuzz_mock_bpa.cpp b/test/fuzz_mock_bpa.cpp deleted file mode 100644 index e7c4373e..00000000 --- a/test/fuzz_mock_bpa.cpp +++ /dev/null @@ -1,104 +0,0 @@ -#include -#include -#include -#include - -template -class Contains { -public: - Contains(const Member &mem) : _mem(mem){} - - template - bool operator()(const Container &ctr) const - { - return ctr.count(_mem) > 0; - } - -private: - Member _mem; -}; - -class TestMockBPA : public ::testing::Test { -protected: - static void SetUpTestSuite() - { - BSL_openlog(); - bsl_mock_bpa_init(); - } - - static void TearDownTestSuite() - { - bsl_mock_bpa_deinit(); - BSL_closelog(); - } -}; - -using Bytes = std::vector; - -class FuzzMockBPA { -public: - FuzzMockBPA() - { - BSL_openlog(); - bsl_mock_bpa_init(); - } - - ~FuzzMockBPA() - { - bsl_mock_bpa_deinit(); - BSL_closelog(); - } - - void loopbackEID(const Bytes &in_data) - { - BSL_HostEID_t eid; - BSL_HostEID_Init(&eid); - ASSERT_NE(eid.handle, nullptr); - { - QCBORDecodeContext decoder; - QCBORDecode_Init(&decoder, (UsefulBufC) { in_data.data(), in_data.size() }, QCBOR_DECODE_MODE_NORMAL); - if (bsl_mock_decode_eid(&decoder, &eid)) - { - BSL_HostEID_Deinit(&eid); - return; - } - std::set valid_decode = { QCBOR_SUCCESS, QCBOR_ERR_EXTRA_BYTES }; - int res = QCBORDecode_Finish(&decoder);; - if (valid_decode.count(res) == 0) - { - BSL_HostEID_Deinit(&eid); - return; - } - } - - BSL_Data_t out_data; - BSL_Data_Init(&out_data); - { - QCBOREncodeContext encoder; - size_t needlen; - - QCBOREncode_Init(&encoder, SizeCalculateUsefulBuf); - ASSERT_EQ(0, bsl_mock_encode_eid(&encoder, &eid)) << "bsl_mock_encode_eid() failed"; - ASSERT_EQ(QCBOR_SUCCESS, QCBOREncode_FinishGetSize(&encoder, &needlen)); - - ASSERT_EQ(0, BSL_Data_Resize(&out_data, needlen)); - QCBOREncode_Init(&encoder, (UsefulBuf) { out_data.ptr, out_data.len }); - ASSERT_EQ(0, bsl_mock_encode_eid(&encoder, &eid)) << "bsl_mock_encode_eid() failed"; - - UsefulBufC out; - ASSERT_EQ(QCBOR_SUCCESS, QCBOREncode_Finish(&encoder, &out)); - } - - // TEST_ASSERT_EQUAL_MEMORY(in_data.ptr, out_data.ptr, in_data.len); - - BSL_Data_Deinit(&out_data); - BSL_HostEID_Deinit(&eid); - } - - std::vector>> seedsEID() - { - return { std::make_tuple({0x82, 0x02, 0x82, 0x01, 0x02 } ) }; - } -}; - -FUZZ_TEST_F(FuzzMockBPA, loopbackEID).WithSeeds(&FuzzMockBPA::seedsEID); diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice b/test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice new file mode 100644 index 00000000..02165637 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice @@ -0,0 +1 @@ +‚n//nodename/svc \ No newline at end of file diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max new file mode 100644 index 00000000..2c06da69 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max @@ -0,0 +1 @@ +‚‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small new file mode 100644 index 00000000..e0beca45 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small @@ -0,0 +1 @@ +‚‚ diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max new file mode 100644 index 00000000..deb9317a --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max @@ -0,0 +1 @@ +‚ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small new file mode 100644 index 00000000..ccd70291 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small @@ -0,0 +1,2 @@ +‚ƒ + diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/private-struct b/test/fuzz_mock_bpa_eid_cbor-corpus/private-struct new file mode 100644 index 0000000000000000000000000000000000000000..a02e98a3f8f7e8ce9c33fe83d722cbe9d0988b0f GIT binary patch literal 19 acmZpYVqj!oSj3o8oS&O2A?ciynGOIeHw6Cx literal 0 HcmV?d00001 diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/reserved b/test/fuzz_mock_bpa_eid_cbor-corpus/reserved new file mode 100644 index 0000000000000000000000000000000000000000..950d4c4cc54d4de411a69a07a9493bcb46f0251a GIT binary patch literal 3 KcmZo-_yzz1e*t*_ literal 0 HcmV?d00001 diff --git a/test/fuzz_mock_bpa_eid_cbor.cpp b/test/fuzz_mock_bpa_eid_cbor.cpp new file mode 100644 index 00000000..2c9e0992 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor.cpp @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2025 The Johns Hopkins University Applied Physics + * Laboratory LLC. + * + * This file is part of the Bundle Protocol Security Library (BSL). + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This work was performed for the Jet Propulsion Laboratory, California + * Institute of Technology, sponsored by the United States Government under + * the prime contract 80NM0018D0004 between the Caltech and NASA under + * subcontract 1700763. + */ +#include +#include "bsl_test_utils.h" +#include + +#define EXPECT_EQ(expect, got) if ((expect) != (got)) { BSL_LOG_ERR("EXPECT failure"); } + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +extern "C" int LLVMFuzzerInitialize(int *argc _U_, char ***argv _U_) +{ + BSL_openlog(); + bsl_mock_bpa_init(); + return 0; +} +/* No cleanup: + bsl_mock_bpa_deinit(); + BSL_closelog(); +*/ + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + int retval = 0; + + BSL_HostEID_t eid; + BSL_HostEID_Init(&eid); + { + QCBORDecodeContext decoder; + QCBORDecode_Init(&decoder, (UsefulBufC) { data, size }, QCBOR_DECODE_MODE_NORMAL); + int res_eid = bsl_mock_decode_eid(&decoder, &eid); + int res_cbor = QCBORDecode_Finish(&decoder); + if (res_eid || (res_cbor != QCBOR_SUCCESS)) + { + retval = -1; + } + } + + BSL_Data_t out_data; + BSL_Data_Init(&out_data); + if (!retval) + { + QCBOREncodeContext encoder; + size_t needlen; + + QCBOREncode_Init(&encoder, SizeCalculateUsefulBuf); + EXPECT_EQ(0, bsl_mock_encode_eid(&encoder, &eid)); + assert(QCBOR_SUCCESS == QCBOREncode_FinishGetSize(&encoder, &needlen)); + + EXPECT_EQ(0, BSL_Data_Resize(&out_data, needlen)); + QCBOREncode_Init(&encoder, (UsefulBuf) { out_data.ptr, out_data.len }); + EXPECT_EQ(0, bsl_mock_encode_eid(&encoder, &eid)); + + UsefulBufC out; + EXPECT_EQ(QCBOR_SUCCESS, QCBOREncode_Finish(&encoder, &out)); + } + + if (!retval) + { + // output may be a subset + if (size != out_data.len) + { + // CBOR tags on input will not be carried + // BSL_LOG_ERR("Bad re-encoding size, expect %" PRIu64 " got %" PRIu64 ", for scheme %" PRIu64, size, out_data.len, ((bsl_mock_eid_t*)eid.handle)->scheme); + retval = -1; + } + if (0 != memcmp(data, out_data.ptr, out_data.len)) + { + retval = -1; + } + } + + BSL_Data_Deinit(&out_data); + BSL_HostEID_Deinit(&eid); + return retval; +} From 49f69f17039dc2a29f61b9a7748614506e955771 Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 11:46:15 -0400 Subject: [PATCH 04/15] format --- test/CMakeLists.txt | 74 ++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 37 deletions(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 1f4a2221..5be1f228 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -51,43 +51,43 @@ install( set(LIB_ORDERED bsl_front bsl_dynamic bsl_mock_bpa bsl_test_utils) if(BUILD_TESTING) -add_unity_test(SOURCE test_MockBPA_text_util.c) -target_link_libraries(test_MockBPA_text_util PUBLIC bsl_mock_bpa) - -add_unity_test(SOURCE test_MockBPA_EID.c) -target_link_libraries(test_MockBPA_EID PUBLIC ${LIB_ORDERED}) - -add_unity_test(SOURCE test_MockBPA_Codecs.c) -target_link_libraries(test_MockBPA_Codecs PUBLIC ${LIB_ORDERED}) - -add_unity_test(SOURCE test_mock_bpa_ctr.c) -target_link_libraries(test_mock_bpa_ctr PUBLIC ${LIB_ORDERED}) - -add_unity_test(SOURCE test_CryptoInterface.c) -target_link_libraries(test_CryptoInterface PUBLIC ${LIB_ORDERED}) - -add_unity_test(SOURCE test_SecurityTypes.c) -target_link_libraries(test_SecurityTypes PUBLIC ${LIB_ORDERED}) - -# Exercises the portion of the backend that interacts with the policy providers -add_unity_test(SOURCE test_BackendSecurityContext.c) -target_link_libraries(test_BackendSecurityContext PUBLIC ${LIB_ORDERED}) - -# Exercises the portion of the backend that interacts with the security contexts -add_unity_test(SOURCE test_BackendPolicyProvider.c) -target_link_libraries(test_BackendPolicyProvider PUBLIC ${LIB_ORDERED}) - -# Specific unit tests for ONLY the "Sample Policy Provider" module (Offered as example provider) -add_unity_test(SOURCE test_SamplePolicyProvider.c) -target_link_libraries(test_SamplePolicyProvider PUBLIC ${LIB_ORDERED}) - -# Specific tests for ONLY the "Default Security Context" module (Implementation of RFC9173) -add_unity_test(SOURCE test_DefaultSecurityContext.c) -target_link_libraries(test_DefaultSecurityContext PUBLIC ${LIB_ORDERED}) - -# Exercises the MockBPA using the publicly exposed BSL front end -add_unity_test(SOURCE test_PublicInterfaceImpl.c) -target_link_libraries(test_PublicInterfaceImpl PUBLIC ${LIB_ORDERED}) + add_unity_test(SOURCE test_MockBPA_text_util.c) + target_link_libraries(test_MockBPA_text_util PUBLIC bsl_mock_bpa) + + add_unity_test(SOURCE test_MockBPA_EID.c) + target_link_libraries(test_MockBPA_EID PUBLIC ${LIB_ORDERED}) + + add_unity_test(SOURCE test_MockBPA_Codecs.c) + target_link_libraries(test_MockBPA_Codecs PUBLIC ${LIB_ORDERED}) + + add_unity_test(SOURCE test_mock_bpa_ctr.c) + target_link_libraries(test_mock_bpa_ctr PUBLIC ${LIB_ORDERED}) + + add_unity_test(SOURCE test_CryptoInterface.c) + target_link_libraries(test_CryptoInterface PUBLIC ${LIB_ORDERED}) + + add_unity_test(SOURCE test_SecurityTypes.c) + target_link_libraries(test_SecurityTypes PUBLIC ${LIB_ORDERED}) + + # Exercises the portion of the backend that interacts with the policy providers + add_unity_test(SOURCE test_BackendSecurityContext.c) + target_link_libraries(test_BackendSecurityContext PUBLIC ${LIB_ORDERED}) + + # Exercises the portion of the backend that interacts with the security contexts + add_unity_test(SOURCE test_BackendPolicyProvider.c) + target_link_libraries(test_BackendPolicyProvider PUBLIC ${LIB_ORDERED}) + + # Specific unit tests for ONLY the "Sample Policy Provider" module (Offered as example provider) + add_unity_test(SOURCE test_SamplePolicyProvider.c) + target_link_libraries(test_SamplePolicyProvider PUBLIC ${LIB_ORDERED}) + + # Specific tests for ONLY the "Default Security Context" module (Implementation of RFC9173) + add_unity_test(SOURCE test_DefaultSecurityContext.c) + target_link_libraries(test_DefaultSecurityContext PUBLIC ${LIB_ORDERED}) + + # Exercises the MockBPA using the publicly exposed BSL front end + add_unity_test(SOURCE test_PublicInterfaceImpl.c) + target_link_libraries(test_PublicInterfaceImpl PUBLIC ${LIB_ORDERED}) endif(BUILD_TESTING) if(BUILD_FUZZING) From f315d4ba2e9e4209e09f36d7b79ca32f8148f48e Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 11:46:34 -0400 Subject: [PATCH 05/15] code format --- src/BPSecLib_Private.h | 1 - test/fuzz_mock_bpa_eid_cbor.cpp | 11 ++++++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/BPSecLib_Private.h b/src/BPSecLib_Private.h index 05cd0edd..3c8bf312 100644 --- a/src/BPSecLib_Private.h +++ b/src/BPSecLib_Private.h @@ -1337,4 +1337,3 @@ struct BSL_SecCtxDesc_s #endif #endif /* BSL_BPSECLIB_PRIVATE_H_ */ - diff --git a/test/fuzz_mock_bpa_eid_cbor.cpp b/test/fuzz_mock_bpa_eid_cbor.cpp index 2c9e0992..e9b9e525 100644 --- a/test/fuzz_mock_bpa_eid_cbor.cpp +++ b/test/fuzz_mock_bpa_eid_cbor.cpp @@ -23,7 +23,11 @@ #include "bsl_test_utils.h" #include -#define EXPECT_EQ(expect, got) if ((expect) != (got)) { BSL_LOG_ERR("EXPECT failure"); } +#define EXPECT_EQ(expect, got) \ + if ((expect) != (got)) \ + { \ + BSL_LOG_ERR("EXPECT failure"); \ + } extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv); extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); @@ -48,7 +52,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { QCBORDecodeContext decoder; QCBORDecode_Init(&decoder, (UsefulBufC) { data, size }, QCBOR_DECODE_MODE_NORMAL); - int res_eid = bsl_mock_decode_eid(&decoder, &eid); + int res_eid = bsl_mock_decode_eid(&decoder, &eid); int res_cbor = QCBORDecode_Finish(&decoder); if (res_eid || (res_cbor != QCBOR_SUCCESS)) { @@ -81,7 +85,8 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) if (size != out_data.len) { // CBOR tags on input will not be carried - // BSL_LOG_ERR("Bad re-encoding size, expect %" PRIu64 " got %" PRIu64 ", for scheme %" PRIu64, size, out_data.len, ((bsl_mock_eid_t*)eid.handle)->scheme); + // BSL_LOG_ERR("Bad re-encoding size, expect %" PRIu64 " got %" PRIu64 ", for scheme %" PRIu64, size, + // out_data.len, ((bsl_mock_eid_t*)eid.handle)->scheme); retval = -1; } if (0 != memcmp(data, out_data.ptr, out_data.len)) From 2971198ab5fb13dc442e295a375ae65abc8b96c3 Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 11:54:53 -0400 Subject: [PATCH 06/15] no need for submodule --- .gitmodules | 4 ---- deps/fuzztest | 1 - 2 files changed, 5 deletions(-) delete mode 160000 deps/fuzztest diff --git a/.gitmodules b/.gitmodules index 9a7a3b7b..5ac9aba0 100644 --- a/.gitmodules +++ b/.gitmodules @@ -10,7 +10,3 @@ path = deps/QCBOR url = https://github.com/laurencelundblade/QCBOR.git branch = master -[submodule "deps/fuzztest"] - path = deps/fuzztest - url = https://github.com/google/fuzztest.git - branch = main diff --git a/deps/fuzztest b/deps/fuzztest deleted file mode 160000 index bdb32e6a..00000000 --- a/deps/fuzztest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bdb32e6a121600e53b5fc97fdf12c61ccfce941b From e8b50b700afe59372c431c3cce349f6a8b17f538 Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 12:04:35 -0400 Subject: [PATCH 07/15] Disambiguate all testing from unit testing --- .github/workflows/build-test.yaml | 4 +-- .github/workflows/fuzzing.yaml | 2 +- CMakeLists.txt | 6 ++-- pkg/bsl.spec.in | 2 +- test/CMakeLists.txt | 59 ++++++++++++++++--------------- test/fuzz_mock_bpa_eid_cbor.cpp | 6 ++-- 6 files changed, 40 insertions(+), 39 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 493a8d77..f0f05ff3 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -84,7 +84,7 @@ jobs: - name: Prep run: | ./build.sh prep \ - -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_TESTING=ON + -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_UNITTEST=ON - name: Build run: ./build.sh - name: Install @@ -153,7 +153,7 @@ jobs: - name: Dependencies run: ./build.sh deps - name: Prep - run: ./build.sh prep -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_TESTING=ON + run: ./build.sh prep -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_UNITTEST=OFF - name: Build run: ./build.sh - name: Install diff --git a/.github/workflows/fuzzing.yaml b/.github/workflows/fuzzing.yaml index b6ce9f44..6960f2bf 100644 --- a/.github/workflows/fuzzing.yaml +++ b/.github/workflows/fuzzing.yaml @@ -84,7 +84,7 @@ jobs: - name: Prep run: > ./build.sh prep - -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_TESTING=OFF -DBUILD_FUZZING=ON + -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_UNITTEST=OFF -DBUILD_FUZZING=ON - name: Build run: ./build.sh - name: Install diff --git a/CMakeLists.txt b/CMakeLists.txt index 10f24e06..a7111d43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,7 @@ option(BUILD_LIB "Build the library itself" ON) option(BUILD_SHARED_LIBS "Build using shared libraries" ON) option(BUILD_DOCS_API "Enable API documentation building" OFF) option(BUILD_DOCS_MAN "Enable manpage building" OFF) -option(BUILD_TESTING "Enable building unit tests" ON) +option(BUILD_UNITTEST "Enable building unit tests" ON) option(TEST_MEMCHECK "Enable test runtime memory checking" ON) option(TEST_COVERAGE "Enable test runtime coverage logging" ON) option(BUILD_FUZZING "Enable building fuzzing executables" OFF) @@ -126,7 +126,7 @@ include(CheckTypeSize) check_type_size("int" SIZE_INT LANGUAGE C) check_type_size("size_t" SIZE_SIZET LANGUAGE C) -if(BUILD_TESTING) +if(BUILD_UNITTEST) if(TEST_MEMCHECK) find_program(MEMCHECK_CMD valgrind) message(STATUS "Using valgrind memcheck for tests: ${MEMCHECK_CMD}") @@ -161,7 +161,7 @@ if(BUILD_TESTING) BASE_DIRECTORY "${PROJECT_SOURCE_DIR}" ) endif(TEST_COVERAGE) -endif(BUILD_TESTING) +endif(BUILD_UNITTEST) if(BUILD_FUZZING) find_package(libfuzzer) diff --git a/pkg/bsl.spec.in b/pkg/bsl.spec.in index f092147a..6bce36fb 100644 --- a/pkg/bsl.spec.in +++ b/pkg/bsl.spec.in @@ -58,7 +58,7 @@ from the API with Doxygen. ./build.sh deps %cmake -DCMAKE_PREFIX_PATH=${PWD}/testroot/usr \ - -DBUILD_TESTING=YES -DTEST_MEMCHECK=NO -DTEST_COVERAGE=NO \ + -DBUILD_UNITTEST=YES -DTEST_MEMCHECK=NO -DTEST_COVERAGE=NO \ -DBUILD_DOCS_MAN=YES %{?with_apidoc:-DBUILD_DOCS_API=YES} %build diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 5be1f228..975c81c1 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,37 +20,38 @@ the prime contract 80NM0018D0004 between the Caltech and NASA under subcontract 1700763. ]] - -set(UNITY_ROOT "${CMAKE_SOURCE_DIR}/deps/unity") -find_package(unitytools) - -add_library(bsl_test_utils) -target_sources(bsl_test_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/bsl_test_utils.h) -target_sources(bsl_test_utils PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bsl_test_utils.c) -set_target_properties(bsl_test_utils - PROPERTIES - VERSION ${CMAKE_PROJECT_VERSION} - SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} -) -target_link_libraries(bsl_test_utils PUBLIC bsl_front) -target_link_libraries(bsl_test_utils PUBLIC bsl_dynamic) -target_link_libraries(bsl_test_utils PUBLIC bsl_mock_bpa) -target_include_directories(bsl_test_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) - -install( - TARGETS bsl_test_utils - RUNTIME - COMPONENT runtime - LIBRARY - COMPONENT runtime - NAMELINK_COMPONENT devel - ARCHIVE - COMPONENT devel -) +if(BUILD_UNITTEST OR BUILD_FUZZING) + add_library(bsl_test_utils) + target_sources(bsl_test_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/bsl_test_utils.h) + target_sources(bsl_test_utils PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/bsl_test_utils.c) + set_target_properties(bsl_test_utils + PROPERTIES + VERSION ${CMAKE_PROJECT_VERSION} + SOVERSION ${CMAKE_PROJECT_VERSION_MAJOR} + ) + target_link_libraries(bsl_test_utils PUBLIC bsl_front) + target_link_libraries(bsl_test_utils PUBLIC bsl_dynamic) + target_link_libraries(bsl_test_utils PUBLIC bsl_mock_bpa) + target_include_directories(bsl_test_utils PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + + install( + TARGETS bsl_test_utils + RUNTIME + COMPONENT runtime + LIBRARY + COMPONENT runtime + NAMELINK_COMPONENT devel + ARCHIVE + COMPONENT devel + ) +endif(BUILD_UNITTEST OR BUILD_FUZZING) set(LIB_ORDERED bsl_front bsl_dynamic bsl_mock_bpa bsl_test_utils) -if(BUILD_TESTING) +if(BUILD_UNITTEST) + set(UNITY_ROOT "${CMAKE_SOURCE_DIR}/deps/unity") + find_package(unitytools) + add_unity_test(SOURCE test_MockBPA_text_util.c) target_link_libraries(test_MockBPA_text_util PUBLIC bsl_mock_bpa) @@ -88,7 +89,7 @@ if(BUILD_TESTING) # Exercises the MockBPA using the publicly exposed BSL front end add_unity_test(SOURCE test_PublicInterfaceImpl.c) target_link_libraries(test_PublicInterfaceImpl PUBLIC ${LIB_ORDERED}) -endif(BUILD_TESTING) +endif(BUILD_UNITTEST) if(BUILD_FUZZING) add_fuzz_test(SOURCE fuzz_mock_bpa_eid_cbor.cpp) diff --git a/test/fuzz_mock_bpa_eid_cbor.cpp b/test/fuzz_mock_bpa_eid_cbor.cpp index e9b9e525..f3cded17 100644 --- a/test/fuzz_mock_bpa_eid_cbor.cpp +++ b/test/fuzz_mock_bpa_eid_cbor.cpp @@ -19,7 +19,7 @@ * the prime contract 80NM0018D0004 between the Caltech and NASA under * subcontract 1700763. */ -#include +#include #include "bsl_test_utils.h" #include @@ -35,11 +35,11 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); extern "C" int LLVMFuzzerInitialize(int *argc _U_, char ***argv _U_) { BSL_openlog(); - bsl_mock_bpa_init(); + bsl_mock_bpa_agent_init(); return 0; } /* No cleanup: - bsl_mock_bpa_deinit(); + bsl_mock_bpa_agent_deinit(); BSL_closelog(); */ From b02233fa1df1f91c228489e4af813a1da342d273 Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 12:10:12 -0400 Subject: [PATCH 08/15] Disambiguate coverage logging from testing --- .github/workflows/build-test.yaml | 12 ++++++--- .github/workflows/codeql.yml | 2 +- .github/workflows/docs.yaml | 2 +- .github/workflows/fuzzing.yaml | 4 +-- CMakeLists.txt | 43 +++++++++++++++---------------- 5 files changed, 33 insertions(+), 30 deletions(-) diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index f0f05ff3..c1287920 100644 --- a/.github/workflows/build-test.yaml +++ b/.github/workflows/build-test.yaml @@ -82,9 +82,10 @@ jobs: - name: Dependencies run: ./build.sh deps - name: Prep - run: | - ./build.sh prep \ - -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_UNITTEST=ON + run: > + ./build.sh prep + -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF + -DBUILD_UNITTEST=ON -DBUILD_COVERAGE=ON - name: Build run: ./build.sh - name: Install @@ -153,7 +154,10 @@ jobs: - name: Dependencies run: ./build.sh deps - name: Prep - run: ./build.sh prep -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_UNITTEST=OFF + run: > + ./build.sh prep + -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF + -DBUILD_UNITTEST=OFF -DBUILD_COVERAGE=ON - name: Build run: ./build.sh - name: Install diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 70b172fa..8d50009d 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -88,7 +88,7 @@ jobs: cmake ninja-build ruby build-essential \ libssl-dev libjansson-dev ./build.sh deps - ./build.sh prep -DTEST_MEMCHECK=OFF -DTEST_COVERAGE=OFF -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF + ./build.sh prep -DTEST_MEMCHECK=OFF -DBUILD_COVERAGE=OFF -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml index 04d92230..b326e08d 100644 --- a/.github/workflows/docs.yaml +++ b/.github/workflows/docs.yaml @@ -55,7 +55,7 @@ jobs: - name: Prep run: > ./build.sh prep - -DBUILD_TESTING=OFF -DTEST_MEMCHECK=OFF -DTEST_COVERAGE=OFF + -DBUILD_TESTING=OFF -DTEST_MEMCHECK=OFF -DBUILD_COVERAGE=OFF -DBUILD_DOCS_API=ON - name: Build run: ./build.sh --target docs-api-html docs-api-pdf docs-api-misspelling diff --git a/.github/workflows/fuzzing.yaml b/.github/workflows/fuzzing.yaml index 6960f2bf..fdbeb518 100644 --- a/.github/workflows/fuzzing.yaml +++ b/.github/workflows/fuzzing.yaml @@ -19,7 +19,7 @@ ## the prime contract 80NM0018D0004 between the Caltech and NASA under ## subcontract 1700763. ## -name: Build and run fuzzing tools +name: Build and run fuzzing on: schedule: - cron: '0 0 * * 0' @@ -36,7 +36,7 @@ jobs: matrix: os: ['ubuntu-24.04', 'centos-9'] compiler: ['clang'] - name: Unit Test (${{matrix.os}} ${{matrix.compiler}}) + name: Fuzzing (${{matrix.os}} ${{matrix.compiler}}) runs-on: ${{ matrix.os == 'centos-9' && 'ubuntu-24.04' || matrix.os }} container: ${{ matrix.os == 'centos-9' && 'quay.io/centos/centos:stream9' || null }} permissions: diff --git a/CMakeLists.txt b/CMakeLists.txt index a7111d43..cb99fe57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,7 +27,7 @@ option(BUILD_DOCS_API "Enable API documentation building" OFF) option(BUILD_DOCS_MAN "Enable manpage building" OFF) option(BUILD_UNITTEST "Enable building unit tests" ON) option(TEST_MEMCHECK "Enable test runtime memory checking" ON) -option(TEST_COVERAGE "Enable test runtime coverage logging" ON) +option(BUILD_COVERAGE "Enable runtime coverage logging and reporting" OFF) option(BUILD_FUZZING "Enable building fuzzing executables" OFF) option(BUILD_PACKAGE "Enable building package outputs" OFF) @@ -141,31 +141,30 @@ if(BUILD_UNITTEST) # Arguments as list into global scope for Findunitytools.cmake set(TEST_EXEC_PREFIX "${MEMCHECK_CMD}" ${MEMCHECK_OPTIONS}) endif(TEST_MEMCHECK) - if(TEST_COVERAGE) - include(CodeCoverage) - append_coverage_compiler_flags() - - set(COVERAGE_EXCLUDES - "${CMAKE_CURRENT_SOURCE_DIR}/deps/*" - "${CMAKE_CURRENT_SOURCE_DIR}/testroot/*" - "${CMAKE_CURRENT_BINARY_DIR}/test/*" - ) - set(GCOVR_ADDITIONAL_ARGS - ) - setup_target_for_coverage_gcovr_xml( - NAME coverage-xml - BASE_DIRECTORY "${PROJECT_SOURCE_DIR}" - ) - setup_target_for_coverage_gcovr_html( - NAME coverage-html - BASE_DIRECTORY "${PROJECT_SOURCE_DIR}" - ) - endif(TEST_COVERAGE) endif(BUILD_UNITTEST) - if(BUILD_FUZZING) find_package(libfuzzer) endif(BUILD_FUZZING) +if(BUILD_COVERAGE) + include(CodeCoverage) + append_coverage_compiler_flags() + + set(COVERAGE_EXCLUDES + "${CMAKE_CURRENT_SOURCE_DIR}/deps/*" + "${CMAKE_CURRENT_SOURCE_DIR}/testroot/*" + "${CMAKE_CURRENT_BINARY_DIR}/test/*" + ) + set(GCOVR_ADDITIONAL_ARGS + ) + setup_target_for_coverage_gcovr_xml( + NAME coverage-xml + BASE_DIRECTORY "${PROJECT_SOURCE_DIR}" + ) + setup_target_for_coverage_gcovr_html( + NAME coverage-html + BASE_DIRECTORY "${PROJECT_SOURCE_DIR}" + ) +endif(BUILD_COVERAGE) include(CTest) set(CMAKE_CTEST_ARGUMENTS From c02c6a645f847a402d1c93644d1973ec8c59c0eb Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 12:12:31 -0400 Subject: [PATCH 09/15] enable fuzz coverage --- .github/workflows/fuzzing.yaml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/fuzzing.yaml b/.github/workflows/fuzzing.yaml index fdbeb518..1caf5a62 100644 --- a/.github/workflows/fuzzing.yaml +++ b/.github/workflows/fuzzing.yaml @@ -84,7 +84,8 @@ jobs: - name: Prep run: > ./build.sh prep - -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_UNITTEST=OFF -DBUILD_FUZZING=ON + -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF + -DBUILD_UNITTEST=OFF -DBUILD_FUZZING=ON -DBUILD_COVERAGE=ON - name: Build run: ./build.sh - name: Install @@ -99,6 +100,4 @@ jobs: name: ${{github.job}}-${{matrix.os}}-${{matrix.compiler}}-coverage path: build/default/coverage* - name: Report coverage - run: | - SRC_COV_PERC=$(xmlstarlet sel -t -v 'floor(/coverage/packages/package[@name="src"]/@line-rate * 100)' -n build/default/coverage-xml.xml) - echo "Source coverage: ${SRC_COV_PERC}%" >> $GITHUB_STEP_SUMMARY + run: ./build.sh coverage-summary >> $GITHUB_STEP_SUMMARY From 8750e29eb914640b2eddf7bd1196e44238027275 Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 12:15:24 -0400 Subject: [PATCH 10/15] attempt gcov-clang fix --- cmake/CodeCoverage.cmake | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/cmake/CodeCoverage.cmake b/cmake/CodeCoverage.cmake index c500cfa4..e0f1db2d 100644 --- a/cmake/CodeCoverage.cmake +++ b/cmake/CodeCoverage.cmake @@ -164,8 +164,13 @@ foreach(LANG ${LANGUAGES}) endif() endforeach() -set(COVERAGE_COMPILER_FLAGS "-g --coverage" - CACHE INTERNAL "") +if(CMAKE_C_COMPILER_ID MATCHES "GNU") + set(COVERAGE_COMPILER_FLAGS "-g --coverage") + set(GCOV_EXEC "--gcov-executable" "gcov") +elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") + set(COVERAGE_COMPILER_FLAGS "-gdwarf-4 --coverage") + set(GCOV_EXEC "--gcov-executable" "llvm-cov gcov") +endif() if(CMAKE_CXX_COMPILER_ID MATCHES "(GNU|Clang)") include(CheckCXXCompilerFlag) @@ -309,7 +314,7 @@ function(setup_target_for_coverage_lcov) if(${Coverage_SONARQUBE}) # Generate SonarQube output set(GCOVR_XML_CMD - ${GCOVR_PATH} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_PATH} ${GCOV_EXEC} --sonarqube ${Coverage_NAME}_sonarqube.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} ) set(GCOVR_XML_CMD_COMMAND @@ -455,7 +460,7 @@ function(setup_target_for_coverage_gcovr_xml) ) # Running gcovr set(GCOVR_XML_CMD - ${GCOVR_PATH} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_PATH} ${GCOV_EXEC} --xml ${Coverage_NAME}.xml -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} ) @@ -551,7 +556,7 @@ function(setup_target_for_coverage_gcovr_html) ) # Running gcovr set(GCOVR_HTML_CMD - ${GCOVR_PATH} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} + ${GCOVR_PATH} ${GCOV_EXEC} --html ${Coverage_NAME}/index.html --html-details -r ${BASEDIR} ${GCOVR_ADDITIONAL_ARGS} ${GCOVR_EXCLUDE_ARGS} --object-directory=${PROJECT_BINARY_DIR} ) From bb8d730413228e2fdfc644b062e7e80ed46ef780 Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 12:21:22 -0400 Subject: [PATCH 11/15] Make rpm happy --- .github/workflows/packages.yaml | 6 +++++- pkg/bsl.spec.in | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/packages.yaml b/.github/workflows/packages.yaml index 17123604..b76dac1f 100644 --- a/.github/workflows/packages.yaml +++ b/.github/workflows/packages.yaml @@ -41,7 +41,7 @@ jobs: dnf config-manager --set-enabled crb dnf install -y epel-release dnf install -y \ - rsync cmake git ninja-build gcc ruby \ + rsync cmake git ninja-build gcc gcc-c++ ccache ruby \ openssl-devel jansson-devel \ doxygen graphviz plantuml texlive-bibtex \ asciidoctor \ @@ -51,6 +51,10 @@ jobs: with: fetch-depth: 0 submodules: recursive + - name: ccache + uses: hendrikmuhs/ccache-action@v1.2 + with: + create-symlink: true - name: Prep run: ./build.sh rpm-prep - name: Build diff --git a/pkg/bsl.spec.in b/pkg/bsl.spec.in index 6bce36fb..820efa86 100644 --- a/pkg/bsl.spec.in +++ b/pkg/bsl.spec.in @@ -11,6 +11,7 @@ Source0: @CPACK_SOURCE_PACKAGE_FILE_NAME@.tar.gz BuildRequires: cmake BuildRequires: gcc +BuildRequires: gcc-c++ BuildRequires: openssl-devel BuildRequires: jansson-devel BuildRequires: asciidoctor From aac817db6ff48e1ef809bf5b01f6a0468104229f Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 12:34:58 -0400 Subject: [PATCH 12/15] Fixing rpm-prep --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 395ef890..6dffc387 100755 --- a/build.sh +++ b/build.sh @@ -111,7 +111,7 @@ function cmd_rpm_prep { then git config --global --add safe.directory ${PWD} fi - ./resources/prep.sh -DBUILD_LIB=OFF -DBUILD_TESTING=OFF -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_PACKAGE=ON + ./resources/prep.sh -DBUILD_LIB=OFF -DBUILD_UNITTEST=OFF -DBUILD_DOCS_API=OFF -DBUILD_DOCS_MAN=OFF -DBUILD_PACKAGE=ON } function cmd_rpm_build { From aea25a6eaac07015ce5cd9b637bc191b1a8d09cc Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 12:45:51 -0400 Subject: [PATCH 13/15] log corpus after fuzzing --- .github/workflows/fuzzing.yaml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/fuzzing.yaml b/.github/workflows/fuzzing.yaml index 1caf5a62..dadcb5d4 100644 --- a/.github/workflows/fuzzing.yaml +++ b/.github/workflows/fuzzing.yaml @@ -57,6 +57,7 @@ jobs: ${{matrix.compiler=='clang' && 'clang llvm' || 'gcc g++'}} \ libssl-dev libjansson-dev \ valgrind gcovr xmlstarlet + sudo gem install cbor-diag - name: Set up OS if: startsWith(matrix.os, 'centos') run: | @@ -70,6 +71,7 @@ jobs: openssl-devel jansson-devel \ valgrind xmlstarlet python3-pip pip3 install gcovr + gem install cbor-diag - name: Checkout repository uses: actions/checkout@v4 with: @@ -92,6 +94,8 @@ jobs: run: ./build.sh install - name: Test run: ./build.sh check + - name: Report corpus + run: for FN in $(find test/*cbor*corpus -type f); do echo $FN; cborseq2diag.rb <$FN; done - name: Collect coverage run: ./build.sh coverage - name: Archive coverage From 983a3594900d3f0b100785cf1dec67dc8448cbe7 Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 13:19:33 -0400 Subject: [PATCH 14/15] Found actual crash and OOM behavior with invalid bundle inputs --- src/mock_bpa/decode.c | 41 ++++--- test/CMakeLists.txt | 3 + .../rfc9173-exA113.cbor | Bin 0 -> 72 bytes .../rfc9173-exA14.cbor | Bin 0 -> 165 bytes .../rfc9173-exA24.cbor | Bin 0 -> 159 bytes .../rfc9173-exA314.cbor | Bin 0 -> 81 bytes .../rfc9173-exA35.cbor | Bin 0 -> 239 bytes .../rfc9173-exA45.cbor | Bin 0 -> 229 bytes test/fuzz_mock_bpa_bpv7_cbor.cpp | 101 ++++++++++++++++++ 9 files changed, 133 insertions(+), 12 deletions(-) create mode 100644 test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA113.cbor create mode 100644 test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA14.cbor create mode 100644 test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA24.cbor create mode 100644 test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA314.cbor create mode 100644 test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA35.cbor create mode 100644 test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA45.cbor create mode 100644 test/fuzz_mock_bpa_bpv7_cbor.cpp diff --git a/src/mock_bpa/decode.c b/src/mock_bpa/decode.c index ee3685c7..49bb61ae 100644 --- a/src/mock_bpa/decode.c +++ b/src/mock_bpa/decode.c @@ -96,13 +96,16 @@ int bsl_mock_decode_eid(QCBORDecodeContext *dec, BSL_HostEID_t *eid) const size_t begin = QCBORDecode_Tell(dec); QCBORDecode_VGetNextConsume(dec, &decitem); const size_t end = QCBORDecode_Tell(dec); - if ((begin != UINT32_MAX) && (end != UINT32_MAX) && (end > begin)) + + if ((QCBOR_SUCCESS == QCBORDecode_GetError(dec)) && (end > begin)) { + const UsefulBufC buf = QCBORDecode_RetrieveUndecodedInput(dec); + BSL_Data_t *raw = &(obj->ssp.as_raw); assert(raw != NULL); BSL_Data_Init(raw); // FIXME expose this from the decoder - BSL_Data_CopyFrom(raw, end - begin, (uint8_t *)dec->InBuf.UB.ptr + begin); + BSL_Data_CopyFrom(raw, end - begin, (const uint8_t *)buf.ptr + begin); } break; } @@ -160,7 +163,7 @@ int bsl_mock_decode_primary(QCBORDecodeContext *dec, MockBPA_PrimaryBlock_t *blk { case BSL_BUNDLECRCTYPE_16: case BSL_BUNDLECRCTYPE_32: - // just ignore the bytes + // just ignore the bytes, CRC check will use the encoded buffer QCBORDecode_GetByteString(dec, &view); break; default: @@ -169,16 +172,21 @@ int bsl_mock_decode_primary(QCBORDecodeContext *dec, MockBPA_PrimaryBlock_t *blk } QCBORDecode_ExitArray(dec); + if (QCBOR_SUCCESS != QCBORDecode_GetError(dec)) + { + return 2; + } const size_t end = QCBORDecode_Tell(dec); - if (!mock_bpa_crc_check(QCBORDecode_RetrieveUndecodedInput(dec), begin, end, blk->crc_type)) + const UsefulBufC buf = QCBORDecode_RetrieveUndecodedInput(dec); + if (!mock_bpa_crc_check(buf, begin, end, blk->crc_type)) { return 4; } blk->cbor_len = end - begin; - blk->cbor = calloc(1, blk->cbor_len); - memcpy(blk->cbor, &((uint8_t *)dec->InBuf.UB.ptr)[begin], blk->cbor_len); + blk->cbor = BSL_MALLOC(blk->cbor_len); + memcpy(blk->cbor, (const uint8_t *)buf.ptr + begin, blk->cbor_len); return 0; } @@ -200,14 +208,19 @@ int bsl_mock_decode_canonical(QCBORDecodeContext *dec, MockBPA_CanonicalBlock_t QCBORDecode_GetByteString(dec, &view); if (QCBOR_SUCCESS == QCBORDecode_GetError(dec)) { - if (blk->btsd == NULL) + if (blk->btsd) { - blk->btsd = calloc(1, view.len); - blk->btsd_len = view.len; + BSL_FREE(blk->btsd); + blk->btsd = NULL; + } + + blk->btsd_len = view.len; + if (blk->btsd_len > 0) + { + blk->btsd = BSL_MALLOC(view.len); + assert(blk->btsd != NULL); + memcpy(blk->btsd, view.ptr, view.len); } - assert(blk->btsd != NULL); - assert(blk->btsd_len > 0); - memcpy(blk->btsd, view.ptr, view.len); } switch (blk->crc_type) @@ -223,6 +236,10 @@ int bsl_mock_decode_canonical(QCBORDecodeContext *dec, MockBPA_CanonicalBlock_t } QCBORDecode_ExitArray(dec); + if (QCBOR_SUCCESS != QCBORDecode_GetError(dec)) + { + return 2; + } const size_t end = QCBORDecode_Tell(dec); if (!mock_bpa_crc_check(QCBORDecode_RetrieveUndecodedInput(dec), begin, end, blk->crc_type)) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 975c81c1..25d023ce 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -94,4 +94,7 @@ endif(BUILD_UNITTEST) if(BUILD_FUZZING) add_fuzz_test(SOURCE fuzz_mock_bpa_eid_cbor.cpp) target_link_libraries(fuzz_mock_bpa_eid_cbor PUBLIC ${LIB_ORDERED}) + + add_fuzz_test(SOURCE fuzz_mock_bpa_bpv7_cbor.cpp) + target_link_libraries(fuzz_mock_bpa_bpv7_cbor PUBLIC ${LIB_ORDERED}) endif(BUILD_FUZZING) diff --git a/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA113.cbor b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA113.cbor new file mode 100644 index 0000000000000000000000000000000000000000..647308194f8c3a6ef521b6e5f7346d7eb961a3ed GIT binary patch literal 72 zcmbQ=!Opm)jZGp7qW- zcaFhs>I*U7Go1^=9=0+v0u5IVN=;0uR4B<;NKeg6ElMm&RY+7YHquS11hNVeD|7M_ HQ~m=02E98O literal 0 HcmV?d00001 diff --git a/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA24.cbor b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA24.cbor new file mode 100644 index 0000000000000000000000000000000000000000..9aad2bacfa6fa4a3045771fd0513b210d458ddc9 GIT binary patch literal 159 zcmbQ=!Op{FlH&H4`j#2+|4 literal 0 HcmV?d00001 diff --git a/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA314.cbor b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA314.cbor new file mode 100644 index 0000000000000000000000000000000000000000..996a11f4995120d302ee71a566ada752d3938dde GIT binary patch literal 81 zcmbQ=!Op@B%@9%BO?PtgmO@7VoIe#NxnjQ dYF=tlVo9n(qJpuJZc-(XRghSjlb@LK9{|z>6(axu literal 0 HcmV?d00001 diff --git a/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA35.cbor b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA35.cbor new file mode 100644 index 0000000000000000000000000000000000000000..0816be9cc9cb094f41c7b4a3736ee440d715d6f3 GIT binary patch literal 239 zcmbQ=!Op&HmFJd Ui9D+4rSzfaI{Vb;cC-Ei0CWma3;+NC literal 0 HcmV?d00001 diff --git a/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA45.cbor b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA45.cbor new file mode 100644 index 0000000000000000000000000000000000000000..4654a9b174be1213019337e4d752f4ca9f0cde76 GIT binary patch literal 229 zcmbQ=!Op`2V<`SjjkZ-kF4% zve&ycL1LGV%53rwpLFZRCfSyrw=ZY6GBN@kr##`+HrLY0=96ZuDDo3~Cf{7r@lah+ UJ95p$A1jjDw_hkTSg7 +#include "bsl_test_utils.h" +#include + +#define EXPECT_EQ(expect, got) \ + if ((expect) != (got)) \ + { \ + BSL_LOG_ERR("EXPECT failure"); \ + } + +extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv); +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size); + +extern "C" int LLVMFuzzerInitialize(int *argc _U_, char ***argv _U_) +{ + BSL_openlog(); + bsl_mock_bpa_agent_init(); + return 0; +} +/* No cleanup: + bsl_mock_bpa_agent_deinit(); + BSL_closelog(); +*/ + +extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) +{ + int retval = 0; + + MockBPA_Bundle_t bundle = { 0 }; + // MockBPA_Bundle_Init(&bundle); + { + QCBORDecodeContext decoder; + QCBORDecode_Init(&decoder, (UsefulBufC) { data, size }, QCBOR_DECODE_MODE_NORMAL); + int res_eid = bsl_mock_decode_bundle(&decoder, &bundle); + int res_cbor = QCBORDecode_Finish(&decoder); + if (res_eid || (res_cbor != QCBOR_SUCCESS)) + { + retval = -1; + } + } + + BSL_Data_t out_data; + BSL_Data_Init(&out_data); + if (!retval) + { + QCBOREncodeContext encoder; + size_t needlen; + + QCBOREncode_Init(&encoder, SizeCalculateUsefulBuf); + EXPECT_EQ(0, bsl_mock_encode_bundle(&encoder, &bundle)); + assert(QCBOR_SUCCESS == QCBOREncode_FinishGetSize(&encoder, &needlen)); + + EXPECT_EQ(0, BSL_Data_Resize(&out_data, needlen)); + QCBOREncode_Init(&encoder, (UsefulBuf) { out_data.ptr, out_data.len }); + EXPECT_EQ(0, bsl_mock_encode_bundle(&encoder, &bundle)); + + UsefulBufC out; + EXPECT_EQ(QCBOR_SUCCESS, QCBOREncode_Finish(&encoder, &out)); + } + + if (!retval) + { + // output may be a subset + if (size != out_data.len) + { + // CBOR tags on input will not be carried + // BSL_LOG_ERR("Bad re-encoding size, expect %" PRIu64 " got %" PRIu64 ", for scheme %" PRIu64, size, + // out_data.len, ((bsl_mock_eid_t*)eid.handle)->scheme); + retval = -1; + } + if (0 != memcmp(data, out_data.ptr, out_data.len)) + { + retval = -1; + } + } + + BSL_Data_Deinit(&out_data); + MockBPA_Bundle_Deinit(&bundle); + return retval; +} From 2a30ed58837478c9e12347a477eec0e14e7ff921 Mon Sep 17 00:00:00 2001 From: Brian Sipos Date: Wed, 13 Aug 2025 13:27:41 -0400 Subject: [PATCH 15/15] file naming --- .../{dtn-shortservice => dtn-shortservice.cbor} | 0 .../{ipn-2elem-max => ipn-2elem-max.cbor} | 0 .../{ipn-2elem-small => ipn-2elem-small.cbor} | 0 .../{ipn-3elem-max => ipn-3elem-max.cbor} | 0 .../{ipn-3elem-small => ipn-3elem-small.cbor} | 0 .../{private-struct => private-struct.cbor} | Bin .../{reserved => reserved.cbor} | Bin 7 files changed, 0 insertions(+), 0 deletions(-) rename test/fuzz_mock_bpa_eid_cbor-corpus/{dtn-shortservice => dtn-shortservice.cbor} (100%) rename test/fuzz_mock_bpa_eid_cbor-corpus/{ipn-2elem-max => ipn-2elem-max.cbor} (100%) rename test/fuzz_mock_bpa_eid_cbor-corpus/{ipn-2elem-small => ipn-2elem-small.cbor} (100%) rename test/fuzz_mock_bpa_eid_cbor-corpus/{ipn-3elem-max => ipn-3elem-max.cbor} (100%) rename test/fuzz_mock_bpa_eid_cbor-corpus/{ipn-3elem-small => ipn-3elem-small.cbor} (100%) rename test/fuzz_mock_bpa_eid_cbor-corpus/{private-struct => private-struct.cbor} (100%) rename test/fuzz_mock_bpa_eid_cbor-corpus/{reserved => reserved.cbor} (100%) diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice b/test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice.cbor similarity index 100% rename from test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice rename to test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice.cbor diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max.cbor similarity index 100% rename from test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max rename to test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max.cbor diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small.cbor similarity index 100% rename from test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small rename to test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small.cbor diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max.cbor similarity index 100% rename from test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max rename to test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max.cbor diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small.cbor similarity index 100% rename from test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small rename to test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small.cbor diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/private-struct b/test/fuzz_mock_bpa_eid_cbor-corpus/private-struct.cbor similarity index 100% rename from test/fuzz_mock_bpa_eid_cbor-corpus/private-struct rename to test/fuzz_mock_bpa_eid_cbor-corpus/private-struct.cbor diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/reserved b/test/fuzz_mock_bpa_eid_cbor-corpus/reserved.cbor similarity index 100% rename from test/fuzz_mock_bpa_eid_cbor-corpus/reserved rename to test/fuzz_mock_bpa_eid_cbor-corpus/reserved.cbor