about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--README.md9
-rw-r--r--cover.c4
-rw-r--r--fix.m493
-rw-r--r--jump.c4
-rw-r--r--reach27
6 files changed, 101 insertions, 38 deletions
diff --git a/Makefile b/Makefile
index 3b1e5f5..d707904 100644
--- a/Makefile
+++ b/Makefile
@@ -8,7 +8,7 @@ PREFIX ?= /usr/local
 BIN_PREFIX ::= $(DESTDIR)$(PREFIX)/bin/taosc-
 DATA_DIR ::= $(DESTDIR)$(PREFIX)/share/taosc
 
-BIN ::= fix measure-stack scout synth trace-call
+BIN ::= fix measure-stack reach scout synth trace-call
 DATA ::= collect cover jump patch
 
 all: $(BIN) $(DATA)
diff --git a/README.md b/README.md
index b1d40e9..7a91a02 100644
--- a/README.md
+++ b/README.md
@@ -4,8 +4,9 @@ Taosc is an automated makeshift patcher for binary programs.
 
 ## Installation
 
-Taosc depends on GDB, [Dyninst] and [E9Patch] and POSIX utilities.
-To build taosc, you need [GNU M4] and a compiler for C++23 and [Zig] 0.15:
+Taosc depends on GDB, [Dyninst], [E9Patch], [FUZZOLIC], [GNU Parallel]
+and POSIX utilities.  To build taosc, you need [GNU M4] and a compiler
+for C++23 and [Zig] 0.15:
 
     make -j$(nproc)
 
@@ -15,7 +16,7 @@ To install taosc to `$prefix`, you'll also need `install(1p)`:
 
 ## Usage
 
-    taosc-fix WORKDIR EXECUTABLE OPTION...
+    taosc-fix WORKDIR TIMEOUT EXECUTABLE PROOFS-OF-CONCEPT [OPTION]...
 
 ## Copying
 
@@ -26,5 +27,7 @@ or (at your option) any later version.
 
 [Dyninst]: https://github.com/dyninst/dyninst
 [E9Patch]: https://github.com/gjduck/e9patch
+[FUZZOLIC]: https://season-lab.github.io/fuzzolic
 [GNU M4]: https://www.gnu.org/software/m4
+[GNU Parallel]: https://www.gnu.org/software/parallel
 [Zig]: https://ziglang.org
