Skip to content
This repository has been archived by the owner on Nov 18, 2022. It is now read-only.

Fix fuzz test with fuzz2 #181

Draft
wants to merge 16 commits into
base: main
Choose a base branch
from
Draft
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
37 changes: 22 additions & 15 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
name: FuzzTest

on: [push, pull_request]
on:
push:
pull_request:
schedule:
- cron: '0 * * * *' # hourly

jobs:
build-and-run-fuzzing:
Expand Down Expand Up @@ -48,39 +52,42 @@ jobs:
version: "11.0"
cached: ${{ steps.cache-llvm.outputs.cache-hit }}

- name: test_rlp on x86 with sanitizers
working-directory: ./polyjuice-tests/fuzz
run: make build/test_rlp && ./build/test_rlp
- name: test_contracts on x86 with sanitizers
working-directory: ./polyjuice-tests/fuzz
run: make build/test_contracts && ./build/test_contracts

- name: Set MAX_FUZZ_TIME for different branches
run: |
if [[ ${{ github.event_name == 'pull_request' }} ]]; then
echo "MAX_FUZZ_TIME=6" >> $GITHUB_ENV
echo "MAX_FUZZ_TIME=60" >> $GITHUB_ENV
elif [[ ${{ startsWith(github.base_ref, 'fuzz') }} ]]; then
echo "MAX_FUZZ_TIME=3600" >> $GITHUB_ENV
else
echo "MAX_FUZZ_TIME=60" >> $GITHUB_ENV
fi

- name: Fuzzing Polyjuice Generator 1 hour
echo "trigger type: ${{ github.event.action }}"
if [[ ${{ github.event_name }} == 'schedule' ]]; then
echo "MAX_FUZZ_TIME=3600" >> $GITHUB_ENV
fi
- name: Fuzzing Polyjuice Generator
working-directory: polyjuice-tests/fuzz
run: |
mkdir -p corpus
mkdir -p corpus-cache
ls corpus-cache
make build/polyjuice_generator_fuzzer && \
./build/polyjuice_generator_fuzzer corpus corpus-cache \
echo "num of corpus: $(ls corpus-cache | wc -l)"
echo "fuzz time: ${{ env.MAX_FUZZ_TIME }}"
make build/fuzzer && \
LLVM_PROFILE_FILE="build/fuzzer.profraw" ./build/fuzzer corpus corpus-cache \
-max_total_time=$MAX_FUZZ_TIME -timeout=120 \
-max_len=25000 -rss_limit_mb=0
# Max data buffer size: 24KB < 25000 bytes
- name: Generate coverage report
working-directory: polyjuice-tests/fuzz
run: |
llvm-profdata merge -sparse build/fuzzer.profraw -o build/fuzzer.profdata
llvm-cov report ./build/fuzzer -instr-profile=build/fuzzer.profdata

- name: merge corpus
working-directory: polyjuice-tests/fuzz
run: |
mkdir -p corpus-new
./build/polyjuice_generator_fuzzer -merge=1 corpus-new corpus-cache corpus
./build/fuzzer -merge=1 corpus-new corpus-cache corpus
rm -rf corpus-cache
mv corpus-new corpus-cache

Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,4 +58,5 @@ integration-test/
/.vscode/*.log
.vscode/c_cpp_properties.json

polyjuice-tests/.vscode/
polyjuice-tests/.vscode/
corpus/
3 changes: 3 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,6 @@
[submodule "deps/godwoken-scripts"]
path = deps/godwoken-scripts
url = https://github.com/nervosnetwork/godwoken-scripts.git
[submodule "deps/gw-syscall-simulator"]
path = deps/gw-syscall-simulator
url = https://github.com/magicalne/gw-syscall-simulator.git
11 changes: 10 additions & 1 deletion c/polyjuice.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@
#define POLYJUICE_SYSTEM_PREFIX 0xFF
#define POLYJUICE_CONTRACT_CODE 0x01
#define POLYJUICE_DESTRUCTED 0x02
#define POLYJUICE_MAX_DEPTH 277

void polyjuice_build_system_key(uint32_t id, uint8_t polyjuice_field_type,
uint8_t key[GW_KEY_BYTES]) {
Expand Down Expand Up @@ -614,6 +615,13 @@ struct evmc_result call(struct evmc_host_context* context,
res.release = release_result;
gw_context_t* gw_ctx = context->gw_ctx;

if (msg->depth > POLYJUICE_MAX_DEPTH) {
debug_print_int("call depth exceeded", msg->depth);
res.status_code = EVMC_CALL_DEPTH_EXCEEDED;
context->error_code = FATAL_CALL_DEPTH_EXCEEDED;
return res;
}

precompiled_contract_gas_fn contract_gas;
precompiled_contract_fn contract;
if (match_precompiled_address(&msg->destination, &contract_gas, &contract)) {
Expand Down Expand Up @@ -961,6 +969,7 @@ int create_new_account(gw_context_t* ctx,
uint8_t script_hash[32];
blake2b_hash(script_hash, new_script_seg.ptr, new_script_seg.size);
ret = ctx->sys_create(ctx, new_script_seg.ptr, new_script_seg.size, &new_account_id);
free(new_script_seg.ptr);
if (ret != 0) {
debug_print_int("sys_create error", ret);

Expand All @@ -970,7 +979,6 @@ int create_new_account(gw_context_t* ctx,
return ret;
}
}
free(new_script_seg.ptr);
*to_id = new_account_id;
memcpy((uint8_t *)msg->destination.bytes, eth_addr, 20);
debug_print_int(">> new to id", *to_id);
Expand Down Expand Up @@ -1117,6 +1125,7 @@ int handle_native_token_transfer(gw_context_t* ctx, uint32_t from_id,
uint32_t new_account_id;
ret = ctx->sys_create(ctx, new_script_seg.ptr, new_script_seg.size,
&new_account_id);
free(new_script_seg.ptr);
if (ret != 0) {
ckb_debug("[handle_native_token_transfer] create new account failed.");
return ret;
Expand Down
1 change: 1 addition & 0 deletions c/polyjuice_errors.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
*/
#define FATAL_POLYJUICE -50
#define FATAL_PRECOMPILED_CONTRACTS -51
#define FATAL_CALL_DEPTH_EXCEEDED -52

