Skip to content

Commit

Permalink
feat(gwos-evm): fix fuzzing test
Browse files Browse the repository at this point in the history
  • Loading branch information
magicalne committed Feb 13, 2023
1 parent 52bf8f4 commit cf2fe66
Show file tree
Hide file tree
Showing 27 changed files with 4,224 additions and 909 deletions.
19 changes: 6 additions & 13 deletions .github/workflows/gwos-evm-fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,9 @@ jobs:
key: ${{ runner.os }}-cargo-${{ hashFiles('Cargo.lock') }}
- name: Install moleculec
run: |
export MOLC_VERSION=$(cat deps/godwoken-scripts/c/Makefile | egrep "MOLC_VERSION :=" | awk '{print $3}')
export MOLC_VERSION=$(cat ../gwos/c/Makefile | egrep "MOLC_VERSION :=" | awk '{print $3}')
test "$(moleculec --version)" = "Moleculec $MOLC_VERSION" \
|| CARGO_TARGET_DIR=target/ cargo install moleculec --version $MOLC_VERSION --force
|| CARGO_TARGET_DIR=target/ cargo install moleculec --version $MOLC_VERSION
- name: Cache LLVM and Clang
id: cache-llvm
uses: actions/cache@v3
Expand All @@ -47,7 +47,7 @@ jobs:
uses: actions/cache@v3
with:
path: |
polyjuice-tests/fuzz/corpus-cache
gwos-evm/polyjuice-tests/fuzz/corpus-cache
key: corpus-${{ env.DATETIME }}
restore-keys: |
corpus
Expand All @@ -58,13 +58,6 @@ jobs:
version: "11.0"
cached: ${{ steps.cache-llvm.outputs.cache-hit }}

- name: test_rlp on x86 with sanitizers
working-directory: ./gwos-evm/polyjuice-tests/fuzz
run: make build/test_rlp && ./build/test_rlp
- name: test_contracts on x86 with sanitizers
working-directory: ./gwos-evm/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
Expand All @@ -80,8 +73,8 @@ jobs:
run: |
mkdir -p corpus-cache
ls corpus-cache
make build/polyjuice_generator_fuzzer && \
./build/polyjuice_generator_fuzzer corpus corpus-cache \
make build/fuzzer && \
./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
Expand All @@ -90,7 +83,7 @@ jobs:
working-directory: gwos-evm/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
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ exclude = [
# working on godwoken.
"crates/autorocks",
"gwos-evm/polyjuice-tests",
"gwos-evm/polyjuice-tests/fuzz2",
"gwos/contracts",
]

Expand Down
2 changes: 1 addition & 1 deletion crates/generator/src/syscalls/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ use self::error_codes::{
GW_SUDT_ERROR_UNPERMITTED_ADDRESS, SUCCESS,
};

mod bn;
pub mod bn;
pub mod error_codes;

/// Max buffer size: 4MB
Expand Down
3 changes: 2 additions & 1 deletion gwos-evm/c/polyjuice.h
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,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 @@ -1005,7 +1006,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 @@ -1152,6 +1152,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
11 changes: 0 additions & 11 deletions gwos-evm/polyjuice-tests/fuzz/.gitignore

This file was deleted.

83 changes: 27 additions & 56 deletions gwos-evm/polyjuice-tests/fuzz/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,36 +10,40 @@ ifeq ($(OS),Unknown)
endif

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

DEPS := ../../deps
GW_SCRIPTS := ../../../gwos/c
BUILD := build

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
CFLAGS_GODWOKEN := -I$(DEPS)/godwoken-scripts/c
CFLAGS_SMT := -I$(GW_SCRIPTS)/deps/sparse-merkle-tree/c
CFLAGS_GODWOKEN := -I$(GW_SCRIPTS)
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 := -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)
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_BN128 := -I$(DEPS)/bn128/include
CFLAGS_GW_SIM := -Igw-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,./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
MOLC_VERSION := $(shell cat $(DEPS)/godwoken-scripts/c/Makefile | egrep "MOLC_VERSION :=" | awk '{print $$3}')
PROTOCOL_VERSION := $(shell cat $(DEPS)/godwoken-scripts/c/Makefile | egrep "PROTOCOL_VERSION :=" | awk '{print $$3}')

MOLC_VERSION := $(shell cat $(GW_SCRIPTS)/Makefile | egrep "MOLC_VERSION :=" | awk '{print $$3}')
PROTOCOL_VERSION := $(shell cat $(GW_SCRIPTS)/Makefile | egrep "PROTOCOL_VERSION :=" | awk '{print $$3}')
PROTOCOL_SCHEMA_URL := https://raw.githubusercontent.com/nervosnetwork/godwoken/${PROTOCOL_VERSION}/crates/types/schemas

ALL_OBJS := $(BUILD)/keccak.o $(BUILD)/keccakf800.o \
Expand All @@ -57,38 +61,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 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 ./gw-syscall-simulator/target/debug -l gw_syscall_simulator -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 ./gw-syscall-simulator/target/debug -l gw_syscall_simulator -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 @@ -170,16 +155,16 @@ build/platform_util.o: $(DEPS)/mbedtls/library/platform_util.c
$(CC) $(CFLAGS) $(LDFLAGS) -c -o $@ $<
build/bignum.o: $(DEPS)/mbedtls/library/bignum.c
$(CC) $(CFLAGS) $(LDFLAGS) -c -o $@ $<

build/sha256.o: $(DEPS)/crypto-algorithms/sha256.c
$(CXX) $(CFLAGS) $(LDFLAGS) -c -o $@ $<

# Secp256k1 refers to the parameters of the elliptic curve used in Bitcoin's public-key cryptography, and is defined in Standards for Efficient Cryptography (SEC)
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 @@ -196,22 +181,8 @@ build/godwoken.h: build/godwoken.mol
${MOLC} --language c --schema-file $< > $@
build/blockchain.mol:
mkdir -p build
curl -L -o $@ ${PROTOCOL_SCHEMA_URL}/blockchain.mol
cp ../../../gwos/crates/types/schemas/blockchain.mol $@
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
cp ../../../gwos/crates/types/schemas/godwoken.mol $@

.PRECIOUS: $(COVERAGE_DIR)/fuzzer.profraw $(COVERAGE_DIR)/fuzzer.profdata
118 changes: 62 additions & 56 deletions gwos-evm/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

0 comments on commit cf2fe66

Please sign in to comment.