Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fuzzing tests #31

Merged
merged 3 commits into from
Feb 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions .clusterfuzzlite/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
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/ledger-app-ergo
COPY --from=LITE_BUILDER /opt/ledger-secure-sdk $SRC/ledger-app-ergo/BOLOS_SDK

# Working directory for build.sh
WORKDIR $SRC/ledger-app-ergo

# Copy build.sh into $SRC dir.
COPY ./.clusterfuzzlite/build.sh $SRC/
9 changes: 9 additions & 0 deletions .clusterfuzzlite/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/bin/bash -eu

# build fuzzers

pushd fuzzing
cmake -DBOLOS_SDK=../BOLOS_SDK -Bbuild -H.
make -C build
mv ./build/*_harness "${OUT}"
popd
1 change: 1 addition & 0 deletions .clusterfuzzlite/project.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
language: c
40 changes: 40 additions & 0 deletions .github/workflows/cflite_cron.yml
Original file line number Diff line number Diff line change
@@ -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 }}
43 changes: 43 additions & 0 deletions .github/workflows/cflite_pr.yml
Original file line number Diff line number Diff line change
@@ -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".
192 changes: 192 additions & 0 deletions fuzzing/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
cmake_minimum_required(VERSION 3.10)

if(${CMAKE_VERSION} VERSION_LESS 3.10)
cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION})
endif()

project(ergo_fuzzers C)

set(CMAKE_C_STANDARD 11)

if (NOT CMAKE_C_COMPILER_ID MATCHES "Clang")
message(FATAL_ERROR "Fuzzer needs to be built with Clang")
endif()

if (NOT DEFINED BOLOS_SDK)
message(FATAL_ERROR "BOLOS_SDK environment variable not found.")
endif()

# 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()


# compatible with ClusterFuzzLite
if (NOT DEFINED ENV{LIB_FUZZING_ENGINE})
add_compile_options(-fsanitize=address,fuzzer-no-link)
add_link_options(-fsanitize=address,fuzzer)
else()
add_link_options($ENV{LIB_FUZZING_ENGINE})
endif()

add_compile_options(-g)


set(SDK_PATH ${BOLOS_SDK})
set(UX_PATH ${SDK_PATH}/lib_ux)
set(ERGO_PATH ..)

set(UX_SOURCE
${UX_PATH}/src/ux_flow_engine.c
${UX_PATH}/src/ux_layout_bb.c
${UX_PATH}/src/ux_layout_bn.c
${UX_PATH}/src/ux_layout_bnn.c
${UX_PATH}/src/ux_layout_bnnn.c
${UX_PATH}/src/ux_layout_nn.c
${UX_PATH}/src/ux_layout_paging.c
${UX_PATH}/src/ux_layout_paging_compute.c
${UX_PATH}/src/ux_layout_pbb.c
${UX_PATH}/src/ux_layout_pb.c
${UX_PATH}/src/ux_layout_pn.c
${UX_PATH}/src/ux_layout_pnn.c
${UX_PATH}/src/ux_layout_utils.c
${UX_PATH}/src/ux_stack.c
)

set(ERGO_SOURCE
${ERGO_PATH}/src/commands/attestinput/ainpt_handler.c
${ERGO_PATH}/src/commands/attestinput/ainpt_response.c
${ERGO_PATH}/src/commands/attestinput/ainpt_ui_bagl.c
${ERGO_PATH}/src/commands/deriveaddress/da_handler.c
${ERGO_PATH}/src/commands/deriveaddress/da_response.c
${ERGO_PATH}/src/commands/deriveaddress/da_ui_bagl.c
${ERGO_PATH}/src/commands/extpubkey/epk_handler.c
${ERGO_PATH}/src/commands/extpubkey/epk_response.c
${ERGO_PATH}/src/commands/extpubkey/epk_ui_bagl.c
${ERGO_PATH}/src/commands/signtx/operations/stx_op_p2pk.c
${ERGO_PATH}/src/commands/signtx/stx_amounts.c
${ERGO_PATH}/src/commands/signtx/stx_handler.c
${ERGO_PATH}/src/commands/signtx/stx_output.c
${ERGO_PATH}/src/commands/signtx/stx_response.c
${ERGO_PATH}/src/commands/signtx/stx_ui_bagl.c
${ERGO_PATH}/src/commands/signtx/stx_ui_common.c
${ERGO_PATH}/src/common/bip32_ext.c
${ERGO_PATH}/src/common/buffer_ext.c
${ERGO_PATH}/src/common/gve.c
${ERGO_PATH}/src/common/rwbuffer.c
${ERGO_PATH}/src/common/bip32_ext.c
${ERGO_PATH}/src/helpers/blake2b.c
${ERGO_PATH}/src/helpers/crypto.c
${ERGO_PATH}/src/helpers/input_frame.c
${ERGO_PATH}/src/ergo/address.c
${ERGO_PATH}/src/ergo/ergo_tree.c
${ERGO_PATH}/src/ergo/schnorr.c
${ERGO_PATH}/src/ergo/tx_ser_box.c
${ERGO_PATH}/src/ergo/tx_ser_full.c
${ERGO_PATH}/src/ergo/tx_ser_input.c
${ERGO_PATH}/src/ergo/tx_ser_table.c
${ERGO_PATH}/src/ui/display.c
${ERGO_PATH}/src/ui/ui_dynamic_flow.c
${ERGO_PATH}/src/ui/ui_approve_reject.c
${ERGO_PATH}/src/ui/ui_application_id_bagl.c
${ERGO_PATH}/src/ui/ui_bip32_path_bagl.c
${ERGO_PATH}/src/ui/ui_sign_reject.c
${ERGO_PATH}/src/ui/ui_main_bagl.c
${ERGO_PATH}/src/ui/ui_menu_bagl.c
${ERGO_PATH}/src/context.c
${BOLOS_SDK}/lib_standard_app/base58.c
${BOLOS_SDK}/lib_standard_app/read.c
${BOLOS_SDK}/lib_standard_app/write.c
${BOLOS_SDK}/lib_standard_app/format.c
)

file(GLOB SHIMS_SRC ${ERGO_PATH}/unit-tests/utils/*.c)

include_directories(
${BOLOS_SDK}/include
${BOLOS_SDK}/target/nanox/include
${BOLOS_SDK}/lib_cxng/include
${BOLOS_SDK}/lib_bagl/include
${BOLOS_SDK}/lib_ux/include
${BOLOS_SDK}/lib_standard_app

${ERGO_PATH}/src
${ERGO_PATH}/src/commands
${ERGO_PATH}/src/commands/attestinput
${ERGO_PATH}/src/commands/deriveaddress
${ERGO_PATH}/src/commands/extpubkey
${ERGO_PATH}/src/commands/signtx
${ERGO_PATH}/src/common
${ERGO_PATH}/src/ergo
${ERGO_PATH}/src/helpers
${ERGO_PATH}/src/ui
./include
)
#include_directories(BEFORE SYSTEM ${ERGO_PATH}/unit-tests/utils)

add_compile_definitions(
FUZZING
HAVE_BAGL
BAGL_WIDTH=128
BAGL_HEIGHT=64
HAVE_UX_FLOW

APPNAME=\"Ergo\"
APPVERSION=\"1.0.0\"
MAJOR_VERSION=1
MINOR_VERSION=0
PATCH_VERSION=0

IO_HID_EP_LENGTH=64
IO_SEPROXYHAL_BUFFER_SIZE_B=300
OS_IO_SEPROXYHAL

HAVE_ECC
HAVE_CRC
HAVE_BLAKE2
HAVE_ECC_WEIERSTRASS
HAVE_SECP256K1_CURVE
HAVE_SECP256R1_CURVE
HAVE_ECC_TWISTED_EDWARDS
HAVE_ED25519_CURVE
HAVE_ECDSA
HAVE_EDDSA
HAVE_HASH
HAVE_HMAC
HAVE_SHA224
HAVE_SHA256
HAVE_SHA3
HAVE_RNG
HAVE_MATH
)

add_compile_options(
-Wno-format -Wno-pointer-to-int-cast -Wno-constant-conversion -Wno-tautological-constant-out-of-range-compare
)

set(SOURCE
${UX_SOURCE}
${SHIMS_SRC}
${ERGO_SOURCE}
./src/utils/os_mocks.c
./src/utils/glyphs.c
./src/utils/crc32.c
)


add_library(ergo ${SOURCE})

set(harnesses
ainpt_harness
da_harness
epk_harness
stx_harness
)

foreach(harness IN LISTS harnesses)
add_executable(${harness}
./src/${harness}.c
)
target_link_libraries(${harness} PUBLIC ergo)
endforeach()
55 changes: 55 additions & 0 deletions fuzzing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Fuzzing tests

## Fuzzing

Fuzzing allows us to test how a program behaves when provided with invalid, unexpected, or random data as input.

In the case of `ledger-app-ergo` we want to test the code that is responsible for handling attest input, derive address, ext pubkey and sign tx APDU handlers.

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.

## 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 `fuzzing` folder to compile the fuzzer:

```console
cd fuzzing

# cmake initialization
cmake -DBOLOS_SDK=/opt/ledger-secure-sdk -DCMAKE_C_COMPILER=/usr/bin/clang -Bbuild -H.

# Fuzzer compilation
make -C build
```

### Run

```console
./build/ainpt_harness
./build/da_harness
./build/epk_harness
./build/stx_harness
```

## Notes

For more context regarding fuzzing check out the app-boilerplate fuzzing [README.md](https://github.com/LedgerHQ/app-boilerplate/blob/master/fuzzing/README.md)
18 changes: 18 additions & 0 deletions fuzzing/include/glyphs.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#pragma once
#include <bagl.h>
#include <stdint.h>

// Code taken from: https://github.com/LedgerHQ/app-cardano/blob/develop/fuzzing/src/os_mocks.c

extern const bagl_icon_details_t C_icon_crossmark;
extern const bagl_icon_details_t C_icon_processing;
extern const bagl_icon_details_t C_icon_eye;
extern const bagl_icon_details_t C_icon_validate_14;
extern const bagl_icon_details_t C_icon_left;
extern const bagl_icon_details_t C_icon_right;
extern const bagl_icon_details_t C_icon_app;
extern const bagl_icon_details_t C_icon_dashboard_x;
extern const bagl_icon_details_t C_icon_back;
extern const bagl_icon_details_t C_icon_certificate;
extern const bagl_icon_details_t C_app_logo_16px;
extern const bagl_icon_details_t C_icon_warning;
Loading
Loading