diff --git a/Cargo.toml b/Cargo.toml index d3f2678..93e39a1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,6 +55,6 @@ incremental = true [features] default = [] +# default = ["redqueen", "custom_feedback"] redqueen = [] custom_feedback = [] -bb_range = [] diff --git a/docker/Dockerfile.snapshot b/docker/Dockerfile.snapshot index e44c053..9fe6260 100644 --- a/docker/Dockerfile.snapshot +++ b/docker/Dockerfile.snapshot @@ -1,4 +1,5 @@ -FROM ubuntu:22.04 +ARG UBUNTU_VERSION=22.04 +FROM ubuntu:$UBUNTU_VERSION # Install prereqs ARG DEBIAN_FRONTEND=noninteractive diff --git a/docker/coverage_scripts/angr_snapchange.py b/docker/coverage_scripts/angr_snapchange.py index bcabbf7..d0c47a2 100644 --- a/docker/coverage_scripts/angr_snapchange.py +++ b/docker/coverage_scripts/angr_snapchange.py @@ -718,10 +718,15 @@ def add_memory(op, memlen=STR_LEN_THRESHOLD, null_term=False): dict_path = cliargs.dict_path +logger.info(f"writing auto-dict entries to {dict_path}") dict_path.mkdir(parents=True, exist_ok=True) +written = 0 for fname, content in auto_dict_files.items(): with (dict_path / fname).open("wb") as f: f.write(content) + written += 1 + +logger.info(f"wrote {written} auto-dict entries") sys.exit(0) diff --git a/docker/utils/snapshot.sh b/docker/utils/snapshot.sh index 3bb1b26..ace2f28 100755 --- a/docker/utils/snapshot.sh +++ b/docker/utils/snapshot.sh @@ -292,7 +292,7 @@ if [[ "$GENERATE_COVERAGE_BREAKPOINTS" -eq 1 ]]; then # Use ghidra to find the coverage basic blocks python3 $SNAPCHANGE_ROOT/coverage_scripts/ghidra_basic_blocks.py --base-addr "$BASE" "$OUTPUT/$BIN_NAME.bin" > "$OUTPUT/ghidra.log" 2>&1 elif [[ "$COVERAGE_BREAKPOINT_COMMAND" == "angr" ]]; then - python3 $SNAPCHANGE_ROOT/coverage_scripts/angr_snapchange.py --auto-dict --base-addr "$BASE" "$OUTPUT/$BIN_NAME.bin" > "$OUTPUT/angr.log" 2>&1 + python3 $SNAPCHANGE_ROOT/coverage_scripts/angr_snapchange.py --dict-path "$OUTPUT/dict" --auto-dict --base-addr "$BASE" "$OUTPUT/$BIN_NAME.bin" > "$OUTPUT/angr.log" 2>&1 elif [[ "$COVERAGE_BREAKPOINT_COMMAND" == "rizin" ]]; then python3 $SNAPCHANGE_ROOT/coverage_scripts/rz_snapchange.py --base-addr "$BASE" "$OUTPUT/$BIN_NAME.bin" > "$OUTPUT/rizin.log" 2>&1 elif [[ "$COVERAGE_BREAKPOINT_COMMAND" == "binaryninja" ]]; then diff --git a/examples/.gitignore b/examples/.gitignore new file mode 100644 index 0000000..3625223 --- /dev/null +++ b/examples/.gitignore @@ -0,0 +1,3 @@ +**/snapshot_image +**/target_image +**/Cargo.lock diff --git a/examples/01_getpid/.dockerignore b/examples/01_getpid/.dockerignore deleted file mode 100644 index 679a4d5..0000000 --- a/examples/01_getpid/.dockerignore +++ /dev/null @@ -1,3 +0,0 @@ -snapshot -target -harness/example1 diff --git a/examples/01_getpid/.dockerignore b/examples/01_getpid/.dockerignore new file mode 120000 index 0000000..3e4e48b --- /dev/null +++ b/examples/01_getpid/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/examples/01_getpid/.gitignore b/examples/01_getpid/.gitignore index 9cf1d1f..57d02ee 100644 --- a/examples/01_getpid/.gitignore +++ b/examples/01_getpid/.gitignore @@ -2,3 +2,8 @@ dockers/snapshot_image dockers/target_image *.min fuzzer.log +snapshot +snapshot/ +target +harness/example1 +harness/report.info diff --git a/examples/01_getpid/Makefile b/examples/01_getpid/Makefile index 0e80b8b..b6b4155 100644 --- a/examples/01_getpid/Makefile +++ b/examples/01_getpid/Makefile @@ -1,27 +1,28 @@ DOCKER ?= docker FUZZ_CORES ?= /2 +DOCKER_IMAGE_NAME ?= snapchange_example1 + all: test base_images: # Build the base snapchange image used for snapshotting cd ../../docker && make -dockers/target_image: - $(DOCKER) build -q -t snapchange_example1:target . -f dockers/Dockerfile.target | tee dockers/target_image +dockers/target_image: dockers/Dockerfile.target + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):target . -f $< > $@ || { rm $@; exit 1; } -dockers/snapshot_image: dockers/target_image - $(DOCKER) build -q -t snapchange_example1:snapshot . -f dockers/Dockerfile.snapshot | tee dockers/snapshot_image +dockers/snapshot_image: dockers/Dockerfile.snapshot dockers/target_image + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):snapshot . -f $< > $@ || { rm $@; exit 1; } snapshot: dockers/snapshot_image harness/example1.c - # mkdir -p snapshot/ $(DOCKER) run --rm -i \ -v $(shell realpath -m ./snapshot):/snapshot \ -e SNAPSHOT_IMGTYPE=initramfs \ - $(shell cat dockers/snapshot_image) + $$(cat dockers/snapshot_image) >/dev/null 2>&1 fuzzer: - cargo build -r + cargo build -r >/dev/null 2>&1 fuzz: snapshot cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) @@ -31,19 +32,21 @@ fuzz-%: snapshot # .PHONY: fuzz-1 fuzz-2 fuzz-3 fuzz-4 fuzz-5 -test: base_images snapshot fuzzer reset +test: snapshot fuzzer reset ./test.sh reset: snapshot cd snapshot && ./reset.sh clean: clean-docker - -$(RM) -rf snapshot + -$(RM) -rf snapshot target clean-docker: + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):snapshot + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):target -$(DOCKER) rmi `cat ./dockers/snapshot_image` - -rm ./dockers/snapshot_image -$(DOCKER) rmi `cat ./dockers/target_image` + -rm ./dockers/snapshot_image -rm ./dockers/target_image .PHONY: fuzzer all base_images test reset fuzz diff --git a/examples/01_getpid/dockers/Dockerfile.target b/examples/01_getpid/dockers/Dockerfile.target index ab4630b..0678b1a 100644 --- a/examples/01_getpid/dockers/Dockerfile.target +++ b/examples/01_getpid/dockers/Dockerfile.target @@ -1,4 +1,4 @@ FROM snapchange_base_target -COPY harness/* /opt/ -RUN cd /opt/ && make +COPY harness/ /opt/ +RUN cd /opt/ && ls && make diff --git a/examples/01_getpid/test.sh b/examples/01_getpid/test.sh index 3e1dee9..fc1ee4a 100755 --- a/examples/01_getpid/test.sh +++ b/examples/01_getpid/test.sh @@ -1,43 +1,11 @@ #!/usr/bin/env bash -if [[ -z "$FUZZ_CORES" ]]; then - FUZZ_CORES="/2" -fi - -if [[ -z "$FUZZ_TIMEOUT" ]]; then - FUZZ_TIMEOUT="1m" -fi - -EX="$(basename $PWD)" - -COLOR_CLEAR='\e[0m' -COLOR_RED='\e[0;31m' -COLOR_GREEN='\e[0;32m' - -function err { - echo -e "${COLOR_RED}ERROR: $EX - $* $COLOR_CLEAR" - exit 1 -} - -function log_success { - echo -e "${COLOR_GREEN}SUCCESS: $EX - $* $COLOR_CLEAR" -} - -if ! test -d snapshot; then - err "require snapshot" -fi - -# Reset the snapshot from a previous run -pushd snapshot > /dev/null -./reset.sh -popd > /dev/null +source ../test.include.sh -# Rebuild the fuzzer -echo "Building Example 01" -cargo build -r >/dev/null 2>/dev/null || err "build failed" +setup_build # Seed the input with an easier input -echo "Begin fuzzing!" +log_info "start fuzzing" mkdir -p snapshot/input echo -n fuzzmetosolvem11 > snapshot/input/test cargo run -r -- fuzz -c "$FUZZ_CORES" --ascii-stats --stop-after-first-crash --stop-after-time "$FUZZ_TIMEOUT" >/dev/null 2>/dev/null diff --git a/examples/02_libtiff/.dockerignore b/examples/02_libtiff/.dockerignore deleted file mode 100644 index f647735..0000000 --- a/examples/02_libtiff/.dockerignore +++ /dev/null @@ -1,2 +0,0 @@ -target -snapshot diff --git a/examples/02_libtiff/.dockerignore b/examples/02_libtiff/.dockerignore new file mode 120000 index 0000000..3e4e48b --- /dev/null +++ b/examples/02_libtiff/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/examples/02_libtiff/.gitignore b/examples/02_libtiff/.gitignore new file mode 100644 index 0000000..5dcccb0 --- /dev/null +++ b/examples/02_libtiff/.gitignore @@ -0,0 +1,4 @@ +target +snapshot +*.log +Cargo.lock diff --git a/examples/02_libtiff/Cargo.lock b/examples/02_libtiff/Cargo.lock deleted file mode 100644 index d42ebc3..0000000 --- a/examples/02_libtiff/Cargo.lock +++ /dev/null @@ -1,1516 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "cpp_demangle", - "fallible-iterator", - "gimli", - "object", - "rustc-demangle", - "smallvec 1.11.0", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c99f64d1e06488f620f932677e24bc6e2897582980441ae90a671415bd7ec2f" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", -] - -[[package]] -name = "aho-corasick" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" -dependencies = [ - "memchr", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ca84f3628370c59db74ee214b3263d58f9aadd9b4fe7e711fd87dc452b7f163" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is-terminal", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a30da5c5f2d5e72842e00bcb57657162cdabef0931f40e2deb9b4140440cecd" - -[[package]] -name = "anstyle-parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938874ff5980b03a87c5524b3ae5b59cf99b1d6bc836848df7bc5ada9643c333" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "180abfa45703aebe0093f79badacc01b8fd4ea2e35118747e5811127f926e188" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] -name = "anyhow" -version = "1.0.72" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b13c32d80ecc7ab747b80c3784bce54ee8a7a0cc4fbda9bf4cda2cf6fe90854" - -[[package]] -name = "array-init" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" -dependencies = [ - "nodrop", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "630be753d4e58660abd17930c71b647fe46c27ea6b63cc59e1e3851406972e42" - -[[package]] -name = "bumpalo" -version = "3.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e2c3daef883ecc1b5d58c15adae93470a91d425f3532ba1695849656af3fc1" - -[[package]] -name = "byteorder" -version = "1.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "cc" -version = "1.0.79" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec837a71355b28f6556dbd569b37b3f363091c0bd4b2e735674521b4c5fd9bc5" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "time", - "wasm-bindgen", - "winapi 0.3.9", -] - -[[package]] -name = "clap" -version = "4.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" -dependencies = [ - "clap_builder", - "clap_derive", - "once_cell", -] - -[[package]] -name = "clap-verbosity-flag" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1eef05769009513df2eb1c3b4613e7fad873a14c600ff025b08f250f59fee7de" -dependencies = [ - "clap", - "log", -] - -[[package]] -name = "clap_builder" -version = "4.3.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.3.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "clap_lex" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core_affinity" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f8a03115cc34fb0d7c321dd154a3914b3ca082ccc5c11d91bf7117dbbe7171f" -dependencies = [ - "kernel32-sys", - "libc", - "num_cpus", - "winapi 0.2.8", -] - -[[package]] -name = "cpp_demangle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossterm" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85525306c4291d1b73ce93c8acf9c339f9b213aef6c1d85c3830cbf1c16325c" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio 0.7.14", - "parking_lot 0.11.2", - "signal-hook", - "signal-hook-mio", - "winapi 0.3.9", -] - -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio 0.8.8", - "parking_lot 0.12.1", - "signal-hook", - "signal-hook-mio", - "winapi 0.3.9", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "ctrlc" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a011bbe2c35ce9c1f143b7af6f94f29a167beb4cd1d29e6740ce836f723120e" -dependencies = [ - "nix 0.26.2", - "windows-sys", -] - -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "errno" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b30f669a7961ef1631673d2766cc92f52d64f7ef354d4fe0ddfd30ed52f0f4f" -dependencies = [ - "errno-dragonfly", - "libc", - "windows-sys", -] - -[[package]] -name = "errno-dragonfly" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" -dependencies = [ - "cc", - "libc", -] - -[[package]] -name = "example_fuzzer" -version = "0.1.0" -dependencies = [ - "anyhow", - "log", - "rand", - "regex", - "snapchange", -] - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "flate2" -version = "1.0.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b9429470923de8e8cbd4d2dc513535400b4b3fef0319fb5c4e1f520a7bef743" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", -] - -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "stable_deref_trait", -] - -[[package]] -name = "hashbrown" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "iana-time-zone" -version = "0.1.57" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "iced-x86" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd366a53278429c028367e0ba22a46cab6d565a57afb959f06e92c7a69e7828" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "indexmap" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5477fe2230a79769d8dc68e0eabf5437907c0457a5614a9e8dddb67f65eb65d" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "is-terminal" -version = "0.4.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb0889898416213fab133e1d33a0e5858a48177452750691bde3666d0fdbaf8b" -dependencies = [ - "hermit-abi 0.3.2", - "rustix", - "windows-sys", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "js-sys" -version = "0.3.64" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5f195fe497f702db0f318b07fdd68edb16955aed830df8363d837542f8f935a" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "kvm-bindings" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe70e65a5b092161d17f5005b66e5eefe7a94a70c332e755036fc4af78c4e79" -dependencies = [ - "vmm-sys-util", -] - -[[package]] -name = "kvm-ioctls" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4" -dependencies = [ - "kvm-bindings", - "libc", - "vmm-sys-util", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.147" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" - -[[package]] -name = "linux-raw-sys" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09fc20d2ca12cb9f044c93e3bd6d32d523e6e2ec3db4f7b2939cd99026ecd3f0" - -[[package]] -name = "lock_api" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1cc9717a20b1bb222f333e6a92fd32f7d8a18ddc5a3191a11af45dcbf4dcd16" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "memchr" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" - -[[package]] -name = "memmap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -dependencies = [ - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi 0.3.9", -] - -[[package]] -name = "mio" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -dependencies = [ - "libc", - "log", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "nix" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" -dependencies = [ - "bitflags 1.3.2", - "cc", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "nix" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" -dependencies = [ - "bitflags 1.3.2", - "cfg-if", - "libc", - "static_assertions", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "ntapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "num-traits" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.2", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "object" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" -dependencies = [ - "flate2", - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.8", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec 1.11.0", - "winapi 0.3.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93f00c865fe7cabf650081affecd3871070f26767e7b2070a3ffae14c654b447" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.3.5", - "smallvec 1.11.0", - "windows-targets", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit", -] - -[[package]] -name = "proc-macro2" -version = "1.0.66" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7b6d6190b7594385f61bd3911cd1be99dfddcfc365a4160cc2ab5bff4aed294" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustix" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a962918ea88d644592894bc6dc55acc6c0956488adcebbfb6e273506b7fd6e5" -dependencies = [ - "bitflags 2.3.3", - "errno", - "libc", - "linux-raw-sys", - "windows-sys", -] - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.179" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a5bf42b8d227d4abf38a1ddb08602e229108a517cd4e5bb28f9c7eaafdce5c0" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-hex" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca37e3e4d1b39afd7ff11ee4e947efae85adfddf4841787bfa47c470e96dc26d" -dependencies = [ - "array-init", - "serde", - "smallvec 0.6.14", -] - -[[package]] -name = "serde_derive" -version = "1.0.179" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "741e124f5485c7e60c03b043f79f320bff3527f4bbf12cf3831750dc46a0ec2c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "serde_json" -version = "1.0.104" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "076066c5f1078eac5b722a31827a8832fe108bed65dfa75e233c89f8206e976c" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96426c9936fd7a0124915f9185ea1d20aa9445cc9821142f0a73bc9207a2e186" -dependencies = [ - "serde", -] - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio 0.7.14", - "mio 0.8.8", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "slog" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smallvec" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9" - -[[package]] -name = "snapchange" -version = "0.1.0" -dependencies = [ - "addr2line", - "ahash", - "anyhow", - "bitflags 1.3.2", - "clap", - "clap-verbosity-flag", - "core_affinity", - "crossterm 0.22.1", - "ctrlc", - "env_logger", - "iced-x86", - "kvm-bindings", - "kvm-ioctls", - "lazy_static", - "libc", - "log", - "memmap", - "nix 0.22.3", - "num_enum", - "rand", - "rand_core", - "serde", - "serde-hex", - "serde_json", - "thiserror", - "toml", - "tui", - "tui-logger", - "vmm-sys-util", - "x86_64", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "static_assertions" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04361975b3f5e348b2189d8dc55bc942f278b2d482a6a0365de5bdd62d351567" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "termcolor" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be55cf8942feac5c765c2c993422806843c9a9a45d4d5c407ad6dd2ea95eb9b6" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", -] - -[[package]] -name = "time" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" -dependencies = [ - "libc", - "wasi 0.10.0+wasi-snapshot-preview1", - "winapi 0.3.9", -] - -[[package]] -name = "toml" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17e963a819c331dcacd7ab957d80bc2b9a9c1e71c804826d2f283dd65306542" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8123f27e969974a3dfba720fdb560be359f57b44302d280ba72e76a74480e8a" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tui" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" -dependencies = [ - "bitflags 1.3.2", - "cassowary", - "crossterm 0.25.0", - "unicode-segmentation", - "unicode-width", -] - -[[package]] -name = "tui-logger" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625647d10e6e215d448915ac099f682d0838d5ba1a6825e06f296fcdf5cf0f1c" -dependencies = [ - "chrono", - "fxhash", - "lazy_static", - "log", - "parking_lot 0.12.1", - "slog", - "tui", -] - -[[package]] -name = "unicode-ident" -version = "1.0.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vmm-sys-util" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd64fe09d8e880e600c324e7d664760a17f56e9672b7495a86381b49e4f72f46" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "volatile" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" - -[[package]] -name = "wasi" -version = "0.10.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7706a72ab36d8cb1f80ffbf0e071533974a60d0a308d01a5d0375bf60499a342" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ef2b6d3c510e9625e5fe6f509ab07d66a760f0885d858736483c32ed7809abd" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.28", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dee495e55982a3bd48105a7b947fd2a9b4a8ae3010041b9e0faab3f9cd028f1d" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.28", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.87" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca6ad05a4870b2bf5fe995117d3728437bd27d7cd5f06f13c17443ef369775a1" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05d4b17490f70499f20b9e791dcf6a299785ce8af4d709018206dc5b4953e95f" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" - -[[package]] -name = "winnow" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8bd122eb777186e60c3fdf765a58ac76e41c582f1f535fbf3314434c6b58f3f7" -dependencies = [ - "memchr", -] - -[[package]] -name = "x86_64" -version = "0.14.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69" -dependencies = [ - "bit_field", - "bitflags 1.3.2", - "rustversion", - "volatile", -] diff --git a/examples/02_libtiff/Dockerfile b/examples/02_libtiff/Dockerfile deleted file mode 100644 index b743309..0000000 --- a/examples/02_libtiff/Dockerfile +++ /dev/null @@ -1,8 +0,0 @@ -FROM snapchange_example2:target as target - -FROM snapchange -COPY --from=target / "$SNAPSHOT_INPUT" - -ENV SNAPSHOT_ENTRYPOINT=/opt/tiff-4.0.4/build/bin/tiffinfo -ENV SNAPSHOT_ENTRYPOINT_ARGUMENTS="-D -j -c -r -s -w /opt/tiff-4.0.4/test/images/logluv-3c-16b.tiff" -ENV SNAPSHOT_EXTRACT="/opt/tiff-4.0.4/test/images" diff --git a/examples/02_libtiff/Dockerfile.target b/examples/02_libtiff/Dockerfile.target deleted file mode 100644 index b2d5c78..0000000 --- a/examples/02_libtiff/Dockerfile.target +++ /dev/null @@ -1,19 +0,0 @@ -FROM alpine:edge as base - -RUN apk add --no-cache --initramfs-diskless-boot python3 gdb curl tar build-base perf \ - clang compiler-rt llvm - -COPY 0001-snapshot.patch /opt -RUN cd /opt/ && \ - wget https://download.osgeo.org/libtiff/tiff-4.0.4.tar.gz && \ - tar -xzvf tiff-4.0.4.tar.gz && \ - rm tiff-4.0.4.tar.gz && \ - cd tiff-4.0.4 && \ - patch -p1 < ../0001-snapshot.patch && \ - CC=clang \ - CXX=clang++ \ - CFLAGS='-ggdb -fsanitize=address' \ - CXXFLAGS='-ggdb -fsanitize=address' \ - ./configure --disable-shared --prefix=$PWD/build && \ - make -j `nproc` && \ - make install diff --git a/examples/02_libtiff/Makefile b/examples/02_libtiff/Makefile new file mode 100644 index 0000000..557164a --- /dev/null +++ b/examples/02_libtiff/Makefile @@ -0,0 +1,59 @@ +DOCKER ?= docker +FUZZ_CORES ?= /2 + +DOCKER_IMAGE_NAME ?= snapchange_example2 + +all: test + +base_images: + # Build the base snapchange image used for snapshotting + cd ../../docker && make + +dockers/target_image: dockers/Dockerfile.target + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):target . -f $< > $@ || { rm $@; exit 1; } + +dockers/snapshot_image: dockers/Dockerfile.snapshot dockers/target_image + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):snapshot . -f $< > $@ || { rm $@; exit 1; } + +snapshot: dockers/snapshot_image 0001-snapshot.patch + $(DOCKER) run --rm -i \ + -v $(shell realpath -m ./snapshot):/snapshot \ + -e SNAPSHOT_IMGTYPE=initramfs \ + $$(cat dockers/snapshot_image) >/dev/null 2>&1 + +snapshot/input: snapshot + # add seed inputs + mkdir -p snapshot/input + find snapshot/image/opt/tiff-4.0.4/test/images/*tiff -size -40k -exec cp {} snapshot/input/ \; + +seeds: snapshot/input + +fuzzer: + cargo build -r >/dev/null 2>&1 + +fuzz: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) + +fuzz-%: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) --stop-after-time $(shell echo $@ | sed 's/fuzz-//g')m +# .PHONY: fuzz-1 fuzz-2 fuzz-3 fuzz-4 fuzz-5 + + +test: snapshot fuzzer reset + ./test.sh + +reset: snapshot + cd snapshot && ./reset.sh + +clean: clean-docker + -$(RM) -rf snapshot target + +clean-docker: + -$(DOCKER) rmi `cat ./dockers/snapshot_image` + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):snapshot + -rm ./dockers/snapshot_image + -$(DOCKER) rmi `cat ./dockers/target_image` + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):target + -rm ./dockers/target_image + +.PHONY: fuzzer all base_images test reset fuzz diff --git a/examples/02_libtiff/test.sh b/examples/02_libtiff/test.sh index 0f3f898..21524ec 100755 --- a/examples/02_libtiff/test.sh +++ b/examples/02_libtiff/test.sh @@ -1,47 +1,19 @@ #!/bin/bash -example2() { - echo "Testing Example 02" +source ../test.include.sh - # Reset the snapshot - pushd snapshot > /dev/null - ./reset.sh - popd > /dev/null +setup_build - # Rebuild the fuzzer - echo "Building Example 02" - cargo build -r 2>/dev/null >/dev/null +start_fuzzing - # Start the fuzzers - echo "Begin fuzzing!" - cargo run -r -- fuzz -c 64 --ascii-stats --stop-after-first-crash --stop-after-time 2m 2>/dev/null >/dev/null & +# Kill the example 02 fuzzers +ps -ef | rg Example02 | tr -s ' ' | cut -d' ' -f2 | xargs kill -9 2>/dev/null >/dev/null - # If we find a crash early, kill the fuzzer - PID=$! - for i in $(seq 0 60); do - ls snapshot/crashes/ASAN_READ* >/dev/null 2>/dev/null - STATUS=$? - if [ "$STATUS" -eq 0 ]; then - ps -ef | rg Example02 | tr -s ' ' | cut -d' ' -f2 | xargs kill -9 2>/dev/null >/dev/null - break - fi - sleep 1 - done - - # Kill the example 02 fuzzers - ps -ef | rg Example02 | tr -s ' ' | cut -d' ' -f2 | xargs kill -9 2>/dev/null >/dev/null - - # Check if the fuzzer found a crash - ls snapshot/crashes/ASAN_READ* >/dev/null - STATUS=$? - if [ "$STATUS" -gt 0 ]; then - echo "Example 2 did not find crash" - ls snapchange-example-02/snapshot/crashes >/dev/null - popd > /dev/null - exit 1 - else - echo -e "\e[32mExample 02 SUCCESS!\e[0m" - fi -} - -example2 +# Check if the fuzzer found a crash +ls snapshot/crashes/ASAN_READ* >/dev/null +STATUS=$? +if [ "$STATUS" -gt 0 ]; then + err "fuzzer did not find crash" +else + log_success "fuzzing" +fi diff --git a/examples/03_ffmpeg_custom_mutator/Makefile b/examples/03_ffmpeg_custom_mutator/Makefile new file mode 100644 index 0000000..e751446 --- /dev/null +++ b/examples/03_ffmpeg_custom_mutator/Makefile @@ -0,0 +1,52 @@ +DOCKER ?= docker +FUZZ_CORES ?= /2 + +DOCKER_IMAGE_NAME ?= snapchange_example3 + +all: test + +base_images: + # Build the base snapchange image used for snapshotting + cd ../../docker && make + +dockers/target_image: dockers/Dockerfile.target + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):target . -f $< > $@ || { rm $@; exit 1; } + +dockers/snapshot_image: dockers/Dockerfile.snapshot dockers/target_image + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):snapshot . -f $< > $@ || { rm $@; exit 1; } + +snapshot: dockers/snapshot_image 0001-snapshot.patch + $(DOCKER) run --rm -i \ + -v $(shell realpath -m ./snapshot):/snapshot \ + -e SNAPSHOT_IMGTYPE=initramfs \ + $$(cat dockers/snapshot_image) >/dev/null 2>&1 + +fuzzer: + cargo build -r >/dev/null 2>&1 + +fuzz: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) + +fuzz-%: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) --stop-after-time $(shell echo $@ | sed 's/fuzz-//g')m +# .PHONY: fuzz-1 fuzz-2 fuzz-3 fuzz-4 fuzz-5 + + +test: snapshot fuzzer reset + ./test.sh + +reset: snapshot + cd snapshot && ./reset.sh + +clean: clean-docker + -$(RM) -rf snapshot target + +clean-docker: + -$(DOCKER) rmi `cat ./dockers/snapshot_image` + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):snapshot + -rm ./dockers/snapshot_image + -$(DOCKER) rmi `cat ./dockers/target_image` + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):target + -rm ./dockers/target_image + +.PHONY: fuzzer all base_images test reset fuzz diff --git a/examples/03_ffmpeg_custom_mutator/src/fuzzer.rs b/examples/03_ffmpeg_custom_mutator/src/fuzzer.rs index 284cb6d..3be8018 100644 --- a/examples/03_ffmpeg_custom_mutator/src/fuzzer.rs +++ b/examples/03_ffmpeg_custom_mutator/src/fuzzer.rs @@ -8,12 +8,8 @@ use anyhow::Result; use rand::Rng as _; use std::sync::Arc; -use snapchange::fuzz_input::InputWithMetadata; -use snapchange::fuzzer::{AddressLookup, Breakpoint, BreakpointType, Fuzzer}; -use snapchange::fuzzvm::FuzzVm; +use snapchange::prelude::*; use snapchange::linux::{read_args, ReadArgs}; -use snapchange::rng::Rng; -use snapchange::Execution; #[derive(Default)] pub struct Example03Fuzzer { @@ -528,6 +524,8 @@ pub struct MovGenerator { } impl snapchange::FuzzInput for MovGenerator { + type MinState = snapchange::fuzz_input::BytesMinimizeState; + fn from_bytes(bytes: &[u8]) -> Result { Ok(MovGenerator { bytes: bytes.to_vec(), @@ -540,9 +538,19 @@ impl snapchange::FuzzInput for MovGenerator { Ok(()) } + fn init_minimize(&mut self) -> (Self::MinState, MinimizeControlFlow) { + self.bytes.init_minimize() + } + /// Minimize the given `input` based on a minimization strategy - fn minimize(input: &mut Self, rng: &mut Rng) { - as snapchange::FuzzInput>::minimize(&mut input.bytes, rng) + fn minimize( + &mut self, + state: &mut Self::MinState, + current_iteration: u32, + last_successful_iteration: u32, + rng: &mut Rng, + ) -> MinimizeControlFlow { + self.bytes.minimize(state, current_iteration, last_successful_iteration, rng) } fn generate( diff --git a/examples/03_ffmpeg_custom_mutator/test.sh b/examples/03_ffmpeg_custom_mutator/test.sh index 47f4bff..e67e2aa 100755 --- a/examples/03_ffmpeg_custom_mutator/test.sh +++ b/examples/03_ffmpeg_custom_mutator/test.sh @@ -1,19 +1,18 @@ #!/bin/bash -echo "Testing Example 03" +source ../test.include.sh -# Reset the snapshot -pushd snapshot > /dev/null -./reset.sh -popd > /dev/null +setup_build -# Rebuild the fuzzer -echo "Building Example 03" -cargo build -r 2>/dev/null >/dev/null - -# Start the fuzzers -echo "Begin fuzzing!" -cargo run -r -- fuzz -c 16 --ascii-stats --stop-after-time 10m --stop-after-first-crash --timeout 1m 2>/dev/null >/dev/null +log_info "start fuzzing" +cargo run -r -- \ + fuzz \ + --ascii-stats \ + --stop-after-first-crash \ + -c "$FUZZ_CORES" \ + --stop-after-time "$FUZZ_TIMEOUT" \ + --timeout 1m \ + >/dev/null 2>&1 # Kill the example 03 fuzzers ps -ef | rg Example03 | tr -s ' ' | cut -d' ' -f2 | xargs kill -9 2>/dev/null >/dev/null @@ -22,8 +21,7 @@ ps -ef | rg Example03 | tr -s ' ' | cut -d' ' -f2 | xargs kill -9 2>/dev/null >/ ls snapshot/crashes/ASAN_WRITE* >/dev/null STATUS=$? if [ "$STATUS" -gt 0 ]; then - echo "Example 3 did not find crash" - exit 1 -else - echo -e "\e[32mExample 03 SUCCESS!\e[0m" + err "did not find crash" fi + +log_success "fuzzing" diff --git a/examples/04_syscall_fuzzer/Makefile b/examples/04_syscall_fuzzer/Makefile new file mode 100644 index 0000000..735766a --- /dev/null +++ b/examples/04_syscall_fuzzer/Makefile @@ -0,0 +1,63 @@ +DOCKER ?= docker +FUZZ_CORES ?= /2 + +DOCKER_IMAGE_NAME ?= snapchange_example4 + +all: test + +base_images: + $(MAKE) -C ../../docker/ + +base_images_old: + # Build the base snapchange image used for snapshotting + cd ../../docker && \ + $(DOCKER) build \ + -t snapchange_snapshot_linux_v5.4 \ + --build-arg LINUX_VERSION="v5.4" \ + --build-arg UBUNTU_VERSION="20.04" \ + . \ + -f Dockerfile.snapshot + +dockers/target_image: dockers/Dockerfile.target + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):target . -f $< > $@ || { rm $@; exit 1; } + +dockers/snapshot_image: dockers/Dockerfile.snapshot dockers/target_image + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):snapshot . -f $< > $@ || { rm $@; exit 1; } + +snapshot: dockers/snapshot_image syscall_harness/Cargo.toml syscall_harness/src/main.rs + # base_images is PHONY and we don't want that as dependency + $(MAKE) base_images + $(DOCKER) run --rm -i \ + -v $(shell realpath -m ./snapshot):/snapshot \ + -e SNAPSHOT_IMGTYPE=initramfs \ + $$(cat dockers/snapshot_image) >/dev/null 2>&1 + +fuzzer: + cargo build -r >/dev/null 2>&1 + +fuzz: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) + +fuzz-%: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) --stop-after-time $(shell echo $@ | sed 's/fuzz-//g')m +# .PHONY: fuzz-1 fuzz-2 fuzz-3 fuzz-4 fuzz-5 + + +test: snapshot fuzzer reset + ./test.sh + +reset: snapshot + cd snapshot && ./reset.sh + +clean: clean-docker + -$(RM) -rf snapshot target + +clean-docker: + -$(DOCKER) rmi `cat ./dockers/snapshot_image` + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):snapshot + -rm ./dockers/snapshot_image + -$(DOCKER) rmi `cat ./dockers/target_image` + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):target + -rm ./dockers/target_image + +.PHONY: fuzzer all base_images test reset fuzz diff --git a/examples/04_syscall_fuzzer/dockers/Dockerfile.snapshot b/examples/04_syscall_fuzzer/dockers/Dockerfile.snapshot index 3a1b20a..95d9f7b 100644 --- a/examples/04_syscall_fuzzer/dockers/Dockerfile.snapshot +++ b/examples/04_syscall_fuzzer/dockers/Dockerfile.snapshot @@ -1,6 +1,39 @@ +# import target FROM snapchange_example4:target as target -FROM snapchange_snapshot_linux_v5.4 +# build the vulnerable kernel +FROM snapchange_snapshot as sc_base +FROM ubuntu:20.04 as kernel_builder +ARG DEBIAN_FRONTEND=noninteractive +ARG TZ="America/Chicago" +RUN apt-get update -q \ + && apt-get install -y --no-install-recommends -q \ + build-essential \ + meson ninja-build cmake \ + git wget curl \ + flex bison sudo \ + libncurses-dev gawk flex bison openssl libssl-dev dkms libelf-dev libudev-dev libpci-dev libiberty-dev autoconf llvm \ + bc bash \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + # && sudo apt-get build-dep linux linux-image-generic \ + +RUN mkdir -p /snapchange/install/ +COPY --from=sc_base /snapchange/install/linux.sh /snapchange/install/linux.sh +RUN chmod +x /snapchange/install/linux.sh + +ENV LINUX_VERSION="v5.4" +RUN git config --global http.sslverify false +RUN /snapchange/install/linux.sh + +# create the snapshot +FROM snapchange_snapshot + +COPY --from=kernel_builder /snapchange/vmlinux /snapchange/ +COPY --from=kernel_builder /snapchange/linux.bzImage /snapchange/ +COPY --from=kernel_builder /snapchange/vmlinux.kasan /snapchange/ +COPY --from=kernel_builder /snapchange/linux.kasan.bzImage /snapchange/ + COPY --from=target / "$SNAPSHOT_INPUT" ENV SNAPSHOT_ENTRYPOINT=/opt/syscall_harness/target/release/syscall_harness diff --git a/examples/04_syscall_fuzzer/src/fuzzer.rs b/examples/04_syscall_fuzzer/src/fuzzer.rs index a3dd9e3..9e62307 100644 --- a/examples/04_syscall_fuzzer/src/fuzzer.rs +++ b/examples/04_syscall_fuzzer/src/fuzzer.rs @@ -5,17 +5,12 @@ #![allow(clippy::cast_lossless)] use anyhow::Result; -use rand::Rng as _; use serde::{Deserialize, Serialize}; use std::path::Path; use std::sync::Arc; use thiserror::Error; -use snapchange::addrs::{Cr3, VirtAddr}; -use snapchange::fuzzer::{AddressLookup, Fuzzer}; -use snapchange::fuzzvm::FuzzVm; -use snapchange::rng::Rng; -use snapchange::InputWithMetadata; +use snapchange::prelude::*; use crate::constants; @@ -498,31 +493,44 @@ impl snapchange::FuzzInput for Syscalls { InputWithMetadata::from_input(Syscalls { data: res }) } + // this is requrired to conform to snapchange's API + type MinState = NullMinimizerState; + /// dummy init minimize + fn init_minimize(&mut self) -> (Self::MinState, MinimizeControlFlow) { + NullMinimizerState::init() + } + /// Minimize the given `input` based on a minimization strategy - fn minimize(input: &mut Self, rng: &mut Rng) { + fn minimize( + &mut self, + _state: &mut Self::MinState, + _current_iteration: u32, + _last_successful_iteration: u32, + rng: &mut Rng, + ) -> MinimizeControlFlow { match rng.next() % 5 { 0 => { // Remove a random syscall - let num_syscalls = input.data.len(); + let num_syscalls = self.data.len(); let index = rng.next() as usize % num_syscalls; // Don't remove the first FsOpen syscall if index == 0 { - return; + return MinimizeControlFlow::Skip; } - input.data.remove(index); + self.data.remove(index); } 1 => { // Minimize a key of a random syscall - let num_syscalls = input.data.len(); - let curr_syscall = &mut input.data[rng.next() as usize % num_syscalls]; + let num_syscalls = self.data.len(); + let curr_syscall = &mut self.data[rng.next() as usize % num_syscalls]; match curr_syscall { Syscall::FsConfig { key, .. } => { let key_len = key.len(); if key_len == 0 { - return; + return MinimizeControlFlow::Skip; } let a = rng.gen::() % key_len; @@ -538,14 +546,14 @@ impl snapchange::FuzzInput for Syscalls { } 2 => { // Minimize a key of a random syscall - let num_syscalls = input.data.len(); - let curr_syscall = &mut input.data[rng.next() as usize % num_syscalls]; + let num_syscalls = self.data.len(); + let curr_syscall = &mut self.data[rng.next() as usize % num_syscalls]; match curr_syscall { Syscall::FsConfig { val, .. } => { let val_len = val.len(); if val_len == 0 { - return; + return MinimizeControlFlow::Skip; } let a = rng.gen::() % val_len; let b = rng.gen::() % val_len; @@ -560,14 +568,14 @@ impl snapchange::FuzzInput for Syscalls { } 3 => { // Replace the bytes of a val with 0xcd - let num_syscalls = input.data.len(); - let curr_syscall = &mut input.data[rng.next() as usize % num_syscalls]; + let num_syscalls = self.data.len(); + let curr_syscall = &mut self.data[rng.next() as usize % num_syscalls]; match curr_syscall { Syscall::FsConfig { key, .. } => { let key_len = key.len(); if key_len == 0 { - return; + return MinimizeControlFlow::Skip; } key.iter_mut().for_each(|x| *x = 0xcd); @@ -579,14 +587,14 @@ impl snapchange::FuzzInput for Syscalls { } 4 => { // Replace the bytes of a val with 0xcd - let num_syscalls = input.data.len(); - let curr_syscall = &mut input.data[rng.next() as usize % num_syscalls]; + let num_syscalls = self.data.len(); + let curr_syscall = &mut self.data[rng.next() as usize % num_syscalls]; match curr_syscall { Syscall::FsConfig { val, .. } => { let val_len = val.len(); if val_len == 0 { - return; + return MinimizeControlFlow::Skip; } val.iter_mut().for_each(|x| *x = 0xcd); @@ -598,14 +606,14 @@ impl snapchange::FuzzInput for Syscalls { } 5 => { // replace the bytes of a val with 0xcd - let num_syscalls = input.data.len(); - let curr_syscall = &mut input.data[rng.next() as usize % num_syscalls]; + let num_syscalls = self.data.len(); + let curr_syscall = &mut self.data[rng.next() as usize % num_syscalls]; match curr_syscall { Syscall::FsConfig { key, .. } => { let key_len = key.len(); if key_len == 0 { - return; + return MinimizeControlFlow::Skip; } key.iter_mut().for_each(|x| *x = 0xcd); @@ -617,5 +625,6 @@ impl snapchange::FuzzInput for Syscalls { } _ => unreachable!(), } + MinimizeControlFlow::Continue } } diff --git a/examples/04_syscall_fuzzer/test.sh b/examples/04_syscall_fuzzer/test.sh index 7230e17..aa980b1 100755 --- a/examples/04_syscall_fuzzer/test.sh +++ b/examples/04_syscall_fuzzer/test.sh @@ -1,47 +1,19 @@ -#!/bin/bash +#!/usr/bin/env bash -example4() { - echo "Testing Example 04" +source ../test.include.sh - # Reset the snapshot - pushd snapshot > /dev/null - ./reset.sh - popd > /dev/null +setup_build - # Rebuild the fuzzer - echo "Building Example 04" - cargo build -r 2>/dev/null >/dev/null +start_fuzzing - # Start the fuzzers - echo "Begin fuzzing!" - cargo run -r -- fuzz -c 8 --ascii-stats --stop-after-first-crash --stop-after-time 1m 2>/dev/null >/dev/null & +# Kill the example 04 fuzzers +ps -ef | rg Example04 | tr -s ' ' | cut -d' ' -f2 | xargs kill -9 2>/dev/null >/dev/null - # If we find a crash early, kill the fuzzer - PID=$! - for i in $(seq 0 60); do - ls snapshot/crashes/KASAN_WRITE* >/dev/null 2>/dev/null - STATUS=$? - if [ "$STATUS" -eq 0 ]; then - kill -9 $PID 2>/dev/null >/dev/null - break - fi - sleep 1 - done +# Check if the fuzzer found a crash +ls snapshot/crashes/KASAN_WRITE* >/dev/null +STATUS=$? +if [ "$STATUS" -gt 0 ]; then + err "did not find crash" +fi - # Kill the example 04 fuzzers - ps -ef | rg Example04 | tr -s ' ' | cut -d' ' -f2 | xargs kill -9 2>/dev/null >/dev/null - - # Check if the fuzzer found a crash - ls snapshot/crashes/KASAN_WRITE* >/dev/null - STATUS=$? - if [ "$STATUS" -gt 0 ]; then - echo "Example 4 did not find crash" - ls snapchange-example-04/snapshot/crashes >/dev/null - popd > /dev/null - exit 1 - else - echo -e "\e[32mExample 04 SUCCESS!\e[0m" - fi -} - -example4 +log_success "fuzzing" diff --git a/examples/05_redqueen/Dockerfile.snapshot b/examples/05_redqueen/Dockerfile.snapshot index 545850d..53e3aae 100644 --- a/examples/05_redqueen/Dockerfile.snapshot +++ b/examples/05_redqueen/Dockerfile.snapshot @@ -14,3 +14,4 @@ COPY --from=target / "$SNAPSHOT_INPUT" ENV SNAPSHOT_ENTRYPOINT=/opt/test_cmpsolves ENV SNAPSHOT_ENTRYPOINT_ARGUMENTS=/opt/input ENV GENERATE_COVERAGE_BREAKPOINTS=0 +ENV SNAPSHOT_IMGTYPE=initramfs diff --git a/examples/05_redqueen/Makefile b/examples/05_redqueen/Makefile new file mode 100644 index 0000000..00cf358 --- /dev/null +++ b/examples/05_redqueen/Makefile @@ -0,0 +1,48 @@ +DOCKER ?= docker +FUZZ_CORES ?= /2 + +DOCKER_IMAGE_NAME ?= snapchange_example5 + +all: base_images test + +base_images: + # Build the base snapchange image used for snapshotting + $(MAKE) -C ../../docker + +snapshot_image: Dockerfile.snapshot harness/test_cmpsolves harness/test_cmpsolves.cmps harness/test_cmpsolves.covbps + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):snapshot . -f $< > $@ + +snapshot: snapshot_image + $(DOCKER) run --rm -i \ + -v $(shell realpath -m ./snapshot):/snapshot \ + $(shell cat $<) + cp harness/*cmps ./snapshot/ + cp harness/*covbps ./snapshot/ + cp harness/*analysis ./snapshot/ + cp harness/config.toml ./snapshot/config.toml + +fuzzer: + cargo build -r + +fuzz: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) + +fuzz-%: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) --stop-after-time $(shell echo $@ | sed 's/fuzz-//g')m +# .PHONY: fuzz-1 fuzz-2 fuzz-3 fuzz-4 fuzz-5 + +test: snapshot fuzzer reset + ./test.sh + +reset: snapshot + cd snapshot && ./reset.sh + +clean: clean-docker + -$(RM) -rf snapshot + +clean-docker: + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):snapshot + -$(DOCKER) rmi `cat ./snapshot_image` + -$(RM) snapshot_image + +.PHONY: fuzzer all base_images test reset fuzz diff --git a/examples/05_redqueen/test.sh b/examples/05_redqueen/test.sh index 301fac9..ee6f08c 100755 --- a/examples/05_redqueen/test.sh +++ b/examples/05_redqueen/test.sh @@ -1,34 +1,25 @@ -#!/bin/bash +#!/usr/bin/env bash -example5() { - echo "Testing Example 05" +source ../test.include.sh - # Reset the snapshot - pushd snapshot > /dev/null - ./reset.sh - popd > /dev/null +setup_build - # Rebuild the fuzzer - echo "Building Example 05" - cargo build -r 2>/dev/null >/dev/null +# Start the fuzzers +log_info "Begin fuzzing! we have $(nproc) cores; using half for fuzzing; using at most $(grep -Eo 'cores = ([0-9]*)' ./harness/config.toml) for redqueen" +cargo run -r -- fuzz -c "$FUZZ_CORES" \ + --ascii-stats \ + --stop-after-first-crash \ + --stop-after-time "$FUZZ_TIMEOUT" \ + >/dev/null 2>&1 || err "fuzzing abnormal exit" - # Start the fuzzers - echo "Begin fuzzing! we have $(nproc) cores; using half for fuzzing; using at most $(grep -Eo 'cores = ([0-9]*)' ./harness/config.toml) for redqueen" - cargo run -r -- fuzz -c /2 --ascii-stats --stop-after-first-crash --stop-after-time 6m - - # Kill the example 05 fuzzers - ps -ef | rg Example05 | tr -s ' ' | cut -d' ' -f2 | xargs kill -9 2>/dev/null >/dev/null +# Kill the example 05 fuzzers +ps -ef | rg Example05 | tr -s ' ' | cut -d' ' -f2 | xargs kill -9 2>/dev/null >/dev/null # Check if the fuzzer found a crash - ls snapshot/crashes/SIGSEGV* >/dev/null - STATUS=$? - if [ "$STATUS" -gt 0 ]; then - echo "Example 5 did not find crash" - exit 1 - else - echo -e "\e[32mExample 05 SUCCESS!\e[0m" - exit 0 - fi -} - -example5 +ls snapshot/crashes/SIGSEGV* >/dev/null +STATUS=$? +if [ "$STATUS" -gt 0 ]; then + err "did not find crash" +else + log_success "fuzz" +fi diff --git a/examples/06_custom_feedback/.dockerignore b/examples/06_custom_feedback/.dockerignore deleted file mode 100644 index 0b4b65d..0000000 --- a/examples/06_custom_feedback/.dockerignore +++ /dev/null @@ -1,10 +0,0 @@ -target -snapshot -**/*.rs.bk -# remove if not needed -Cargo.lock -perf.data* -harness/maze.small -harness/maze.big -harness/maze.big.nobt -harness/maze.small.nobt diff --git a/examples/06_custom_feedback/.dockerignore b/examples/06_custom_feedback/.dockerignore new file mode 120000 index 0000000..3e4e48b --- /dev/null +++ b/examples/06_custom_feedback/.dockerignore @@ -0,0 +1 @@ +.gitignore \ No newline at end of file diff --git a/examples/06_custom_feedback/.gitignore b/examples/06_custom_feedback/.gitignore index 33edc18..173eb62 100644 --- a/examples/06_custom_feedback/.gitignore +++ b/examples/06_custom_feedback/.gitignore @@ -1,39 +1,21 @@ -# Created by https://www.toptal.com/developers/gitignore/api/rust -# Edit at https://www.toptal.com/developers/gitignore?templates=rust - +# snapshot-specific snapshot/ +snapshot_image src/constants.rs -### Rust ### -# Generated by Cargo -# will have compiled files and executables +# rust build artifacts debug/ target/ - -# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries -# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock - -# These are backup files generated by rustfmt **/*.rs.bk - -# MSVC Windows builds of rustc generate these, which store debugging information *.pdb -# End of https://www.toptal.com/developers/gitignore/api/rust -target -snapshot -**/*.rs.bk -# remove if not needed -Cargo.lock -perf.data* -maze-harness/maze.small -maze-harness/maze.big -maze-harness/maze.big.nobt -maze-harness/maze.small.nobt +# built harness binaries +harness/maze +# other misc data +perf.data* flamegraph.svg strace.log fuzz*.log - *.log diff --git a/examples/06_custom_feedback/Dockerfile b/examples/06_custom_feedback/Dockerfile index c5efebd..c726242 100644 --- a/examples/06_custom_feedback/Dockerfile +++ b/examples/06_custom_feedback/Dockerfile @@ -20,6 +20,6 @@ FROM ghcr.io/awslabs/snapchange COPY --from=base / "$SNAPSHOT_INPUT" # set the path to the target within the root filesystem of the build environment -# ENV SNAPSHOT_ENTRYPOINT=/opt/ +ENV SNAPSHOT_ENTRYPOINT="/opt/maze" # optionally: set the working directory of the target ENV SNAPSHOT_ENTRYPOINT_CWD="/opt/" diff --git a/examples/06_custom_feedback/Makefile b/examples/06_custom_feedback/Makefile new file mode 100644 index 0000000..5c2f54e --- /dev/null +++ b/examples/06_custom_feedback/Makefile @@ -0,0 +1,48 @@ +DOCKER ?= docker +FUZZ_CORES ?= /2 + +DOCKER_IMAGE_NAME ?= snapchange_example6 + +all: base_images test + +base_images: + # Build the base snapchange image used for snapshotting + $(MAKE) -C ../../docker + +snapshot_image: Dockerfile harness/maze.c harness/Makefile + $(DOCKER) build -q -t $(DOCKER_IMAGE_NAME):snapshot . -f $< > $@ + +snapshot: snapshot_image + $(DOCKER) run --rm -i \ + -v $(shell realpath -m ./snapshot):/snapshot \ + -e SNAPSHOT_IMGTYPE=initramfs \ + $(shell cat $<) + +fuzzer: + cargo build -r + +fuzz: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) + +fuzz-%: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) --stop-after-time $(shell echo $@ | sed 's/fuzz-//g')m +# .PHONY: fuzz-1 fuzz-2 fuzz-3 fuzz-4 fuzz-5 + +test: snapshot fuzzer reset + ./test.sh + +bench: snapshot fuzzer reset + ./bench.sh + +reset: snapshot + cd snapshot && ./reset.sh + +clean: clean-docker + -$(RM) -rf snapshot target + +clean-docker: + -$(DOCKER) rmi `cat ./snapshot_image` + -$(DOCKER) rmi $(DOCKER_IMAGE_NAME):snapshot + -$(RM) snapshot_image + +.PHONY: fuzzer all base_images test reset fuzz diff --git a/examples/06_custom_feedback/bench.sh b/examples/06_custom_feedback/bench.sh index 8293a1d..36a86b5 100755 --- a/examples/06_custom_feedback/bench.sh +++ b/examples/06_custom_feedback/bench.sh @@ -3,38 +3,51 @@ set -eu CORES_SETTINGS="/2 1 16" -VARIANTS="maze.small maze.small.nobt maze.big maze.big.nobt" TRIES=3 rm fuzz.bench.log || true touch fuzz.bench.log + +cargo build --release -for bin in $VARIANTS; do - # build the fuzzer variant - export MAZE_TARGET="$bin" - cargo build --release - for CORES in $CORES_SETTINGS; do - for i in $(seq "$TRIES"); do - logf="fuzz.$bin.c$CORES.$i.log" - if [[ "$CORES" == "/2" ]]; then - logf="fuzz.$bin.c_half.$i.log" - fi - pushd "./snapshot/$MAZE_TARGET"; ./reset.sh; popd >/dev/null - \time -v ./target/release/maze_fuzzer -p "./snapshot/$MAZE_TARGET" fuzz \ - -c "$CORES" --ascii-stats -v \ - --stop-after-first-crash \ - --stop-after-time 30m \ - 2>&1 | tee "$logf" - statsf="./snapshot/$MAZE_TARGET/data/stats.toml" - cat "$statsf" >> "$logf" - - success="SUCCESS" - if grep "crashes = 0" "$statsf" >/dev/null; then - success="FAILURE - NO CRASHES" - fi - - cat fuzz.bench.log - echo "$bin [$i] => $(grep 'wall clock' "$logf") [$(grep 'exec.*sec' "$logf" | tail -n 1) on $CORES cores] $success" | tee -a fuzz.bench.log +for maze_size in small big; do + for nobt in 1 0; do + for CORES in $CORES_SETTINGS; do + for i in $(seq "$TRIES"); do + bin="maze.$maze_size" + if [[ "$nobt" -eq 1 ]]; then + bin="$bin.nobt" + fi + export MAZE_NO_BT="$nobt" + if [[ "$maze_size" == "small" ]]; then + export USE_MAZE=0 + elif [[ "$maze_size" == "big" ]]; then + export USE_MAZE=1 + fi + export CHECK_CODE=0 + + logf="fuzz.$bin.c$CORES.$i.log" + if [[ "$CORES" == "/2" ]]; then + logf="fuzz.$bin.c_half.$i.log" + fi + pushd ./snapshot; ./reset.sh; popd >/dev/null + \time -v ./target/release/maze_fuzzer fuzz \ + -c "$CORES" --ascii-stats -v \ + --stop-after-first-crash \ + --stop-after-time 30m \ + 2>&1 | tee "$logf" + + statsf="./snapshot/data/stats.toml" + cat "$statsf" >> "$logf" + + success="SUCCESS" + if grep "crashes = 0" "$statsf" >/dev/null; then + success="FAILURE - NO CRASHES" + fi + + cat fuzz.bench.log + echo "$bin [$i] => $(grep 'wall clock' "$logf") [$(grep 'exec.*sec' "$logf" | tail -n 1) on $CORES cores] $success" | tee -a fuzz.bench.log + done done done done diff --git a/examples/06_custom_feedback/build.rs b/examples/06_custom_feedback/build.rs index 8a6e02e..1035246 100644 --- a/examples/06_custom_feedback/build.rs +++ b/examples/06_custom_feedback/build.rs @@ -4,13 +4,10 @@ use std::fs::File; use std::io::Write; fn main() { - println!("cargo:rerun-if-env-changed=MAZE_TARGET"); - let target = std::env::var("MAZE_TARGET").unwrap_or("maze.small.nobt".to_string()); + println!("cargo:rerun-if-changed=snapshot/fuzzvm.qemuregs"); + println!("cargo:rerun-if-changed=snapshot/vm.log"); - println!("cargo:rerun-if-changed=snapshot/*/fuzzvm.qemuregs"); - println!("cargo:rerun-if-changed=snapshot/*/vm.log"); - - let qemuregs = fs::read_to_string(format!("./snapshot/{target}/fuzzvm.qemuregs")).unwrap(); + let qemuregs = fs::read_to_string("./snapshot/fuzzvm.qemuregs").unwrap(); let mut w = File::create("src/constants.rs").unwrap(); writeln!(w, "#![allow(unused)]").unwrap(); @@ -25,41 +22,9 @@ fn main() { let rip = &captures.get(1).unwrap().as_str(); writeln!(w, "pub const RIP: u64 = 0x{};", rip).unwrap(); - let vmlog = fs::read_to_string(format!("./snapshot/{target}/vm.log")).unwrap(); + let vmlog = fs::read_to_string("./snapshot/vm.log").unwrap(); let re = Regex::new(r"SNAPSHOT Data buffer: (0x[0-9A-Fa-f]+)").unwrap(); let captures = re.captures(&vmlog).unwrap(); let input_addr = &captures.get(1).unwrap().as_str(); writeln!(w, "pub const INPUT: u64 = {};", input_addr).unwrap(); - - writeln!(w, "pub const TARGET: &'static str = {:?};", target).unwrap(); - writeln!( - w, - "pub const TARGET_LOG_POS: &'static str = {:?};", - format!("{target}!log_pos") - ) - .unwrap(); - writeln!( - w, - "pub const TARGET_LOSE: &'static str = {:?};", - format!("{target}!lose") - ) - .unwrap(); - writeln!( - w, - "pub const TARGET_WIN: &'static str = {:?};", - format!("{target}!win") - ) - .unwrap(); - writeln!( - w, - "pub const TARGET_BYE: &'static str = {:?};", - format!("{target}!bye") - ) - .unwrap(); - writeln!( - w, - "pub const TARGET_EQ16: &'static str = {:?};", - format!("{target}!eq16") - ) - .unwrap(); } diff --git a/examples/06_custom_feedback/fuzz.sh b/examples/06_custom_feedback/fuzz.sh deleted file mode 100755 index 5d50806..0000000 --- a/examples/06_custom_feedback/fuzz.sh +++ /dev/null @@ -1,7 +0,0 @@ -#!/usr/bin/env bash - -export MAZE_TARGET="$1" -shift -pushd "./snapshot/$MAZE_TARGET"; ./reset.sh; popd >/dev/null -set -x -\time -v cargo run -r -- -p "./snapshot/$MAZE_TARGET" fuzz $@ diff --git a/examples/06_custom_feedback/harness/Makefile b/examples/06_custom_feedback/harness/Makefile index 6bd1fb1..6035cd4 100644 --- a/examples/06_custom_feedback/harness/Makefile +++ b/examples/06_custom_feedback/harness/Makefile @@ -1,21 +1,9 @@ ifeq ($(origin CC),default) CC = clang endif -CFLAGS ?= -O3 -ggdb -fuse-ld=lld -DCHECK_CODE +CFLAGS ?= -O3 -ggdb -fuse-ld=lld -all: maze.small maze.big maze.big.nobt maze.small.nobt - -maze.small: maze.c - $(CC) $(CFLAGS) -DMAZE_SMALL -o $@ $< - -maze.big: maze.c - $(CC) $(CFLAGS) -DMAZE_BIG -o $@ $< - -maze.small.nobt: maze.c - $(CC) $(CFLAGS) -DMAZE_SMALL -DMAZE_NO_BT -o $@ $< - -maze.big.nobt: maze.c - $(CC) $(CFLAGS) -DMAZE_BIG -DMAZE_NO_BT -o $@ $< +all: maze clean: - -$(RM) maze.small maze.big maze.big.nobt maze.small.nobt maze.small.fuzz maze.big.fuzz maze.big.bt.fuzz maze.small.bt.fuzz + -$(RM) maze diff --git a/examples/06_custom_feedback/harness/big.h b/examples/06_custom_feedback/harness/big.h deleted file mode 100644 index 159b085..0000000 --- a/examples/06_custom_feedback/harness/big.h +++ /dev/null @@ -1,18 +0,0 @@ -#define H 13 -#define W 17 - -char maze[H][W]={ -"+-+-------------+", -"| | |", -"| | +-----* *---+", -"| | |", -"+---+-* *-------+", -"| |", -"+ +-------------+", -"| | | |#|", -"| | *---+ * * * |", -"| | | | |", -"| +---* +-------+", -"| |", -"+---------------+", -}; diff --git a/examples/06_custom_feedback/harness/maze.c b/examples/06_custom_feedback/harness/maze.c index 22d8e27..68a03ff 100644 --- a/examples/06_custom_feedback/harness/maze.c +++ b/examples/06_custom_feedback/harness/maze.c @@ -9,6 +9,7 @@ #include /*****************************************************************************/ +// basic obfuscation code. __attribute__((noinline, optnone)) uint8_t eq16(const uint8_t *x, const uint8_t *y) { uint8_t r = 0; @@ -115,11 +116,49 @@ bool check_contra_code(const char *program, size_t cur, size_t size) { // adapted according to the changes from the ijon paper: // https://github.com/RUB-SysSec/ijon-data/tree/master/maze -#ifdef MAZE_BIG -#include "big.h" -#else -#include "small.h" -#endif +// maze settings - defaults to small maze with backtracking. +uint8_t MAZE_NO_BT = false; +uint8_t CHECK_CODE = true; +uint32_t USE_MAZE = 0; + +// struct to contain the maze +#define MAZE_DIM_SIZE (32) +typedef struct maze { + int h; + int w; + char maze[MAZE_DIM_SIZE][MAZE_DIM_SIZE]; +} maze_t; + +// two mazes +maze_t maze_big = {13, + 17, + { + "+-+-------------+", + "| | |", + "| | +-----* *---+", + "| | |", + "+---+-* *-------+", + "| |", + "+ +-------------+", + "| | | |#|", + "| | *---+ * * * |", + "| | | | |", + "| +---* +-------+", + "| |", + "+---------------+", + }}; + +maze_t maze_small = {7, + 11, + { + "+-+---+---+", + "| | |#|", + "| | --+ | |", + "| | | | |", + "| +-- | | |", + "| | |", + "+-----+---+", + }}; __attribute__((noinline, optnone)) void log_pos(uint16_t x, uint16_t y) { printf("pos = (%u, %u)\n", x, y); @@ -130,11 +169,11 @@ __attribute__((noinline, optnone)) void bye(int i) { exit(i); } -void draw() { +void draw(maze_t *maze) { int i, j; - for (i = 0; i < H; i++) { - for (j = 0; j < W; j++) - printf("%c", maze[i][j]); + for (i = 0; i < maze->h; i++) { + for (j = 0; j < maze->w; j++) + printf("%c", maze->maze[i][j]); printf("\n"); } printf("\n"); @@ -154,7 +193,7 @@ __attribute__((noinline, optnone)) void win(uint16_t x, uint16_t y) { log_pos(x, y); } -void walk_maze(const char *program, const size_t iters) { +void walk_maze(const char *program, const size_t iters, maze_t maze) { uint16_t x, y; // Player position uint16_t ox, oy; // Old player position size_t i = 0; // Iteration number @@ -162,15 +201,15 @@ void walk_maze(const char *program, const size_t iters) { y = 1; ox = x; oy = y; - maze[y][x] = 'X'; - draw(); + maze.maze[y][x] = 'X'; + draw(&maze); while (i < iters) { - if (x > W || y > H) { + if (x > maze.w || y > maze.h) { lose("OOB", x, y); } -#ifndef MAZE_NO_BT - maze[y][x] = ' '; -#endif + if (MAZE_NO_BT) { + maze.maze[y][x] = ' '; + } ox = x; // Save old player position oy = y; @@ -191,43 +230,43 @@ void walk_maze(const char *program, const size_t iters) { break; case 'x': case 'y': - draw(); + draw(&maze); break; case '\n': case '\0': logmsg("final state"); - draw(); + draw(&maze); lose("You give up", ox, oy); default: - lose("Wrong command!(only w,s,a,d accepted!)", ox, oy); + lose("Wrong command! (only w,s,a,d,x,y accepted!)", ox, oy); } - if (maze[y][x] == '#') { + if (maze.maze[y][x] == '#') { win(x, y); -#ifdef CHECK_CODE - if (check_contra_code(program, i, iters)) { - logmsg("oh oh.."); - assert(0); + if (CHECK_CODE) { + if (check_contra_code(program, i, iters)) { + logmsg("oh oh.."); + assert(0); + } else { + logmsg("... or did you really?"); + bye(0); + } } else { - logmsg("... or did you really?"); - bye(0); + // notify fuzzer + assert(0); } -#else - // notify fuzzer - assert(0); -#endif } - if (maze[y][x] != ' ') { + if (maze.maze[y][x] != ' ') { x = ox; y = oy; } -#ifdef MAZE_NO_BT - if (ox == x && oy == y) { - lose("No movement", ox, oy); + if (MAZE_NO_BT) { + if (ox == x && oy == y) { + lose("No movement", ox, oy); + } } -#endif - maze[y][x] = 'X'; - draw(); // draw it + maze.maze[y][x] = 'X'; + draw(&maze); // draw it i++; } @@ -244,6 +283,19 @@ char program[MAX_ITERS] = { int main(int argc, char *argv[]) { + char *e = getenv("USE_MAZE"); + if (e != NULL) { + USE_MAZE = atoi(e); + } + e = getenv("MAZE_NO_BT"); + if (e != NULL) { + MAZE_NO_BT = atoi(e); + } + e = getenv("CHECK_CODE"); + if (e != NULL) { + CHECK_CODE = atoi(e); + } + if (getenv("SNAPSHOT") != NULL) { printf("SNAPSHOT Data buffer: %p\n", program); fflush(stdout); @@ -260,6 +312,15 @@ int main(int argc, char *argv[]) { fclose(f); } } - walk_maze(program, MAX_ITERS); + // dynamically select the maze + maze_t maze; + if (USE_MAZE == 0) { + maze = maze_small; + } else if (USE_MAZE == 1) { + maze = maze_big; + } else { + maze = maze_small; + } + walk_maze(program, MAX_ITERS, maze); bye(0); } diff --git a/examples/06_custom_feedback/harness/small.h b/examples/06_custom_feedback/harness/small.h deleted file mode 100644 index 5c39662..0000000 --- a/examples/06_custom_feedback/harness/small.h +++ /dev/null @@ -1,9 +0,0 @@ -#define H 7 -#define W 11 -char maze[H][W] = { "+-+---+---+", - "| | |#|", - "| | --+ | |", - "| | | | |", - "| +-- | | |", - "| | |", - "+-----+---+" }; diff --git a/examples/06_custom_feedback/make_snapshot.sh b/examples/06_custom_feedback/make_snapshot.sh deleted file mode 100755 index 3be9b04..0000000 --- a/examples/06_custom_feedback/make_snapshot.sh +++ /dev/null @@ -1,15 +0,0 @@ -#!/bin/bash -set -ex - -NAME="snapchange-$(basename "$PWD")" -docker build -f ./Dockerfile -t "$NAME" . - -mkdir -p snapshot/ -for bin in maze.small maze.big maze.big.nobt maze.small.nobt; do - BINARY="$bin" - echo "snapshotting maze variant $BINARY" - docker run -i \ - -v $(realpath -m ./snapshot/$BINARY):/snapshot/ \ - -e "SNAPSHOT_ENTRYPOINT=/opt/$BINARY" \ - "$NAME" -done diff --git a/examples/06_custom_feedback/run.sh b/examples/06_custom_feedback/run.sh deleted file mode 100755 index 0388ce0..0000000 --- a/examples/06_custom_feedback/run.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -export MAZE_TARGET="$1" -shift -set -x -exec cargo run -r -- -p "./snapshot/$MAZE_TARGET" $@ diff --git a/examples/06_custom_feedback/src/fuzzer.rs b/examples/06_custom_feedback/src/fuzzer.rs index f1092b4..332283a 100644 --- a/examples/06_custom_feedback/src/fuzzer.rs +++ b/examples/06_custom_feedback/src/fuzzer.rs @@ -4,15 +4,8 @@ #![allow(clippy::cast_possible_truncation)] #![allow(clippy::cast_lossless)] -use anyhow::Result; -use rand::Rng as _; - -use snapchange::addrs::{Cr3, VirtAddr}; -use snapchange::fuzzer::{AddressLookup, Breakpoint, BreakpointType, Fuzzer}; -use snapchange::fuzzvm::FuzzVm; -use snapchange::rng::Rng; -use snapchange::Execution; -use snapchange::InputWithMetadata; +use snapchange::fuzz_input::MinimizerState; +use snapchange::prelude::*; use std::sync::Arc; use crate::constants; @@ -80,6 +73,21 @@ impl std::default::Default for WasdArray { } } +#[derive(Clone, Copy, Debug, Hash, Default, PartialEq, Eq)] +pub enum WasdMinState { + TruncateTo(usize), + Delete(usize), + Replace(usize, Wasd), + #[default] + End, +} + +impl MinimizerState for WasdMinState { + fn is_stop_state(&self) -> bool { + matches!(self, Self::End) + } +} + #[derive(Default)] pub struct MazeFuzzer { /// this is used for input scheduling - assigning weights to each input. The latest corpus entry @@ -119,10 +127,7 @@ impl Fuzzer for MazeFuzzer { "ld-musl-x86_64.so.1!fputs", "ld-musl-x86_64.so.1!fprintf", "ld-musl-x86_64.so.1!printf", - "maze.small!draw", - "maze.small.nobt!draw", - "maze.big!draw", - "maze.big.nobt!draw", + "maze!draw", ] { if fuzzvm .patch_bytes_permanent(AddressLookup::SymbolOffset(sym, 0), &[0xc3]) @@ -130,14 +135,42 @@ impl Fuzzer for MazeFuzzer { { log::warn!("inserting immediate ret at sym {}", sym); } else { - log::warn!(" ret at sym {}", sym); + log::warn!("fail to set ret at sym {}", sym); } } + + // configure snapshot for the right variant of the maze. + if let Ok(use_maze) = std::env::var("USE_MAZE") { + let use_maze: i32 = use_maze.parse()?; + let (addr, cr3) = AddressLookup::SymbolOffset("maze!USE_MAZE", 0).get(fuzzvm)?; + fuzzvm.write(addr, cr3, use_maze)?; + log::info!( + "using the {} maze", + if use_maze == 1 { "big" } else { "small" } + ); + } else { + log::info!("using the small maze"); + } + + if let Ok(val) = std::env::var("MAZE_NO_BT") { + let bt: u8 = val.parse()?; + let (addr, cr3) = AddressLookup::SymbolOffset("maze!MAZE_NO_BT", 0).get(fuzzvm)?; + fuzzvm.write(addr, cr3, bt)?; + log::info!("set MAZE_NO_BT to {bt}"); + } + + if let Ok(val) = std::env::var("CHECK_CODE") { + let cc: u8 = val.parse()?; + let (addr, cr3) = AddressLookup::SymbolOffset("maze!CHECK_CODE", 0).get(fuzzvm)?; + fuzzvm.write(addr, cr3, cc)?; + log::info!("set CHECK_CODE to {cc}"); + } + Ok(()) } fn reset_breakpoints(&self) -> Option<&[AddressLookup]> { - Some(&[AddressLookup::SymbolOffset(constants::TARGET_BYE, 0x0)]) + Some(&[AddressLookup::SymbolOffset("maze!bye", 0x0)]) } fn breakpoints(&self) -> Option<&[Breakpoint]> { @@ -146,7 +179,7 @@ impl Fuzzer for MazeFuzzer { // this breakpoint gathers the last (x, y) position in the maze. this is good enough // for the fuzzer to achieve it's goal of reaching the end of the maze. Breakpoint { - lookup: AddressLookup::SymbolOffset(constants::TARGET_LOSE, 0x0), + lookup: AddressLookup::SymbolOffset("maze!lose", 0x0), bp_type: BreakpointType::Repeated, bp_hook: |fuzzvm: &mut FuzzVm, _input, _fuzzer, feedback| { if let Some(feedback) = feedback { @@ -187,7 +220,7 @@ impl Fuzzer for MazeFuzzer { // we also record the winning position as custom feedback, otherwise we only see the // position in the feedback when we lose. Breakpoint { - lookup: AddressLookup::SymbolOffset(constants::TARGET_WIN, 0x0), + lookup: AddressLookup::SymbolOffset("maze!win", 0x0), bp_type: BreakpointType::Repeated, bp_hook: |fuzzvm: &mut FuzzVm, _input, _fuzzer, feedback| { if let Some(feedback) = feedback { @@ -201,11 +234,11 @@ impl Fuzzer for MazeFuzzer { }, }, // This breakpoint showcases custom feedback to minimize a string distance between - // two memory values. We do not want to reverse the obfuscation of the secret code, + // two memory values. We do not want to manually reverse the obfuscation of the secret code, // so we let the fuzzer discover the correct inputs by minimizing the distance - // between the stored + // between the stored value and the provided input. Breakpoint { - lookup: AddressLookup::SymbolOffset(constants::TARGET_EQ16, 0x0), + lookup: AddressLookup::SymbolOffset("maze!eq16", 0x0), bp_type: BreakpointType::Repeated, bp_hook: |fuzzvm: &mut FuzzVm, _input, _fuzzer, feedback| { if let Some(feedback) = feedback { @@ -479,37 +512,85 @@ impl snapchange::FuzzInput for WasdArray { mutations } - /// Minimize a `WasdArray` by truncating it or removing directions. - fn minimize(input: &mut Self, rng: &mut Rng) { - // Cannot minimize an empty input - if input.data.is_empty() { - return; - } + // this is requrired to conform to snapchange's API + type MinState = WasdMinState; + /// dummy init minimize + fn init_minimize(&mut self) -> (Self::MinState, MinimizeControlFlow) { + ( + WasdMinState::TruncateTo(self.data.len().saturating_sub(1)), + MinimizeControlFlow::ContinueFor(self.data.len().try_into().unwrap()), + ) + } - enum MinimizeAction { - Truncate, - Delete, + /// Minimize a `WasdArray` by truncating it, removing directions, or replacing directions. + fn minimize( + &mut self, + state: &mut Self::MinState, + current_iteration: u32, + last_successful_iteration: u32, + _rng: &mut Rng, + ) -> MinimizeControlFlow { + log::trace!("minimize state: {:?}", *state); + + // Cannot minimize an empty input + if self.data.is_empty() { + *state = WasdMinState::End; + return MinimizeControlFlow::Stop; } - // Randomly pick the type of minimization - let state = match rng.gen_range(0..=1) { - 0 => MinimizeAction::Truncate, - 1 => MinimizeAction::Delete, - _ => unreachable!(), - }; + use MinimizeControlFlow::*; + use WasdMinState::*; // Perform the minimization strategy for this state - match state { - MinimizeAction::Truncate => { - if input.data.len() > 1 { - input.data.truncate(input.data.len() - 1); + let (cf, next) = match *state { + TruncateTo(0) => { + self.data.clear(); + (Continue, Delete(usize::MAX)) + } + TruncateTo(new_len) => { + if current_iteration == 0 || last_successful_iteration + 1 == current_iteration { + self.data.truncate(new_len); + (Continue, TruncateTo(new_len.saturating_sub(1))) + } else { + // last truncation failed, so we skip further truncates + (Skip, Delete(usize::MAX)) } } - MinimizeAction::Delete => { - let l = input.data.len(); - let idx = rng.gen_range(0..l); - input.data.remove(idx); + Delete(0) => { + self.data.remove(0); + (Continue, Replace(usize::MAX, Wasd::W)) // next state is Replace } - } + Delete(index) => { + let index = std::cmp::min(self.data.len() - 1, index); + self.data.remove(index); + (Continue, Delete(index.saturating_sub(1))) + } + Replace(0, val) => { + let last_index = self.data.len() - 1; + *self.data.get_mut(0).unwrap() = val; + match val { + Wasd::W => (Continue, Replace(last_index, Wasd::A)), + Wasd::A => (Continue, Replace(last_index, Wasd::S)), + Wasd::S => (Continue, Replace(last_index, Wasd::D)), + Wasd::D => (Continue, Replace(last_index, Wasd::X)), + Wasd::X => (Continue, Replace(last_index, Wasd::Y)), + Wasd::Y => (Continue, End), + Wasd::Stop => (Stop, End), + } + } + Replace(index, val) => { + let index = std::cmp::min(self.data.len() - 1, index); + if let Some(p) = self.data.get_mut(index) { + *p = val; + (Continue, Replace(index - 1, val)) + } else { + (Skip, Replace(self.data.len().saturating_sub(1), val)) + } + } + End => (Stop, End), + }; + + *state = next; + cf } } diff --git a/examples/06_custom_feedback/test.sh b/examples/06_custom_feedback/test.sh index 0734179..d985a6c 100755 --- a/examples/06_custom_feedback/test.sh +++ b/examples/06_custom_feedback/test.sh @@ -1,28 +1,70 @@ -#!/bin/bash +#!/usr/bin/env bash -export MAZE_TARGET="maze.small" +source ../test.include.sh -# Reset the snapshot from a previous run -pushd "snapshot/$MAZE_TARGET" > /dev/null -./reset.sh -popd > /dev/null +export USE_MAZE=0 +export CHECK_CODE=1 +export MAZE_NO_BT=0 -# Rebuild the fuzzer -echo "Building Example 06" -cargo build -r >/dev/null 2>/dev/null || { echo "Example 6 build failure"; exit 1; } +setup_build -echo "Begin fuzzing!" -timeout 65s cargo run -r -- \ - -p "snapshot/$MAZE_TARGET/" \ - fuzz -c /2 --ascii-stats \ - --stop-after-first-crash --stop-after-time 60s >/dev/null 2>/dev/null +start_fuzzing # Check if the fuzzer found a crash -ls snapshot/$MAZE_TARGET/crashes/*assert_fail* >/dev/null +ls snapshot/crashes/*assert_fail* >/dev/null STATUS=$? if [ "$STATUS" -gt 0 ]; then - echo "Example 6 did not find crash" - exit 1 -else - echo -e "\e[32mExample 06 SUCCESS!\e[0m" + err "did not find a crash" fi + +log_success "fuzz" + +CRASH_FILE="$(find ./snapshot/crashes/*assert_fail* -type f | head -n 1)" +CORPUS_FILE="$(find ./snapshot/current_corpus/ -type f | tail -n 1)" + +if [[ -z "$CRASH_FILE" ]]; then + err "failed to identify a crash" +fi +if [[ -z "$CORPUS_FILE" ]]; then + err "failed to identify a corpus file" +fi + +pushd harness >/dev/null +make >/dev/null 2>&1 || err "failed to build harness/maze on host" +popd >/dev/null + +### Test minimize ### +# minimize only according to rip + custom feedback +MIN_FLAGS="--rip-only --consider-coverage=none" +# cargo run -r -- minimize $MIN_FLAGS "$CRASH_FILE" 2>/dev/null >/dev/null || err "running minimize subcommand on $CRASH_FILE failed" +cargo run -r -- minimize $MIN_FLAGS "$CORPUS_FILE" 2>/dev/null >/dev/null || err "running minimize subcommand on $CORPUS_FILE failed" + +for CRASH_FILE in ./snapshot/crashes/*assert_fail*/*; do + cargo run -r -- minimize $MIN_FLAGS "$CRASH_FILE" 2>/dev/null >/dev/null || err "running minimize subcommand on $CRASH_FILE failed" + if [[ -e "$CRASH_FILE.min" ]]; then + if [[ "$(sha1sum "$CRASH_FILE" | awk '{print $1}')" == "$(sha1sum "$CRASH_FILE.min")" ]]; then + err "minimized file $CRASH_FILE.min equal to original" + fi + # out_orig="$(./harness/maze "$CRASH_FILE")" + out_min="$(./harness/maze "$CRASH_FILE.min" 2>&1)" + if echo "$out_min" | grep "Assertion" >/dev/null 2>&1; then + true + else + err "minimized file $CRASH_FILE.min does not trigger crash" + fi + else + err "no minimized file found for $CRASH_FILE" + fi +done + +log_success "minimize" + + +### Test corpus-min ### +PRIOR="$(ls -l ./snapshot/current_corpus | wc -l)" +cargo run -r -- corpus-min 2>/dev/null >/dev/null || err "running corpus-min subcommand failed" +NEW="$(ls -l ./snapshot/current_corpus | wc -l)" +if [ "$NEW" -ge "$PRIOR" ]; then + err "corpus-min did not reduce corpus size" +fi +log_success "corpus-min" diff --git a/examples/07_libfuzzer/.gitignore b/examples/07_libfuzzer/.gitignore index 9a7364c..f466ef5 100644 --- a/examples/07_libfuzzer/.gitignore +++ b/examples/07_libfuzzer/.gitignore @@ -11,3 +11,4 @@ perf.data perf.*analyze *stats.toml stats*.toml +Cargo.lock diff --git a/examples/07_libfuzzer/Cargo.lock b/examples/07_libfuzzer/Cargo.lock deleted file mode 100644 index 2bba572..0000000 --- a/examples/07_libfuzzer/Cargo.lock +++ /dev/null @@ -1,1476 +0,0 @@ -# This file is automatically @generated by Cargo. -# It is not intended for manual editing. -version = 3 - -[[package]] -name = "addr2line" -version = "0.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9ecd88a8c8378ca913a680cd98f0f13ac67383d35993f86c90a70e3f137816b" -dependencies = [ - "cpp_demangle", - "fallible-iterator", - "gimli", - "object", - "rustc-demangle", - "smallvec 1.11.1", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" - -[[package]] -name = "ahash" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a" -dependencies = [ - "cfg-if", - "getrandom", - "once_cell", - "version_check", - "zerocopy", -] - -[[package]] -name = "aho-corasick" -version = "1.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -dependencies = [ - "memchr", -] - -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - -[[package]] -name = "android_system_properties" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" -dependencies = [ - "libc", -] - -[[package]] -name = "anstream" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" -dependencies = [ - "anstyle", - "anstyle-parse", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "utf8parse", -] - -[[package]] -name = "anstyle" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" - -[[package]] -name = "anstyle-parse" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" -dependencies = [ - "utf8parse", -] - -[[package]] -name = "anstyle-query" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" -dependencies = [ - "windows-sys", -] - -[[package]] -name = "anstyle-wincon" -version = "3.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" -dependencies = [ - "anstyle", - "windows-sys", -] - -[[package]] -name = "anyhow" -version = "1.0.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4668cab20f66d8d020e1fbc0ebe47217433c1b6c8f2040faf858554e394ace6" - -[[package]] -name = "array-init" -version = "0.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23589ecb866b460d3a0f1278834750268c607e8e28a1b982c907219f3178cd72" -dependencies = [ - "nodrop", -] - -[[package]] -name = "atty" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" -dependencies = [ - "hermit-abi 0.1.19", - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "autocfg" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - -[[package]] -name = "bitflags" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -[[package]] -name = "bitflags" -version = "2.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" - -[[package]] -name = "bumpalo" -version = "3.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" - -[[package]] -name = "byteorder" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" - -[[package]] -name = "cassowary" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" - -[[package]] -name = "cc" -version = "1.0.83" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" -dependencies = [ - "libc", -] - -[[package]] -name = "cfg-if" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" - -[[package]] -name = "chrono" -version = "0.4.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" -dependencies = [ - "android-tzdata", - "iana-time-zone", - "js-sys", - "num-traits", - "wasm-bindgen", - "windows-targets", -] - -[[package]] -name = "clap" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" -dependencies = [ - "clap_builder", - "clap_derive", -] - -[[package]] -name = "clap-verbosity-flag" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5fdbb015d790cfb378aca82caf9cc52a38be96a7eecdb92f31b4366a8afc019" -dependencies = [ - "clap", - "log", -] - -[[package]] -name = "clap_builder" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" -dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", -] - -[[package]] -name = "clap_derive" -version = "4.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "clap_lex" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" - -[[package]] -name = "colorchoice" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" - -[[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "core_affinity" -version = "0.5.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f8a03115cc34fb0d7c321dd154a3914b3ca082ccc5c11d91bf7117dbbe7171f" -dependencies = [ - "kernel32-sys", - "libc", - "num_cpus", - "winapi 0.2.8", -] - -[[package]] -name = "cpp_demangle" -version = "0.3.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eeaa953eaad386a53111e47172c2fedba671e5684c8dd601a5f474f4f118710f" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crc32fast" -version = "1.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossterm" -version = "0.22.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85525306c4291d1b73ce93c8acf9c339f9b213aef6c1d85c3830cbf1c16325c" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio 0.7.14", - "parking_lot 0.11.2", - "signal-hook", - "signal-hook-mio", - "winapi 0.3.9", -] - -[[package]] -name = "crossterm" -version = "0.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e64e6c0fbe2c17357405f7c758c1ef960fce08bdfb2c03d88d2a18d7e09c4b67" -dependencies = [ - "bitflags 1.3.2", - "crossterm_winapi", - "libc", - "mio 0.8.9", - "parking_lot 0.12.1", - "signal-hook", - "signal-hook-mio", - "winapi 0.3.9", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "ctrlc" -version = "3.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82e95fbd621905b854affdc67943b043a0fbb6ed7385fd5a25650d19a8a6cfdf" -dependencies = [ - "nix 0.27.1", - "windows-sys", -] - -[[package]] -name = "env_logger" -version = "0.9.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12e6657c4c97ebab115a42dcee77225f7f482cdd841cf7088c657a42e9e00e7" -dependencies = [ - "atty", - "humantime", - "log", - "regex", - "termcolor", -] - -[[package]] -name = "equivalent" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" - -[[package]] -name = "example_fuzzer" -version = "0.1.0" -dependencies = [ - "anyhow", - "log", - "rand", - "regex", - "snapchange", -] - -[[package]] -name = "extended" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9673d8203fcb076b19dfd17e38b3d4ae9f44959416ea532ce72415a6020365" - -[[package]] -name = "fallible-iterator" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4443176a9f2c162692bd3d352d745ef9413eec5782a80d8fd6f8a1ac692a07f7" - -[[package]] -name = "flate2" -version = "1.0.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fxhash" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" -dependencies = [ - "byteorder", -] - -[[package]] -name = "getrandom" -version = "0.2.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -dependencies = [ - "cfg-if", - "libc", - "wasi", -] - -[[package]] -name = "gimli" -version = "0.26.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22030e2c5a68ec659fde1e949a745124b48e6fa8b045b7ed5bd1fe4ccc5c4e5d" -dependencies = [ - "fallible-iterator", - "stable_deref_trait", -] - -[[package]] -name = "hashbrown" -version = "0.14.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f93e7192158dbcda357bdec5fb5788eebf8bbac027f3f33e719d29135ae84156" - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "hermit-abi" -version = "0.1.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" -dependencies = [ - "libc", -] - -[[package]] -name = "hermit-abi" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7" - -[[package]] -name = "humantime" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" - -[[package]] -name = "iana-time-zone" -version = "0.1.58" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] - -[[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] - -[[package]] -name = "iced-x86" -version = "1.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdd366a53278429c028367e0ba22a46cab6d565a57afb959f06e92c7a69e7828" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "indexmap" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -dependencies = [ - "equivalent", - "hashbrown", -] - -[[package]] -name = "instant" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "itoa" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" - -[[package]] -name = "js-sys" -version = "0.3.65" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" -dependencies = [ - "wasm-bindgen", -] - -[[package]] -name = "kernel32-sys" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" -dependencies = [ - "winapi 0.2.8", - "winapi-build", -] - -[[package]] -name = "kvm-bindings" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efe70e65a5b092161d17f5005b66e5eefe7a94a70c332e755036fc4af78c4e79" -dependencies = [ - "vmm-sys-util", -] - -[[package]] -name = "kvm-ioctls" -version = "0.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a321cabd827642499c77e27314f388dd83a717a5ca716b86476fb947f73ae4" -dependencies = [ - "kvm-bindings", - "libc", - "vmm-sys-util", -] - -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - -[[package]] -name = "libc" -version = "0.2.150" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" - -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - -[[package]] -name = "log" -version = "0.4.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" - -[[package]] -name = "maybe-uninit" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00" - -[[package]] -name = "memchr" -version = "2.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -[[package]] -name = "memmap" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" -dependencies = [ - "libc", - "winapi 0.3.9", -] - -[[package]] -name = "memoffset" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" -dependencies = [ - "autocfg", -] - -[[package]] -name = "miniz_oxide" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -dependencies = [ - "adler", -] - -[[package]] -name = "mio" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8067b404fe97c70829f082dec8bcf4f71225d7eaea1d8645349cb76fa06205cc" -dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi 0.3.9", -] - -[[package]] -name = "mio" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dce281c5e46beae905d4de1870d8b1509a9142b62eedf18b443b011ca8343d0" -dependencies = [ - "libc", - "log", - "wasi", - "windows-sys", -] - -[[package]] -name = "miow" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "nix" -version = "0.22.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4916f159ed8e5de0082076562152a76b7a1f64a01fd9d1e0fea002c37624faf" -dependencies = [ - "bitflags 1.3.2", - "cc", - "cfg-if", - "libc", - "memoffset", -] - -[[package]] -name = "nix" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" -dependencies = [ - "bitflags 2.4.1", - "cfg-if", - "libc", -] - -[[package]] -name = "nodrop" -version = "0.1.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" - -[[package]] -name = "ntapi" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28774a7fd2fbb4f0babd8237ce554b73af68021b5f695a3cebd6c59bac0980f" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "num-traits" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" -dependencies = [ - "autocfg", -] - -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi 0.3.3", - "libc", -] - -[[package]] -name = "num_enum" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f646caf906c20226733ed5b1374287eb97e3c2a5c227ce668c1f2ce20ae57c9" -dependencies = [ - "num_enum_derive", -] - -[[package]] -name = "num_enum_derive" -version = "0.5.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcbff9bc912032c62bf65ef1d5aea88983b420f4f839db1e9b0c281a25c9c799" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 1.0.109", -] - -[[package]] -name = "object" -version = "0.27.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67ac1d3f9a1d3616fd9a60c8d74296f22406a238b6a72f5cc1e6f314df4ffbf9" -dependencies = [ - "flate2", - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - -[[package]] -name = "parking_lot" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -dependencies = [ - "lock_api", - "parking_lot_core 0.9.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec 1.11.1", - "winapi 0.3.9", -] - -[[package]] -name = "parking_lot_core" -version = "0.9.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -dependencies = [ - "cfg-if", - "libc", - "redox_syscall 0.4.1", - "smallvec 1.11.1", - "windows-targets", -] - -[[package]] -name = "ppv-lite86" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit", -] - -[[package]] -name = "proc-macro2" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "quote" -version = "1.0.33" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom", -] - -[[package]] -name = "redox_syscall" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -dependencies = [ - "bitflags 1.3.2", -] - -[[package]] -name = "regex" -version = "1.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -[[package]] -name = "rustc-demangle" -version = "0.1.23" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustversion" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" - -[[package]] -name = "ryu" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" - -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - -[[package]] -name = "serde" -version = "1.0.190" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91d3c334ca1ee894a2c6f6ad698fe8c435b76d504b13d436f0685d648d6d96f7" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde-hex" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca37e3e4d1b39afd7ff11ee4e947efae85adfddf4841787bfa47c470e96dc26d" -dependencies = [ - "array-init", - "serde", - "smallvec 0.6.14", -] - -[[package]] -name = "serde_derive" -version = "1.0.190" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c5609f394e5c2bd7fc51efda478004ea80ef42fee983d5c67a65e34f32c0e3" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "serde_json" -version = "1.0.108" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" -dependencies = [ - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "serde_spanned" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12022b835073e5b11e90a14f86838ceb1c8fb0325b72416845c487ac0fa95e80" -dependencies = [ - "serde", -] - -[[package]] -name = "signal-hook" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" -dependencies = [ - "libc", - "signal-hook-registry", -] - -[[package]] -name = "signal-hook-mio" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" -dependencies = [ - "libc", - "mio 0.7.14", - "mio 0.8.9", - "signal-hook", -] - -[[package]] -name = "signal-hook-registry" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -dependencies = [ - "libc", -] - -[[package]] -name = "slog" -version = "2.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" - -[[package]] -name = "smallvec" -version = "0.6.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b97fcaeba89edba30f044a10c6a3cc39df9c3f17d7cd829dd1446cab35f890e0" -dependencies = [ - "maybe-uninit", -] - -[[package]] -name = "smallvec" -version = "1.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" - -[[package]] -name = "snapchange" -version = "0.1.0" -dependencies = [ - "addr2line", - "ahash", - "anyhow", - "bitflags 1.3.2", - "clap", - "clap-verbosity-flag", - "core_affinity", - "crossterm 0.22.1", - "ctrlc", - "env_logger", - "extended", - "iced-x86", - "kvm-bindings", - "kvm-ioctls", - "lazy_static", - "libc", - "log", - "memmap", - "nix 0.22.3", - "num_enum", - "rand", - "rand_core", - "rustc-hash", - "serde", - "serde-hex", - "serde_json", - "thiserror", - "toml", - "tui", - "tui-logger", - "vmm-sys-util", - "x86_64", -] - -[[package]] -name = "stable_deref_trait" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" - -[[package]] -name = "strsim" -version = "0.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.39" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "termcolor" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6093bad37da69aab9d123a8091e4be0aa4a03e4d601ec641c327398315f62b64" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" -dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] - -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit", -] - -[[package]] -name = "toml_datetime" -version = "0.6.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime", - "winnow", -] - -[[package]] -name = "tui" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccdd26cbd674007e649a272da4475fb666d3aa0ad0531da7136db6fab0e5bad1" -dependencies = [ - "bitflags 1.3.2", - "cassowary", - "crossterm 0.25.0", - "unicode-segmentation", - "unicode-width", -] - -[[package]] -name = "tui-logger" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625647d10e6e215d448915ac099f682d0838d5ba1a6825e06f296fcdf5cf0f1c" -dependencies = [ - "chrono", - "fxhash", - "lazy_static", - "log", - "parking_lot 0.12.1", - "slog", - "tui", -] - -[[package]] -name = "unicode-ident" -version = "1.0.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" - -[[package]] -name = "unicode-segmentation" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dd624098567895118886609431a7c3b8f516e41d30e0643f03d94592a147e36" - -[[package]] -name = "unicode-width" -version = "0.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" - -[[package]] -name = "utf8parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" - -[[package]] -name = "version_check" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" - -[[package]] -name = "vmm-sys-util" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48b7b084231214f7427041e4220d77dfe726897a6d41fddee450696e66ff2a29" -dependencies = [ - "bitflags 1.3.2", - "libc", -] - -[[package]] -name = "volatile" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" - -[[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" - -[[package]] -name = "wasm-bindgen" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" -dependencies = [ - "cfg-if", - "wasm-bindgen-macro", -] - -[[package]] -name = "wasm-bindgen-backend" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" -dependencies = [ - "bumpalo", - "log", - "once_cell", - "proc-macro2", - "quote", - "syn 2.0.39", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-macro" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" -dependencies = [ - "quote", - "wasm-bindgen-macro-support", -] - -[[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", - "wasm-bindgen-backend", - "wasm-bindgen-shared", -] - -[[package]] -name = "wasm-bindgen-shared" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" - -[[package]] -name = "winapi" -version = "0.2.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-build" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - -[[package]] -name = "winapi-util" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" -dependencies = [ - "winapi 0.3.9", -] - -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - -[[package]] -name = "windows-core" -version = "0.51.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-sys" -version = "0.48.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" -dependencies = [ - "windows-targets", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", -] - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - -[[package]] -name = "winnow" -version = "0.5.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "829846f3e3db426d4cee4510841b71a8e58aa2a76b1132579487ae430ccd9c7b" -dependencies = [ - "memchr", -] - -[[package]] -name = "x86_64" -version = "0.14.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b835097a84e4457323331ec5d6eb23d096066cbfb215d54096dcb4b2e85f500" -dependencies = [ - "bit_field", - "bitflags 2.4.1", - "rustversion", - "volatile", -] - -[[package]] -name = "zerocopy" -version = "0.7.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cd369a67c0edfef15010f980c3cbe45d7f651deac2cd67ce097cd801de16557" -dependencies = [ - "zerocopy-derive", -] - -[[package]] -name = "zerocopy-derive" -version = "0.7.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2f140bda219a26ccc0cdb03dba58af72590c53b22642577d88a927bc5c87d6b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.39", -] diff --git a/examples/07_libfuzzer/Dockerfile b/examples/07_libfuzzer/Dockerfile index 89a68fe..6e335d1 100644 --- a/examples/07_libfuzzer/Dockerfile +++ b/examples/07_libfuzzer/Dockerfile @@ -23,5 +23,5 @@ COPY --from=base / "$SNAPSHOT_INPUT" ENV SNAPSHOT_ENTRYPOINT=/root/example ENV SNAPSHOT_ENTRYPOINT_CWD="/tmp/" ENV SNAPSHOT_IMGTYPE=initramfs -ENV GENERATE_COVERAGE_BREAKPOINTS=0 +ENV COVERAGE_BREAKPOINT_COMMAND=angr ENV LIBFUZZER=1 diff --git a/examples/07_libfuzzer/Makefile b/examples/07_libfuzzer/Makefile new file mode 100644 index 0000000..8fd4cf5 --- /dev/null +++ b/examples/07_libfuzzer/Makefile @@ -0,0 +1,50 @@ +DOCKER ?= docker +FUZZ_CORES ?= /2 + +all: test + +base_images: + # Build the base snapchange image used for snapshotting + cd ../../docker && make + +snapshot_image: Dockerfile + $(DOCKER) build -q -t snapchange_example7:snapshot . -f $< | tee $@ || { rm $@; exit 1; } + +snapshot: snapshot_image harness/example.c + # mkdir -p snapshot/ + $(DOCKER) run --rm -i \ + -v $(shell realpath -m ./snapshot):/snapshot \ + -e SNAPSHOT_IMGTYPE=initramfs \ + $$(cat snapshot_image) >/dev/null 2>&1 + # Remove odd dictionary entries just to speed up the example + find ./snapshot/dict -type f | grep -v cafe | grep -v dead | xargs rm + # Remove the unneeded angr covbps since we are using the sancov bps + rm snapshot/*angr.covbps + cp config.toml snapshot/ + +fuzzer: + cargo build -r >/dev/null 2>&1 + +fuzz: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) + +fuzz-%: snapshot + cargo run -r -- -p ./snapshot fuzz -c $(FUZZ_CORES) --stop-after-time $(shell echo $@ | sed 's/fuzz-//g')m +# .PHONY: fuzz-1 fuzz-2 fuzz-3 fuzz-4 fuzz-5 + +test: snapshot fuzzer reset + ./test.sh + +reset: snapshot + cd snapshot && ./reset.sh + +clean: clean-docker + -$(RM) -rf snapshot target + +clean-docker: + -$(DOCKER) rmi `cat ./dockers/snapshot_image` + -rm ./dockers/snapshot_image + -$(DOCKER) rmi `cat ./dockers/target_image` + -rm ./dockers/target_image + +.PHONY: fuzzer all base_images test reset fuzz diff --git a/examples/07_libfuzzer/test.sh b/examples/07_libfuzzer/test.sh index 3e2f172..dff4477 100755 --- a/examples/07_libfuzzer/test.sh +++ b/examples/07_libfuzzer/test.sh @@ -1,17 +1,15 @@ -#!/bin/bash +#!/usr/bin/env bash -# Reset the snapshot from a previous run -pushd snapshot > /dev/null -./reset.sh -popd > /dev/null - -# Rebuild the fuzzer -echo "Building Example 07" -cargo build -r >/dev/null 2>/dev/null +source ../test.include.sh # Seed the input with an easier input -echo "Begin fuzzing!" -cargo run -r -- fuzz -c 8 --ascii-stats --stop-after-first-crash --stop-after-time 5m +log_info "start fuzzing" +cargo run -r -- \ + fuzz \ + -c "$FUZZ_CORES" \ + --ascii-stats \ + --stop-after-first-crash \ + --stop-after-time "$FUZZ_TIMEOUT" >/dev/null 2>&1 || err "fuzzing failed" ls snapshot/crashes/SIGSEGV* >/dev/null 2>/dev/null STATUS=$? @@ -24,8 +22,7 @@ fi ls snapshot/crashes/SIGSEGV* >/dev/null STATUS=$? if [ "$STATUS" -gt 0 ]; then - echo "Example 7 did not find crash" - exit 1 + err "did not find crash" fi -echo -e "\e[32mExample 07 fuzz SUCCESS!\e[0m" +log_success "fuzzing" diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..fd0203b --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,15 @@ +all: + echo "targets: clean test" + +clean: + -$(MAKE) -C ./01_getpid clean + -$(MAKE) -C ./02_libtiff clean + -$(MAKE) -C ./03_ffmpeg_custom_mutator clean + -$(MAKE) -C ./04_syscall_fuzzer clean + -$(MAKE) -C ./05_redqueen clean + -$(MAKE) -C ./06_custom_feedback clean + -$(MAKE) -C ./07_libfuzzer clean + find . -name snapshot -exec sudo rm -rf \{\} \; + +test: + ./test.sh diff --git a/examples/test.include.sh b/examples/test.include.sh new file mode 100644 index 0000000..f92c742 --- /dev/null +++ b/examples/test.include.sh @@ -0,0 +1,66 @@ +#!/usr/bin/env bash + +if [[ -z "$FUZZ_CORES" ]]; then + FUZZ_CORES="/2" +fi + +if [[ -z "$FUZZ_TIMEOUT" ]]; then + FUZZ_TIMEOUT="5m" +fi + +EX="$(basename $PWD)" + +COLOR_CLEAR='\e[0m' +COLOR_RED='\e[0;31m' +COLOR_GREEN='\e[0;32m' + +function err { + echo -e "${COLOR_RED}ERROR: $EX - $* $COLOR_CLEAR" + exit 1 +} + +function log_success { + echo -e "${COLOR_GREEN}SUCCESS: $EX - $* $COLOR_CLEAR" +} + +function log_info { + echo -e "INFO: $EX - $*" +} + +function setup_build { + if [[ -d snapshot ]]; then + # Reset the snapshot from a previous run + pushd snapshot > /dev/null + ./reset.sh + popd > /dev/null + else + log_info "creating snapshot" + if [[ -e Makefile ]]; then + make snapshot >/dev/null 2>&1 + STATUS=$? + elif [[ -e ./make_snapshot.sh ]]; then + ./make_snapshot.sh >/dev/null 2>&1 + STATUS=$? + else + err "no script to create snapshot" + fi + if [[ "$STATUS" -gt 0 ]]; then + err "failed to create snapshot" + fi + fi + + # Rebuild the fuzzer + log_info "building fuzzer" + cargo build -r >/dev/null 2>&1 || err "build failure" +} + +function start_fuzzing { + log_info "start fuzzing" + cargo run -r -- \ + fuzz \ + -c "$FUZZ_CORES" \ + --ascii-stats \ + --stop-after-time "$FUZZ_TIMEOUT" \ + --stop-after-first-crash \ + >/dev/null 2>&1 +} diff --git a/examples/test.sh b/examples/test.sh index 8b7ddca..77fdb5f 100755 --- a/examples/test.sh +++ b/examples/test.sh @@ -1,30 +1,62 @@ -#!/bin/bash +#!/usr/bin/env bash -# DIRS="01_getpid 02_libtiff 03_ffmpeg_custom_mutator 04_syscall_fuzzer 05_redqueen 06_custom_feedback" -DIRS="01_getpid 02_libtiff 04_syscall_fuzzer 05_redqueen 06_custom_feedback 07_libfuzzer" +COLOR_CLEAR='\e[0m' +COLOR_RED='\e[0;31m' +COLOR_GREEN='\e[0;32m' + +function err { + echo -e "${COLOR_RED}ERROR: $* $COLOR_CLEAR" + exit 1 +} + +scriptdir="$(dirname "$(realpath "$0")")" +echo "INFO: Building snapchange base images" +make -s -C "$scriptdir/../docker/" >/dev/null 2>&1 || err "failed to build docker base images" + +DIRS="01_getpid 02_libtiff 03_ffmpeg_custom_mutator 04_syscall_fuzzer 05_redqueen 06_custom_feedback 07_libfuzzer" +# DIRS="01_getpid 02_libtiff 04_syscall_fuzzer 05_redqueen 06_custom_feedback 07_libfuzzer" + +success="" +failed="" # Make and test all included examples -for dir in $DIRS; do - echo Testing $dir - pushd $dir >/dev/null - # Create the snapshot for this example or reset the snapshot for testing - if ! [ -d ./snapshot ]; then - ./make_snapshot.sh - else - pushd ./snapshot >/dev/null - ./reset.sh - popd >/dev/null +for dir in $DIRS; do + if ! [[ -d "$dir" ]]; then + err "directory $dir not found" fi - # Test this example - ./test.sh + pushd "$dir" >/dev/null + + if [[ -e Makefile ]]; then + make -s test + STATUS=$? + else + if ! [[ -d ./snapshot ]]; then + if ! ./make_snapshot.sh >/dev/null 2>&1; then + failed="$failed $dir" + continue + fi + else + pushd ./snapshot >/dev/null + ./reset.sh >/dev/null 2>&1 + popd >/dev/null + fi + + # Test this example + ./test.sh + STATUS=$? + fi # Check the result of this test - STATUS=$? - popd >/dev/null + popd >/dev/null - if [ "$STATUS" -gt 0 ]; then - echo "Test $dir failed!" - exit 1 - fi + if [[ "$STATUS" -gt 0 ]]; then + failed="$failed $dir" + else + success="$success $dir" + fi done + +echo "=== results ===" +echo "success: $success" +echo "failed: $failed" diff --git a/rustfmt.toml b/rustfmt.toml index ec987e3..89fda0c 100644 --- a/rustfmt.toml +++ b/rustfmt.toml @@ -46,7 +46,7 @@ enum_discrim_align_threshold = 0 match_arm_blocks = true match_arm_leading_pipes = "Never" force_multiline_blocks = false -fn_args_layout = "Tall" +fn_params_layout = "Tall" brace_style = "SameLineWhere" control_brace_style = "AlwaysSameLine" trailing_semicolon = true diff --git a/src/_docs/mod.rs b/src/_docs/mod.rs index f227eb8..5313565 100644 --- a/src/_docs/mod.rs +++ b/src/_docs/mod.rs @@ -1,6 +1,6 @@ //! Documentation module pub mod architecture; pub mod cookbook; +pub mod docker_snapshot; pub mod examples; pub mod fuzzer_lifecycle; -pub mod docker_snapshot; diff --git a/src/addrs.rs b/src/addrs.rs index 8770699..e96df02 100644 --- a/src/addrs.rs +++ b/src/addrs.rs @@ -135,7 +135,7 @@ impl std::str::FromStr for VirtAddr { /// A wrapper around the cr3 #[repr(transparent)] -#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize, Hash)] pub struct Cr3(pub u64); impl std::ops::Deref for Cr3 { diff --git a/src/cmdline.rs b/src/cmdline.rs index cfe4475..71e1f1a 100644 --- a/src/cmdline.rs +++ b/src/cmdline.rs @@ -1,12 +1,14 @@ //! Command line arguments use anyhow::{anyhow, ensure, Context, Result}; +use clap::builder::ArgAction; use clap::Parser; use log::debug; use smallvec::SmallVec; use thiserror::Error; -use std::collections::{BTreeMap, HashMap}; +use std::collections::HashMap; +use std::num::NonZeroUsize; use std::ops::Range; use std::path::{Path, PathBuf}; use std::time::Duration; @@ -15,7 +17,8 @@ use crate::addrs::{Cr3, VirtAddr}; use crate::cmp_analysis::{Conditional, Operand, RedqueenArguments, Size}; use crate::config::Config; use crate::feedback::FeedbackTracker; -use crate::fuzzer::ResetBreakpointType; + +use crate::fuzzvm::ResetBreakpoints; use crate::stack_unwinder::StackUnwinders; use crate::symbols::{Symbol, LINUX_KERNEL_SYMBOLS, LINUX_USERLAND_SYMBOLS}; use crate::vbcpu::{VbCpu, VmSelector, X86FxState, X86XSaveArea, X86XSaveHeader, X86XsaveYmmHi}; @@ -87,7 +90,7 @@ pub struct ProjectState { /// Addresses to apply single shot breakpoints for the purposes of coverage. /// Addresses assume the CR3 of the beginning of the snapshot. - pub(crate) coverage_breakpoints: Option, + pub(crate) coverage_basic_blocks: Option, /// Module metadata containing where each module is loaded in the snapshot. Primarily /// used for dumping lighthouse coverage maps @@ -137,7 +140,7 @@ impl ProjectState { prev_coverage = serde_json::from_str(&data)?; } - if let Some(breakpoints) = self.coverage_breakpoints.as_ref() { + if let Some(breakpoints) = self.coverage_basic_blocks.as_ref() { // The coverage left to hit let coverage_left = breakpoints .keys() @@ -356,7 +359,7 @@ pub struct Fuzz { /// Number of cores to fuzz with. Negative numbers interpretted as MAX_CORES - CORES. Prefix /// with `/N` to specify a fraction of available cores. #[clap(short, long, allow_hyphen_values = true, value_parser = parse_cores)] - pub(crate) cores: Option, + pub(crate) cores: Option, /// Set the timeout (in seconds) of the execution of the VM. [0-9]+(ns|us|ms|s|m|h) #[clap(long, value_parser = parse_timeout, default_value = "1s")] @@ -415,10 +418,14 @@ pub(crate) enum MinimizeCodeCovLevel { /// Minimize subcommand #[derive(Parser, Debug)] pub struct Minimize { - /// Enable single step, single execution trace of the guest using the given file as - /// the input + /// Minimize the given file or alternatively, if a directory is passed, everything inside of the + /// directory. pub(crate) path: PathBuf, + /// Whether to replace the original file with the minimized file. + #[clap(short = 'I', long)] + pub(crate) in_place: bool, + /// Set the timeout (in seconds) of the execution of the VM. [0-9]+(ns|us|ms|s|m|h) #[clap(long, value_parser = parse_timeout, default_value = "60s")] pub(crate) timeout: Duration, @@ -428,29 +435,57 @@ pub struct Minimize { #[clap(short, long, default_value_t = 50000)] pub(crate) iterations_per_stage: u32, + /// Specify, which type of code coverage feedback should be considered, when minimizing + /// the given input. `none` ignores all code coverage. `basic-blocks` is the regular fuzzing + /// coverage. `symbols` is function-level coverage when symbols are available. `hitcounts` + /// is basic-block coverage considering hitcounts. + #[clap(long, value_enum)] + pub(crate) consider_coverage: Option, + /// Only check the RIP register for checking if the register state is the same after /// minimizing an input - #[clap(short, long)] + #[clap( + short, long, + default_value("false"), + default_missing_value("true"), + num_args(0..=1), + require_equals(true), + action = ArgAction::Set, + )] pub(crate) rip_only: bool, /// Ignore the feedback returned through coverage breakpoints and custom feedback, /// when checking for same state after minimizing an input. - #[clap(long)] + #[clap( + long, + default_value("false"), + default_missing_value("true"), + num_args(0..=1), + require_equals(true), + action = ArgAction::Set, + )] pub(crate) ignore_feedback: bool, - /// Specify, which type of code coverage feedback should be considered, when minimizing - /// the given input. `none` ignores all code coverage. `basic-blocks` is the regular fuzzing - /// coverage. `symbols` is function-level coverage when symbols are available. `hitcounts` - /// is basic-block coverage considering hitcounts. - #[clap(long, value_enum, default_value_t = MinimizeCodeCovLevel::None)] - pub(crate) consider_coverage: MinimizeCodeCovLevel, - /// Ignore stack contents when checking for same state after minimizing an input. - #[clap(long)] + #[clap( + long, + default_value("true"), + default_missing_value("true"), + num_args(0..=1), + require_equals(true), + action = ArgAction::Set, + )] pub(crate) ignore_stack: bool, /// Ignore the consoel output when checking for same state after minimizing an input. - #[clap(long)] + #[clap( + long, + default_value("true"), + default_missing_value("true"), + num_args(0..=1), + require_equals(true), + action = ArgAction::Set, + )] pub(crate) ignore_console_output: bool, /// Dump the observed feedback into a file. Useful when debugging minimization according to @@ -465,7 +500,7 @@ pub struct CorpusMin { /// Number of cores to fuzz with. Negative numbers interpretted as MAX_CORES - CORES. Prefix /// with `/N` to specify a fraction of available cores. #[clap(short, long, allow_hyphen_values = true, value_parser = parse_cores)] - pub(crate) cores: Option, + pub(crate) cores: Option, /// The path to the corpus containing input files to minimize #[clap(short, long, default_value = "./snapshot/current_corpus")] @@ -527,9 +562,10 @@ pub struct FindInput { pub(crate) timeout: Duration, /// Number of cores to fuzz with. Negative numbers interpretted as MAX_CORES - CORES. Prefix - /// with `/N` to specify a fraction of available cores. + /// with `/N` to specify a fraction of available cores. Use `/1` for all cores. + /// Recommended: `/2` on systems with hyperthreading. #[clap(short, long, allow_hyphen_values = true, value_parser = parse_cores)] - pub(crate) cores: Option, + pub(crate) cores: Option, } /// Subcommands available for the command line specific to the project @@ -627,9 +663,6 @@ pub struct WriteMem { pub(crate) cr3: Option, } -/// Set of addresses that, if hit, signal a crash in the guest -pub type ResetBreakpoints = BTreeMap<(VirtAddr, Cr3), ResetBreakpointType>; - /// Get the [`ProjectState`] from the given project directory /// /// # Errors @@ -879,7 +912,7 @@ pub fn get_project_state(dir: &Path, cmd: Option<&SubCommand>) -> Result) -> Result Result<(Option, Option)> { // Init the resulting values let mut symbols = None; - let mut reset_breakpoints = Some(BTreeMap::new()); + let mut reset_breakpoints = Some(ResetBreakpoints::default()); if let Some(ref syms) = symbols_arg { let mut curr_symbols = crate::SymbolList::new(); @@ -949,7 +982,7 @@ pub fn parse_symbols( }); } - let mut symbol_bps = BTreeMap::new(); + let mut symbol_bps = ResetBreakpoints::default(); // Sanity check the symbols are sorted for sym in &curr_symbols { @@ -1574,21 +1607,21 @@ pub fn parse_timeout(input: &str) -> anyhow::Result { Ok(res) } -fn parse_cores(str: &str) -> Result { +fn parse_cores(str: &str) -> Result { let num_cores = core_affinity::get_core_ids().unwrap().len(); let cores = if let Some(cores) = str.strip_prefix('/') { - let i = cores.parse::()?; + let i = cores.parse::()?; num_cores / i } else { let i = str.parse::()?; if i < 0 { - ((num_cores as i64) + i) as usize + ((num_cores as i64) + i).try_into()? } else { - i as usize + i.try_into()? } }; - Ok(cores) + Ok(cores.try_into()?) } /// Parse the cmp analysis breakpoints. This will read the given path and parse diff --git a/src/cmp_analysis.rs b/src/cmp_analysis.rs index 0e614ab..df61f71 100644 --- a/src/cmp_analysis.rs +++ b/src/cmp_analysis.rs @@ -320,24 +320,16 @@ pub enum Operand { ConstF64(f64), /// A memory location to read the operand - Load { - address: Box, - }, + Load { address: Box }, /// Bitwise inversion of the operand - Not { - src: Box, - }, + Not { src: Box }, /// Sign inversion of the operand - Neg { - src: Box, - }, + Neg { src: Box }, /// Sign inversion of the operand - SignExtend { - src: Box, - }, + SignExtend { src: Box }, /// Arithmetic shift right ArithmeticShiftRight { @@ -440,7 +432,7 @@ macro_rules! impl_read_for_type { } Operand::Sub { left, right } => Ok(left.$func(fuzzvm)? - right.$func(fuzzvm)?), Operand::Mul { left, right } => Ok(left.$func(fuzzvm)? * right.$func(fuzzvm)?), - Operand::Div{ left, right } => Ok(left.$func(fuzzvm)? / right.$func(fuzzvm)?), + Operand::Div { left, right } => Ok(left.$func(fuzzvm)? / right.$func(fuzzvm)?), Operand::Or { left, right } => Ok(left.$func(fuzzvm)? | right.$func(fuzzvm)?), Operand::Not { src } => Ok(!src.$func(fuzzvm)?), Operand::Neg { src } => Ok(src.$func(fuzzvm)?.wrapping_neg()), @@ -1166,10 +1158,10 @@ pub fn gather_comparison( #[rustfmt::skip] impl_primitive_sizes!( - U8, u8, read_u8, + U8, u8, read_u8, U16, u16, read_u16, - U32, u32, read_u32, - U64, u64, read_u64, + U32, u32, read_u32, + U64, u64, read_u64, U128, u128, read_u128 ); diff --git a/src/commands/corpus_min.rs b/src/commands/corpus_min.rs index 2e6159c..58694da 100644 --- a/src/commands/corpus_min.rs +++ b/src/commands/corpus_min.rs @@ -1,4 +1,4 @@ -//! Execute the `coverage` command +//! Execute the `corpus-min` command use anyhow::{ensure, Context, Result}; @@ -6,7 +6,7 @@ use std::cmp::Reverse; use std::collections::{BTreeMap, BTreeSet, BinaryHeap}; use std::mem::size_of; use std::os::unix::io::AsRawFd; -use std::path::{Path, PathBuf}; +use std::path::PathBuf; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, RwLock}; use std::time::Duration; @@ -18,31 +18,12 @@ use kvm_ioctls::VmFd; use crate::config::Config; use crate::fuzz_input::InputWithMetadata; use crate::fuzzer::Fuzzer; -use crate::fuzzvm::{FuzzVm, FuzzVmExit}; +use crate::fuzzvm::{CoverageBreakpoints, FuzzVm, FuzzVmExit}; use crate::memory::Memory; -use crate::{cmdline, fuzzvm, unblock_sigalrm, SymbolList, THREAD_IDS}; +use crate::utils::get_files; +use crate::{cmdline, fuzzvm, fuzzvm::ResetBreakpoints, unblock_sigalrm, SymbolList, THREAD_IDS}; use crate::{handle_vmexit, init_environment, KvmEnvironment, ProjectState}; -use crate::{Cr3, Execution, ResetBreakpointType, VbCpu, VirtAddr}; - -/// Get all of the files found in the given path recursively -fn get_files(path: &Path) -> Result, std::io::Error> { - let mut files = Vec::new(); - let entries = std::fs::read_dir(path)?; - - for entry in entries { - let entry = entry?; - let file_type = entry.file_type()?; - - if file_type.is_dir() { - let dir_files = get_files(&entry.path())?; - files.extend(dir_files); - } else if file_type.is_file() { - files.push(entry.path()); - } - } - - Ok(files) -} +use crate::{Cr3, Execution, VbCpu}; /// Execute the Coverage subcommand to gather coverage for a particular input pub(crate) fn run( @@ -50,16 +31,12 @@ pub(crate) fn run( args: &cmdline::CorpusMin, ) -> Result<()> { ensure!( - project_state.coverage_breakpoints.is_some(), + project_state.coverage_basic_blocks.is_some(), "Must have covbps to gather minimize a corpus" ); // Get the number of cores to fuzz with - let mut cores = args.cores.unwrap_or(1); - if cores == 0 { - log::warn!("No cores given. Defaulting to 1 core"); - cores = 1; - } + let cores: usize = args.cores.map_or(1, |c| c.into()); let KvmEnvironment { kvm, @@ -71,7 +48,7 @@ pub(crate) fn run( } = init_environment(project_state)?; // Init the coverage breakpoints mapping to byte - let mut covbp_bytes = BTreeMap::new(); + let mut covbp_bytes = crate::fuzzvm::CoverageBreakpoints::default(); // let cr3 = Cr3(project_state.vbcpu.cr3); let mut total_coverage = Vec::new(); @@ -80,7 +57,7 @@ pub(crate) fn run( { let curr_clean_snapshot = clean_snapshot.read().unwrap(); for addr in project_state - .coverage_breakpoints + .coverage_basic_blocks .as_ref() .unwrap() .keys() @@ -104,7 +81,7 @@ pub(crate) fn run( ); // Gather the paths to gather the coverage for - let paths = get_files(&args.input_dir)?; + let paths = get_files(&args.input_dir, true)?; log::info!("Found {} coverage", covbp_bytes.keys().len()); log::info!("Found {} files to minimize", paths.len()); @@ -255,7 +232,7 @@ pub(crate) fn run( } // Gather the paths to gather the coverage for - let new_paths = get_files(&args.input_dir)?; + let new_paths = get_files(&args.input_dir, true)?; log::info!("Reduced corpus from {} to {}", paths.len(), new_paths.len()); let cov_out = project_state.path.join("coverage.addresses.min"); @@ -298,8 +275,8 @@ pub(crate) fn start_core( snapshot_fd: i32, clean_snapshot: Arc>, symbols: &Option, - symbol_breakpoints: Option>, - coverage_breakpoints: BTreeMap, + symbol_breakpoints: Option, + coverage_breakpoints: CoverageBreakpoints, vm_timeout: Duration, config: Config, paths: Arc>, diff --git a/src/commands/coverage.rs b/src/commands/coverage.rs index f3784b7..1bafb71 100644 --- a/src/commands/coverage.rs +++ b/src/commands/coverage.rs @@ -2,7 +2,6 @@ use anyhow::{anyhow, ensure, Context, Result}; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::path::PathBuf; use std::sync::{Arc, RwLock}; @@ -14,11 +13,10 @@ use kvm_ioctls::VmFd; use crate::fuzz_input::InputWithMetadata; use crate::fuzzer::Fuzzer; -use crate::fuzzvm::FuzzVm; +use crate::fuzzvm::{FuzzVm, ResetBreakpoints}; use crate::memory::Memory; use crate::{cmdline, fuzzvm, unblock_sigalrm, SymbolList, THREAD_IDS}; use crate::{init_environment, KvmEnvironment, ProjectState}; -use crate::{Cr3, ResetBreakpointType, VirtAddr}; /// Execute the Coverage subcommand to gather coverage for a particular input pub(crate) fn run( @@ -26,7 +24,7 @@ pub(crate) fn run( args: &cmdline::Coverage, ) -> Result<()> { ensure!( - project_state.coverage_breakpoints.is_some(), + project_state.coverage_basic_blocks.is_some(), "Must have covbps to gather coverage" ); @@ -51,7 +49,7 @@ pub(crate) fn run( log::info!( "Init {} coverage", project_state - .coverage_breakpoints + .coverage_basic_blocks .as_ref() .context("coverage command requires coverage breakpoints!")? .len() @@ -85,7 +83,7 @@ pub(crate) fn start_core( snapshot_fd: i32, clean_snapshot: Arc>, symbols: &Option, - symbol_breakpoints: Option>, + symbol_breakpoints: Option, input_case: &PathBuf, vm_timeout: Duration, project_state: &ProjectState, @@ -153,7 +151,7 @@ pub(crate) fn start_core( &input, vm_timeout, project_state - .coverage_breakpoints + .coverage_basic_blocks .as_ref() .unwrap() .keys() diff --git a/src/commands/find_input.rs b/src/commands/find_input.rs index 898778f..172b1c6 100644 --- a/src/commands/find_input.rs +++ b/src/commands/find_input.rs @@ -2,7 +2,7 @@ use anyhow::{anyhow, ensure, Context, Result}; -use std::collections::{BTreeMap, HashSet}; +use std::collections::HashSet; use std::os::unix::io::AsRawFd; use std::path::{Path, PathBuf}; use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering}; @@ -21,7 +21,7 @@ use crate::memory::Memory; use crate::stack_unwinder::StackUnwinders; use crate::{cmdline, fuzzvm, unblock_sigalrm, SymbolList, THREAD_IDS}; use crate::{handle_vmexit, init_environment, KvmEnvironment, ProjectState}; -use crate::{Cr3, Execution, ResetBreakpointType, VbCpu, VirtAddr}; +use crate::{Execution, VbCpu, VirtAddr}; /// Thread worker to execute a single input and write the single step trace for that /// input @@ -34,7 +34,7 @@ fn start_core( snapshot_fd: i32, clean_snapshot: Arc>, symbols: &Option, - symbol_breakpoints: Option>, + symbol_breakpoints: Option, vm_timeout: Duration, config: Config, next_file_index: Arc, @@ -218,11 +218,7 @@ pub(crate) fn run( let files = Arc::new(files); // Get the number of cores to fuzz with - let mut cores = args.cores.unwrap_or(1); - if cores == 0 { - log::warn!("No cores given. Defaulting to 1 core"); - cores = 1; - } + let cores: usize = args.cores.map_or(1, |c| c.into()); let core_ids = core_affinity::get_core_ids().ok_or_else(|| anyhow!("Failed to get core ids"))?; diff --git a/src/commands/fuzz.rs b/src/commands/fuzz.rs index 593af3b..9b3d9e5 100644 --- a/src/commands/fuzz.rs +++ b/src/commands/fuzz.rs @@ -2,7 +2,8 @@ use anyhow::{ensure, Context, Result}; -use std::collections::{BTreeMap, BTreeSet}; +use std::collections::BTreeSet; + use std::os::unix::io::AsRawFd; use std::path::PathBuf; use std::sync::atomic::{AtomicBool, Ordering}; @@ -21,7 +22,7 @@ use crate::coverage_analysis::CoverageAnalysis; use crate::enable_manual_dirty_log_protect; use crate::fuzz_input::{FuzzInput, InputWithMetadata}; use crate::fuzzer::Fuzzer; -use crate::fuzzvm::FuzzVm; +use crate::fuzzvm::{CoverageBreakpoints, FuzzVm, ResetBreakpoints}; use crate::rng::Rng; use crate::stats::PerfStatTimer; use crate::stats::{self, PerfMark}; @@ -31,7 +32,7 @@ use crate::{block_sigalrm, kick_cores, Stats, FINISHED}; use crate::memory::Memory; use crate::{fuzzvm, unblock_sigalrm, write_crash_input, THREAD_IDS}; use crate::{handle_vmexit, init_environment, KvmEnvironment, ProjectState}; -use crate::{Cr3, Execution, ResetBreakpointType, VbCpu, VirtAddr}; +use crate::{Cr3, Execution, VbCpu, VirtAddr}; use crate::feedback::FeedbackTracker; @@ -70,11 +71,7 @@ pub(crate) fn run( } = init_environment(&project_state)?; // Get the number of cores to fuzz with - let mut cores = args.cores.unwrap_or(1); - if cores == 0 { - log::warn!("No cores given. Defaulting to 1 core"); - cores = 1; - } + let cores: usize = args.cores.map_or(1, |c| c.into()); // Init list of all cores executing let mut threads = Vec::new(); @@ -266,7 +263,7 @@ pub(crate) fn run( ); // Init the coverage breakpoints mapping to byte - let mut covbp_bytes = BTreeMap::new(); + let mut covbp_bytes = fuzzvm::CoverageBreakpoints::default(); // Start timer for writing all coverage breakpoints let start = Instant::now(); @@ -681,8 +678,8 @@ fn start_core( snapshot_fd: i32, clean_snapshot: Arc>, symbols: &Option, - symbol_breakpoints: Option>, - coverage_breakpoints: Option>, + symbol_breakpoints: Option, + coverage_breakpoints: Option, core_stats: &Arc>>, project_dir: &PathBuf, vm_timeout: Duration, @@ -1277,14 +1274,6 @@ fn start_core( core_stats.lock().unwrap().feedback.merge(&feedback); // core_stats.lock().unwrap().redqueen_coverage.append(&mut redqueen_coverage); - // Write this current corpus to disk - for input in &corpus { - save_input_in_project(input, &project_dir, "current_corpus").unwrap(); - } - - // Save the corpus in old_corpus for stats to sync with - // core_stats.lock().unwrap().old_corpus = Some(corpus); - // Send the current corpus to the main corpus collection if core_stats.lock().unwrap().old_corpus.is_none() { core_stats.lock().unwrap().old_corpus = Some(corpus); diff --git a/src/commands/minimize.rs b/src/commands/minimize.rs index d126c48..081d846 100644 --- a/src/commands/minimize.rs +++ b/src/commands/minimize.rs @@ -2,7 +2,6 @@ use anyhow::{anyhow, ensure, Context, Result}; use rustc_hash::FxHashSet; -use std::collections::BTreeMap; use std::os::unix::io::AsRawFd; use std::path::PathBuf; use std::sync::{Arc, RwLock}; @@ -14,14 +13,14 @@ use kvm_ioctls::VmFd; use crate::cmdline::{self, MinimizeCodeCovLevel}; use crate::config::Config; -use crate::fuzz_input::{FuzzInput, InputWithMetadata}; +use crate::fuzz_input::{FuzzInput, InputWithMetadata, MinimizeControlFlow, MinimizerState}; use crate::fuzzer::{BreakpointType, Fuzzer}; -use crate::fuzzvm::FuzzVm; +use crate::fuzzvm::{FuzzVm, ResetBreakpoints}; use crate::memory::Memory; use crate::stack_unwinder::StackUnwinders; use crate::{fuzzvm, unblock_sigalrm, SymbolList, THREAD_IDS}; use crate::{init_environment, KvmEnvironment, ProjectState}; -use crate::{Cr3, Execution, ResetBreakpointType, VbCpu, VirtAddr}; +use crate::{Cr3, Execution, VbCpu, VirtAddr}; /// Stages to measure performance during minimization #[derive(Debug, Copy, Clone)] @@ -34,6 +33,7 @@ enum Counters { CheckResult, } +#[derive(Debug, Clone, Copy)] pub(crate) struct MinimizerConfig { ignore_reg_state: bool, ignore_feedback: bool, @@ -52,9 +52,10 @@ fn start_core( snapshot_fd: i32, clean_snapshot: Arc>, symbols: &Option, - symbol_reset_breakpoints: Option>, - coverage_breakpoints: Option>, - input_case: &PathBuf, + symbol_reset_breakpoints: Option<&ResetBreakpoints>, + coverage_breakpoints: Option<&FxHashSet>, + input_fuzzcase: &PathBuf, + output_fuzzcase: &PathBuf, vm_timeout: Duration, max_iterations: u32, config: Config, @@ -133,7 +134,7 @@ fn start_core( snapshot_fd.as_raw_fd(), clean_snapshot, None, // do not setup regular coverage breakpoints - symbol_reset_breakpoints, + symbol_reset_breakpoints.cloned(), symbols, config, StackUnwinders::default(), @@ -145,11 +146,11 @@ fn start_core( // Get the initial input - let input_bytes = std::fs::read(&input_case)?; + let input_bytes = std::fs::read(&input_fuzzcase)?; let start_input_size = input_bytes.len(); let starting_input: InputWithMetadata = - InputWithMetadata::from_path(input_case, project_dir)?; + InputWithMetadata::from_path(input_fuzzcase, project_dir)?; let mut input = starting_input.fork(); let start_length = input.len(); @@ -172,7 +173,7 @@ fn start_core( orig_feedback.ensure_clean(); // remove the feedback log if min_params.dump_feedback { let data = serde_json::to_string(&orig_feedback)?; - let mut save_path = std::path::PathBuf::from(input_case); + let mut save_path = std::path::PathBuf::from(input_fuzzcase); save_path.set_extension("feedback"); std::fs::write(save_path, data)?; } @@ -234,11 +235,27 @@ fn start_core( let mut last_feedback = None; let mut last_execution = Execution::Continue; - for iters in 0..max_iterations { + let mut current_iteration = 0u32; + let mut last_successful_iteration = 0u32; + let mut max_iterations = max_iterations; + let (mut minimizer_state, initial_cf) = input.input.init_minimize(); + match initial_cf { + MinimizeControlFlow::Stop => { + // odd, but ok... + max_iterations = 0; + } + MinimizeControlFlow::ContinueFor(required_iterations) => { + if (current_iteration + required_iterations) < max_iterations { + max_iterations += required_iterations; + } + } + _ => {} + } + while current_iteration < max_iterations { if timer.elapsed() > Duration::from_secs(1) { log::info!( - "Iters {iters:6}/{max_iterations} | Exec/sec {:6.2}", - f64::from(iters) / start.elapsed().as_secs_f64() + "Iters {current_iteration:6}/{max_iterations} Last success at {last_successful_iteration} | Exec/sec {:6.2}", + f64::from(current_iteration) / start.elapsed().as_secs_f64() ); if let Some(length) = input.len() { log::info!( @@ -280,19 +297,64 @@ fn start_core( let mut curr_input = time!(InputClone, { input.fork() }); // Minimize the input based on the Input type - time!(InputMinimize, { - FUZZER::Input::minimize(&mut curr_input, &mut rng); + let cf = time!(InputMinimize, { + curr_input.minimize( + &mut minimizer_state, + current_iteration, + last_successful_iteration, + &mut rng, + ) }); + match cf { + // minimize was not able to create a new input, so we try with the next iteration + MinimizeControlFlow::Skip => { + continue; + } + MinimizeControlFlow::ContinueFor(required_iterations) => { + if (current_iteration + required_iterations) < max_iterations { + max_iterations += required_iterations; + } + } + _ => {} + } + let (execution, mut feedback) = time!( RunInput, - fuzzvm.gather_feedback( - &mut fuzzer, - &curr_input, - vm_timeout, - covbps_addrs.iter().cloned(), - bp_type - )? + if min_params.codecov_level >= MinimizeCodeCovLevel::Hitcounts { + // run input without any feedback mechanism -> fast basic check + let (execution, feedback) = fuzzvm.gather_feedback( + &mut fuzzer, + &curr_input, + vm_timeout, + vec![], + bp_type, + )?; + + // fast check whether we superficially hit the same exit and not something + // completely different. + if orig_reg_state.rip == fuzzvm.rip() && orig_execution == execution { + // and only if it looks to be the same; do a reset and gather detailed feedback + // with hitcounts. + fuzzvm.gather_feedback( + &mut fuzzer, + &curr_input, + vm_timeout, + covbps_addrs.iter().cloned(), + bp_type, + )? + } else { + (execution, feedback) + } + } else { + fuzzvm.gather_feedback( + &mut fuzzer, + &curr_input, + vm_timeout, + covbps_addrs.iter().cloned(), + bp_type, + )? + } ); // Check if the VM resulted in the same crashing state. If so, keep the minimized input as the @@ -349,8 +411,15 @@ fn start_core( feedback.ensure_clean(); // remove the feedback log last_feedback = Some(feedback.clone()); last_execution = execution; + last_successful_iteration = current_iteration; } }); + + if matches!(cf, MinimizeControlFlow::Stop) || minimizer_state.is_stop_state() { + break; + } + + current_iteration += 1; } if input == starting_input { @@ -367,21 +436,13 @@ fn start_core( result_bytes.len() ); - // Get the new minimized filename - let orig_file_name = input_case - .file_name() - .unwrap_or_else(|| std::ffi::OsStr::new("UNKNOWNFILENAME")); - let mut min_file = std::path::PathBuf::from(orig_file_name); - min_file.as_mut_os_string().push(".min"); // we are not using set_extension, since we do not - // want to replace an existing extension. - - log::info!("Writing minimized file: {:?}", min_file); - std::fs::write(&min_file, &result_bytes)?; + log::info!("Writing minimized file: {:?}", output_fuzzcase); + std::fs::write(&output_fuzzcase, &result_bytes)?; if min_params.dump_feedback { if let Some(feedback) = last_feedback { let data = serde_json::to_string(&feedback)?; - let mut save_path = min_file.clone(); + let mut save_path = output_fuzzcase.clone(); save_path.as_mut_os_string().push(".feedback"); std::fs::write(save_path, data)?; } else { @@ -392,7 +453,7 @@ fn start_core( if last_execution.is_crash() { // Allow the fuzzer to handle the crashing state // Useful for things like syscall fuzzer to write a C file from the input - fuzzer.handle_crash(&input, &mut fuzzvm, &min_file)?; + fuzzer.handle_crash(&input, &mut fuzzvm, &output_fuzzcase)?; } Ok(()) @@ -424,17 +485,34 @@ pub(crate) fn run( ignore_stack: args.ignore_stack, ignore_reg_state: args.rip_only, ignore_feedback: args.ignore_feedback, - codecov_level: args.consider_coverage, ignore_console: args.ignore_console_output, dump_feedback: args.dump_feedback_to_file, + codecov_level: args.consider_coverage.unwrap_or_else(|| { + // try to guess what we are minimizing and decide what kind of code coverage we want to + // look at. + let current_corpus = Some(std::ffi::OsStr::new("current_corpus")); + if args.path.file_name() == current_corpus + || args + .path + .parent() + .map_or(true, |parent| parent.file_name() == current_corpus) + { + log::info!("guessing you want to minimize based on code coverage with hitcounts"); + MinimizeCodeCovLevel::Hitcounts + } else { + MinimizeCodeCovLevel::None + } + }), }; + log::debug!("{:?}", minparams); + // only use coverage breakpoints if we are supposed to ignore the coverage feedback. let covbps = if !args.ignore_feedback && minparams.codecov_level > MinimizeCodeCovLevel::None { // Init the coverage breakpoints mapping to byte let mut covbp_bytes = FxHashSet::default(); // Write the remaining coverage breakpoints into the "clean" snapshot - if let Some(covbps) = project_state.coverage_breakpoints.as_ref() { + if let Some(covbps) = project_state.coverage_basic_blocks.as_ref() { let cr3 = Cr3(project_state.vbcpu.cr3); // Small scope to drop the clean snapshot lock let mut curr_clean_snapshot = clean_snapshot.write().unwrap(); @@ -451,24 +529,52 @@ pub(crate) fn run( None }; - // Start executing on this core - start_core::( - core_id, - &vm, - &project_state.vbcpu, - &cpuids, - physmem_file.as_raw_fd(), - clean_snapshot, - &symbols, - symbol_breakpoints, - covbps, - &args.path, - args.timeout, - args.iterations_per_stage, - project_state.config.clone(), - minparams, - &project_state.path, - )?; + let filepaths = if args.path.is_dir() { + crate::utils::get_files(&args.path, true)? + } else { + vec![args.path.clone()] + }; + + let mut minimized = 0_u32; + for (infile, outfile) in filepaths + .iter() + // only check files that are not minimized already + .filter(|p| p.extension().map_or(true, |x| x != "min")) + .map(|infile| { + if args.in_place { + let outfile = infile.clone(); + (infile, outfile) + } else { + let outfile = infile.with_extension("min"); + (infile, outfile) + } + }) + { + // TODO(mrodler): the second iteration of this panics! fix this. + // Start executing on this core + start_core::( + core_id, + &vm, + &project_state.vbcpu, + &cpuids, + physmem_file.as_raw_fd(), + clean_snapshot.clone(), + &symbols, + symbol_breakpoints.as_ref(), + covbps.as_ref(), + &infile, + &outfile, + args.timeout, + args.iterations_per_stage, + project_state.config.clone(), + minparams.clone(), + &project_state.path, + )?; + minimized += 1; + } + if minimized > 1 { + log::info!("minimized {} files", minimized); + } // Success Ok(()) diff --git a/src/commands/project.rs b/src/commands/project.rs index cda9c75..0fd1031 100644 --- a/src/commands/project.rs +++ b/src/commands/project.rs @@ -309,7 +309,11 @@ pub(crate) fn run(project_state: &ProjectState, args: &cmdline::Project) -> Resu log::info!( "loaded debug info with {} debug locations based on {} coverage breakpoints", debug_info.len(), - project_state.coverage_breakpoints.as_ref().and_then(|covbps| Some(covbps.len())).unwrap_or(0_usize) + project_state + .coverage_basic_blocks + .as_ref() + .and_then(|covbps| Some(covbps.len())) + .unwrap_or(0_usize) ); let filepath = project_state.path.join("debug_info.json"); std::fs::write(&filepath, serde_json::to_string(&debug_info)?) diff --git a/src/commands/redqueen.rs b/src/commands/redqueen.rs index a437003..bbaf5a8 100644 --- a/src/commands/redqueen.rs +++ b/src/commands/redqueen.rs @@ -14,7 +14,7 @@ use kvm_bindings::CpuId; use kvm_ioctls::VmFd; #[cfg(feature = "redqueen")] -use std::{collections::BTreeMap, fs::File, os::unix::io::AsRawFd, path::PathBuf, time::Duration}; +use std::{fs::File, os::unix::io::AsRawFd, path::PathBuf, time::Duration}; #[cfg(feature = "redqueen")] use crate::{ @@ -25,11 +25,10 @@ use crate::{ fuzz_input::{FuzzInput, InputWithMetadata}, fuzzer::Fuzzer, fuzzvm, - fuzzvm::FuzzVm, + fuzzvm::{CoverageBreakpoints, FuzzVm, ResetBreakpoints}, init_environment, stack_unwinder::StackUnwinders, - unblock_sigalrm, Cr3, KvmEnvironment, Memory, ProjectState, ResetBreakpointType, SymbolList, - VbCpu, VirtAddr, THREAD_IDS, + unblock_sigalrm, KvmEnvironment, Memory, ProjectState, SymbolList, VbCpu, THREAD_IDS, }; /// Execute the c subcommand to gather coverage for a particular input @@ -39,7 +38,7 @@ pub(crate) fn run( args: &cmdline::RedqueenAnalysis, ) -> Result<()> { ensure!( - project_state.coverage_breakpoints.is_some(), + project_state.coverage_basic_blocks.is_some(), "Must have covbps to gather coverage" ); @@ -62,7 +61,7 @@ pub(crate) fn run( .ok_or_else(|| anyhow!("No valid cores"))?; // Init the fake coverage breakpoints for this command - let covbp_bytes = BTreeMap::new(); + let covbp_bytes = CoverageBreakpoints::default(); // Start executing on this core start_core::( @@ -93,12 +92,13 @@ pub(crate) fn start_core( snapshot_fd: i32, clean_snapshot: Arc>, symbols: &Option, - symbol_breakpoints: Option>, - coverage_breakpoints: BTreeMap, + symbol_breakpoints: Option, + coverage_breakpoints: CoverageBreakpoints, input_case: &PathBuf, project_state: &ProjectState, ) -> Result<()> { // Store the thread ID of this thread used for passing the SIGALRM to this thread + let thread_id = unsafe { libc::pthread_self() }; *THREAD_IDS[core_id.id].lock().unwrap() = Some(thread_id); diff --git a/src/commands/trace.rs b/src/commands/trace.rs index 14c7492..7dc7dc0 100644 --- a/src/commands/trace.rs +++ b/src/commands/trace.rs @@ -3,7 +3,6 @@ use anyhow::{anyhow, ensure, Context, Result}; use rustc_hash::FxHashSet; -use std::collections::BTreeMap; use std::fs::File; use std::os::unix::io::AsRawFd; use std::path::PathBuf; @@ -16,12 +15,14 @@ use kvm_ioctls::VmFd; use crate::fuzz_input::InputWithMetadata; use crate::fuzzer::{BreakpointType, Fuzzer}; -use crate::fuzzvm::{BreakpointHook, BreakpointMemory, FuzzVm, FuzzVmExit}; +use crate::fuzzvm::{ + BreakpointHook, BreakpointMemory, CoverageBreakpoints, FuzzVm, FuzzVmExit, ResetBreakpoints, +}; use crate::interrupts::IdtEntry; use crate::memory::Memory; use crate::{cmdline, fuzzvm, symbols, unblock_sigalrm, SymbolList, THREAD_IDS}; use crate::{handle_vmexit, init_environment, KvmEnvironment, ProjectState}; -use crate::{Cr3, Execution, ResetBreakpointType, Symbol, VbCpu, VirtAddr}; +use crate::{Cr3, Execution, Symbol, VbCpu, VirtAddr}; /// Number of iterations to execute the same input through the VM (for debugging) const NUMBER_OF_ITERATIONS: usize = 1; @@ -36,8 +37,8 @@ fn start_core( snapshot_fd: i32, clean_snapshot: Arc>, symbols: &Option, - symbol_breakpoints: Option>, - coverage_breakpoints: Option>, + symbol_breakpoints: Option, + coverage_breakpoints: Option, input_case: &Option, vm_timeout: Duration, single_step: bool, @@ -135,7 +136,7 @@ fn start_core( } } - if let Some(covbps) = project_state.coverage_breakpoints.as_ref() { + if let Some(covbps) = project_state.coverage_basic_blocks.as_ref() { for addr in covbps.keys().copied() { let res = fuzzvm.set_breakpoint( addr, diff --git a/src/fuzz_input.rs b/src/fuzz_input.rs index b09fb7c..8625667 100644 --- a/src/fuzz_input.rs +++ b/src/fuzz_input.rs @@ -26,12 +26,48 @@ use crate::cmp_analysis::RedqueenCoverage; #[cfg(feature = "redqueen")] use rustc_hash::FxHashSet; +/// Returned by the [`FuzzInput::minimize`] function to signal how to progress further. +#[derive(Debug, Clone, Copy, Default)] +pub enum MinimizeControlFlow { + /// Continue with minimization + #[default] + Continue, + /// Stop minimization after the current step + Stop, + /// Skip the current step, e.g., because the current minimization rule could not be applied. + Skip, + /// Continue minimization for at least that many steps. + ContinueFor(u32), +} + +impl MinimizeControlFlow { + /// continue minimization for at least one more iteration + pub fn one_more() -> Self { + Self::ContinueFor(1) + } + + /// continue minimization + pub fn cont() -> Self { + Self::Continue + } + + /// stop minimization after current step + pub fn stop() -> Self { + Self::Stop + } +} + /// An abstract input used in fuzzing. This trait provides methods for mutating, generating, and /// minimizing an input. This trait also has required methods for enabling Redqueen analysis /// for an input. pub trait FuzzInput: Sized + Debug + Default + Clone + Send + Hash + Eq + std::panic::UnwindSafe { + /// Type that represents minimization state. Use [`NullMinimizerState`] if you do not have a + /// useful state for your minimization algorithm. The state type must implement the + /// [`MinimizerState`] trait. + type MinState: MinimizerState + std::panic::RefUnwindSafe; + /// Function signature for a mutator of this type #[allow(clippy::type_complexity)] type MutatorFunc = fn( @@ -91,8 +127,22 @@ pub trait FuzzInput: max_length: usize, ) -> InputWithMetadata; + /// init stateful minimization + fn init_minimize(&mut self) -> (Self::MinState, MinimizeControlFlow) { + panic!( + "Minimize not implemented for {:?}", + std::any::type_name::() + ); + } + /// Minimize the given `input` based on a minimization strategy - fn minimize(_input: &mut Self, _rng: &mut Rng) { + fn minimize( + &mut self, + _state: &mut Self::MinState, + _current_iteration: u32, + _last_successful_iteration: u32, + _rng: &mut Rng, + ) -> MinimizeControlFlow { panic!( "Minimize not implemented for {:?}", std::any::type_name::() @@ -160,6 +210,45 @@ pub trait FuzzInput: } } +/// This is the trait to implement when you want to do provide a type for stateful minimization. +/// See [`BytesMinimizeState`] for an example. +pub trait MinimizerState: + Sized + Debug + Default + Clone + Send + Hash + Eq + std::panic::UnwindSafe +{ + /// Return true if the state is a stop state, i.e., there is nothing else to try. + fn is_stop_state(&self) -> bool; +} + +/// In case there is no useful minimization state, use this type as your minimization state. +/// +/// ```rust,ignore +/// +/// impl FuzzInput for YourType { +/// type MinState = NullMinimizerState; +/// +/// fn init_minimize(&mut self) -> (Self::MinState, MinimizeControlFlow) { +/// NullMinimizerState::init() +/// } +/// +/// } +/// ``` +#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)] +pub struct NullMinimizerState; + +impl MinimizerState for NullMinimizerState { + fn is_stop_state(&self) -> bool { + false + } +} + +#[allow(dead_code)] +impl NullMinimizerState { + /// to be used in [`FuzzInput:: + pub const fn init() -> (Self, MinimizeControlFlow) { + (Self {}, MinimizeControlFlow::Continue) + } +} + impl FuzzInput for Vec { #[cfg(feature = "redqueen")] type RuleCandidate = (usize, Endian); @@ -259,6 +348,158 @@ impl FuzzInput for Vec { mutations } + type MinState = BytesMinimizeState; + + fn init_minimize(&mut self) -> (Self::MinState, MinimizeControlFlow) { + ( + if self.is_empty() { + BytesMinimizeState::End + } else { + BytesMinimizeState::StartTruncate + }, + MinimizeControlFlow::Continue, + ) + } + + /// Minimize a `Vec` with a variety of different techniques + fn minimize( + &mut self, + state: &mut Self::MinState, + current_iteration: u32, + last_successful_iteration: u32, + rng: &mut Rng, + ) -> MinimizeControlFlow { + // Cannot minimize an empty input + if self.is_empty() { + *state = BytesMinimizeState::End; + return MinimizeControlFlow::Stop; + } + let last_succeeded = current_iteration == (last_successful_iteration + 1); + use BytesMinimizeState::*; + let mut cf = MinimizeControlFlow::Continue; + // Advance the state machine + *state = match *state { + // == deterministic minimization steps === + StartTruncate => { + cf = MinimizeControlFlow::one_more(); + FindTruncate(0, self.len()) + } + FindTruncate(low, high) => { + if low < high && low < self.len() { + cf = MinimizeControlFlow::one_more(); + let index = (low + high) / 2; + if last_succeeded { + FindTruncate(low, index - 1) + } else { + FindTruncate(index + 1, high) + } + } else { + // otherwise we transition to the next strategy. Replace with a constant + // starting from the back. We start by replacing whole + if self.len() >= 8 { + cf = MinimizeControlFlow::ContinueFor((self.len() / 8).try_into().unwrap()); + ReplaceConstBytes(self.len() - 8, &[b'A'; 8]) + } else { + cf = MinimizeControlFlow::ContinueFor(self.len().try_into().unwrap()); + Replace(self.len() - 1, b'A') + } + } + } + ReplaceConstBytes(0, data) => { + if data == [b'A'; 8] && self.len() >= 8 { + cf = MinimizeControlFlow::ContinueFor((self.len() / 8).try_into().unwrap()); + ReplaceConstBytes(self.len() - 8, &[0_u8; 8]) + } else { + cf = MinimizeControlFlow::ContinueFor(self.len().try_into().unwrap()); + Replace(self.len() - 1, 0) + } + } + ReplaceConstBytes(index, data) => ReplaceConstBytes(index.saturating_sub(8), data), + Replace(0, b'A') => { + cf = MinimizeControlFlow::ContinueFor(self.len().try_into().unwrap()); + Replace(self.len() - 1, 0) + } + Replace(0, 0) => Slices, + Replace(index, what) => Replace(index - 1, what), + // == probabilistic minimization steps === + // loop endlessly between states as long as the fuzzer wants + Slices => MultiBytes, + MultiBytes => SingleBytes, + SingleBytes => Slices, + // == end state === + End => End, + }; + + log::trace!("minimize with {:?}", state); + + // Perform the minimization strategy for this state + match *state { + StartTruncate => { + return MinimizeControlFlow::Skip; + } + FindTruncate(low, high) => { + let index = (low + high) / 2; + self.truncate(index); + } + ReplaceConstBytes(index, byte_slice) => { + let repl_len = byte_slice.len(); + if self[index..].len() >= repl_len { + self[index..(index + repl_len)].copy_from_slice(byte_slice); + } else { + return MinimizeControlFlow::Skip; + } + } + Replace(index, byte) => { + if let Some(b) = self.get_mut(index) { + if *b != byte { + *b = byte; + } else { + return MinimizeControlFlow::Skip; + } + } else { + return MinimizeControlFlow::Skip; + } + } + Slices => { + let curr_input_len = self.len(); + + let a = rng.gen_range(0..curr_input_len); + let b = rng.gen_range(0..curr_input_len); + let (first, second) = if a < b { (a, b) } else { (b, a) }; + self.splice(first..second, []); + } + MultiBytes => { + let count = rng.gen_range(0u32..32); + for _ in 0..count { + let curr_input_len = self.len(); + if curr_input_len > 1 { + let index = rng.gen_range(0..curr_input_len); + self.remove(index); + } else { + break; + } + } + } + SingleBytes => { + let index = rng.gen_range(0..self.len()); + self.remove(index); + } + End => { + return MinimizeControlFlow::Stop; + } + } + + if current_iteration > (3 * last_successful_iteration) + && matches!(state, Slices | MultiBytes | SingleBytes) + { + log::debug!("At iteration {current_iteration} and no progress since {last_successful_iteration} - giving up"); + *state = End; + MinimizeControlFlow::Stop + } else { + cf + } + } + #[cfg(feature = "redqueen")] fn apply_redqueen_rule( &mut self, @@ -508,63 +749,6 @@ impl FuzzInput for Vec { InputWithMetadata::from_input(result) } - /// Minimize a `Vec` with a variety of different techniques - fn minimize(input: &mut Self, rng: &mut Rng) { - // Cannot minimize an empty input - if input.is_empty() { - return; - } - - // Randomly pick the type of minimization - let state = match rng.next() % 12 { - 0..4 => MinimizeState::Slices, - 4..8 => MinimizeState::MultiBytes, - 8..10 => MinimizeState::SingleBytes, - 10..12 => MinimizeState::Replace(0xcd), - _ => unreachable!(), - }; - - // Perform the minimization strategy for this state - match state { - MinimizeState::Slices => { - let curr_input_len = input.len(); - - let a = rng.gen::() % curr_input_len; - let b = rng.gen::() % curr_input_len; - let (first, second) = if a < b { (a, b) } else { (b, a) }; - - let _slice_len = rng.gen::() % (curr_input_len - second); - - input.splice(first..second, []); - } - MinimizeState::MultiBytes => { - for _ in 0..=(rng.gen::() % 32) { - if input.is_empty() { - return; - } - - let curr_input_len = input.len(); - input.remove(rng.gen::() % curr_input_len); - } - } - MinimizeState::SingleBytes => { - let curr_input_len = input.len(); - input.remove(rng.gen::() % curr_input_len); - } - MinimizeState::Replace(redqueen_byte) => { - let curr_input_len = input.len(); - - for _ in 0..rng.gen::() % 32 { - // Get the new offset to replace - let offset = rng.gen::() % curr_input_len; - - // Found a newly replaced byte, replace it - input[offset] = redqueen_byte; - } - } - } - } - /// return shannon byte entropy for the bytes slice fn entropy_metric(&self) -> Option { Some(crate::utils::byte_entropy(self)) @@ -689,26 +873,45 @@ fn get_redqueen_rule_candidates_for_vec( candidates } -/// Stages of the minimization process -/// -/// Stages: -/// Slice - Attempt to delete slices of data to make the input smaller -/// Bytes - Attempt to delele individual bytes to make the input smaller -/// Replace - Attempt to find unnecessary byte values by replacing bytes with `?` -#[derive(Debug)] -enum MinimizeState { - /// This state tries to delete bytes of the input to make the input smaller +/// Stages for the minimization process of a byte string (e.g., `Vec`). +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub enum BytesMinimizeState { + /// Start a binary search to identify the right length to truncate the testcase to. + StartTruncate, + + /// Attempt to truncate to the given size. + FindTruncate(usize, usize), + + /// Replace unused sub-slices in the input with a constant slice. + ReplaceConstBytes(usize, &'static [u8]), + + /// Replace unused bytes in the input with a constant byte. + Replace(usize, u8), + + /// Delete a randomly-selected sub-slice of the input to make the input smaller. Slices, - /// This state tries to delete random bytes of the input to make the input smaller + /// Randomly select and delete multiple bytes in the input to make the input smaller. MultiBytes, - /// This state tries to delete single bytes of the input to make the input smaller + /// Delete a single randomly-selected byte in the input to make the input smaller. SingleBytes, - /// This state tries to identify unnecessary bytes in the input with a `?` to signify - /// the byte is not needed for the crash - Replace(u8), + /// signal immediate stop of minimization. + End, +} + +impl MinimizerState for BytesMinimizeState { + /// test if the given state represents a stop state + fn is_stop_state(&self) -> bool { + matches!(self, Self::End) + } +} + +impl Default for BytesMinimizeState { + fn default() -> Self { + Self::End + } } /// Endianness for the redqueen rules diff --git a/src/fuzzvm.rs b/src/fuzzvm.rs index 13a0ce8..b15a7a3 100644 --- a/src/fuzzvm.rs +++ b/src/fuzzvm.rs @@ -87,6 +87,13 @@ pub enum BreakpointMemory { NotDirty, } +/// Type that stores coverage breakpoints. +pub type CoverageBreakpoints = crate::FxIndexMap; +/// Map a tuple `(VirtAddr, Cr3)` pair to a index. Used to retrieve e.g., a breakpoint handler. +pub type Breakpoints = crate::FxIndexMap<(VirtAddr, Cr3), usize>; +/// Map a tuple `(VirtAddr, Cr3)` to a [`ResetBreakpointType`]. +pub type ResetBreakpoints = crate::FxIndexMap<(VirtAddr, Cr3), ResetBreakpointType>; + /// Hook function protoype pub type HookFn = fn( fuzzvm: &mut FuzzVm, @@ -487,7 +494,7 @@ pub struct FuzzVm<'a, FUZZER: Fuzzer> { /// Breakpoints currently set in the VM keyed with their original byte to potentially /// restore after it has been hit. The value in this map is the index into the /// various breakpoint arrays. This DOES NOT contain coverage breakpoints. - pub breakpoints: BTreeMap<(VirtAddr, Cr3), usize>, + pub breakpoints: Breakpoints, /// Original bytes for breakpoints in the VM indexed by the value in /// `self.breakpoints`. @@ -530,7 +537,7 @@ pub struct FuzzVm<'a, FUZZER: Fuzzer> { /// Current set of single shot breakpoints set that, when hit, add the address to the /// coverage database. This is an Option to enable a `.take()` to avoid a /// `&mut self ` collision when applying then breakpoints - pub coverage_breakpoints: Option>, + pub coverage_breakpoints: Option, /// Signifies if this VM will exit on syscalls. Handles whether /// `EferFlags::SYSTEM_CALL_EXTENSIONS` is enabled. @@ -539,7 +546,7 @@ pub struct FuzzVm<'a, FUZZER: Fuzzer> { /// Breakpoints that, if hit, signify a crash or reset in the guest. This is an /// Option to enable a `.take()` to avoid a `&mut self ` collision when applying then /// breakpoints - pub reset_breakpoints: Option>, + pub reset_breakpoints: Option, /// List of symbols available in this VM pub symbols: &'a Option, @@ -657,8 +664,8 @@ impl<'a, FUZZER: Fuzzer> FuzzVm<'a, FUZZER> { cpuid: &CpuId, snapshot_fd: i32, clean_snapshot: Arc>, - coverage_breakpoints: Option>, - reset_breakpoints: Option>, + coverage_breakpoints: Option, + reset_breakpoints: Option, symbols: &'a Option, config: Config, unwinders: StackUnwinders, @@ -757,7 +764,7 @@ impl<'a, FUZZER: Fuzzer> FuzzVm<'a, FUZZER> { vm, vcpu, vbcpu: *virtualbox_cpu, - breakpoints: BTreeMap::new(), + breakpoints: Breakpoints::default(), breakpoint_original_bytes: Vec::new(), breakpoint_types: Vec::new(), breakpoint_hooks: Vec::new(), @@ -884,7 +891,7 @@ impl<'a, FUZZER: Fuzzer> FuzzVm<'a, FUZZER> { // Add all of the reset/crash breakpoints given by the fuzzer if let Some(ref mut reset_bps) = fuzzvm.reset_breakpoints { - reset_bps.append(&mut new_reset_bps); + reset_bps.extend(&new_reset_bps); } // Remove all reset breakpoints from the coverage breakpoints if they exist @@ -4216,7 +4223,7 @@ impl<'a, FUZZER: Fuzzer> FuzzVm<'a, FUZZER> { { let _timer = self.scoped_timer(PerfMark::RqRecordCodeCov); if let FuzzVmExit::CoverageBreakpoint(rip) = &ret { - feedback.record_codecov(VirtAddr(*rip)); + feedback.record_codecov_hitcount(VirtAddr(*rip)); } } @@ -4398,7 +4405,7 @@ impl<'a, FUZZER: Fuzzer> FuzzVm<'a, FUZZER> { // Set the input into the VM as per the fuzzer fuzzer.set_input(input, self)?; - let mut hit_breakpoints = BTreeMap::new(); + let mut hit_breakpoints = CoverageBreakpoints::default(); let cr3 = self.cr3(); diff --git a/src/lib.rs b/src/lib.rs index ea630e9..f4f5962 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -124,7 +124,6 @@ use vmm_sys_util::fam::FamStructWrapper; extern crate bitflags; -use std::collections::BTreeMap; use std::convert::TryInto; use std::fs::{File, OpenOptions}; use std::os::unix::io::AsRawFd; @@ -157,7 +156,6 @@ mod page_table; pub mod fuzzer; pub use fuzzer::Fuzzer; -use fuzzer::ResetBreakpointType; mod symbols; pub use symbols::Symbol; @@ -842,7 +840,7 @@ struct KvmEnvironment { symbols: Option, /// Parsed symbol breakpoints if any coverage breakpoints are available in the project - symbol_breakpoints: Option>, + symbol_breakpoints: Option, } /// Perform KVM initialization routines and common project setup steps for all @@ -1015,6 +1013,7 @@ pub mod prelude { addrs::{Cr3, VirtAddr}, anyhow, anyhow::Result, + fuzz_input::{BytesMinimizeState, MinimizeControlFlow, NullMinimizerState}, fuzzer::{AddressLookup, Breakpoint, BreakpointType, Fuzzer}, fuzzvm::FuzzVm, rand, diff --git a/src/stack_unwinder.rs b/src/stack_unwinder.rs index 3c74eac..4ec130f 100644 --- a/src/stack_unwinder.rs +++ b/src/stack_unwinder.rs @@ -314,7 +314,9 @@ impl StackUnwinders { let mut iter = backtrace.iter().skip(backtrace.len() - MAX_SAME_RECURSION); // Get the first element from the iterator to check against - let Some(first) = iter.next() else { continue; }; + let Some(first) = iter.next() else { + continue; + }; if iter.all(|addr| addr == first) { // Found a backtrace with too many recursions, end the backtrace here @@ -358,8 +360,9 @@ pub fn get_instr_containing( // Get the instruction for the current address let Ok(instr) = fuzzvm .memory - .get_instruction_at(VirtAddr(curr_addr), fuzzvm.cr3()) else { - continue; + .get_instruction_at(VirtAddr(curr_addr), fuzzvm.cr3()) + else { + continue; }; let ending_addr = curr_addr + instr.len() as u64; @@ -403,15 +406,15 @@ impl StackUnwinder { // If we don't have an .eh_frame_hdr, return None let Some(ref eh_frame_hdr) = object.section_by_name(".eh_frame_hdr") else { - log::info!("{binary:?} has no .eh_frame_hdr"); - return Ok(None); - }; + log::info!("{binary:?} has no .eh_frame_hdr"); + return Ok(None); + }; // If we don't have an .eh_frame, return None let Some(ref eh_frame) = object.section_by_name(".eh_frame") else { - log::info!("{binary:?} has no .eh_frame"); - return Ok(None); - }; + log::info!("{binary:?} has no .eh_frame"); + return Ok(None); + }; // Set the address of the sections in the base addresses base_addresses = base_addresses.set_eh_frame_hdr(eh_frame_hdr.address() + base_address); diff --git a/src/stats.rs b/src/stats.rs index bd8287d..47d9be5 100644 --- a/src/stats.rs +++ b/src/stats.rs @@ -1515,7 +1515,6 @@ pub fn worker( ) .unwrap(); } - } else { } } } @@ -1962,6 +1961,11 @@ pub fn worker( iters_index = (iters_index + 1) % ITERS_WINDOW_SIZE; } // Start of iteration loop + let mut corpus_len = 0; + for input in &total_corpus { + corpus_len += save_input_in_project(input, project_dir, "current_corpus")?; + } + if tui { // Restore the terminal to the original state crate::stats_tui::restore_terminal()?; @@ -2005,12 +2009,6 @@ pub fn worker( .join("\n"), )?; - let mut corpus_len = 0; - - for input in &total_corpus { - corpus_len += save_input_in_project(input, project_dir, "current_corpus")?; - } - std::fs::write( &coverage_blockers_in_path_file, coverage_blockers_in_path.join("\n"), @@ -2213,7 +2211,7 @@ impl DebugInfo { contexts: &'a ContextSlice, ) -> Result<()> { let cov_bps = project_state - .coverage_breakpoints + .coverage_basic_blocks .as_ref() .ok_or(anyhow::anyhow!( "Coverage breakpoints required for source coverage" diff --git a/src/utils.rs b/src/utils.rs index 3008940..e34b011 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -492,6 +492,31 @@ pub fn hexdigest(t: &T) -> String { format!("{h:016x}") } +/// Get all of the files found in the given path recursively +pub fn get_files>( + path: P, + recurse: bool, +) -> Result, std::io::Error> { + let mut files = Vec::new(); + let entries = std::fs::read_dir(path)?; + + for entry in entries { + let entry = entry?; + let file_type = entry.file_type()?; + + if file_type.is_dir() { + if recurse { + let dir_files = get_files(&entry.path(), recurse)?; + files.extend(dir_files); + } + } else if file_type.is_file() { + files.push(entry.path()); + } + } + + Ok(files) +} + /// Save the [`InputWithMetadata`] into the project directory using the hash of input as the filename /// /// # Errors @@ -499,10 +524,10 @@ pub fn hexdigest(t: &T) -> String { /// * Given `input.to_bytes()` failed /// * Creating the corpus or metadata directory failed /// * Failed to write the bytes to disk -pub fn save_input_in_project( +pub fn save_input_in_project>( input: &InputWithMetadata, project_dir: &Path, - dir: &str, + dir: P, ) -> Result { let input_bytes = input.input_as_bytes()?; let length = input_bytes.len(); @@ -510,7 +535,7 @@ pub fn save_input_in_project( // Create the filename for this input let filename = hexdigest(&input); - let corpus_dir = project_dir.join(dir); + let corpus_dir = project_dir.join(dir.as_ref()); let metadata_dir = project_dir.join("metadata"); // Ensure the corpus and metadata directories exist