diff --git a/.github/workflows/build-test.yaml b/.github/workflows/build-test.yaml index 493a8d77..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_TESTING=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_TESTING=ON + 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 new file mode 100644 index 00000000..dadcb5d4 --- /dev/null +++ b/.github/workflows/fuzzing.yaml @@ -0,0 +1,107 @@ +## +## 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 +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: 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: + 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 + sudo gem install cbor-diag + - 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 + gem install cbor-diag + - 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_UNITTEST=OFF -DBUILD_FUZZING=ON -DBUILD_COVERAGE=ON + - name: Build + run: ./build.sh + - name: Install + 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 + uses: actions/upload-artifact@v4 + with: + name: ${{github.job}}-${{matrix.os}}-${{matrix.compiler}}-coverage + path: build/default/coverage* + - name: Report coverage + run: ./build.sh coverage-summary >> $GITHUB_STEP_SUMMARY 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/CMakeLists.txt b/CMakeLists.txt index 2a56abdc..cb99fe57 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,9 +25,10 @@ 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_COVERAGE "Enable runtime coverage logging and reporting" 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") @@ -59,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} ) @@ -75,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") @@ -127,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}") @@ -142,33 +141,36 @@ if(BUILD_TESTING) # 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) - - include(CTest) - set(CMAKE_CTEST_ARGUMENTS - --output-junit testresults.xml - --output-on-failure +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/*" ) -endif(BUILD_TESTING) + 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 + --output-junit testresults.xml + --output-on-failure +) # Install config used by tall targets include(GNUInstallDirs) @@ -176,11 +178,9 @@ 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/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 { 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} ) 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/pkg/bsl.spec.in b/pkg/bsl.spec.in index f092147a..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 @@ -58,7 +59,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/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..3c8bf312 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,8 @@ 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..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 (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/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..25d023ce 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -20,70 +20,81 @@ 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) -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}) +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) + + 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_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/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_bpv7_cbor-corpus/rfc9173-exA113.cbor b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA113.cbor new file mode 100644 index 00000000..64730819 Binary files /dev/null and b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA113.cbor differ diff --git a/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA14.cbor b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA14.cbor new file mode 100644 index 00000000..ebd58b03 Binary files /dev/null and b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA14.cbor differ 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 00000000..9aad2bac Binary files /dev/null and b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA24.cbor differ 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 00000000..996a11f4 Binary files /dev/null and b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA314.cbor differ 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 00000000..0816be9c Binary files /dev/null and b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA35.cbor differ 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 00000000..4654a9b1 Binary files /dev/null and b/test/fuzz_mock_bpa_bpv7_cbor-corpus/rfc9173-exA45.cbor differ diff --git a/test/fuzz_mock_bpa_bpv7_cbor.cpp b/test/fuzz_mock_bpa_bpv7_cbor.cpp new file mode 100644 index 00000000..3ba5abe2 --- /dev/null +++ b/test/fuzz_mock_bpa_bpv7_cbor.cpp @@ -0,0 +1,101 @@ +/* + * 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_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; +} diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice.cbor b/test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice.cbor new file mode 100644 index 00000000..02165637 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/dtn-shortservice.cbor @@ -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.cbor b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max.cbor new file mode 100644 index 00000000..2c06da69 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-max.cbor @@ -0,0 +1 @@ +‚‚ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small.cbor b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small.cbor new file mode 100644 index 00000000..e0beca45 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-2elem-small.cbor @@ -0,0 +1 @@ +‚‚ diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max.cbor b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max.cbor new file mode 100644 index 00000000..deb9317a --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-max.cbor @@ -0,0 +1 @@ +‚ƒÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ \ No newline at end of file diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small.cbor b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small.cbor new file mode 100644 index 00000000..ccd70291 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor-corpus/ipn-3elem-small.cbor @@ -0,0 +1,2 @@ +‚ƒ + diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/private-struct.cbor b/test/fuzz_mock_bpa_eid_cbor-corpus/private-struct.cbor new file mode 100644 index 00000000..a02e98a3 Binary files /dev/null and b/test/fuzz_mock_bpa_eid_cbor-corpus/private-struct.cbor differ diff --git a/test/fuzz_mock_bpa_eid_cbor-corpus/reserved.cbor b/test/fuzz_mock_bpa_eid_cbor-corpus/reserved.cbor new file mode 100644 index 00000000..950d4c4c Binary files /dev/null and b/test/fuzz_mock_bpa_eid_cbor-corpus/reserved.cbor differ diff --git a/test/fuzz_mock_bpa_eid_cbor.cpp b/test/fuzz_mock_bpa_eid_cbor.cpp new file mode 100644 index 00000000..f3cded17 --- /dev/null +++ b/test/fuzz_mock_bpa_eid_cbor.cpp @@ -0,0 +1,101 @@ +/* + * 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_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; + + 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; +} 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;