/* Normal errors in polyjuice */
#define ERROR_MOD_EXP -80
Expand Down
1 change: 1 addition & 0 deletions deps/gw-syscall-simulator
Submodule gw-syscall-simulator added at 5066e1
11 changes: 0 additions & 11 deletions polyjuice-tests/fuzz/.gitignore

This file was deleted.

66 changes: 17 additions & 49 deletions polyjuice-tests/fuzz/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ ifeq ($(OS),Unknown)
endif

NPROC?=4
CC=clang
CXX=clang++
CC=clang
LLVM_PROFDATA=llvm-profdata
LLVM_COV=llvm-cov

Expand All @@ -22,7 +22,7 @@ SECP_DIR := $(DEPS)/secp256k1-fix
SECP256K1_SRC := $(SECP_DIR)/src/ecmult_static_pre_context.h

CFLAGS_SECP := -isystem $(SECP_DIR)/src -isystem $(SECP_DIR)
CFLAGS_CKB_STD = -I./ -I$(DEPS)/ckb-c-stdlib -I$(DEPS)/ckb-c-stdlib/molecule
CFLAGS_CKB_STD = -I./ -I$(DEPS)/ckb-c-stdlib -I$(DEPS)/ckb-c-stdlib/molecule -Wno-incompatible-pointer-types
CFLAGS_ETHASH := -I$(DEPS)/ethash/include -I$(DEPS)/ethash/lib/ethash -I$(DEPS)/ethash/lib/keccak -I$(DEPS)/ethash/lib/support
CFLAGS_EVMONE := -I$(DEPS)/evmone/lib/evmone -I$(DEPS)/evmone/include -I$(DEPS)/evmone/evmc/include -I$(DEPS)/evmone/evmc/tools/
CFLAGS_SMT := -I$(DEPS)/godwoken-scripts/c/deps/sparse-merkle-tree/c
Expand All @@ -31,11 +31,12 @@ CFLAGS_MBEDTLS := -I$(DEPS)/mbedtls/include
CFLAGS_CRYPTO_ALGORITHMS := -I$(DEPS)/crypto-algorithms
CFLAGS_INTX := -I$(DEPS)/intx/lib/intx -I$(DEPS)/intx/include
CFLAGS_BN128 := -I$(DEPS)/bn128/include
CFLAGS := -Wall -O2 -I../../c -I../../c/ripemd160 $(CFLAGS_CKB_STD) $(CFLAGS_ETHASH) $(CFLAGS_EVMONE) $(CFLAGS_SMT) $(CFLAGS_GODWOKEN) $(CFLAGS_SECP) $(CFLAGS_MBEDTLS) $(CFLAGS_CRYPTO_ALGORITHMS) $(CFLAGS_INTX) $(CFLAGS_BN128)
CXXFLAGS := $(CFLAGS) -std=c++1z
LDFLAGS := -Wl,--gc-sections
SANITIZER_FLAGS := -g -O1 -fsanitize=address,undefined -Wno-incompatible-pointer-types
LIMIT_ERROR := -ferror-limit=1
CFLAGS_GW_SIM := -I$(DEPS)/gw-syscall-simulator/include
CFLAGS := -Wall -O3 -I../../c -I../../c/ripemd160 $(CFLAGS_CKB_STD) $(CFLAGS_ETHASH) $(CFLAGS_EVMONE) $(CFLAGS_SMT) $(CFLAGS_GODWOKEN) $(CFLAGS_SECP) $(CFLAGS_MBEDTLS) $(CFLAGS_CRYPTO_ALGORITHMS) $(CFLAGS_INTX) $(CFLAGS_BN128) $(CFLAGS_GW_SIM)
CXXFLAGS := $(CFLAGS) -std=c++1z
LDFLAGS := -Wl,--gc-sections,-rpath,$(DEPS)/gw-syscall-simulator/target/debug
SANITIZER_FLAGS := -g -O1 -fsanitize=fuzzer,address,undefined,leak -Wno-incompatible-pointer-types
LIMIT_ERROR := -ferror-limit=10

