Skip to content

Commit

Permalink
Add 1st fuzzing
Browse files Browse the repository at this point in the history
  • Loading branch information
cedelavergne-ledger committed Dec 16, 2024
1 parent 2ae8e0d commit 398218e
Show file tree
Hide file tree
Showing 10 changed files with 489 additions and 0 deletions.
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/app-ethereum
COPY --from=LITE_BUILDER /opt/ledger-secure-sdk $SRC/app-ethereum/BOLOS_SDK

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

# 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 tests/fuzzing
cmake -DBOLOS_SDK=../BOLOS_SDK -Bbuild -H.
make -C build
mv ./build/fuzz_app_eth "${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.

Check warning on line 31 in .github/workflows/cflite_cron.yml

View workflow job for this annotation

GitHub Actions / Check yaml files

31:23 [comments] too few spaces before comment
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

Check warning on line 38 in .github/workflows/cflite_cron.yml

View workflow job for this annotation

GitHub Actions / Check yaml files

38:29 [comments] too few spaces before comment
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".
170 changes: 170 additions & 0 deletions tests/fuzzing/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
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")

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_FLAGS
"${CMAKE_C_FLAGS} -Wall -Wextra -pedantic -Wno-unused-parameter -Wno-embedded-directive -g -O0 -fsanitize=fuzzer,address,undefined -fprofile-instr-generate -fcoverage-mapping"
)

# 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)
set(BOLOS_SDK /opt/${TARGET_DEVICE}-secure-sdk)
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})
83 changes: 83 additions & 0 deletions tests/fuzzing/README.md
Original file line number Diff line number Diff line change
@@ -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 -DCMAKE_C_COMPILER=/usr/bin/clang -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:
<https://google.github.io/clusterfuzzlite/>

### 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
```
Loading

0 comments on commit 398218e

Please sign in to comment.