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; }