# TODO: read PROTOCOL_VERSION from deps/godwoken-scripts/c/Makefile
MOLC := moleculec
Expand All @@ -58,38 +59,19 @@ ifeq ($(OS),MacOS)
COVERAGE_FLAGS+=-Wl,-U,_LLVMFuzzerCustomMutator -Wl,-U,_LLVMFuzzerInitialize
endif

EXTERNAL_HEADERS := $(DEPS)/ckb-c-stdlib-simulator-only/ckb_consts.h

#TODO: coverage
all: generate-protocol build/polyjuice_generator_fuzzer

build/polyjuice_generator_fuzzer: generate-protocol $(GENERATOR_DEPS)
$(CXX) $(CFLAGS) $(LDFLAGS) $(SANITIZER_FLAGS) $(LIMIT_ERROR) -fsanitize=fuzzer -Ibuild -o $@ polyjuice_generator_fuzzer.cc $(ALL_OBJS)
build/polyjuice_generator_fuzzer_log: generate-protocol $(GENERATOR_DEPS)
$(CXX) $(CFLAGS) $(LDFLAGS) $(SANITIZER_FLAGS) $(LIMIT_ERROR) -fsanitize=fuzzer -Ibuild -o $@ polyjuice_generator_fuzzer.cc $(ALL_OBJS) -DPOLYJUICE_DEBUG_LOG

###
# TODO:
show: $(COVERAGE_DIR)/fuzzer.profdata
$(LLVM_COV) show --instr-profile=$(COVERAGE_DIR)/fuzzer.profdata smt_coverage
# TODO: report
report: $(COVERAGE_DIR)/fuzzer.profdata coverage $(EXTERNAL_HEADERS)
$(LLVM_COV) report --show-functions --instr-profile=$(COVERAGE_DIR)/fuzzer.profdata smt_coverage $(EXTERNAL_HEADERS)
# TODO:
coverage: $(EXTERNAL_HEADERS)
clang $(COVERAGE_FLAGS) smt_coverage.c smt_fuzzer.c -o smt_coverage
EXTERNAL_HEADERS := $(DEPS)/ckb-c-stdlib-simulator/ckb_consts.hckb-c-stdlib-simulator-only

# start-fuzzer: fuzzer
# ./smt_fuzzer -max_len=800000 -workers=$(NPROC) -jobs=$(NPROC) corpus
build-gw-syscall-simulator:
cd $(DEPS)/gw-syscall-simulator/ && cargo build

# start-fuzzer2: fuzzer
# ./smt_fuzzer -max_len=800000 corpus
build/fuzzer: generate-protocol $(GENERATOR_DEPS) build-gw-syscall-simulator
$(CXX) $(CFLAGS) $(LDFLAGS) $(SANITIZER_FLAGS) $(LIMIT_ERROR) -fprofile-instr-generate -fcoverage-mapping -L $(DEPS)/gw-syscall-simulator/target/debug -l gw_syscall_simluator -Ibuild -o $@ polyjuice_fuzzer.cc $(ALL_OBJS)
build/fuzzer_log: generate-protocol $(GENERATOR_DEPS) build-gw-syscall-simulator
$(CXX) $(CFLAGS) $(LDFLAGS) $(SANITIZER_FLAGS) $(LIMIT_ERROR) -L $(DEPS)/gw-syscall-simulator/target/debug -l gw_syscall_simluator -Ibuild -o $@ polyjuice_fuzzer.cc $(ALL_OBJS) -DPOLYJUICE_DEBUG_LOG

