forked from p98n2ja4z/AFLplusplus
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
453 lines
11 KiB
453 lines
11 KiB
#!/bin/sh
|
|
#
|
|
# american fuzzy lop++ - status check tool
|
|
# ----------------------------------------
|
|
#
|
|
# Originally written by Michal Zalewski
|
|
#
|
|
# Copyright 2015 Google Inc. All rights reserved.
|
|
# Copyright 2019-2024 AFLplusplus Project. All rights reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at:
|
|
#
|
|
# https://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# This tool summarizes the status of any locally-running synchronized
|
|
# instances of afl-fuzz.
|
|
#
|
|
|
|
test "$1" = "-h" -o "$1" = "-hh" && {
|
|
echo "$0 status check tool for afl-fuzz by Michal Zalewski"
|
|
echo
|
|
echo "Usage: $0 [-s] [-d] afl_output_directory"
|
|
echo
|
|
echo Options:
|
|
echo " -d - include dead fuzzer stats"
|
|
echo " -m - just show minimal stats"
|
|
echo " -n - no color output"
|
|
echo " -s - skip details and output summary results only"
|
|
echo
|
|
exit 1
|
|
}
|
|
|
|
unset MINIMAL_ONLY
|
|
unset NO_COLOR
|
|
unset PROCESS_DEAD
|
|
unset SUMMARY_ONLY
|
|
unset RED
|
|
unset GREEN
|
|
unset YELLOW
|
|
unset BLUE
|
|
unset NC
|
|
unset RESET
|
|
|
|
if [ -z "$TERM" ]; then export TERM=vt220; fi
|
|
|
|
while [ "$1" = "-d" -o "$1" = "-m" -o "$1" = "-n" -o "$1" = "-s" ]; do
|
|
|
|
if [ "$1" = "-d" ]; then
|
|
PROCESS_DEAD=1
|
|
fi
|
|
|
|
if [ "$1" = "-m" ]; then
|
|
MINIMAL_ONLY=1
|
|
fi
|
|
|
|
if [ "$1" = "-n" ]; then
|
|
NO_COLOR=1
|
|
fi
|
|
|
|
if [ "$1" = "-s" ]; then
|
|
SUMMARY_ONLY=1
|
|
fi
|
|
|
|
shift
|
|
|
|
done
|
|
|
|
DIR="$1"
|
|
|
|
if [ "$DIR" = "" -o "$DIR" = "-h" -o "$DIR" = "--help" ]; then
|
|
|
|
echo "$0 status check tool for afl-fuzz by Michal Zalewski" 1>&2
|
|
echo 1>&2
|
|
echo "Usage: $0 [-d] [-m] [-n] [-s] afl_output_directory" 1>&2
|
|
echo 1>&2
|
|
echo Options: 1>&2
|
|
echo " -d - include dead fuzzer stats" 1>&2
|
|
echo " -m - just show minimal stats" 1>&2
|
|
echo " -n - no color output" 1>&2
|
|
echo " -s - skip details and output summary results only" 1>&2
|
|
echo 1>&2
|
|
exit 1
|
|
|
|
fi
|
|
|
|
if [ -z "$MINIMAL_ONLY" ]; then
|
|
echo "$0 status check tool for afl-fuzz by Michal Zalewski"
|
|
echo
|
|
fi
|
|
|
|
cd "$DIR" || exit 1
|
|
|
|
if [ -d queue ]; then
|
|
|
|
echo "[-] Error: parameter is an individual output directory, not a sync dir." 1>&2
|
|
exit 1
|
|
|
|
fi
|
|
|
|
BC=`which bc 2>/dev/null`
|
|
FUSER=`which fuser 2>/dev/null`
|
|
|
|
if [ -z "$NO_COLOR" ]; then
|
|
RED=`tput setaf 9 1 1 2>/dev/null`
|
|
GREEN=`tput setaf 2 1 1 2>/dev/null`
|
|
BLUE=`tput setaf 4 1 1 2>/dev/null`
|
|
YELLOW=`tput setaf 11 1 1 2>/dev/null`
|
|
NC=`tput sgr0`
|
|
RESET="$NC"
|
|
fi
|
|
|
|
PLATFORM=`uname -s`
|
|
#if [ "$PLATFORM" = "Linux" ] ; then
|
|
# CUR_TIME=`cat /proc/uptime | awk '{printf "%.0f\n", $1}'`
|
|
#else
|
|
# This will lead to inacurate results but will prevent the script from breaking on platforms other than Linux
|
|
CUR_TIME=`date +%s`
|
|
#fi
|
|
|
|
TMP=`mktemp -t .afl-whatsup-XXXXXXXX` || TMP=`mktemp -p /data/local/tmp .afl-whatsup-XXXXXXXX` || TMP=`mktemp -p /data/local/tmp .afl-whatsup-XXXXXXXX` || exit 1
|
|
trap "rm -f $TMP" 1 2 3 13 15
|
|
|
|
ALIVE_CNT=0
|
|
DEAD_CNT=0
|
|
START_CNT=0
|
|
|
|
TOTAL_TIME=0
|
|
TOTAL_EXECS=0
|
|
TOTAL_EPS=0
|
|
TOTAL_EPLM=0
|
|
TOTAL_CRASHES=0
|
|
TOTAL_HANGS=0
|
|
TOTAL_PFAV=0
|
|
TOTAL_PENDING=0
|
|
TOTAL_COVERAGE=
|
|
|
|
# Time since last find / crash / hang, formatted as string
|
|
FMT_TIME="0 days 0 hours"
|
|
FMT_FIND="${RED}none seen yet${NC}"
|
|
FMT_CRASH="none seen yet"
|
|
FMT_HANG="none seen yet"
|
|
|
|
if [ "$SUMMARY_ONLY" = "" ]; then
|
|
|
|
echo "Individual fuzzers"
|
|
echo "=================="
|
|
echo
|
|
|
|
fi
|
|
|
|
fmt_duration()
|
|
{
|
|
DUR_STRING=
|
|
if [ $1 -le 0 ]; then
|
|
return 1
|
|
fi
|
|
|
|
local duration=$((CUR_TIME - $1))
|
|
local days=$((duration / 60 / 60 / 24))
|
|
local hours=$(((duration / 60 / 60) % 24))
|
|
local minutes=$(((duration / 60) % 60))
|
|
local seconds=$((duration % 60))
|
|
|
|
if [ $duration -le 0 ]; then
|
|
DUR_STRING="0 seconds"
|
|
elif [ $duration -eq 1 ]; then
|
|
DUR_STRING="1 second"
|
|
elif [ $days -gt 0 ]; then
|
|
DUR_STRING="$days days, $hours hours"
|
|
elif [ $hours -gt 0 ]; then
|
|
DUR_STRING="$hours hours, $minutes minutes"
|
|
elif [ $minutes -gt 0 ]; then
|
|
DUR_STRING="$minutes minutes, $seconds seconds"
|
|
else
|
|
DUR_STRING="$seconds seconds"
|
|
fi
|
|
}
|
|
|
|
FIRST=true
|
|
TOTAL_WCOP=
|
|
TOTAL_LAST_FIND=0
|
|
|
|
for j in `find . -maxdepth 2 -iname fuzzer_setup | sort`; do
|
|
|
|
DIR=$(dirname "$j")
|
|
i=$DIR/fuzzer_stats
|
|
|
|
if [ -f "$i" ]; then
|
|
|
|
IS_STARTING=
|
|
IS_DEAD=
|
|
sed 's/^command_line.*$/_skip:1/;s/[ ]*:[ ]*/="/;s/$/"/' "$i" >"$TMP"
|
|
. "$TMP"
|
|
DIRECTORY=$DIR
|
|
DIR=${DIR##*/}
|
|
RUN_UNIX=$run_time
|
|
RUN_DAYS=$((RUN_UNIX / 60 / 60 / 24))
|
|
RUN_HRS=$(((RUN_UNIX / 60 / 60) % 24))
|
|
COVERAGE=$(echo $bitmap_cvg|tr -d %)
|
|
if [ -n "$TOTAL_COVERAGE" -a -n "$COVERAGE" -a -n "$BC" ]; then
|
|
if [ "$(echo "$TOTAL_COVERAGE < $COVERAGE" | bc)" -eq 1 ]; then
|
|
TOTAL_COVERAGE=$COVERAGE
|
|
fi
|
|
fi
|
|
if [ -z "$TOTAL_COVERAGE" ]; then TOTAL_COVERAGE=$COVERAGE ; fi
|
|
|
|
test -n "$cycles_wo_finds" && {
|
|
test -z "$FIRST" && TOTAL_WCOP="${TOTAL_WCOP}/"
|
|
TOTAL_WCOP="${TOTAL_WCOP}${cycles_wo_finds}"
|
|
FIRST=
|
|
}
|
|
|
|
if [ "$SUMMARY_ONLY" = "" ]; then
|
|
|
|
echo ">>> $afl_banner instance: $DIR ($RUN_DAYS days, $RUN_HRS hrs) fuzzer PID: $fuzzer_pid <<<"
|
|
echo
|
|
|
|
fi
|
|
|
|
if ! kill -0 "$fuzzer_pid" 2>/dev/null; then
|
|
|
|
if [ -e "$i" ] && [ -e "$j" ] && [ -n "$FUSER" ]; then
|
|
|
|
if [ "$i" -ot "$j" ]; then
|
|
|
|
# fuzzer_setup is newer than fuzzer_stats, maybe the instance is starting?
|
|
TMP_PID=`fuser -v "$DIRECTORY" 2>&1 | grep afl-fuzz`
|
|
|
|
if [ -n "$TMP_PID" ]; then
|
|
|
|
if [ "$SUMMARY_ONLY" = "" ]; then
|
|
|
|
echo " Instance is still starting up, skipping."
|
|
echo
|
|
|
|
fi
|
|
|
|
START_CNT=$((START_CNT + 1))
|
|
last_find=0
|
|
IS_STARTING=1
|
|
|
|
if [ "$PROCESS_DEAD" = "" ]; then
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
if [ -z "$IS_STARTING" ]; then
|
|
|
|
if [ "$SUMMARY_ONLY" = "" ]; then
|
|
|
|
echo " Instance is dead or running remotely, skipping."
|
|
echo
|
|
|
|
fi
|
|
|
|
DEAD_CNT=$((DEAD_CNT + 1))
|
|
IS_DEAD=1
|
|
last_find=0
|
|
|
|
if [ "$PROCESS_DEAD" = "" ]; then
|
|
|
|
continue
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
ALIVE_CNT=$((ALIVE_CNT + 1))
|
|
|
|
EXEC_SEC=0
|
|
EXEC_MIN=0
|
|
test -z "$RUN_UNIX" -o "$RUN_UNIX" = 0 || EXEC_SEC=$((execs_done / RUN_UNIX))
|
|
PATH_PERC=$((cur_item * 100 / corpus_count))
|
|
|
|
test "$IS_DEAD" = 1 || EXEC_MIN=$(echo $execs_ps_last_min|sed 's/\..*//')
|
|
|
|
TOTAL_TIME=$((TOTAL_TIME + RUN_UNIX))
|
|
TOTAL_EPS=$((TOTAL_EPS + EXEC_SEC))
|
|
TOTAL_EPLM=$((TOTAL_EPLM + EXEC_MIN))
|
|
TOTAL_EXECS=$((TOTAL_EXECS + execs_done))
|
|
TOTAL_CRASHES=$((TOTAL_CRASHES + saved_crashes))
|
|
TOTAL_HANGS=$((TOTAL_HANGS + saved_hangs))
|
|
TOTAL_PENDING=$((TOTAL_PENDING + pending_total))
|
|
TOTAL_PFAV=$((TOTAL_PFAV + pending_favs))
|
|
|
|
if [ "$last_find" -gt "$TOTAL_LAST_FIND" ]; then
|
|
TOTAL_LAST_FIND=$last_find
|
|
fi
|
|
|
|
if [ "$SUMMARY_ONLY" = "" ]; then
|
|
|
|
# Warnings in red
|
|
TIMEOUT_PERC=$((exec_timeout * 100 / execs_done))
|
|
if [ $TIMEOUT_PERC -ge 10 ]; then
|
|
echo " ${RED}timeout_ratio $TIMEOUT_PERC%${NC}"
|
|
fi
|
|
|
|
if [ $EXEC_SEC -eq 0 ]; then
|
|
echo " ${YELLOW}no data yet, 0 execs/sec${NC}"
|
|
elif [ $EXEC_SEC -lt 100 ]; then
|
|
echo " ${RED}slow execution, $EXEC_SEC execs/sec${NC}"
|
|
fi
|
|
|
|
fmt_duration $last_find && FMT_FIND=$DUR_STRING
|
|
fmt_duration $last_crash && FMT_CRASH=$DUR_STRING
|
|
fmt_duration $last_hang && FMT_HANG=$DUR_STRING
|
|
FMT_CWOP="not available"
|
|
test -n "$cycles_wo_finds" && {
|
|
test "$cycles_wo_finds" = 0 && FMT_CWOP="$cycles_wo_finds"
|
|
test "$cycles_wo_finds" -gt 10 && FMT_CWOP="${YELLOW}$cycles_wo_finds${NC}"
|
|
test "$cycles_wo_finds" -gt 50 && FMT_CWOP="${RED}$cycles_wo_finds${NC}"
|
|
}
|
|
|
|
echo " last_find : $FMT_FIND"
|
|
echo " last_crash : $FMT_CRASH"
|
|
if [ -z "$MINIMAL_ONLY" ]; then
|
|
echo " last_hang : $FMT_HANG"
|
|
echo " cycles_wo_finds : $FMT_CWOP"
|
|
fi
|
|
echo " coverage : $COVERAGE%"
|
|
|
|
if [ -z "$MINIMAL_ONLY" ]; then
|
|
|
|
CPU_USAGE=$(ps aux | grep -w $fuzzer_pid | grep -v grep | awk '{print $3}')
|
|
MEM_USAGE=$(ps aux | grep -w $fuzzer_pid | grep -v grep | awk '{print $4}')
|
|
|
|
echo " cpu usage $CPU_USAGE%, memory usage $MEM_USAGE%"
|
|
|
|
fi
|
|
|
|
echo " cycles $((cycles_done + 1)), lifetime speed $EXEC_SEC execs/sec, items $cur_item/$corpus_count (${PATH_PERC}%)"
|
|
|
|
if [ "$saved_crashes" = "0" ]; then
|
|
echo " pending $pending_favs/$pending_total, coverage $bitmap_cvg, no crashes yet"
|
|
else
|
|
echo " pending $pending_favs/$pending_total, coverage $bitmap_cvg, crashes saved $saved_crashes (!)"
|
|
fi
|
|
|
|
echo
|
|
|
|
fi
|
|
|
|
else
|
|
|
|
if [ ! -e "$i" -a -e "$j" ]; then
|
|
|
|
if [ '!' "$PROCESS_DEAD" = "" ]; then
|
|
ALIVE_CNT=$((ALIVE_CNT + 1))
|
|
fi
|
|
START_CNT=$((START_CNT + 1))
|
|
last_find=0
|
|
IS_STARTING=1
|
|
|
|
fi
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
# Formatting for total time, time since last find, crash, and hang
|
|
fmt_duration $((CUR_TIME - TOTAL_TIME)) && FMT_TIME=$DUR_STRING
|
|
# Formatting for total execution
|
|
FMT_EXECS="0 millions"
|
|
EXECS_MILLION=$((TOTAL_EXECS / 1000 / 1000))
|
|
EXECS_THOUSAND=$((TOTAL_EXECS / 1000 % 1000))
|
|
if [ $EXECS_MILLION -gt 9 ]; then
|
|
FMT_EXECS="$EXECS_MILLION millions"
|
|
elif [ $EXECS_MILLION -gt 0 ]; then
|
|
FMT_EXECS="$EXECS_MILLION millions, $EXECS_THOUSAND thousands"
|
|
else
|
|
FMT_EXECS="$EXECS_THOUSAND thousands"
|
|
fi
|
|
|
|
rm -f "$TMP"
|
|
|
|
TOTAL_DAYS=$((TOTAL_TIME / 60 / 60 / 24))
|
|
TOTAL_HRS=$(((TOTAL_TIME / 60 / 60) % 24))
|
|
|
|
test -z "$TOTAL_WCOP" && TOTAL_WCOP="not available"
|
|
fmt_duration $TOTAL_LAST_FIND && TOTAL_LAST_FIND=$DUR_STRING
|
|
|
|
test "$TOTAL_TIME" = "0" && TOTAL_TIME=1
|
|
|
|
if [ "$PROCESS_DEAD" = "" ]; then
|
|
|
|
TXT="excluded from stats"
|
|
|
|
else
|
|
|
|
TXT="included in stats"
|
|
ALIVE_CNT=$(($ALIVE_CNT - $DEAD_CNT - $START_CNT))
|
|
|
|
fi
|
|
|
|
echo "Summary stats"
|
|
echo "============="
|
|
if [ -z "$SUMMARY_ONLY" -o -z "$MINIMAL_ONLY" ]; then
|
|
echo
|
|
fi
|
|
|
|
echo " Fuzzers alive : $ALIVE_CNT"
|
|
|
|
if [ ! "$START_CNT" = "0" ]; then
|
|
echo " Starting up : $START_CNT ($TXT)"
|
|
fi
|
|
|
|
if [ ! "$DEAD_CNT" = "0" ]; then
|
|
echo " Dead or remote : $DEAD_CNT ($TXT)"
|
|
fi
|
|
|
|
echo " Total run time : $FMT_TIME"
|
|
if [ -z "$MINIMAL_ONLY" ]; then
|
|
echo " Total execs : $FMT_EXECS"
|
|
echo " Cumulative speed : $TOTAL_EPS execs/sec"
|
|
if [ "$ALIVE_CNT" -gt "0" ]; then
|
|
echo " Total average speed : $((TOTAL_EPS / ALIVE_CNT)) execs/sec"
|
|
fi
|
|
fi
|
|
if [ "$ALIVE_CNT" -gt "0" ]; then
|
|
echo "Current average speed : $TOTAL_EPLM execs/sec"
|
|
fi
|
|
if [ -z "$MINIMAL_ONLY" ]; then
|
|
echo " Pending items : $TOTAL_PFAV faves, $TOTAL_PENDING total"
|
|
fi
|
|
|
|
if [ "$ALIVE_CNT" -gt "1" -o -n "$MINIMAL_ONLY" ]; then
|
|
if [ "$ALIVE_CNT" -gt "0" ]; then
|
|
echo " Pending per fuzzer : $((TOTAL_PFAV/ALIVE_CNT)) faves, $((TOTAL_PENDING/ALIVE_CNT)) total (on average)"
|
|
fi
|
|
fi
|
|
|
|
echo " Coverage reached : ${TOTAL_COVERAGE}%"
|
|
echo " Crashes saved : $TOTAL_CRASHES"
|
|
if [ -z "$MINIMAL_ONLY" ]; then
|
|
echo " Hangs saved : $TOTAL_HANGS"
|
|
echo " Cycles without finds : $TOTAL_WCOP"
|
|
fi
|
|
echo " Time without finds : $TOTAL_LAST_FIND"
|
|
echo
|
|
|
|
exit 0
|