From a48055ff6b1ccb1603e31242dca7ba44e606b20c Mon Sep 17 00:00:00 2001 From: Charles-Edouard de la Vergne Date: Mon, 16 Dec 2024 18:58:41 +0100 Subject: [PATCH 1/9] Add 1st fuzzing --- .clusterfuzzlite/Dockerfile | 17 +++ .clusterfuzzlite/build.sh | 9 ++ .clusterfuzzlite/project.yaml | 1 + .github/workflows/cflite_cron.yml | 40 +++++++ .github/workflows/cflite_pr.yml | 43 +++++++ tests/fuzzing/CMakeLists.txt | 184 ++++++++++++++++++++++++++++++ tests/fuzzing/README.md | 83 ++++++++++++++ tests/fuzzing/src/fuzz_app_eth.c | 36 ++++++ tests/fuzzing/src/glyphs.h | 0 tests/fuzzing/src/mock.c | 110 ++++++++++++++++++ 10 files changed, 523 insertions(+) create mode 100644 .clusterfuzzlite/Dockerfile create mode 100644 .clusterfuzzlite/build.sh create mode 100644 .clusterfuzzlite/project.yaml create mode 100644 .github/workflows/cflite_cron.yml create mode 100644 .github/workflows/cflite_pr.yml create mode 100644 tests/fuzzing/CMakeLists.txt create mode 100644 tests/fuzzing/README.md create mode 100644 tests/fuzzing/src/fuzz_app_eth.c create mode 100644 tests/fuzzing/src/glyphs.h create mode 100644 tests/fuzzing/src/mock.c diff --git a/.clusterfuzzlite/Dockerfile b/.clusterfuzzlite/Dockerfile new file mode 100644 index 000000000..32efe99a5 --- /dev/null +++ b/.clusterfuzzlite/Dockerfile @@ -0,0 +1,17 @@ +FROM ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-lite:latest AS LITE_BUILDER + +# Base image with clang toolchain +FROM gcr.io/oss-fuzz-base/base-builder:v1 + +# Copy the project's source code. +COPY . $SRC/app-ethereum +COPY --from=LITE_BUILDER /opt/ledger-secure-sdk $SRC/app-ethereum/BOLOS_SDK + +# Add the ethereum-plugin-sdk submodule +RUN git clone https://github.com/LedgerHQ/ethereum-plugin-sdk.git $SRC/app-ethereum/ethereum-plugin-sdk + +# Working directory for build.sh +WORKDIR $SRC/app-ethereum + +# Copy build.sh into $SRC dir. +COPY ./.clusterfuzzlite/build.sh $SRC/ diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh new file mode 100644 index 000000000..7d9169f61 --- /dev/null +++ b/.clusterfuzzlite/build.sh @@ -0,0 +1,9 @@ +#!/bin/bash -eu + +# build fuzzers + +pushd tests/fuzzing +cmake -DBOLOS_SDK=../../BOLOS_SDK -Bbuild -H. +make -C build +mv ./build/fuzz_app_eth "${OUT}" +popd diff --git a/.clusterfuzzlite/project.yaml b/.clusterfuzzlite/project.yaml new file mode 100644 index 000000000..b455aa397 --- /dev/null +++ b/.clusterfuzzlite/project.yaml @@ -0,0 +1 @@ +language: c diff --git a/.github/workflows/cflite_cron.yml b/.github/workflows/cflite_cron.yml new file mode 100644 index 000000000..17c1e65a2 --- /dev/null +++ b/.github/workflows/cflite_cron.yml @@ -0,0 +1,40 @@ +name: ClusterFuzzLite cron tasks +on: + workflow_dispatch: + push: + branches: + - main # Use your actual default branch here. + schedule: + - cron: '0 13 * * 6' # At 01:00 PM, only on Saturday +permissions: read-all +jobs: + Fuzzing: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: + - mode: batch + sanitizer: address + - mode: batch + sanitizer: memory + - mode: prune + sanitizer: address + - mode: coverage + sanitizer: coverage + steps: + - name: Build Fuzzers (${{ matrix.mode }} - ${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + language: c # Change this to the language you are fuzzing. + sanitizer: ${{ matrix.sanitizer }} + - name: Run Fuzzers (${{ matrix.mode }} - ${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 300 # 5 minutes + mode: ${{ matrix.mode }} + sanitizer: ${{ matrix.sanitizer }} diff --git a/.github/workflows/cflite_pr.yml b/.github/workflows/cflite_pr.yml new file mode 100644 index 000000000..09f91dafe --- /dev/null +++ b/.github/workflows/cflite_pr.yml @@ -0,0 +1,43 @@ +name: ClusterFuzzLite PR fuzzing +on: + pull_request: + paths: + - '**' +permissions: read-all +jobs: + PR: + runs-on: ubuntu-latest + concurrency: + group: ${{ github.workflow }}-${{ matrix.sanitizer }}-${{ github.ref }} + cancel-in-progress: true + strategy: + fail-fast: false + matrix: + sanitizer: [address, undefined, memory] # Override this with the sanitizers you want. + steps: + - name: Build Fuzzers (${{ matrix.sanitizer }}) + id: build + uses: google/clusterfuzzlite/actions/build_fuzzers@v1 + with: + language: c # Change this to the language you are fuzzing. + github-token: ${{ secrets.GITHUB_TOKEN }} + sanitizer: ${{ matrix.sanitizer }} + # Optional but recommended: used to only run fuzzers that are affected + # by the PR. + # storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/OWNER/STORAGE-REPO-NAME.git + # storage-repo-branch: main # Optional. Defaults to "main" + # storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages". + - name: Run Fuzzers (${{ matrix.sanitizer }}) + id: run + uses: google/clusterfuzzlite/actions/run_fuzzers@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + fuzz-seconds: 300 # 5 minutes + mode: 'code-change' + sanitizer: ${{ matrix.sanitizer }} + output-sarif: true + # Optional but recommended: used to download the corpus produced by + # batch fuzzing. + # storage-repo: https://${{ secrets.PERSONAL_ACCESS_TOKEN }}@github.com/OWNER/STORAGE-REPO-NAME.git + # storage-repo-branch: main # Optional. Defaults to "main" + # storage-repo-branch-coverage: gh-pages # Optional. Defaults to "gh-pages". diff --git a/tests/fuzzing/CMakeLists.txt b/tests/fuzzing/CMakeLists.txt new file mode 100644 index 000000000..1a59c68f7 --- /dev/null +++ b/tests/fuzzing/CMakeLists.txt @@ -0,0 +1,184 @@ +cmake_minimum_required(VERSION 3.10) + +if(${CMAKE_VERSION} VERSION_LESS 3.10) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +endif() + +# project information +project(Fuzzer + VERSION 1.0 + DESCRIPTION "Eth Fuzzer" + LANGUAGES C) + +set(CMAKE_C_COMPILER clang) + +set(CMAKE_BUILD_TYPE "Debug") + +# compatible with ClusterFuzzLite +if (NOT DEFINED ENV{LIB_FUZZING_ENGINE}) + set(COMPILATION_FLAGS_ "-g -Wall -fsanitize=fuzzer,address,undefined") +else() + set(COMPILATION_FLAGS_ "$ENV{LIB_FUZZING_ENGINE} $ENV{CXXFLAGS}") +endif() + +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + +string(REPLACE " " ";" COMPILATION_FLAGS ${COMPILATION_FLAGS_}) + +# specify C standard +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED True) +set(CMAKE_C_FLAGS_DEBUG + "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra -Wno-unused-function -DFUZZ -pedantic -g -O0" +) + +# guard against in-source builds +if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) + message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ") +endif() + +set(TARGET_DEVICE flex) +if (NOT DEFINED BOLOS_SDK) + message(FATAL_ERROR "BOLOS_SDK environment variable not found.") +endif() + +set(DEFINES + gcc + APPNAME=\"Fuzzing\" + API_LEVEL=21 + TARGET=\"flex\" + TARGET_NAME=\"TARGET_FLEX\" + APPVERSION=\"1.1.0\" + SDK_NAME=\"ledger-secure-sdk\" + SDK_VERSION=\"v21.3.3\" + SDK_HASH=\"d88d4db3c93665f52b5b1f45099d9d36dfaa06ba\" + gcc + __IO=volatile + NDEBUG + HAVE_BAGL_FONT_INTER_REGULAR_28PX + HAVE_BAGL_FONT_INTER_SEMIBOLD_28PX + HAVE_BAGL_FONT_INTER_MEDIUM_36PX + HAVE_INAPP_BLE_PAIRING + HAVE_NBGL + HAVE_PIEZO_SOUND + HAVE_SE_TOUCH + HAVE_SE_EINK_DISPLAY + NBGL_PAGE + NBGL_USE_CASE + SCREEN_SIZE_WALLET + HAVE_FAST_HOLD_TO_APPROVE + HAVE_LEDGER_PKI + HAVE_NES_CRYPT + HAVE_ST_AES + NATIVE_LITTLE_ENDIAN + HAVE_CRC + HAVE_HASH + HAVE_RIPEMD160 + HAVE_SHA224 + HAVE_SHA256 + HAVE_SHA3 + HAVE_SHA384 + HAVE_SHA512 + HAVE_SHA512_WITH_BLOCK_ALT_METHOD + HAVE_SHA512_WITH_BLOCK_ALT_METHOD_M0 + HAVE_BLAKE2 + HAVE_HMAC + HAVE_PBKDF2 + HAVE_AES + HAVE_MATH + HAVE_RNG + HAVE_RNG_RFC6979 + HAVE_RNG_SP800_90A + HAVE_ECC + HAVE_ECC_WEIERSTRASS + HAVE_ECC_TWISTED_EDWARDS + HAVE_ECC_MONTGOMERY + HAVE_SECP256K1_CURVE + HAVE_SECP256R1_CURVE + HAVE_SECP384R1_CURVE + HAVE_SECP521R1_CURVE + HAVE_FR256V1_CURVE + HAVE_STARK256_CURVE + HAVE_BRAINPOOL_P256R1_CURVE + HAVE_BRAINPOOL_P256T1_CURVE + HAVE_BRAINPOOL_P320R1_CURVE + HAVE_BRAINPOOL_P320T1_CURVE + HAVE_BRAINPOOL_P384R1_CURVE + HAVE_BRAINPOOL_P384T1_CURVE + HAVE_BRAINPOOL_P512R1_CURVE + HAVE_BRAINPOOL_P512T1_CURVE + HAVE_BLS12_381_G1_CURVE + HAVE_CV25519_CURVE + HAVE_CV448_CURVE + HAVE_ED25519_CURVE + HAVE_ED448_CURVE + HAVE_ECDH + HAVE_ECDSA + HAVE_EDDSA + HAVE_ECSCHNORR + HAVE_X25519 + HAVE_X448 + HAVE_AES_GCM + HAVE_CMAC + HAVE_AES_SIV + COIN_VARIANT=1 + HAVE_BOLOS_APP_STACK_CANARY + IO_SEPROXYHAL_BUFFER_SIZE_B=300 + HAVE_BLE + BLE_COMMAND_TIMEOUT_MS=2000 + HAVE_BLE_APDU + BLE_SEGMENT_SIZE=32 + HAVE_DEBUG_THROWS + NBGL_QRCODE + MAJOR_VERSION=1 + MINOR_VERSION=1 + PATCH_VERSION=0 + IO_HID_EP_LENGTH=64 + HAVE_SPRINTF + HAVE_SNPRINTF_FORMAT_U + HAVE_IO_USB + HAVE_L4_USBLIB + IO_USB_MAX_ENDPOINTS=4 + HAVE_USB_APDU + USB_SEGMENT_SIZE=64 + HAVE_WEBUSB + WEBUSB_URL_SIZE_B=0 + WEBUSB_URL= + OS_IO_SEPROXYHAL + STANDARD_APP_SYNC_RAPDU + HAVE_GENERIC_TX_PARSER + HAVE_TRUSTED_NAME + HAVE_DYN_MEM_ALLOC + HAVE_SWAP + HAVE_ENUM_VALUE + HAVE_NFT_SUPPORT +) +set(DEFINE ${DEFINES} HAVE_PRINTF PRINTF=printf) + +add_compile_definitions(${DEFINES}) + +FILE(GLOB_RECURSE SDK_STD_SOURCES ${BOLOS_SDK}/lib_standard_app/write.c src/mock.c) + + +include_directories( + ${CMAKE_SOURCE_DIR}/../../ethereum-plugin-sdk/src/ + ${CMAKE_SOURCE_DIR}/../../src + ${CMAKE_SOURCE_DIR}/../../src_features/provideDynamicNetwork/ + ${BOLOS_SDK}/include + ${BOLOS_SDK}/lib_standard_app + ${BOLOS_SDK}/target/${TARGET_DEVICE}/include + ${BOLOS_SDK}/lib_cxng/include + ${BOLOS_SDK}/lib_cxng/src + ${BOLOS_SDK}/lib_ux_nbgl + ${BOLOS_SDK}/lib_nbgl/include + ${CMAKE_SOURCE_DIR}/src +) + +FILE(GLOB_RECURSE SOURCES + ${CMAKE_SOURCE_DIR}/../../src_features/provideDynamicNetwork/*.c + ${CMAKE_SOURCE_DIR}/../../src/hash_bytes.c +) + +add_executable(fuzz_app_eth src/fuzz_app_eth.c ${SDK_STD_SOURCES} ${SOURCES}) +target_compile_options(fuzz_app_eth PUBLIC ${COMPILATION_FLAGS}) +target_link_options(fuzz_app_eth PUBLIC ${COMPILATION_FLAGS}) diff --git a/tests/fuzzing/README.md b/tests/fuzzing/README.md new file mode 100644 index 000000000..43ad67b02 --- /dev/null +++ b/tests/fuzzing/README.md @@ -0,0 +1,83 @@ +# Fuzzing Tests + +## Fuzzing + +Fuzzing allows us to test how a program behaves when provided with invalid, unexpected, or random data as input. + +Our fuzz target needs to implement `int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size)`, +which provides an array of random bytes that can be used to simulate a serialized buffer. +If the application crashes, or a [sanitizer](https://github.com/google/sanitizers) detects +any kind of access violation, the fuzzing process is stopped, a report regarding the vulnerability is shown, +and the input that triggered the bug is written to disk under the name `crash-*`. +The vulnerable input file created can be passed as an argument to the fuzzer to triage the issue. + +> **Note**: Usually we want to write a separate fuzz target for each functionality. + +## Manual usage based on Ledger container + +### Preparation + +The fuzzer can run from the docker `ledger-app-builder-legacy`. You can download it from the `ghcr.io` docker repository: + +```console +sudo docker pull ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest +``` + +You can then enter this development environment by executing the following command from the repository root directory: + +```console +sudo docker run --rm -ti --user "$(id -u):$(id -g)" -v "$(realpath .):/app" ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest +``` + +### Compilation + +Once in the container, go into the `tests/fuzzing` folder to compile the fuzzer: + +```console +cd tests/fuzzing + +# cmake initialization +cmake -DBOLOS_SDK=/opt/ledger-secure-sdk -Bbuild -H. + +# Fuzzer compilation +make -C build +``` + +### Run + +```console +./build/fuzz_app_eth +``` + +## Full usage based on `clusterfuzzlite` container + +Exactly the same context as the CI, directly using the `clusterfuzzlite` environment. + +More info can be found here: + + +### Preparation + +The principle is to build the container, and run it to perform the fuzzing. + +> **Note**: The container contains a copy of the sources (they are not cloned), +> which means the `docker build` command must be re-executed after each code modification. + +```console +# Prepare directory tree +mkdir tests/fuzzing/{corpus,out} +# Container generation +docker build -t app-ethereum --file .clusterfuzzlite/Dockerfile . +``` + +### Compilation + +```console +docker run --rm --privileged -e FUZZING_LANGUAGE=c -v "$(realpath .)/tests/fuzzing/out:/out" -ti app-ethereum +``` + +### Run + +```console +docker run --rm --privileged -e FUZZING_ENGINE=libfuzzer -e RUN_FUZZER_MODE=interactive -v "$(realpath .)/tests/fuzzing/corpus:/tmp/fuzz_corpus" -v "$(realpath .)/tests/fuzzing/out:/out" -ti gcr.io/oss-fuzz-base/base-runner run_fuzzer fuzz_app_eth +``` diff --git a/tests/fuzzing/src/fuzz_app_eth.c b/tests/fuzzing/src/fuzz_app_eth.c new file mode 100644 index 000000000..c4f55501a --- /dev/null +++ b/tests/fuzzing/src/fuzz_app_eth.c @@ -0,0 +1,36 @@ +#include +#include +#include +#include +#include +#include + +#include "shared_context.h" +#include "network_dynamic.h" + +unsigned char G_io_apdu_buffer[IO_APDU_BUFFER_SIZE]; +tmpContent_t tmpContent; +const chain_config_t *chainConfig; +txContext_t txContext; + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + explicit_bzero(G_io_apdu_buffer, 500); + explicit_bzero(&tmpContent, sizeof(tmpContent_t)); + explicit_bzero(&txContext, sizeof(txContext_t)); + size_t offset = 0; + size_t len = 0; + uint8_t p1; + uint8_t p2; + unsigned int tx; + + while (size - offset > 4) { + if (data[offset++] == 0) break; + p1 = data[offset++]; + p2 = data[offset++]; + len = data[offset++]; + if (size - offset < len) return 0; + handleNetworkConfiguration(p1, p2, data + offset, len, &tx); + offset += len; + } + return 0; +} diff --git a/tests/fuzzing/src/glyphs.h b/tests/fuzzing/src/glyphs.h new file mode 100644 index 000000000..e69de29bb diff --git a/tests/fuzzing/src/mock.c b/tests/fuzzing/src/mock.c new file mode 100644 index 000000000..7328a6adc --- /dev/null +++ b/tests/fuzzing/src/mock.c @@ -0,0 +1,110 @@ +#include + +#include "cx_errors.h" +#include "cx_sha256.h" +#include "cx_sha3.h" + +cx_err_t cx_sha256_init_no_throw(cx_sha256_t *hash) { + memset(hash, 0, sizeof(cx_sha256_t)); + return CX_OK; +} + +cx_err_t cx_hash_no_throw(cx_hash_t *hash, + uint32_t mode, + const uint8_t *in, + size_t len, + uint8_t *out, + size_t out_len) { + UNUSED(hash); + UNUSED(mode); + if (len > 0 && out_len > 0) out[out_len - 1] = in[len - 1]; + return CX_OK; +} + +void assert_exit(bool confirm) { + UNUSED(confirm); + exit(1); +} + +cx_err_t cx_keccak_256_hash_iovec(const cx_iovec_t *iovec, + size_t iovec_len, + uint8_t digest[static CX_KECCAK_256_SIZE]) { + UNUSED(iovec); + UNUSED(iovec_len); + digest[CX_KECCAK_256_SIZE - 1] = 0; + return CX_OK; +} + +cx_err_t cx_sha256_hash_iovec(const cx_iovec_t *iovec, + size_t iovec_len, + uint8_t digest[static CX_SHA256_SIZE]) { + UNUSED(iovec); + UNUSED(iovec_len); + digest[CX_SHA256_SIZE - 1] = 0; + return CX_OK; +} + +int check_signature_with_pubkey(const char *tag, + uint8_t *buffer, + const uint8_t bufLen, + const uint8_t *PubKey, + const uint8_t keyLen, +#ifdef HAVE_LEDGER_PKI + const uint8_t keyUsageExp, +#endif + uint8_t *signature, + const uint8_t sigLen) { + UNUSED(tag); + UNUSED(buffer); + UNUSED(bufLen); + UNUSED(PubKey); +#ifdef HAVE_LEDGER_PKI + UNUSED(keyUsageExp); +#endif + UNUSED(keyLen); + UNUSED(signature); + UNUSED(sigLen); + return CX_OK; +} + +uint64_t u64_from_BE(const uint8_t *in, uint8_t size) { + uint8_t i = 0; + uint64_t res = 0; + + while (i < size && i < sizeof(res)) { + res <<= 8; + res |= in[i]; + i++; + } + + return res; +} + +bool u64_to_string(uint64_t src, char *dst, uint8_t dst_size) { + // Copy the numbers in ASCII format. + uint8_t i = 0; + do { + // Checking `i + 1` to make sure we have enough space for '\0'. + if (i + 1 >= dst_size) { + return false; + } + dst[i] = src % 10 + '0'; + src /= 10; + i++; + } while (src); + + // Null terminate string + dst[i] = '\0'; + + // Revert the string + i--; + uint8_t j = 0; + while (j < i) { + char tmp = dst[i]; + dst[i] = dst[j]; + dst[j] = tmp; + i--; + j++; + } + return true; +} From ce5962a0fc37ec27ed95f3914b0f16f506372128 Mon Sep 17 00:00:00 2001 From: Charles-Edouard de la Vergne Date: Mon, 16 Dec 2024 18:57:54 +0100 Subject: [PATCH 2/9] fix fuzzing findings --- src/shared_context.h | 2 +- .../provideDynamicNetwork/network_dynamic.c | 25 ++++++++++++++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/src/shared_context.h b/src/shared_context.h index eaac84fb0..d329d043f 100644 --- a/src/shared_context.h +++ b/src/shared_context.h @@ -16,7 +16,7 @@ #include "nbgl_types.h" #endif -extern void app_exit(); +extern void app_exit(void); extern void common_app_init(void); #define SELECTOR_LENGTH 4 diff --git a/src_features/provideDynamicNetwork/network_dynamic.c b/src_features/provideDynamicNetwork/network_dynamic.c index 3ff7d7c09..45f5474ad 100644 --- a/src_features/provideDynamicNetwork/network_dynamic.c +++ b/src_features/provideDynamicNetwork/network_dynamic.c @@ -288,6 +288,7 @@ static uint16_t parse_icon_buffer(void) { uint8_t digest[CX_SHA256_SIZE]; const uint8_t *data = g_network_icon[g_current_slot].bitmap; const uint16_t field_len = g_icon_payload.received_size; + cx_err_t error = CX_INTERNAL_ERROR; // Check the icon header sw = check_icon_header(data, field_len, &img_len); @@ -298,7 +299,7 @@ static uint16_t parse_icon_buffer(void) { CHECK_FIELD_OVERFLOW("NETWORK_ICON", g_network_icon[g_current_slot].bitmap); // Check icon hash - cx_sha256_hash(data, field_len, digest); + CX_CHECK(cx_sha256_hash(data, field_len, digest)); if (memcmp(digest, g_network_icon[g_current_slot].hash, CX_SHA256_SIZE) != 0) { PRINTF("NETWORK_ICON hash mismatch!\n"); return APDU_RESPONSE_INVALID_DATA; @@ -313,7 +314,9 @@ static uint16_t parse_icon_buffer(void) { DYNAMIC_NETWORK_INFO[g_current_slot].icon.isFile = true; COPY_FIELD(DYNAMIC_NETWORK_INFO[g_current_slot].icon.bitmap); print_icon_info(); - return APDU_RESPONSE_OK; + error = APDU_RESPONSE_OK; +end: + return error; } /** @@ -376,6 +379,12 @@ static uint16_t handle_next_icon_chunk(const uint8_t *data, uint8_t length) { */ static uint16_t handle_icon_chunks(uint8_t p1, const uint8_t *data, uint8_t length) { uint16_t sw = APDU_RESPONSE_UNKNOWN; + uint8_t hash[CX_SHA256_SIZE] = {0}; + + if (memcmp(g_network_icon[g_current_slot].hash, hash, CX_SHA256_SIZE) == 0) { + PRINTF("Error: Icon hash not set!\n"); + return APDU_RESPONSE_INVALID_DATA; + } // Check the received chunk index if (p1 == P1_FIRST_CHUNK) { @@ -432,16 +441,24 @@ static bool verify_signature(s_sig_ctx *sig_ctx) { CX_CHECK( cx_hash_no_throw((cx_hash_t *) &sig_ctx->hash_ctx, CX_LAST, NULL, 0, hash, INT256_LENGTH)); +#ifdef HAVE_LEDGER_PKI CX_CHECK(check_signature_with_pubkey("Dynamic Network", hash, sizeof(hash), LEDGER_SIGNATURE_PUBLIC_KEY, sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), -#ifdef HAVE_LEDGER_PKI CERTIFICATE_PUBLIC_KEY_USAGE_COIN_META, -#endif (uint8_t *) (sig_ctx->sig), sig_ctx->sig_size)); +#else + CX_CHECK(check_signature_with_pubkey("Dynamic Network", + hash, + sizeof(hash), + LEDGER_SIGNATURE_PUBLIC_KEY, + sizeof(LEDGER_SIGNATURE_PUBLIC_KEY), + (uint8_t *) (sig_ctx->sig), + sig_ctx->sig_size)); +#endif ret_code = true; end: From 3811c4c192c91c29256a405b265f207b6057d877 Mon Sep 17 00:00:00 2001 From: Baptistin BOILOT Date: Tue, 3 Dec 2024 17:48:40 +0100 Subject: [PATCH 3/9] test: add gcs fuzzer --- .gitignore | 2 + fuzzing/CMakeLists.txt | 200 +++++++++++++++++++ fuzzing/README.md | 21 ++ fuzzing/coverage.sh | 3 + fuzzing/fuzzer_tlv.c | 93 +++++++++ fuzzing/mock/glyphs.h | 0 fuzzing/mock/mock.c | 87 ++++++++ src_features/generic_tx_parser/cmd_tx_info.h | 2 + src_features/signTx/logic_signTx.c | 2 +- 9 files changed, 409 insertions(+), 1 deletion(-) create mode 100644 fuzzing/CMakeLists.txt create mode 100644 fuzzing/README.md create mode 100755 fuzzing/coverage.sh create mode 100644 fuzzing/fuzzer_tlv.c create mode 100644 fuzzing/mock/glyphs.h create mode 100644 fuzzing/mock/mock.c diff --git a/.gitignore b/.gitignore index 20f25ff19..019c7207d 100644 --- a/.gitignore +++ b/.gitignore @@ -15,3 +15,5 @@ __version__.py .vscode .idea + +fuzzing/CORPUS/ \ No newline at end of file diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt new file mode 100644 index 000000000..5dcefbb46 --- /dev/null +++ b/fuzzing/CMakeLists.txt @@ -0,0 +1,200 @@ +cmake_minimum_required(VERSION 3.14) + +if(${CMAKE_VERSION} VERSION_LESS 3.14) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +endif() + +# project information +project(FuzzWithdraw + VERSION 1.0 + DESCRIPTION "Fuzzing of Ethereum app" + LANGUAGES C) + +# guard against bad build-type strings +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +if (NOT CMAKE_C_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR "Fuzzer needs to be built with Clang") +endif() + +# default fuzz device target +if (NOT TARGET_DEVICE) + set(TARGET_DEVICE "flex") +endif() + +set(BOLOS_SDK /opt/${TARGET_DEVICE}-secure-sdk) +if (NOT DEFINED BOLOS_SDK) + message(FATAL_ERROR "BOLOS_SDK environment variable not found.") +endif() + +# compatible with ClusterFuzzLite +if (NOT DEFINED ENV{LIB_FUZZING_ENGINE}) + set(COMPILATION_FLAGS -std=gnu99 -Wall -Wextra -pedantic -fshort-enums -Wno-unused-parameter -Wno-embedded-directive -g -O0 -fsanitize=fuzzer,address,undefined -fprofile-instr-generate -fcoverage-mapping) +else() + set(COMPILATION_FLAGS $ENV{LIB_FUZZING_ENGINE} $ENV{CXXFLAGS}) +endif() + +set(DEFINES + gcc + APPNAME=\"Fuzzing\" + API_LEVEL=21 + TARGET=\"flex\" + TARGET_NAME=\"TARGET_FLEX\" + APPVERSION=\"1.1.0\" + SDK_NAME=\"ledger-secure-sdk\" + SDK_VERSION=\"v21.3.3\" + SDK_HASH=\"d88d4db3c93665f52b5b1f45099d9d36dfaa06ba\" + gcc + __IO=volatile + NDEBUG + HAVE_BAGL_FONT_INTER_REGULAR_28PX + HAVE_BAGL_FONT_INTER_SEMIBOLD_28PX + HAVE_BAGL_FONT_INTER_MEDIUM_36PX + HAVE_INAPP_BLE_PAIRING + HAVE_NBGL + HAVE_PIEZO_SOUND + HAVE_SE_TOUCH + HAVE_SE_EINK_DISPLAY + NBGL_PAGE + NBGL_USE_CASE + SCREEN_SIZE_WALLET + HAVE_FAST_HOLD_TO_APPROVE + HAVE_LEDGER_PKI + HAVE_NES_CRYPT + HAVE_ST_AES + NATIVE_LITTLE_ENDIAN + HAVE_CRC + HAVE_HASH + HAVE_RIPEMD160 + HAVE_SHA224 + HAVE_SHA256 + HAVE_SHA3 + HAVE_SHA384 + HAVE_SHA512 + HAVE_SHA512_WITH_BLOCK_ALT_METHOD + HAVE_SHA512_WITH_BLOCK_ALT_METHOD_M0 + HAVE_BLAKE2 + HAVE_HMAC + HAVE_PBKDF2 + HAVE_AES + HAVE_MATH + HAVE_RNG + HAVE_RNG_RFC6979 + HAVE_RNG_SP800_90A + HAVE_ECC + HAVE_ECC_WEIERSTRASS + HAVE_ECC_TWISTED_EDWARDS + HAVE_ECC_MONTGOMERY + HAVE_SECP256K1_CURVE + HAVE_SECP256R1_CURVE + HAVE_SECP384R1_CURVE + HAVE_SECP521R1_CURVE + HAVE_FR256V1_CURVE + HAVE_STARK256_CURVE + HAVE_BRAINPOOL_P256R1_CURVE + HAVE_BRAINPOOL_P256T1_CURVE + HAVE_BRAINPOOL_P320R1_CURVE + HAVE_BRAINPOOL_P320T1_CURVE + HAVE_BRAINPOOL_P384R1_CURVE + HAVE_BRAINPOOL_P384T1_CURVE + HAVE_BRAINPOOL_P512R1_CURVE + HAVE_BRAINPOOL_P512T1_CURVE + HAVE_BLS12_381_G1_CURVE + HAVE_CV25519_CURVE + HAVE_CV448_CURVE + HAVE_ED25519_CURVE + HAVE_ED448_CURVE + HAVE_ECDH + HAVE_ECDSA + HAVE_EDDSA + HAVE_ECSCHNORR + HAVE_X25519 + HAVE_X448 + HAVE_AES_GCM + HAVE_CMAC + HAVE_AES_SIV + COIN_VARIANT=1 + HAVE_BOLOS_APP_STACK_CANARY + IO_SEPROXYHAL_BUFFER_SIZE_B=300 + HAVE_BLE + BLE_COMMAND_TIMEOUT_MS=2000 + HAVE_BLE_APDU + BLE_SEGMENT_SIZE=32 + HAVE_DEBUG_THROWS + NBGL_QRCODE + MAJOR_VERSION=1 + MINOR_VERSION=1 + PATCH_VERSION=0 + IO_HID_EP_LENGTH=64 + HAVE_SPRINTF + HAVE_SNPRINTF_FORMAT_U + HAVE_IO_USB + HAVE_L4_USBLIB + IO_USB_MAX_ENDPOINTS=4 + HAVE_USB_APDU + USB_SEGMENT_SIZE=64 + HAVE_WEBUSB + WEBUSB_URL_SIZE_B=0 + WEBUSB_URL= + OS_IO_SEPROXYHAL + STANDARD_APP_SYNC_RAPDU + HAVE_GENERIC_TX_PARSER + HAVE_TRUSTED_NAME + HAVE_DYN_MEM_ALLOC + HAVE_SWAP + HAVE_ENUM_VALUE + HAVE_NFT_SUPPORT +) + +add_compile_definitions(${DEFINES}) + +include_directories( + ${CMAKE_SOURCE_DIR}/../ethereum-plugin-sdk/src/ + ${CMAKE_SOURCE_DIR}/../src + ${CMAKE_SOURCE_DIR}/../src_features/generic_tx_parser/ + ${CMAKE_SOURCE_DIR}/../src_features/provide_enum_value/ + ${CMAKE_SOURCE_DIR}/../src_features/provideDynamicNetwork/ + ${CMAKE_SOURCE_DIR}/../src_features/signTx/ + ${CMAKE_SOURCE_DIR}/../src_features/provideTrustedName/ + ${CMAKE_SOURCE_DIR}/../src_features/getChallenge/ + ${CMAKE_SOURCE_DIR}/../src_features/signMessageEIP712/ + ${BOLOS_SDK}/include + ${BOLOS_SDK}/target/${TARGET_DEVICE}/include + ${BOLOS_SDK}/lib_cxng/include + ${BOLOS_SDK}/lib_cxng/src + ${BOLOS_SDK}/lib_ux_nbgl + ${BOLOS_SDK}/lib_nbgl/include + ${BOLOS_SDK}/lib_standard_app/ + ./mock/ +) + +FILE(GLOB_RECURSE SDK_STD_SOURCES ${BOLOS_SDK}/lib_standard_app/*.c ${CMAKE_SOURCE_DIR}/../ethereum-plugin-sdk/src/*.c ./mock/mock.c) +list(REMOVE_ITEM SDK_STD_SOURCES ${BOLOS_SDK}/lib_standard_app/io.c ${CMAKE_SOURCE_DIR}/../ethereum-plugin-sdk/src/main.c ${BOLOS_SDK}/lib_standard_app/main.c ${BOLOS_SDK}/lib_standard_app/crypto_helpers.c) + +FILE(GLOB_RECURSE SOURCES + ${CMAKE_SOURCE_DIR}/../src_features/generic_tx_parser/*.c + ${CMAKE_SOURCE_DIR}/../src_features/provideTrustedName/*.c + ${CMAKE_SOURCE_DIR}/../src_features/getChallenge/*.c + ${CMAKE_SOURCE_DIR}/../src_features/provide_enum_value/*.c + ${CMAKE_SOURCE_DIR}/../src_features/provideDynamicNetwork/*.c + ${CMAKE_SOURCE_DIR}/../src/mem.c + ${CMAKE_SOURCE_DIR}/../src/mem_utils.c + ${CMAKE_SOURCE_DIR}/../src/network.c + ${CMAKE_SOURCE_DIR}/../src/tlv.c + ${CMAKE_SOURCE_DIR}/../src/tlv_apdu.c + ${CMAKE_SOURCE_DIR}/../src/uint128.c + ${CMAKE_SOURCE_DIR}/../src/uint256.c + ${CMAKE_SOURCE_DIR}/../src/time_format.c + ${CMAKE_SOURCE_DIR}/../src/uint_common.c + ${CMAKE_SOURCE_DIR}/../src/utils.c + ${CMAKE_SOURCE_DIR}/../src/manage_asset_info.c + ${CMAKE_SOURCE_DIR}/../src/hash_bytes.c +) + +add_executable(fuzzer_tlv fuzzer_tlv.c ${SDK_STD_SOURCES} ${SOURCES}) +target_link_libraries(fuzzer_tlv bsd) # strlcpy / strlcat + +target_compile_options(fuzzer_tlv PRIVATE ${COMPILATION_FLAGS}) +target_link_options(fuzzer_tlv PRIVATE ${COMPILATION_FLAGS}) \ No newline at end of file diff --git a/fuzzing/README.md b/fuzzing/README.md new file mode 100644 index 000000000..503fc4049 --- /dev/null +++ b/fuzzing/README.md @@ -0,0 +1,21 @@ +# Fuzzing ethereum app + +## Build + +The fuzzer can be built using the following commands from the app directory: +```bash +docker run --rm -it --user "$(id -u):$(id -g)" -v "$(realpath .):/app" ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest bash + +apt install libbsd-dev + +cd fuzzing +cmake -DBOLOS_SDK=/opt/ledger-secure-sdk -DCMAKE_C_COMPILER=/usr/bin/clang -Bbuild -H. +cmake --build build +``` + +## Run + +The fuzzer can be run using the following commands from the fuzzing directory: +```bash +./build/fuzzer_tlv -max_len=8192 +``` diff --git a/fuzzing/coverage.sh b/fuzzing/coverage.sh new file mode 100755 index 000000000..a6c2c3b93 --- /dev/null +++ b/fuzzing/coverage.sh @@ -0,0 +1,3 @@ +llvm-profdata merge -sparse *.profraw -o default.profdata +llvm-cov show build/fuzzer_tlv -instr-profile=default.profdata -format=html > report.html +llvm-cov report build/fuzzer_tlv -instr-profile=default.profdata diff --git a/fuzzing/fuzzer_tlv.c b/fuzzing/fuzzer_tlv.c new file mode 100644 index 000000000..6bc67087e --- /dev/null +++ b/fuzzing/fuzzer_tlv.c @@ -0,0 +1,93 @@ +#include +#include + +#include "cmd_field.h" +#include "cmd_tx_info.h" +#include "cmd_enum_value.h" + +#include "gtp_field.h" +#include "gtp_tx_info.h" +#include "enum_value.h" + +#include "shared_context.h" +#include "tlv.h" + +cx_sha3_t sha3 = {0}; +unsigned char G_io_apdu_buffer[260]; +tmpContent_t tmpContent; +txContext_t txContext; +txContent_t txContent; +chain_config_t config = { + .coinName = "FUZZ", + .chainId = 0x42, +}; +const chain_config_t *chainConfig = &config; +uint8_t appState; +tmpCtx_t tmpCtx; +strings_t strings; + + +int fuzzGenericParserFieldCmd(const uint8_t *data, size_t size) { + s_field field = {0}; + s_field_ctx ctx = {0}; + ctx.field = &field; + + if (!tlv_parse(data, size, (f_tlv_data_handler)&handle_field_struct, &ctx)) + return 1; + + if (!verify_field_struct(&ctx)) + return 1; + + return format_field(&field); +} + +int fuzzGenericParserTxInfoCmd(const uint8_t *data, size_t size) { + s_tx_info tx_info = {0}; + s_tx_info_ctx ctx = {0}; + ctx.tx_info = &tx_info; + + if (!tlv_parse(data, size, (f_tlv_data_handler)&handle_tx_info_struct, &ctx)) + return 1; + + return verify_tx_info_struct(&ctx); +} + +int fuzzGenericParserEnumCmd(const uint8_t *data, size_t size) { + s_enum_value_ctx ctx = {0}; + + if (!tlv_parse(data, size, (f_tlv_data_handler)&handle_enum_value_struct, &ctx)) + return 1; + + return verify_enum_value_struct(&ctx); +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + explicit_bzero(&tmpContent, sizeof(tmpContent_t)); + explicit_bzero(&txContext, sizeof(txContext_t)); + explicit_bzero(&txContent, sizeof(txContent_t)); + explicit_bzero(&tmpCtx, sizeof(tmpCtx_t)); + explicit_bzero(&strings, sizeof(strings_t)); + explicit_bzero(&G_io_apdu_buffer, 260); + + txContext.content = &txContent; + txContext.sha3 = &sha3; + + if (size < 1) + return 0; + switch (data[0]) + { + case 0: + fuzzGenericParserFieldCmd(++data, --size); + break; + case 1: + fuzzGenericParserTxInfoCmd(++data, --size); + break; + case 2: + fuzzGenericParserEnumCmd(++data, --size); + break; + default: + return 0; + } + + return 0; +} diff --git a/fuzzing/mock/glyphs.h b/fuzzing/mock/glyphs.h new file mode 100644 index 000000000..e69de29bb diff --git a/fuzzing/mock/mock.c b/fuzzing/mock/mock.c new file mode 100644 index 000000000..36d687b38 --- /dev/null +++ b/fuzzing/mock/mock.c @@ -0,0 +1,87 @@ +#include + +#include "cx_errors.h" +#include "cx_sha256.h" +#include "cx_sha3.h" + +void *pic(void *addr) +{ + return addr; +} + +cx_err_t cx_sha256_init_no_throw(cx_sha256_t *hash) +{ + memset(hash, 0, sizeof(cx_sha256_t)); + return CX_OK; +} + +cx_err_t cx_sha3_init_no_throw(cx_sha3_t *hash PLENGTH(sizeof(cx_sha3_t)), size_t size) +{ + memset(hash, 0, sizeof(cx_sha3_t)); + return CX_OK; +} + +cx_err_t cx_hash_no_throw(cx_hash_t *hash, + uint32_t mode, + const uint8_t *in, + size_t len, + uint8_t *out, + size_t out_len) +{ + if (len > 0 && out_len > 0) + out[out_len - 1] = in[len - 1]; + return CX_OK; +} + +cx_err_t cx_math_mult_no_throw(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len) +{ + return CX_OK; +} + +void cx_rng_no_throw(uint8_t *buffer, size_t len) +{ + if (len > 0) + buffer[len - 1] = 0; +} + +uint16_t get_public_key(uint8_t *out, uint8_t outLength) { + if (outLength > 0) + out[outLength - 1] = 0; + return 0; +} + +void assert_exit(bool confirm) +{ + exit(1); +} + +cx_err_t cx_keccak_256_hash_iovec(const cx_iovec_t *iovec, + size_t iovec_len, + uint8_t digest[static CX_KECCAK_256_SIZE]) { + digest[CX_KECCAK_256_SIZE - 1] = 0; + return CX_OK; +} + +cx_err_t cx_sha256_hash_iovec(const cx_iovec_t *iovec, + size_t iovec_len, + uint8_t digest[static CX_SHA256_SIZE]) +{ + digest[CX_SHA256_SIZE - 1] = 0; + return CX_OK; +} + +int check_signature_with_pubkey(const char *tag, + uint8_t *buffer, + const uint8_t bufLen, + const uint8_t *PubKey, + const uint8_t keyLen, +#ifdef HAVE_LEDGER_PKI + const uint8_t keyUsageExp, +#endif + uint8_t *signature, + const uint8_t sigLen) { + return CX_OK; +} + +void ui_gcs_cleanup(void) { +} diff --git a/src_features/generic_tx_parser/cmd_tx_info.h b/src_features/generic_tx_parser/cmd_tx_info.h index a093669b4..6f8d94607 100644 --- a/src_features/generic_tx_parser/cmd_tx_info.h +++ b/src_features/generic_tx_parser/cmd_tx_info.h @@ -2,7 +2,9 @@ #define CMD_TX_INFO_H_ #include +#include "gtp_tx_info.h" + bool verify_struct(const s_tx_info_ctx *context); uint16_t handle_tx_info(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *payload); void gcs_cleanup(void); diff --git a/src_features/signTx/logic_signTx.c b/src_features/signTx/logic_signTx.c index 1f9085346..6d3c304ca 100644 --- a/src_features/signTx/logic_signTx.c +++ b/src_features/signTx/logic_signTx.c @@ -343,7 +343,7 @@ __attribute__((noreturn)) void send_swap_error(uint8_t error_code, const char *str1, const char *str2) { uint32_t tx = 0; - uint len = 0; + size_t len = 0; PRINTF("APDU_RESPONSE_MODE_CHECK_FAILED: 0x%x\n", error_code); // Set RAPDU error codes G_io_apdu_buffer[tx++] = error_code; From 432393500065238d851480f7a6c9b072293c91d5 Mon Sep 17 00:00:00 2001 From: Baptistin BOILOT Date: Fri, 27 Dec 2024 14:33:17 +0100 Subject: [PATCH 4/9] chore: merge all fuzzers and clean the cmake --- .clusterfuzzlite/build.sh | 6 +- .gitignore | 9 +- fuzzing/CMakeLists.txt | 200 ------------------- fuzzing/README.md | 21 -- fuzzing/coverage.sh | 3 - fuzzing/fuzzer_tlv.c | 93 --------- fuzzing/mock/glyphs.h | 0 fuzzing/mock/mock.c | 87 -------- src_features/generic_tx_parser/cmd_tx_info.h | 1 - tests/fuzzing/CMakeLists.txt | 97 +++++---- tests/fuzzing/README.md | 11 +- tests/fuzzing/src/fuzz_app_eth.c | 36 ---- tests/fuzzing/src/fuzzer.c | 140 +++++++++++++ tests/fuzzing/src/mock.c | 74 +++---- 14 files changed, 257 insertions(+), 521 deletions(-) delete mode 100644 fuzzing/CMakeLists.txt delete mode 100644 fuzzing/README.md delete mode 100755 fuzzing/coverage.sh delete mode 100644 fuzzing/fuzzer_tlv.c delete mode 100644 fuzzing/mock/glyphs.h delete mode 100644 fuzzing/mock/mock.c delete mode 100644 tests/fuzzing/src/fuzz_app_eth.c create mode 100644 tests/fuzzing/src/fuzzer.c diff --git a/.clusterfuzzlite/build.sh b/.clusterfuzzlite/build.sh index 7d9169f61..6fc75a929 100644 --- a/.clusterfuzzlite/build.sh +++ b/.clusterfuzzlite/build.sh @@ -3,7 +3,7 @@ # build fuzzers pushd tests/fuzzing -cmake -DBOLOS_SDK=../../BOLOS_SDK -Bbuild -H. -make -C build -mv ./build/fuzz_app_eth "${OUT}" +cmake -DBOLOS_SDK=$(pwd)/../../BOLOS_SDK -B build -S . +cmake --build build +mv ./build/fuzzer "${OUT}" popd diff --git a/.gitignore b/.gitignore index 019c7207d..ed206c628 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,11 @@ __version__.py .vscode .idea -fuzzing/CORPUS/ \ No newline at end of file +# Fuzzing +tests/fuzzing/corpus/ +tests/fuzzing/out/ +default.profraw +default.profdata +fuzz-*.log +crash-* +report.html diff --git a/fuzzing/CMakeLists.txt b/fuzzing/CMakeLists.txt deleted file mode 100644 index 5dcefbb46..000000000 --- a/fuzzing/CMakeLists.txt +++ /dev/null @@ -1,200 +0,0 @@ -cmake_minimum_required(VERSION 3.14) - -if(${CMAKE_VERSION} VERSION_LESS 3.14) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -endif() - -# project information -project(FuzzWithdraw - VERSION 1.0 - DESCRIPTION "Fuzzing of Ethereum app" - LANGUAGES C) - -# guard against bad build-type strings -if (NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Debug") -endif() - -if (NOT CMAKE_C_COMPILER_ID MATCHES "Clang") - message(FATAL_ERROR "Fuzzer needs to be built with Clang") -endif() - -# default fuzz device target -if (NOT TARGET_DEVICE) - set(TARGET_DEVICE "flex") -endif() - -set(BOLOS_SDK /opt/${TARGET_DEVICE}-secure-sdk) -if (NOT DEFINED BOLOS_SDK) - message(FATAL_ERROR "BOLOS_SDK environment variable not found.") -endif() - -# compatible with ClusterFuzzLite -if (NOT DEFINED ENV{LIB_FUZZING_ENGINE}) - set(COMPILATION_FLAGS -std=gnu99 -Wall -Wextra -pedantic -fshort-enums -Wno-unused-parameter -Wno-embedded-directive -g -O0 -fsanitize=fuzzer,address,undefined -fprofile-instr-generate -fcoverage-mapping) -else() - set(COMPILATION_FLAGS $ENV{LIB_FUZZING_ENGINE} $ENV{CXXFLAGS}) -endif() - -set(DEFINES - gcc - APPNAME=\"Fuzzing\" - API_LEVEL=21 - TARGET=\"flex\" - TARGET_NAME=\"TARGET_FLEX\" - APPVERSION=\"1.1.0\" - SDK_NAME=\"ledger-secure-sdk\" - SDK_VERSION=\"v21.3.3\" - SDK_HASH=\"d88d4db3c93665f52b5b1f45099d9d36dfaa06ba\" - gcc - __IO=volatile - NDEBUG - HAVE_BAGL_FONT_INTER_REGULAR_28PX - HAVE_BAGL_FONT_INTER_SEMIBOLD_28PX - HAVE_BAGL_FONT_INTER_MEDIUM_36PX - HAVE_INAPP_BLE_PAIRING - HAVE_NBGL - HAVE_PIEZO_SOUND - HAVE_SE_TOUCH - HAVE_SE_EINK_DISPLAY - NBGL_PAGE - NBGL_USE_CASE - SCREEN_SIZE_WALLET - HAVE_FAST_HOLD_TO_APPROVE - HAVE_LEDGER_PKI - HAVE_NES_CRYPT - HAVE_ST_AES - NATIVE_LITTLE_ENDIAN - HAVE_CRC - HAVE_HASH - HAVE_RIPEMD160 - HAVE_SHA224 - HAVE_SHA256 - HAVE_SHA3 - HAVE_SHA384 - HAVE_SHA512 - HAVE_SHA512_WITH_BLOCK_ALT_METHOD - HAVE_SHA512_WITH_BLOCK_ALT_METHOD_M0 - HAVE_BLAKE2 - HAVE_HMAC - HAVE_PBKDF2 - HAVE_AES - HAVE_MATH - HAVE_RNG - HAVE_RNG_RFC6979 - HAVE_RNG_SP800_90A - HAVE_ECC - HAVE_ECC_WEIERSTRASS - HAVE_ECC_TWISTED_EDWARDS - HAVE_ECC_MONTGOMERY - HAVE_SECP256K1_CURVE - HAVE_SECP256R1_CURVE - HAVE_SECP384R1_CURVE - HAVE_SECP521R1_CURVE - HAVE_FR256V1_CURVE - HAVE_STARK256_CURVE - HAVE_BRAINPOOL_P256R1_CURVE - HAVE_BRAINPOOL_P256T1_CURVE - HAVE_BRAINPOOL_P320R1_CURVE - HAVE_BRAINPOOL_P320T1_CURVE - HAVE_BRAINPOOL_P384R1_CURVE - HAVE_BRAINPOOL_P384T1_CURVE - HAVE_BRAINPOOL_P512R1_CURVE - HAVE_BRAINPOOL_P512T1_CURVE - HAVE_BLS12_381_G1_CURVE - HAVE_CV25519_CURVE - HAVE_CV448_CURVE - HAVE_ED25519_CURVE - HAVE_ED448_CURVE - HAVE_ECDH - HAVE_ECDSA - HAVE_EDDSA - HAVE_ECSCHNORR - HAVE_X25519 - HAVE_X448 - HAVE_AES_GCM - HAVE_CMAC - HAVE_AES_SIV - COIN_VARIANT=1 - HAVE_BOLOS_APP_STACK_CANARY - IO_SEPROXYHAL_BUFFER_SIZE_B=300 - HAVE_BLE - BLE_COMMAND_TIMEOUT_MS=2000 - HAVE_BLE_APDU - BLE_SEGMENT_SIZE=32 - HAVE_DEBUG_THROWS - NBGL_QRCODE - MAJOR_VERSION=1 - MINOR_VERSION=1 - PATCH_VERSION=0 - IO_HID_EP_LENGTH=64 - HAVE_SPRINTF - HAVE_SNPRINTF_FORMAT_U - HAVE_IO_USB - HAVE_L4_USBLIB - IO_USB_MAX_ENDPOINTS=4 - HAVE_USB_APDU - USB_SEGMENT_SIZE=64 - HAVE_WEBUSB - WEBUSB_URL_SIZE_B=0 - WEBUSB_URL= - OS_IO_SEPROXYHAL - STANDARD_APP_SYNC_RAPDU - HAVE_GENERIC_TX_PARSER - HAVE_TRUSTED_NAME - HAVE_DYN_MEM_ALLOC - HAVE_SWAP - HAVE_ENUM_VALUE - HAVE_NFT_SUPPORT -) - -add_compile_definitions(${DEFINES}) - -include_directories( - ${CMAKE_SOURCE_DIR}/../ethereum-plugin-sdk/src/ - ${CMAKE_SOURCE_DIR}/../src - ${CMAKE_SOURCE_DIR}/../src_features/generic_tx_parser/ - ${CMAKE_SOURCE_DIR}/../src_features/provide_enum_value/ - ${CMAKE_SOURCE_DIR}/../src_features/provideDynamicNetwork/ - ${CMAKE_SOURCE_DIR}/../src_features/signTx/ - ${CMAKE_SOURCE_DIR}/../src_features/provideTrustedName/ - ${CMAKE_SOURCE_DIR}/../src_features/getChallenge/ - ${CMAKE_SOURCE_DIR}/../src_features/signMessageEIP712/ - ${BOLOS_SDK}/include - ${BOLOS_SDK}/target/${TARGET_DEVICE}/include - ${BOLOS_SDK}/lib_cxng/include - ${BOLOS_SDK}/lib_cxng/src - ${BOLOS_SDK}/lib_ux_nbgl - ${BOLOS_SDK}/lib_nbgl/include - ${BOLOS_SDK}/lib_standard_app/ - ./mock/ -) - -FILE(GLOB_RECURSE SDK_STD_SOURCES ${BOLOS_SDK}/lib_standard_app/*.c ${CMAKE_SOURCE_DIR}/../ethereum-plugin-sdk/src/*.c ./mock/mock.c) -list(REMOVE_ITEM SDK_STD_SOURCES ${BOLOS_SDK}/lib_standard_app/io.c ${CMAKE_SOURCE_DIR}/../ethereum-plugin-sdk/src/main.c ${BOLOS_SDK}/lib_standard_app/main.c ${BOLOS_SDK}/lib_standard_app/crypto_helpers.c) - -FILE(GLOB_RECURSE SOURCES - ${CMAKE_SOURCE_DIR}/../src_features/generic_tx_parser/*.c - ${CMAKE_SOURCE_DIR}/../src_features/provideTrustedName/*.c - ${CMAKE_SOURCE_DIR}/../src_features/getChallenge/*.c - ${CMAKE_SOURCE_DIR}/../src_features/provide_enum_value/*.c - ${CMAKE_SOURCE_DIR}/../src_features/provideDynamicNetwork/*.c - ${CMAKE_SOURCE_DIR}/../src/mem.c - ${CMAKE_SOURCE_DIR}/../src/mem_utils.c - ${CMAKE_SOURCE_DIR}/../src/network.c - ${CMAKE_SOURCE_DIR}/../src/tlv.c - ${CMAKE_SOURCE_DIR}/../src/tlv_apdu.c - ${CMAKE_SOURCE_DIR}/../src/uint128.c - ${CMAKE_SOURCE_DIR}/../src/uint256.c - ${CMAKE_SOURCE_DIR}/../src/time_format.c - ${CMAKE_SOURCE_DIR}/../src/uint_common.c - ${CMAKE_SOURCE_DIR}/../src/utils.c - ${CMAKE_SOURCE_DIR}/../src/manage_asset_info.c - ${CMAKE_SOURCE_DIR}/../src/hash_bytes.c -) - -add_executable(fuzzer_tlv fuzzer_tlv.c ${SDK_STD_SOURCES} ${SOURCES}) -target_link_libraries(fuzzer_tlv bsd) # strlcpy / strlcat - -target_compile_options(fuzzer_tlv PRIVATE ${COMPILATION_FLAGS}) -target_link_options(fuzzer_tlv PRIVATE ${COMPILATION_FLAGS}) \ No newline at end of file diff --git a/fuzzing/README.md b/fuzzing/README.md deleted file mode 100644 index 503fc4049..000000000 --- a/fuzzing/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# Fuzzing ethereum app - -## Build - -The fuzzer can be built using the following commands from the app directory: -```bash -docker run --rm -it --user "$(id -u):$(id -g)" -v "$(realpath .):/app" ghcr.io/ledgerhq/ledger-app-builder/ledger-app-builder-legacy:latest bash - -apt install libbsd-dev - -cd fuzzing -cmake -DBOLOS_SDK=/opt/ledger-secure-sdk -DCMAKE_C_COMPILER=/usr/bin/clang -Bbuild -H. -cmake --build build -``` - -## Run - -The fuzzer can be run using the following commands from the fuzzing directory: -```bash -./build/fuzzer_tlv -max_len=8192 -``` diff --git a/fuzzing/coverage.sh b/fuzzing/coverage.sh deleted file mode 100755 index a6c2c3b93..000000000 --- a/fuzzing/coverage.sh +++ /dev/null @@ -1,3 +0,0 @@ -llvm-profdata merge -sparse *.profraw -o default.profdata -llvm-cov show build/fuzzer_tlv -instr-profile=default.profdata -format=html > report.html -llvm-cov report build/fuzzer_tlv -instr-profile=default.profdata diff --git a/fuzzing/fuzzer_tlv.c b/fuzzing/fuzzer_tlv.c deleted file mode 100644 index 6bc67087e..000000000 --- a/fuzzing/fuzzer_tlv.c +++ /dev/null @@ -1,93 +0,0 @@ -#include -#include - -#include "cmd_field.h" -#include "cmd_tx_info.h" -#include "cmd_enum_value.h" - -#include "gtp_field.h" -#include "gtp_tx_info.h" -#include "enum_value.h" - -#include "shared_context.h" -#include "tlv.h" - -cx_sha3_t sha3 = {0}; -unsigned char G_io_apdu_buffer[260]; -tmpContent_t tmpContent; -txContext_t txContext; -txContent_t txContent; -chain_config_t config = { - .coinName = "FUZZ", - .chainId = 0x42, -}; -const chain_config_t *chainConfig = &config; -uint8_t appState; -tmpCtx_t tmpCtx; -strings_t strings; - - -int fuzzGenericParserFieldCmd(const uint8_t *data, size_t size) { - s_field field = {0}; - s_field_ctx ctx = {0}; - ctx.field = &field; - - if (!tlv_parse(data, size, (f_tlv_data_handler)&handle_field_struct, &ctx)) - return 1; - - if (!verify_field_struct(&ctx)) - return 1; - - return format_field(&field); -} - -int fuzzGenericParserTxInfoCmd(const uint8_t *data, size_t size) { - s_tx_info tx_info = {0}; - s_tx_info_ctx ctx = {0}; - ctx.tx_info = &tx_info; - - if (!tlv_parse(data, size, (f_tlv_data_handler)&handle_tx_info_struct, &ctx)) - return 1; - - return verify_tx_info_struct(&ctx); -} - -int fuzzGenericParserEnumCmd(const uint8_t *data, size_t size) { - s_enum_value_ctx ctx = {0}; - - if (!tlv_parse(data, size, (f_tlv_data_handler)&handle_enum_value_struct, &ctx)) - return 1; - - return verify_enum_value_struct(&ctx); -} - -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - explicit_bzero(&tmpContent, sizeof(tmpContent_t)); - explicit_bzero(&txContext, sizeof(txContext_t)); - explicit_bzero(&txContent, sizeof(txContent_t)); - explicit_bzero(&tmpCtx, sizeof(tmpCtx_t)); - explicit_bzero(&strings, sizeof(strings_t)); - explicit_bzero(&G_io_apdu_buffer, 260); - - txContext.content = &txContent; - txContext.sha3 = &sha3; - - if (size < 1) - return 0; - switch (data[0]) - { - case 0: - fuzzGenericParserFieldCmd(++data, --size); - break; - case 1: - fuzzGenericParserTxInfoCmd(++data, --size); - break; - case 2: - fuzzGenericParserEnumCmd(++data, --size); - break; - default: - return 0; - } - - return 0; -} diff --git a/fuzzing/mock/glyphs.h b/fuzzing/mock/glyphs.h deleted file mode 100644 index e69de29bb..000000000 diff --git a/fuzzing/mock/mock.c b/fuzzing/mock/mock.c deleted file mode 100644 index 36d687b38..000000000 --- a/fuzzing/mock/mock.c +++ /dev/null @@ -1,87 +0,0 @@ -#include - -#include "cx_errors.h" -#include "cx_sha256.h" -#include "cx_sha3.h" - -void *pic(void *addr) -{ - return addr; -} - -cx_err_t cx_sha256_init_no_throw(cx_sha256_t *hash) -{ - memset(hash, 0, sizeof(cx_sha256_t)); - return CX_OK; -} - -cx_err_t cx_sha3_init_no_throw(cx_sha3_t *hash PLENGTH(sizeof(cx_sha3_t)), size_t size) -{ - memset(hash, 0, sizeof(cx_sha3_t)); - return CX_OK; -} - -cx_err_t cx_hash_no_throw(cx_hash_t *hash, - uint32_t mode, - const uint8_t *in, - size_t len, - uint8_t *out, - size_t out_len) -{ - if (len > 0 && out_len > 0) - out[out_len - 1] = in[len - 1]; - return CX_OK; -} - -cx_err_t cx_math_mult_no_throw(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len) -{ - return CX_OK; -} - -void cx_rng_no_throw(uint8_t *buffer, size_t len) -{ - if (len > 0) - buffer[len - 1] = 0; -} - -uint16_t get_public_key(uint8_t *out, uint8_t outLength) { - if (outLength > 0) - out[outLength - 1] = 0; - return 0; -} - -void assert_exit(bool confirm) -{ - exit(1); -} - -cx_err_t cx_keccak_256_hash_iovec(const cx_iovec_t *iovec, - size_t iovec_len, - uint8_t digest[static CX_KECCAK_256_SIZE]) { - digest[CX_KECCAK_256_SIZE - 1] = 0; - return CX_OK; -} - -cx_err_t cx_sha256_hash_iovec(const cx_iovec_t *iovec, - size_t iovec_len, - uint8_t digest[static CX_SHA256_SIZE]) -{ - digest[CX_SHA256_SIZE - 1] = 0; - return CX_OK; -} - -int check_signature_with_pubkey(const char *tag, - uint8_t *buffer, - const uint8_t bufLen, - const uint8_t *PubKey, - const uint8_t keyLen, -#ifdef HAVE_LEDGER_PKI - const uint8_t keyUsageExp, -#endif - uint8_t *signature, - const uint8_t sigLen) { - return CX_OK; -} - -void ui_gcs_cleanup(void) { -} diff --git a/src_features/generic_tx_parser/cmd_tx_info.h b/src_features/generic_tx_parser/cmd_tx_info.h index 6f8d94607..d8c13442c 100644 --- a/src_features/generic_tx_parser/cmd_tx_info.h +++ b/src_features/generic_tx_parser/cmd_tx_info.h @@ -4,7 +4,6 @@ #include #include "gtp_tx_info.h" - bool verify_struct(const s_tx_info_ctx *context); uint16_t handle_tx_info(uint8_t p1, uint8_t p2, uint8_t lc, const uint8_t *payload); void gcs_cleanup(void); diff --git a/tests/fuzzing/CMakeLists.txt b/tests/fuzzing/CMakeLists.txt index 1a59c68f7..466765889 100644 --- a/tests/fuzzing/CMakeLists.txt +++ b/tests/fuzzing/CMakeLists.txt @@ -1,53 +1,48 @@ -cmake_minimum_required(VERSION 3.10) - -if(${CMAKE_VERSION} VERSION_LESS 3.10) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -endif() +cmake_minimum_required(VERSION 3.14) # project information -project(Fuzzer +project(EthereumAppFuzzer VERSION 1.0 DESCRIPTION "Eth Fuzzer" LANGUAGES C) -set(CMAKE_C_COMPILER clang) +if (NOT CMAKE_C_COMPILER_ID MATCHES "Clang") + message(FATAL_ERROR "Fuzzer needs to be built with Clang") +endif() + +# guard against bad build-type strings +if (NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE "Debug") +endif() + +# default fuzz device target +if (NOT TARGET_DEVICE) + set(TARGET_DEVICE "flex") +endif() -set(CMAKE_BUILD_TYPE "Debug") +if (NOT DEFINED BOLOS_SDK) + set(BOLOS_SDK /opt/${TARGET_DEVICE}-secure-sdk) +endif() # compatible with ClusterFuzzLite if (NOT DEFINED ENV{LIB_FUZZING_ENGINE}) - set(COMPILATION_FLAGS_ "-g -Wall -fsanitize=fuzzer,address,undefined") + set(COMPILATION_FLAGS -g -O0 -Wall -Wextra -fsanitize=fuzzer,address,undefined -fprofile-instr-generate -fcoverage-mapping) else() - set(COMPILATION_FLAGS_ "$ENV{LIB_FUZZING_ENGINE} $ENV{CXXFLAGS}") + set(COMPILATION_FLAGS "$ENV{LIB_FUZZING_ENGINE} $ENV{CFLAGS}") + separate_arguments(COMPILATION_FLAGS) endif() -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) - -string(REPLACE " " ";" COMPILATION_FLAGS ${COMPILATION_FLAGS_}) - -# specify C standard -set(CMAKE_C_STANDARD 11) -set(CMAKE_C_STANDARD_REQUIRED True) -set(CMAKE_C_FLAGS_DEBUG - "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra -Wno-unused-function -DFUZZ -pedantic -g -O0" -) - # guard against in-source builds if(${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) message(FATAL_ERROR "In-source builds not allowed. Please make a new directory (called a build directory) and run CMake from there. You may need to remove CMakeCache.txt. ") endif() -set(TARGET_DEVICE flex) -if (NOT DEFINED BOLOS_SDK) - message(FATAL_ERROR "BOLOS_SDK environment variable not found.") -endif() - set(DEFINES gcc APPNAME=\"Fuzzing\" API_LEVEL=21 TARGET=\"flex\" - TARGET_NAME=\"TARGET_FLEX\" + TARGET_NAME=\"TARGET_FUZZ\" APPVERSION=\"1.1.0\" SDK_NAME=\"ledger-secure-sdk\" SDK_VERSION=\"v21.3.3\" @@ -153,32 +148,64 @@ set(DEFINES HAVE_ENUM_VALUE HAVE_NFT_SUPPORT ) -set(DEFINE ${DEFINES} HAVE_PRINTF PRINTF=printf) add_compile_definitions(${DEFINES}) -FILE(GLOB_RECURSE SDK_STD_SOURCES ${BOLOS_SDK}/lib_standard_app/write.c src/mock.c) - +FILE( + GLOB_RECURSE SDK_STD_SOURCES + ${BOLOS_SDK}/lib_standard_app/*.c + ${CMAKE_SOURCE_DIR}/../../ethereum-plugin-sdk/src/*.c + ./src/mock.c +) +list( + REMOVE_ITEM SDK_STD_SOURCES + ${BOLOS_SDK}/lib_standard_app/io.c + ${CMAKE_SOURCE_DIR}/../../ethereum-plugin-sdk/src/main.c + ${BOLOS_SDK}/lib_standard_app/main.c + ${BOLOS_SDK}/lib_standard_app/crypto_helpers.c +) include_directories( ${CMAKE_SOURCE_DIR}/../../ethereum-plugin-sdk/src/ ${CMAKE_SOURCE_DIR}/../../src + ${CMAKE_SOURCE_DIR}/../../src_features/generic_tx_parser/ + ${CMAKE_SOURCE_DIR}/../../src_features/provide_enum_value/ ${CMAKE_SOURCE_DIR}/../../src_features/provideDynamicNetwork/ + ${CMAKE_SOURCE_DIR}/../../src_features/signTx/ + ${CMAKE_SOURCE_DIR}/../../src_features/provideTrustedName/ + ${CMAKE_SOURCE_DIR}/../../src_features/getChallenge/ + ${CMAKE_SOURCE_DIR}/../../src_features/signMessageEIP712/ ${BOLOS_SDK}/include - ${BOLOS_SDK}/lib_standard_app ${BOLOS_SDK}/target/${TARGET_DEVICE}/include ${BOLOS_SDK}/lib_cxng/include ${BOLOS_SDK}/lib_cxng/src ${BOLOS_SDK}/lib_ux_nbgl ${BOLOS_SDK}/lib_nbgl/include - ${CMAKE_SOURCE_DIR}/src + ${BOLOS_SDK}/lib_standard_app/ + ${CMAKE_SOURCE_DIR}/src/ ) FILE(GLOB_RECURSE SOURCES + ${CMAKE_SOURCE_DIR}/../../src_features/generic_tx_parser/*.c + ${CMAKE_SOURCE_DIR}/../../src_features/provideTrustedName/*.c + ${CMAKE_SOURCE_DIR}/../../src_features/getChallenge/*.c + ${CMAKE_SOURCE_DIR}/../../src_features/provide_enum_value/*.c ${CMAKE_SOURCE_DIR}/../../src_features/provideDynamicNetwork/*.c + ${CMAKE_SOURCE_DIR}/../../src_features/provideNFTInformation/*.c + ${CMAKE_SOURCE_DIR}/../../src/mem.c + ${CMAKE_SOURCE_DIR}/../../src/mem_utils.c + ${CMAKE_SOURCE_DIR}/../../src/network.c + ${CMAKE_SOURCE_DIR}/../../src/tlv.c + ${CMAKE_SOURCE_DIR}/../../src/tlv_apdu.c + ${CMAKE_SOURCE_DIR}/../../src/uint128.c + ${CMAKE_SOURCE_DIR}/../../src/uint256.c + ${CMAKE_SOURCE_DIR}/../../src/time_format.c + ${CMAKE_SOURCE_DIR}/../../src/uint_common.c + ${CMAKE_SOURCE_DIR}/../../src/utils.c + ${CMAKE_SOURCE_DIR}/../../src/manage_asset_info.c ${CMAKE_SOURCE_DIR}/../../src/hash_bytes.c ) -add_executable(fuzz_app_eth src/fuzz_app_eth.c ${SDK_STD_SOURCES} ${SOURCES}) -target_compile_options(fuzz_app_eth PUBLIC ${COMPILATION_FLAGS}) -target_link_options(fuzz_app_eth PUBLIC ${COMPILATION_FLAGS}) +add_executable(fuzzer src/fuzzer.c ${SDK_STD_SOURCES} ${SOURCES}) +target_compile_options(fuzzer PRIVATE ${COMPILATION_FLAGS}) +target_link_options(fuzzer PRIVATE ${COMPILATION_FLAGS}) diff --git a/tests/fuzzing/README.md b/tests/fuzzing/README.md index 43ad67b02..6db9fafe8 100644 --- a/tests/fuzzing/README.md +++ b/tests/fuzzing/README.md @@ -11,7 +11,6 @@ any kind of access violation, the fuzzing process is stopped, a report regarding and the input that triggered the bug is written to disk under the name `crash-*`. The vulnerable input file created can be passed as an argument to the fuzzer to triage the issue. -> **Note**: Usually we want to write a separate fuzz target for each functionality. ## Manual usage based on Ledger container @@ -37,18 +36,20 @@ Once in the container, go into the `tests/fuzzing` folder to compile the fuzzer: cd tests/fuzzing # cmake initialization -cmake -DBOLOS_SDK=/opt/ledger-secure-sdk -Bbuild -H. +cmake -DBOLOS_SDK=/opt/ledger-secure-sdk -DCMAKE_C_COMPILER=/usr/bin/clang -Bbuild -S. # Fuzzer compilation -make -C build +cmake --build build ``` ### Run ```console -./build/fuzz_app_eth +./build/fuzzer -max_len=8192 ``` +If you want to do a fuzzing campain on more than one core and compute the coverage results, you can use the `local_run.sh` script within the container. + ## Full usage based on `clusterfuzzlite` container Exactly the same context as the CI, directly using the `clusterfuzzlite` environment. @@ -79,5 +80,5 @@ docker run --rm --privileged -e FUZZING_LANGUAGE=c -v "$(realpath .)/tests/fuzzi ### Run ```console -docker run --rm --privileged -e FUZZING_ENGINE=libfuzzer -e RUN_FUZZER_MODE=interactive -v "$(realpath .)/tests/fuzzing/corpus:/tmp/fuzz_corpus" -v "$(realpath .)/tests/fuzzing/out:/out" -ti gcr.io/oss-fuzz-base/base-runner run_fuzzer fuzz_app_eth +docker run --rm --privileged -e FUZZING_ENGINE=libfuzzer -e RUN_FUZZER_MODE=interactive -v "$(realpath .)/tests/fuzzing/corpus:/tmp/fuzz_corpus" -v "$(realpath .)/tests/fuzzing/out:/out" -ti gcr.io/oss-fuzz-base/base-runner run_fuzzer fuzzer ``` diff --git a/tests/fuzzing/src/fuzz_app_eth.c b/tests/fuzzing/src/fuzz_app_eth.c deleted file mode 100644 index c4f55501a..000000000 --- a/tests/fuzzing/src/fuzz_app_eth.c +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include "shared_context.h" -#include "network_dynamic.h" - -unsigned char G_io_apdu_buffer[IO_APDU_BUFFER_SIZE]; -tmpContent_t tmpContent; -const chain_config_t *chainConfig; -txContext_t txContext; - -int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { - explicit_bzero(G_io_apdu_buffer, 500); - explicit_bzero(&tmpContent, sizeof(tmpContent_t)); - explicit_bzero(&txContext, sizeof(txContext_t)); - size_t offset = 0; - size_t len = 0; - uint8_t p1; - uint8_t p2; - unsigned int tx; - - while (size - offset > 4) { - if (data[offset++] == 0) break; - p1 = data[offset++]; - p2 = data[offset++]; - len = data[offset++]; - if (size - offset < len) return 0; - handleNetworkConfiguration(p1, p2, data + offset, len, &tx); - offset += len; - } - return 0; -} diff --git a/tests/fuzzing/src/fuzzer.c b/tests/fuzzing/src/fuzzer.c new file mode 100644 index 000000000..3b024c80b --- /dev/null +++ b/tests/fuzzing/src/fuzzer.c @@ -0,0 +1,140 @@ +#include +#include + +#include "network_dynamic.h" + +#include "cmd_field.h" +#include "cmd_tx_info.h" +#include "cmd_enum_value.h" + +#include "gtp_field.h" +#include "gtp_tx_info.h" +#include "enum_value.h" + +#include "shared_context.h" +#include "tlv.h" +#include "apdu_constants.h" + +// Fuzzing harness interface +typedef int (*harness)(const uint8_t *data, size_t size); + +// Global state required by the app features +cx_sha3_t sha3; +unsigned char G_io_apdu_buffer[260]; +tmpContent_t tmpContent; +txContext_t txContext; +txContent_t txContent; +chain_config_t config = { + .coinName = "FUZZ", + .chainId = 0x42, +}; +const chain_config_t *chainConfig = &config; +uint8_t appState; +tmpCtx_t tmpCtx; +strings_t strings; + +int fuzzGenericParserFieldCmd(const uint8_t *data, size_t size) { + s_field field = {0}; + s_field_ctx ctx = {0}; + ctx.field = &field; + + if (!tlv_parse(data, size, (f_tlv_data_handler) &handle_field_struct, &ctx)) return 1; + + if (!verify_field_struct(&ctx)) return 1; + + return format_field(&field); +} + +int fuzzGenericParserTxInfoCmd(const uint8_t *data, size_t size) { + s_tx_info tx_info = {0}; + s_tx_info_ctx ctx = {0}; + ctx.tx_info = &tx_info; + + if (!tlv_parse(data, size, (f_tlv_data_handler) &handle_tx_info_struct, &ctx)) return 1; + + return verify_tx_info_struct(&ctx); +} + +int fuzzGenericParserEnumCmd(const uint8_t *data, size_t size) { + s_enum_value_ctx ctx = {0}; + + if (!tlv_parse(data, size, (f_tlv_data_handler) &handle_enum_value_struct, &ctx)) return 1; + + return verify_enum_value_struct(&ctx); +} + +int fuzzDynamicNetworks(const uint8_t *data, size_t size) { + size_t offset = 0; + size_t len = 0; + uint8_t p1; + uint8_t p2; + unsigned int tx; + + while (size - offset > 4) { + if (data[offset++] == 0) break; + p1 = data[offset++]; + p2 = data[offset++]; + len = data[offset++]; + if (size - offset < len) return 0; + if (handleNetworkConfiguration(p1, p2, data + offset, len, &tx) != APDU_RESPONSE_OK) + return 1; + offset += len; + } + return 0; +} + +int fuzzTrustedNames(const uint8_t *data, size_t size) { + size_t offset = 0; + size_t len = 0; + uint8_t p1; + + while (size - offset > 3) { + if (data[offset++] == 0) break; + p1 = data[offset++]; + len = data[offset++]; + if (size - offset < len) return 0; + if (handle_provide_trusted_name(p1, data + offset, len) != APDU_RESPONSE_OK) return 1; + offset += len; + } + return 0; +} + +int fuzzNFTInfo(const uint8_t *data, size_t size) { + unsigned int tx; + return handleProvideNFTInformation(data, size, &tx) != APDU_RESPONSE_OK; +} + +// Array of fuzzing harness functions +harness harnesses[] = { + fuzzGenericParserFieldCmd, + fuzzGenericParserTxInfoCmd, + fuzzGenericParserEnumCmd, + fuzzDynamicNetworks, + fuzzTrustedNames, + fuzzNFTInfo, +}; + +/* Main fuzzing handler called by libfuzzer */ +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + // Clear global structures to ensure a clean state for each fuzzing iteration + explicit_bzero(&tmpContent, sizeof(tmpContent_t)); + explicit_bzero(&txContext, sizeof(txContext_t)); + explicit_bzero(&txContent, sizeof(txContent_t)); + explicit_bzero(&tmpCtx, sizeof(tmpCtx_t)); + explicit_bzero(&strings, sizeof(strings_t)); + explicit_bzero(&G_io_apdu_buffer, 260); + explicit_bzero(&sha3, sizeof(sha3)); + + uint8_t target; + + txContext.content = &txContent; + txContext.sha3 = &sha3; + + // Determine which harness function to call based on the first byte of data + if (size < 1) return 0; + target = data[0]; + if (target >= sizeof(harnesses) / sizeof(harnesses[0])) return 0; + + // Call the selected harness function with the remaining data (which can be of size 0) + return harnesses[target](++data, --size); +} diff --git a/tests/fuzzing/src/mock.c b/tests/fuzzing/src/mock.c index 7328a6adc..2e8d8ab86 100644 --- a/tests/fuzzing/src/mock.c +++ b/tests/fuzzing/src/mock.c @@ -1,14 +1,29 @@ +#include #include #include "cx_errors.h" #include "cx_sha256.h" #include "cx_sha3.h" +size_t strlcpy(char *dst, const char *src, size_t size) { + return strncpy(dst, src, size); +} + +size_t strlcat(char *dst, const char *src, size_t size) { + return strncat(dst, src, size); +} + cx_err_t cx_sha256_init_no_throw(cx_sha256_t *hash) { memset(hash, 0, sizeof(cx_sha256_t)); return CX_OK; } +cx_err_t cx_sha3_init_no_throw(cx_sha3_t *hash PLENGTH(sizeof(cx_sha3_t)), size_t size) { + UNUSED(size); + memset(hash, 0, sizeof(cx_sha3_t)); + return CX_OK; +} + cx_err_t cx_hash_no_throw(cx_hash_t *hash, uint32_t mode, const uint8_t *in, @@ -67,44 +82,31 @@ int check_signature_with_pubkey(const char *tag, return CX_OK; } -uint64_t u64_from_BE(const uint8_t *in, uint8_t size) { - uint8_t i = 0; - uint64_t res = 0; +void *pic(void *addr) { + return addr; +} - while (i < size && i < sizeof(res)) { - res <<= 8; - res |= in[i]; - i++; - } +cx_err_t cx_math_mult_no_throw(uint8_t *r, const uint8_t *a, const uint8_t *b, size_t len) { + UNUSED(r); + UNUSED(a); + UNUSED(b); + UNUSED(len); + return CX_OK; +} + +void cx_rng_no_throw(uint8_t *buffer, size_t len) { + if (len > 0) buffer[len - 1] = 0; +} - return res; +uint16_t get_public_key(uint8_t *out, uint8_t outLength) { + if (outLength > 0) out[outLength - 1] = 0; + return 0; } -bool u64_to_string(uint64_t src, char *dst, uint8_t dst_size) { - // Copy the numbers in ASCII format. - uint8_t i = 0; - do { - // Checking `i + 1` to make sure we have enough space for '\0'. - if (i + 1 >= dst_size) { - return false; - } - dst[i] = src % 10 + '0'; - src /= 10; - i++; - } while (src); - - // Null terminate string - dst[i] = '\0'; - - // Revert the string - i--; - uint8_t j = 0; - while (j < i) { - char tmp = dst[i]; - dst[i] = dst[j]; - dst[j] = tmp; - i--; - j++; - } - return true; +void ui_gcs_cleanup(void) { +} + +size_t cx_hash_sha256(const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) { + if (in_len > 0 && out_len > 0) out[out_len - 1] = in[in_len - 1]; + return CX_OK; } From f86eb28696deff2aa3a5e843eb42638c6cc9d18d Mon Sep 17 00:00:00 2001 From: Baptistin BOILOT Date: Fri, 27 Dec 2024 14:33:50 +0100 Subject: [PATCH 5/9] test: add a script to run the fuzzer and compute the coverage --- tests/fuzzing/local_run.sh | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100755 tests/fuzzing/local_run.sh diff --git a/tests/fuzzing/local_run.sh b/tests/fuzzing/local_run.sh new file mode 100755 index 000000000..e76ee005b --- /dev/null +++ b/tests/fuzzing/local_run.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Clean +rm -rf build + +# Build the fuzzer +cmake -B build -S . -DCMAKE_C_COMPILER=/usr/bin/clang +cmake --build build + +# Create the corpus directory if it doesn't exist +if ! [ -d ./corpus ]; then + mkdir corpus +fi + +# Run the fuzzer on half CPU cores +ncpus=$(nproc) +jobs=$(($ncpus/2)) +echo "The fuzzer will start very soon, press Ctrl-C when you want to stop it and compute the coverage" +./build/fuzzer -max_len=8192 -jobs="$jobs" ./corpus + + +read -p "Would you like to compute coverage (y/n)? " -n 1 -r +echo +if [[ $REPLY =~ ^[Nn]$ ]] +then + exit 0 +fi + +# Remove previous artifcats +rm default.profdata default.profraw + +# Run profiling on the corpus +./build/fuzzer -max_len=8192 -runs=0 ./corpus + +# Compute coverage +llvm-profdata merge -sparse *.profraw -o default.profdata +llvm-cov show build/fuzzer -instr-profile=default.profdata -format=html -ignore-filename-regex='ethereum-plugin-sdk\/|secure-sdk\/' > report.html +llvm-cov report build/fuzzer -instr-profile=default.profdata -ignore-filename-regex='ethereum-plugin-sdk\/|secure-sdk\/' From d20c831cc24097efcb30b78b3c3a92151240162e Mon Sep 17 00:00:00 2001 From: Baptistin BOILOT Date: Fri, 27 Dec 2024 16:49:02 +0100 Subject: [PATCH 6/9] fix: use a uninitialized values --- src_features/generic_tx_parser/gtp_param_datetime.c | 2 +- src_features/generic_tx_parser/gtp_param_duration.c | 2 +- src_features/generic_tx_parser/gtp_param_trusted_name.c | 2 +- src_features/generic_tx_parser/gtp_tx_info.c | 6 +++--- src_features/provideTrustedName/cmd_provide_trusted_name.c | 2 +- src_features/provide_enum_value/enum_value.c | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src_features/generic_tx_parser/gtp_param_datetime.c b/src_features/generic_tx_parser/gtp_param_datetime.c index ca3fdade0..2242aded1 100644 --- a/src_features/generic_tx_parser/gtp_param_datetime.c +++ b/src_features/generic_tx_parser/gtp_param_datetime.c @@ -68,7 +68,7 @@ bool format_param_datetime(const s_param_datetime *param, const char *name) { s_parsed_value_collection collec; char *buf = strings.tmp.tmp; size_t buf_size = sizeof(strings.tmp.tmp); - uint8_t time_buf[sizeof(uint32_t)]; + uint8_t time_buf[sizeof(uint32_t)] = {0}; time_t timestamp; uint256_t block_height; diff --git a/src_features/generic_tx_parser/gtp_param_duration.c b/src_features/generic_tx_parser/gtp_param_duration.c index d55a94e58..01eae53a5 100644 --- a/src_features/generic_tx_parser/gtp_param_duration.c +++ b/src_features/generic_tx_parser/gtp_param_duration.c @@ -60,7 +60,7 @@ bool format_param_duration(const s_param_duration *param, const char *name) { uint8_t minutes; uint8_t seconds; uint64_t remaining; - uint8_t raw_buf[sizeof(remaining)]; + uint8_t raw_buf[sizeof(remaining)] = {0}; int off; if (!value_get(¶m->value, &collec)) { diff --git a/src_features/generic_tx_parser/gtp_param_trusted_name.c b/src_features/generic_tx_parser/gtp_param_trusted_name.c index 5fffb9fe2..4c6c27989 100644 --- a/src_features/generic_tx_parser/gtp_param_trusted_name.c +++ b/src_features/generic_tx_parser/gtp_param_trusted_name.c @@ -78,7 +78,7 @@ bool format_param_trusted_name(const s_param_trusted_name *param, const char *na char *buf = strings.tmp.tmp; size_t buf_size = sizeof(strings.tmp.tmp); uint64_t chain_id; - uint8_t addr[ADDRESS_LENGTH]; + uint8_t addr[ADDRESS_LENGTH] = {0}; const char *tname; e_param_type param_type; diff --git a/src_features/generic_tx_parser/gtp_tx_info.c b/src_features/generic_tx_parser/gtp_tx_info.c index 7913ab08d..bb523ff54 100644 --- a/src_features/generic_tx_parser/gtp_tx_info.c +++ b/src_features/generic_tx_parser/gtp_tx_info.c @@ -55,7 +55,7 @@ static bool handle_version(const s_tlv_data *data, s_tx_info_ctx *context) { static bool handle_chain_id(const s_tlv_data *data, s_tx_info_ctx *context) { uint64_t chain_id; - uint8_t buf[sizeof(chain_id)]; + uint8_t buf[sizeof(chain_id)] = {0}; if (data->length > sizeof(buf)) { return false; @@ -72,7 +72,7 @@ static bool handle_chain_id(const s_tlv_data *data, s_tx_info_ctx *context) { } static bool handle_contract_addr(const s_tlv_data *data, s_tx_info_ctx *context) { - uint8_t buf[ADDRESS_LENGTH]; + uint8_t buf[ADDRESS_LENGTH] = {0}; if (data->length > sizeof(buf)) { return false; @@ -165,7 +165,7 @@ static bool handle_contract_name(const s_tlv_data *data, s_tx_info_ctx *context) } static bool handle_deploy_date(const s_tlv_data *data, s_tx_info_ctx *context) { - uint8_t buf[sizeof(uint32_t)]; + uint8_t buf[sizeof(uint32_t)] = {0}; time_t timestamp; if (data->length > sizeof(buf)) { diff --git a/src_features/provideTrustedName/cmd_provide_trusted_name.c b/src_features/provideTrustedName/cmd_provide_trusted_name.c index a540c2369..d0015e973 100644 --- a/src_features/provideTrustedName/cmd_provide_trusted_name.c +++ b/src_features/provideTrustedName/cmd_provide_trusted_name.c @@ -822,7 +822,7 @@ static bool parse_tlv(const s_tlv_payload *payload, {.tag = NFT_ID, .func = &handle_nft_id}, }; e_tlv_step step = TLV_TAG; - s_tlv_data data; + s_tlv_data data = {0}; size_t offset = 0; size_t tag_start_off; diff --git a/src_features/provide_enum_value/enum_value.c b/src_features/provide_enum_value/enum_value.c index 64cf2fc30..db0dfb9a1 100644 --- a/src_features/provide_enum_value/enum_value.c +++ b/src_features/provide_enum_value/enum_value.c @@ -28,7 +28,7 @@ static bool handle_version(const s_tlv_data *data, s_enum_value_ctx *context) { } static bool handle_chain_id(const s_tlv_data *data, s_enum_value_ctx *context) { - uint8_t buf[sizeof(context->enum_value.entry.chain_id)]; + uint8_t buf[sizeof(context->enum_value.entry.chain_id)] = {0}; if (data->length > sizeof(buf)) { return false; From 649aa607371d7f10c890cf23d1de483a45adeb98 Mon Sep 17 00:00:00 2001 From: Baptistin BOILOT Date: Mon, 30 Dec 2024 17:57:07 +0100 Subject: [PATCH 7/9] fix: improve the mocks to avoid false positives --- tests/fuzzing/src/mock.c | 71 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 65 insertions(+), 6 deletions(-) diff --git a/tests/fuzzing/src/mock.c b/tests/fuzzing/src/mock.c index 2e8d8ab86..09a2a4a87 100644 --- a/tests/fuzzing/src/mock.c +++ b/tests/fuzzing/src/mock.c @@ -5,12 +5,67 @@ #include "cx_sha256.h" #include "cx_sha3.h" +#define explicit_bzero(X, Y) memset_s(X, 0, Y) + +/** MemorySanitizer does not wrap explicit_bzero https://github.com/google/sanitizers/issues/1507 + * which results in false positives when running MemorySanitizer. + */ +void memset_s(void *buffer, char c, size_t n) { + if (buffer == NULL) return; + + volatile char *ptr = buffer; + while (n--) *ptr++ = c; +} + size_t strlcpy(char *dst, const char *src, size_t size) { - return strncpy(dst, src, size); + const char *s = src; + size_t n = size; + + if (n != 0) { + while (--n != 0) { + if ((*dst++ = *s++) == '\0') { + break; + } + } + } + + if (n == 0) { + if (size != 0) { + *dst = '\0'; + } + while (*s++) + ; + } + + return (s - src - 1); } size_t strlcat(char *dst, const char *src, size_t size) { - return strncat(dst, src, size); + char *d = dst; + const char *s = src; + size_t n = size; + size_t dsize; + + while (n-- != 0 && *d != '\0') { + d++; + } + dsize = d - dst; + n = size - dsize; + + if (n == 0) { + return (dsize + strlen(s)); + } + + while (*s != '\0') { + if (n != 1) { + *d++ = *s; + n--; + } + s++; + } + *d = '\0'; + + return (dsize + (s - src)); } cx_err_t cx_sha256_init_no_throw(cx_sha256_t *hash) { @@ -32,6 +87,8 @@ cx_err_t cx_hash_no_throw(cx_hash_t *hash, size_t out_len) { UNUSED(hash); UNUSED(mode); + explicit_bzero(out, out_len); // let's initialize the buffer + // if arrays are not empty, read the last element of in and write it in the last element of out if (len > 0 && out_len > 0) out[out_len - 1] = in[len - 1]; return CX_OK; } @@ -46,7 +103,7 @@ cx_err_t cx_keccak_256_hash_iovec(const cx_iovec_t *iovec, uint8_t digest[static CX_KECCAK_256_SIZE]) { UNUSED(iovec); UNUSED(iovec_len); - digest[CX_KECCAK_256_SIZE - 1] = 0; + explicit_bzero(digest, CX_SHA256_SIZE); return CX_OK; } @@ -55,7 +112,7 @@ cx_err_t cx_sha256_hash_iovec(const cx_iovec_t *iovec, uint8_t digest[static CX_SHA256_SIZE]) { UNUSED(iovec); UNUSED(iovec_len); - digest[CX_SHA256_SIZE - 1] = 0; + explicit_bzero(digest, CX_SHA256_SIZE); return CX_OK; } @@ -95,11 +152,11 @@ cx_err_t cx_math_mult_no_throw(uint8_t *r, const uint8_t *a, const uint8_t *b, s } void cx_rng_no_throw(uint8_t *buffer, size_t len) { - if (len > 0) buffer[len - 1] = 0; + explicit_bzero(buffer, len); } uint16_t get_public_key(uint8_t *out, uint8_t outLength) { - if (outLength > 0) out[outLength - 1] = 0; + explicit_bzero(out, outLength); return 0; } @@ -107,6 +164,8 @@ void ui_gcs_cleanup(void) { } size_t cx_hash_sha256(const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) { + explicit_bzero(out, out_len); // let's initialize the buffer + // if arrays are not empty, read the last element of in and write it in the last element of out if (in_len > 0 && out_len > 0) out[out_len - 1] = in[in_len - 1]; return CX_OK; } From 3a1dd71c34e56ebf7d2deb58b8fc0dee373a0aec Mon Sep 17 00:00:00 2001 From: Baptistin BOILOT Date: Mon, 30 Dec 2024 17:58:24 +0100 Subject: [PATCH 8/9] build: support msan and asan for fuzzing locally --- tests/fuzzing/CMakeLists.txt | 9 ++++++++- tests/fuzzing/README.md | 4 ++-- tests/fuzzing/local_run.sh | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/tests/fuzzing/CMakeLists.txt b/tests/fuzzing/CMakeLists.txt index 466765889..f27ae3401 100644 --- a/tests/fuzzing/CMakeLists.txt +++ b/tests/fuzzing/CMakeLists.txt @@ -26,7 +26,14 @@ endif() # compatible with ClusterFuzzLite if (NOT DEFINED ENV{LIB_FUZZING_ENGINE}) - set(COMPILATION_FLAGS -g -O0 -Wall -Wextra -fsanitize=fuzzer,address,undefined -fprofile-instr-generate -fcoverage-mapping) + set(COMPILATION_FLAGS -g -O0 -Wall -Wextra -fprofile-instr-generate -fcoverage-mapping) + if (SANITIZER MATCHES "address") + set(COMPILATION_FLAGS ${COMPILATION_FLAGS} -fsanitize=fuzzer,address,undefined) + elseif (SANITIZER MATCHES "memory") + set(COMPILATION_FLAGS ${COMPILATION_FLAGS} -fsanitize=fuzzer,memory,undefined -fsanitize-memory-track-origins -fsanitize=fuzzer-no-link) + else() + message(FATAL_ERROR "Unkown sanitizer type. It must be set to `address` or `memory`.") + endif() else() set(COMPILATION_FLAGS "$ENV{LIB_FUZZING_ENGINE} $ENV{CFLAGS}") separate_arguments(COMPILATION_FLAGS) diff --git a/tests/fuzzing/README.md b/tests/fuzzing/README.md index 6db9fafe8..89db1e283 100644 --- a/tests/fuzzing/README.md +++ b/tests/fuzzing/README.md @@ -36,7 +36,7 @@ Once in the container, go into the `tests/fuzzing` folder to compile the fuzzer: cd tests/fuzzing # cmake initialization -cmake -DBOLOS_SDK=/opt/ledger-secure-sdk -DCMAKE_C_COMPILER=/usr/bin/clang -Bbuild -S. +cmake -DBOLOS_SDK=/opt/ledger-secure-sdk -DCMAKE_C_COMPILER=/usr/bin/clang -DSANITIZER=[address|memory] -B build -S . # Fuzzer compilation cmake --build build @@ -48,7 +48,7 @@ cmake --build build ./build/fuzzer -max_len=8192 ``` -If you want to do a fuzzing campain on more than one core and compute the coverage results, you can use the `local_run.sh` script within the container. +If you want to do a fuzzing campain on more than one core and compute the coverage results, you can use the `local_run.sh` script within the container (it'll only run the address and UB sanitizers). ## Full usage based on `clusterfuzzlite` container diff --git a/tests/fuzzing/local_run.sh b/tests/fuzzing/local_run.sh index e76ee005b..cbc179247 100755 --- a/tests/fuzzing/local_run.sh +++ b/tests/fuzzing/local_run.sh @@ -4,7 +4,7 @@ rm -rf build # Build the fuzzer -cmake -B build -S . -DCMAKE_C_COMPILER=/usr/bin/clang +cmake -B build -S . -DCMAKE_C_COMPILER=/usr/bin/clang -DSANITIZER=address cmake --build build # Create the corpus directory if it doesn't exist From 0ff2f9491f6ae0a4a8d2f32de717c62742dbd2be Mon Sep 17 00:00:00 2001 From: Baptistin BOILOT Date: Thu, 2 Jan 2025 17:07:41 +0100 Subject: [PATCH 9/9] refactor: remove explicit_bzero macro in fuzzing mocks --- tests/fuzzing/src/mock.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/tests/fuzzing/src/mock.c b/tests/fuzzing/src/mock.c index 09a2a4a87..cfcd65c83 100644 --- a/tests/fuzzing/src/mock.c +++ b/tests/fuzzing/src/mock.c @@ -5,8 +5,6 @@ #include "cx_sha256.h" #include "cx_sha3.h" -#define explicit_bzero(X, Y) memset_s(X, 0, Y) - /** MemorySanitizer does not wrap explicit_bzero https://github.com/google/sanitizers/issues/1507 * which results in false positives when running MemorySanitizer. */ @@ -69,13 +67,13 @@ size_t strlcat(char *dst, const char *src, size_t size) { } cx_err_t cx_sha256_init_no_throw(cx_sha256_t *hash) { - memset(hash, 0, sizeof(cx_sha256_t)); + memset_s(hash, 0, sizeof(cx_sha256_t)); return CX_OK; } cx_err_t cx_sha3_init_no_throw(cx_sha3_t *hash PLENGTH(sizeof(cx_sha3_t)), size_t size) { UNUSED(size); - memset(hash, 0, sizeof(cx_sha3_t)); + memset_s(hash, 0, sizeof(cx_sha3_t)); return CX_OK; } @@ -87,7 +85,7 @@ cx_err_t cx_hash_no_throw(cx_hash_t *hash, size_t out_len) { UNUSED(hash); UNUSED(mode); - explicit_bzero(out, out_len); // let's initialize the buffer + memset_s(out, 0, out_len); // let's initialize the buffer // if arrays are not empty, read the last element of in and write it in the last element of out if (len > 0 && out_len > 0) out[out_len - 1] = in[len - 1]; return CX_OK; @@ -103,7 +101,7 @@ cx_err_t cx_keccak_256_hash_iovec(const cx_iovec_t *iovec, uint8_t digest[static CX_KECCAK_256_SIZE]) { UNUSED(iovec); UNUSED(iovec_len); - explicit_bzero(digest, CX_SHA256_SIZE); + memset_s(digest, 0, CX_SHA256_SIZE); return CX_OK; } @@ -112,7 +110,7 @@ cx_err_t cx_sha256_hash_iovec(const cx_iovec_t *iovec, uint8_t digest[static CX_SHA256_SIZE]) { UNUSED(iovec); UNUSED(iovec_len); - explicit_bzero(digest, CX_SHA256_SIZE); + memset_s(digest, 0, CX_SHA256_SIZE); return CX_OK; } @@ -152,11 +150,11 @@ cx_err_t cx_math_mult_no_throw(uint8_t *r, const uint8_t *a, const uint8_t *b, s } void cx_rng_no_throw(uint8_t *buffer, size_t len) { - explicit_bzero(buffer, len); + memset_s(buffer, 0, len); } uint16_t get_public_key(uint8_t *out, uint8_t outLength) { - explicit_bzero(out, outLength); + memset_s(out, 0, outLength); return 0; } @@ -164,7 +162,7 @@ void ui_gcs_cleanup(void) { } size_t cx_hash_sha256(const uint8_t *in, size_t in_len, uint8_t *out, size_t out_len) { - explicit_bzero(out, out_len); // let's initialize the buffer + memset_s(out, 0, out_len); // let's initialize the buffer // if arrays are not empty, read the last element of in and write it in the last element of out if (in_len > 0 && out_len > 0) out[out_len - 1] = in[in_len - 1]; return CX_OK;