clean:
rm -rf $(BUILD)/*

###

build/generator: ../../c/generator.c $(GENERATOR_DEPS)
cd $(SECP_DIR) && (git apply workaround-fix-g++-linking.patch || true) && cd - # apply patch
$(CXX) $(SANITIZER_FLAGS) $(CFLAGS) $(LDFLAGS) -Ibuild -o $@ ../../c/generator.c $(ALL_OBJS)
Expand Down Expand Up @@ -179,8 +161,8 @@ build/sha256.o: $(DEPS)/crypto-algorithms/sha256.c
build/secp256k1_data_info.h: build/dump_secp256k1_data
$<
build/dump_secp256k1_data: ../../c/dump_secp256k1_data.c $(SECP256K1_SRC)
mkdir -p build
gcc $(CFLAGS) -o $@ $<
#mkdir -p build
$(CC) $(CFLAGS) -o $@ $<
$(SECP256K1_SRC):
cd $(SECP_DIR) && (git apply -R workaround-fix-g++-linking.patch || true) && \
chmod +x autogen.sh && ./autogen.sh && \
Expand All @@ -202,17 +184,3 @@ build/godwoken.mol:
mkdir -p build
curl -L -o $@ ${PROTOCOL_SCHEMA_URL}/godwoken.mol


#TODO:
#%.h:
# ln -s $(CURDIR)/../$@ $(CURDIR)/$@

# %.profraw: coverage
# LLVM_PROFILE_FILE=$@ ./smt_coverage $(CORPUS_DIR)/*

%.profdata: %.profraw
$(LLVM_PROFDATA) merge --sparse $< -o $@

.PHONY: all fuzzer coverage report

.PRECIOUS: $(COVERAGE_DIR)/fuzzer.profraw $(COVERAGE_DIR)/fuzzer.profdata
118 changes: 62 additions & 56 deletions polyjuice-tests/fuzz/README.md
Original file line number Diff line number Diff line change
@@ -1,56 +1,62 @@
# Polyjuice Fuzz Test

[![FuzzTest](https://github.com/Flouse/godwoken-polyjuice/actions/workflows/fuzz.yml/badge.svg?branch=fuzz-v2)](https://github.com/Flouse/godwoken-polyjuice/actions/workflows/fuzz.yml)

These two file were created to simulate `gw_syscalls`:
- polyjuice-tests/fuzz/ckb_syscalls.h
- polyjuice-tests/fuzz/mock_godwoken.hpp

## Polyjuice Generator Fuzzer
```bash
make build/polyjuice_generator_fuzzer
./build/polyjuice_generator_fuzzer corpus -max_total_time=6

# or fuzzing in debug mode
make build/polyjuice_generator_fuzzer_log
./build/polyjuice_generator_fuzzer_log corpus -max_total_time=2
```

### General Algorithm
```pseudo code
// pseudo code
Instrument program for code coverage
load pre-defined transactions such as contracts deploying and then execute run_polyjuice()
while(true) {
Choose random input from corpus
Mutate/populate input into transactions
Execute run_polyjuice() and collect coverage
If new coverage/paths are hit add it to corpus (corpus - directory with test-cases)
}
```

## test_contracts on x86 with [sanitizers](https://github.com/google/sanitizers)
```bash
make build/test_contracts
./build/test_contracts

make build/test_rlp
./build/test_rlp
```

## How to debug Polyjuice generator on x86?
1. Compile Polyjuice generator on x86
```bash
cd fuzz
make build/polyjuice_generator_fuzzer
```
2. Construct `pre_defined_test_case` in [polyjuice_generator_fuzzer.cc](./polyjuice_generator_fuzzer.cc)
3. Run `build/polyjuice_generator_fuzzer_log` with GDB debugger, see: [launch.json](../../.vscode/launch.json)

## Coverage Report[WIP]
TBD

### Related materials
- https://llvm.org/docs/LibFuzzer.html
- [What makes a good fuzz target](https://github.com/google/fuzzing/blob/master/docs/good-fuzz-target.md)
- [Clang's source-based code coverage](https://clang.llvm.org/docs/SourceBasedCodeCoverage.html)
# Polyjuice fuzz testing

## Build

### 1. normal build

```sh
make build/fuzzer
```

### 2. or build with debug log

```sh
make build/fuzzer_log
```

## Run

Simply just run with:
```sh
build/fuzzer
```

Or run `fuzzer_log`:
```sh
build/fuzzer_log
```

### Corpus and Seed

Feeding fuzz testing with some predefined testcases: Seed. (Optional)
And save to `corpus` folder if any good cases are generated during running.

```sh
build/fuzzer corpus seed
```

## Coverage Profile

To genreate a coverage profile, we need to set `LLVM_PROFILE_FILE` and `max_total_time` first.

```sh
LLVM_PROFILE_FILE="build/fuzzer.profraw" build/fuzzer corpus -max_total_time=10
```

### Generate .profdata

```sh
llvm-profdata merge -sparse build/fuzzer.profraw -o build/fuzzer.profdata
```

### Show coverage in detail (Optional)

```sh
llvm-cov show build/fuzzer -instr-profile=build/fuzzer.profdata --show-branches=count --show-expansions > log
```

### Report

```sh
llvm-cov report ./build/fuzzer -instr-profile=build/fuzzer.profdata
```
Loading