diff --git a/cover.c b/cover.c
index 3061646..591224c 100644
--- a/cover.c
+++ b/cover.c
@@ -1,6 +1,6 @@
 /*
- * Live variable collector
- * Copyright (C) 2024-2025  Nguyễn Gia Phong
+ * Patch for coverage reporting
+ * Copyright (C) 2025  Nguyễn Gia Phong
  *
  * This file is part of taosc.
  *
diff --git a/fix.m4 b/fix.m4
index 065f91e..3425e53 100644
--- a/fix.m4
+++ b/fix.m4
@@ -20,30 +20,40 @@
 set -eux -o pipefail
 save_exit_code() {
   set +e
-  # TODO: make timeout configurable
-  timeout -k 1 5 $@ 2>&1 1>/dev/null
+  timeout -k 1 $1 ${@:2} 1>/dev/null 2>&1
   exit_code=$?
   set -e
 }
 
-if test $# -lt 3
+bad() {
+  save_exit_code $@
+  test $exit_code -gt 128 ||
+    test $exit_code -ge 124 -a $exit_code -le 127 # timeout
+}
+
+if test $# -lt 4
 then
-  echo Usage: taosc-fix WORKDIR EXECUTABLE OPTION...
+  echo Usage: taosc-fix WORKDIR TIMEOUT EXECUTABLE PROOFS-OF-CONCEPT [OPTION]...
   exit 1
 fi
 wd="$(realpath $1)"
 test -d "$wd"
-bin="$wd/$(basename $2)"
-binary="$(realpath $2)"
+timeout=$2
+bin="$wd/$(basename $3)"
+binary="$(realpath $3)"
 test -x "$binary"
-opts="${@:3}" # TODO: interpolation
+poc="$(realpath $4)"
+test -d "$poc"
+test "$(ls -A "$poc")"
+options="${@:5}" # TODO: interpolation
 
-test -d "$wd/exploits"
-test -n "$(ls -A "$wd/exploits")"
-for exploit in "$wd/exploits"/*
+mkdir -p "$wd"
+rm -fr "$wd/poc"
+cp -r "$poc" "$wd/poc"
+for exploit in "$wd"/poc/*
 do
   gdb --batch --ex run --ex backtrace --args\
-    "$binary" "$opts" "$exploit" 2>/dev/null |
+    "$binary" $options "$exploit" 2>/dev/null |
     grep '^#[0-9]\+ \+0x[0-9a-f]\+' |
     awk '!$7 || $7 == bin {print $1, $2}' "bin=$binary" |
     sed 's/^#//'
@@ -58,8 +68,8 @@ grep -v '^0 0x[0-9a-f]\+$' "$wd/stack-trace" |
   taosc-trace-call "$binary" >> "$wd/call-trace"
 
 rm -f "$wd/patch-location"
-pushd DATA_DIR > /dev/null
-trap 'popd > /dev/null' EXIT
+pushd DATA_DIR 1>/dev/null
+trap 'popd 1>/dev/null' EXIT
 taosc-scout "$binary" < "$wd/call-trace" |
   while read loc destinations && test ! -f "$wd/patch-location"
   do
@@ -67,11 +77,12 @@ taosc-scout "$binary" < "$wd/call-trace" |
     rm -f "$wd/destinations"
     for dest in $destinations
     do
-      for exploit in "$wd/exploits"/*
+      # In case $wd/poc got poluted
+      rm -fr "$wd/poc"
+      cp -r "$poc" "$wd/poc"
+      for exploit in "$wd/poc"/*
       do
-        save_exit_code env TAOSC_DEST=0x$dest "$bin.jump" "$opts" "$exploit"
-        if test $exit_code -gt 128 ||
-          test $exit_code -ge 124 -a $exit_code -le 127 # timeout
+        if bad $timeout env TAOSC_DEST=0x$dest "$bin.jump" $options "$exploit"
         then
           continue 2 # next destination
         fi
@@ -79,27 +90,49 @@ taosc-scout "$binary" < "$wd/call-trace" |
       echo $loc > "$wd/patch-location"
       echo $dest >> "$wd/destinations"
     done
-  done 2>&1 1>/dev/null
+  done 1>/dev/null 2>&1
 test -s "$wd/patch-location"
 test -s "$wd/destinations"
 
+stack_size=$(taosc-measure-stack "$binary" < "$wd/patch-location")
 patch_loc=0x$(< "$wd/patch-location")
 e9tool -100 -M addr=$patch_loc -P 'report()@cover' -o "$bin.covered" "$binary"
 e9tool -100 -M addr=$patch_loc -P 'log(state)@collect'\
   -o "$bin.collect" "$binary"
 e9tool -100 -M addr=$patch_loc -P 'if dest(state)@patch goto'\
   -o "$bin.patched" "$binary"
-exit
-stack_size=$(taosc-measure-stack "$binary" < "$wd/patch-location")
 
-afl-dyninst -x "$binary" "$bin.fuzzee"
-install -Dm 644 DATA_DIR/collection "$wd/vars/list"
-# TODO: augment number of executions
-afl-dyninst-env afl-fuzz -i "$wd/fuzz/exploits" -o "$wd/fuzz/crashes"\
-  -CE 10000 -- "$bin.fuzzee" $opts @@
-install -d "$wd/vars/neg"
-find "$wd/fuzz/crashes/default/crashes" -name id:* | parallel\
-  TAOSC_OUTPUT="$wd/vars/neg/"'$(basename {})' "$bin.collect" $opts {} || true
-taosc-synth "$wd/vars" > "$wd/predicates"
-taosc-scout "$binary" "$address" > "$wd/destinations"
+# TODO: FUZZOLIC's options
+fuzzolic -kmprst 90000 -i "$poc" -o "$wd/fuzzolic" -- "$binary" $option @@
+rm -fr "$wd/input"
+mkdir -p "$wd/input/benign"
+cp -r "$poc" "$wd/input/malicious"
+# TODO: use parallel
+for dat in "$wd"/fuzzolic/fuzzolic-*/test_case_*.dat
+do
+  if taosc-reach $timeout "$bin.covered" $options "$dat" 1>/dev/null 2>&1
+  then
+    if bad $timeout "$binary" $options "$dat"
+    then
+      cp $dat "$wd/input/malicious"
+    else
+      cp $dat "$wd/input/benign"
+    fi
+  fi
+done
+
+rm -fr "$wd/values"
+for input_dir in "$wd"/input/*
+do
+  output_dir="$wd/values/$(basename "$input_dir")"
+  mkdir -p "$output_dir"
+  # TODO: use parallel
+  for input in "$input_dir"/*
+  do
+    output="$output_dir/$(basename "$input")"
+    save_exit_code $timeout\
+      env TAOSC_STACK_SIZE=$stack_size TAOSC_OUTPUT=$output\
+      "$bin.collect" $options "$input"
+  done
+done
 # vim: filetype=sh.m4
diff --git a/jump.c b/jump.c
index 794d79d..5110ae9 100644
--- a/jump.c
+++ b/jump.c
@@ -1,5 +1,5 @@
 /*
- * TODO
+ * Dynamic patch to jump to bailout action
  * Copyright (C) 2024-2025  Nguyễn Gia Phong
  *
  * This file is part of taosc.
@@ -26,7 +26,7 @@ static const void *destination;
  * Get an environment variable and parse as a number.
  * Return 0 on error.
  */
-uint64_t getenvull(const char *name)
+static uint64_t getenvull(const char *name)
 {
 	const char *const s = getenv(name);
 	if (s == NULL)
diff --git a/reach b/reach
new file mode 100644
index 0000000..1827fc5
--- /dev/null
+++ b/reach
@@ -0,0 +1,27 @@
+#!/bin/sh
+# Patch location coverage checker
+# Copyright (C) 2025  Nguyễn Gia Phong
+#
+# This file is part of taosc.
+#
+# Taosc is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# Taosc is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with taosc.  If not, see <https://www.gnu.org/licenses/>.
+
+set -u
+test $# -lt 2 && echo Usage: taosc-reach DURATION COMMAND [ARG]... && exit 1
+reached=$(mktemp -u)
+TAOSC_OUTPUT=$reached timeout -k 0 $1 ${@:2}
+test -f $reached || exit 2
+trap "rm $reached" EXIT
+test -s $reached && exit 3
+exit 0