diff --git a/README.md b/README.md index 29d70c0..979a13e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,82 @@ # CITRUS: C++ Unit Testing for Real-world Usage -CITRUS is an implementation of Unit-level Fuzzing for C++ based on random method sequence generation. -Implemented using C++14 and backporting library to utilize C++17's `std::optional` feature. +## Introduction -Tested working on: Ubuntu 16.04, 18.04, 20.04 using LLVM/Clang++ 11.0.1 +CITRUS is an implementation of Unit-level Testing for C++ based on random method call sequence generation. --- -Developed by SWTV Lab, KAIST +CITRUS automatically generates test driver files for the target program `P`, each of which consists of various method calls +of `P`. In +addition, CITRUS improves the test coverage of `P` further +by applying **libfuzzer** to change `P`’s state by mutating +arguments of the methods. + +For more details, please refer to CITRUS technical paper. + +## Requirements + +CITRUS was tested running on Ubuntu 16.04, 18.04, 20.04. The requirements of CITRUS are: +1. LLVM/Clang++ 11.0.1, +1. LCOV coverage measurement tool (we used a [modified LCOV](https://github.com/henry2cox/lcov/tree/diffcov_initial) for CITRUS development), +1. CMake 3.15, +1. Python 3. + +We provide a shell script to install all CITRUS requirements. (**root privilege required**) +```shell +./scripts/dep.sh +``` + + +## Build Instruction + +To build CITRUS is simply executing the build script +```shell +./scripts/build.sh +``` +CITRUS will be built in `build` directory. + + +## Building CITRUS Subjects + +We provide the target programs we use for our experiment at +`replication` directory. For simplicity, you can execute the following shell script (from the CITRUS **root** project directory) to build all our experiments subjects. +```shell +./scripts/bootstrap.sh subjects # to build in subjects dir +``` + +## Running CITRUS Method Call Sequence Generation + +Currently CITRUS only supports command-line interface. +```shell +./build/citrus ${TRANS_UNIT} \ + --obj-dir ${OBJ_DIR} \ + --src-dir ${SRC_DIR} \ + --max-depth ${MAX_DEPTH} \ + --fuzz-timeout ${TIMEOUT} \ + --xtra-ld "${XTRA_LD}" \ + --out-prefix ${OUT_PREFIX} +``` +For easier usage, we recommend to write separate shell script(s) to configure the command-line arguments as demonstrated in `run` directory. For example, to run CITRUS on `hjson` library: +```shell +./run/hjson.sh 43200 tc_hjson subjects/hjson-cpp # 12 hours +``` +where `tc_hjson` represents the target directory where the generated test cases will be put at, and `subjects/hjson-cpp` represents the `hjson` directory. + +## Running CITRUS libfuzzer + +Currently the libfuzzer stage must be manually triggered after the method call sequence generation. CITRUS writes the libfuzzer harness drivers in `out_libfuzzer` directory. Each driver has compilation instruction at the end of the file. + +To ease the libfuzzer stage, we provide `batch_libfuzzer.py` script (i.e., CITRUS already puts this script in `out_libfuzzer` directory) to collect all compilation, running, and test case replaying instructions for libfuzzer stage. +```shell +# Compilation (from out_libfuzzer directory) +python3 batch_libfuzzer gen # initializes the scripts +./tst_compile.sh # compile all harness drivers + +# Running libfuzzer +./tst_run.sh # default: 5 mins each driver + +# Replaying libfuzzer generated test cases +./tst_repl.sh +``` + +--- +Developed by **SWTV Lab**, **KAIST** diff --git a/run/_template.sh b/run/_template.sh index 47cbbdd..d7c732f 100755 --- a/run/_template.sh +++ b/run/_template.sh @@ -5,11 +5,6 @@ TARGET=build/citrus TIMEOUT=$1 OUT_PREFIX=$2 SUBJ_DIR=$3 -#USE_FUNC_COMP=$2 -#FUNC_COMP= -#if [[ "${USE_FUNC_COMP^^}" == "NO" ]]; then -# FUNC_COMP=__none.txt -#fi TRANS_UNIT=${SUBJ_DIR}/all.cpp OBJ_DIR=${SUBJ_DIR} @@ -28,5 +23,4 @@ ${TARGET} ${TRANS_UNIT} \ --max-depth ${MAX_DEPTH} \ --fuzz-timeout ${TIMEOUT} \ --xtra-ld "${XTRA_LD}" \ - --out-prefix ${OUT_PREFIX} \ - --func-comp ${FUNC_COMP} + --out-prefix ${OUT_PREFIX} diff --git a/run/hjson.sh b/run/hjson.sh index 68e62dd..e3101a4 100755 --- a/run/hjson.sh +++ b/run/hjson.sh @@ -5,12 +5,6 @@ TARGET=build/citrus TIMEOUT=$1 OUT_PREFIX=$2 SUBJ_DIR=$3 -FUNC_COMP=__none.txt -#USE_FUNC_COMP=$2 -#FUNC_COMP=func_comp/hjson.txt -#if [[ "${USE_FUNC_COMP^^}" == "NO" ]]; then -# FUNC_COMP=__none.txt -#fi TRANS_UNIT=${SUBJ_DIR}/src/hjson_encode.cpp OBJ_DIR=${SUBJ_DIR}/build/src/CMakeFiles/hjson.dir @@ -29,5 +23,4 @@ ${TARGET} ${TRANS_UNIT} \ --max-depth ${MAX_DEPTH} \ --fuzz-timeout ${TIMEOUT} \ --xtra-ld "${XTRA_LD}" \ - --out-prefix ${OUT_PREFIX} \ - --func-comp ${FUNC_COMP} + --out-prefix ${OUT_PREFIX} diff --git a/run/json-voorhees.sh b/run/json-voorhees.sh index 131201e..683af91 100755 --- a/run/json-voorhees.sh +++ b/run/json-voorhees.sh @@ -5,11 +5,6 @@ TARGET=build/citrus TIMEOUT=$1 OUT_PREFIX=$2 SUBJ_DIR=$3 -#USE_FUNC_COMP=$2 -#FUNC_COMP=func_comp/json-voorhees.txt -#if [[ "${USE_FUNC_COMP^^}" == "NO" ]]; then -# FUNC_COMP=__none.txt -#fi TRANS_UNIT=${SUBJ_DIR}/src/jsonv/all.cpp OBJ_DIR=${SUBJ_DIR}/build/CMakeFiles/jsonv.dir/src/jsonv @@ -28,5 +23,4 @@ ${TARGET} ${TRANS_UNIT} \ --max-depth ${MAX_DEPTH} \ --fuzz-timeout ${TIMEOUT} \ --xtra-ld "${XTRA_LD}" \ - --out-prefix ${OUT_PREFIX} \ - --func-comp ${FUNC_COMP} \ No newline at end of file + --out-prefix ${OUT_PREFIX} \ No newline at end of file diff --git a/run/jsonbox.sh b/run/jsonbox.sh index 6bb533e..401213f 100755 --- a/run/jsonbox.sh +++ b/run/jsonbox.sh @@ -5,11 +5,6 @@ TARGET=build/citrus TIMEOUT=$1 OUT_PREFIX=$2 SUBJ_DIR=$3 -#USE_FUNC_COMP=$2 -#FUNC_COMP=func_comp/jsonbox.txt -#if [[ "${USE_FUNC_COMP^^}" == "NO" ]]; then -# FUNC_COMP=__none.txt -#fi TRANS_UNIT=${SUBJ_DIR}/src/all.cpp OBJ_DIR=${SUBJ_DIR}/build/CMakeFiles/JsonBox.dir/src @@ -28,5 +23,4 @@ ${TARGET} ${TRANS_UNIT} \ --max-depth ${MAX_DEPTH} \ --fuzz-timeout ${TIMEOUT} \ --xtra-ld "${XTRA_LD}" \ - --out-prefix ${OUT_PREFIX} \ - --func-comp ${FUNC_COMP} + --out-prefix ${OUT_PREFIX} diff --git a/run/jsoncpp.sh b/run/jsoncpp.sh index e5b4105..e58be72 100755 --- a/run/jsoncpp.sh +++ b/run/jsoncpp.sh @@ -5,12 +5,6 @@ TARGET=build/citrus TIMEOUT=$1 OUT_PREFIX=$2 SUBJ_DIR=$3 -FUNC_COMP=__none.txt -#USE_FUNC_COMP=$2 -#FUNC_COMP=func_comp/jsoncpp.txt -#if [[ "${USE_FUNC_COMP^^}" == "NO" ]]; then -# FUNC_COMP=__none.txt -#fi TRANS_UNIT=${SUBJ_DIR}/src/lib_json/all.cpp OBJ_DIR=${SUBJ_DIR}/build/src/lib_json/CMakeFiles/jsoncpp_lib.dir @@ -29,5 +23,4 @@ ${TARGET} ${TRANS_UNIT} \ --max-depth ${MAX_DEPTH} \ --fuzz-timeout ${TIMEOUT} \ --xtra-ld "${XTRA_LD}" \ - --out-prefix ${OUT_PREFIX} \ - --func-comp ${FUNC_COMP} + --out-prefix ${OUT_PREFIX} diff --git a/run/jvar.sh b/run/jvar.sh index 6fb7a0f..0800e03 100755 --- a/run/jvar.sh +++ b/run/jvar.sh @@ -5,11 +5,6 @@ TARGET=build/citrus TIMEOUT=$1 OUT_PREFIX=$2 SUBJ_DIR=$3 -#USE_FUNC_COMP=$2 -#FUNC_COMP=func_comp/jvar.txt -#if [[ "${USE_FUNC_COMP^^}" == "NO" ]]; then -# FUNC_COMP=__none.txt -#fi TRANS_UNIT=${SUBJ_DIR}/src/all.cpp OBJ_DIR=${SUBJ_DIR}/build/CMakeFiles/jvar.dir/src @@ -28,5 +23,4 @@ ${TARGET} ${TRANS_UNIT} \ --max-depth ${MAX_DEPTH} \ --fuzz-timeout ${TIMEOUT} \ --xtra-ld "${XTRA_LD}" \ - --out-prefix ${OUT_PREFIX} \ - --func-comp ${FUNC_COMP} + --out-prefix ${OUT_PREFIX} diff --git a/run/re2.sh b/run/re2.sh index f910d44..8df5537 100755 --- a/run/re2.sh +++ b/run/re2.sh @@ -5,11 +5,6 @@ TARGET=build/citrus TIMEOUT=$1 OUT_PREFIX=$2 SUBJ_DIR=$3 -#USE_FUNC_COMP=$2 -#FUNC_COMP=func_comp/re2.txt -#if [[ "${USE_FUNC_COMP^^}" == "NO" ]]; then -# FUNC_COMP=__none.txt -#fi TRANS_UNIT=${SUBJ_DIR}/re2/all.cpp OBJ_DIR=${SUBJ_DIR}/build/CMakeFiles/re2.dir/ @@ -28,5 +23,4 @@ ${TARGET} ${TRANS_UNIT} \ --max-depth ${MAX_DEPTH} \ --fuzz-timeout ${TIMEOUT} \ --xtra-ld "${XTRA_LD}" \ - --out-prefix ${OUT_PREFIX} \ - --func-comp ${FUNC_COMP} + --out-prefix ${OUT_PREFIX} diff --git a/run/tinyxml2.sh b/run/tinyxml2.sh index b994462..2cde3d7 100755 --- a/run/tinyxml2.sh +++ b/run/tinyxml2.sh @@ -5,12 +5,6 @@ TARGET=build/citrus TIMEOUT=$1 OUT_PREFIX=$2 SUBJ_DIR=$3 -FUNC_COMP=__none.txt -#USE_FUNC_COMP=$2 -#FUNC_COMP=func_comp/tinyxml2.txt -#if [[ "${USE_FUNC_COMP^^}" == "NO" ]]; then -# FUNC_COMP=__none.txt -#fi TRANS_UNIT=${SUBJ_DIR}/tinyxml2.cpp OBJ_DIR=${SUBJ_DIR}/build/CMakeFiles/tinyxml2.dir @@ -29,5 +23,4 @@ ${TARGET} ${TRANS_UNIT} \ --max-depth ${MAX_DEPTH} \ --fuzz-timeout ${TIMEOUT} \ --xtra-ld "${XTRA_LD}" \ - --out-prefix ${OUT_PREFIX} \ - --func-comp ${FUNC_COMP} + --out-prefix ${OUT_PREFIX} diff --git a/run/yaml-cpp.sh b/run/yaml-cpp.sh index 16ec3d6..e1527d7 100755 --- a/run/yaml-cpp.sh +++ b/run/yaml-cpp.sh @@ -5,12 +5,6 @@ TARGET=build/citrus TIMEOUT=$1 OUT_PREFIX=$2 SUBJ_DIR=$3 -FUNC_COMP=__none.txt -#USE_FUNC_COMP=$2 -#FUNC_COMP=func_comp/re2.txt -#if [[ "${USE_FUNC_COMP^^}" == "NO" ]]; then -# FUNC_COMP=__none.txt -#fi TRANS_UNIT=${SUBJ_DIR}/src/all.cpp OBJ_DIR=${SUBJ_DIR}/build/CMakeFiles/yaml-cpp.dir/src @@ -29,5 +23,4 @@ ${TARGET} ${TRANS_UNIT} \ --max-depth ${MAX_DEPTH} \ --fuzz-timeout ${TIMEOUT} \ --xtra-ld "${XTRA_LD}" \ - --out-prefix ${OUT_PREFIX} \ - --func-comp ${FUNC_COMP} + --out-prefix ${OUT_PREFIX}