about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--Makefile2
-rw-r--r--collect.c44
-rw-r--r--collection17
-rw-r--r--fix.m465
-rw-r--r--measure-stack.cc46
5 files changed, 87 insertions, 87 deletions
diff --git a/Makefile b/Makefile
index 9de3e0d..d677d39 100644
--- a/Makefile
+++ b/Makefile
@@ -9,7 +9,7 @@ BIN_PREFIX ::= $(DESTDIR)$(PREFIX)/bin/taosc-
 DATA_DIR ::= $(DESTDIR)$(PREFIX)/share/taosc
 
 BIN ::= fix measure-stack scout synth trace-call
-DATA ::= collect collection jump patch
+DATA ::= collect jump patch
 
 all: $(BIN) $(DATA)
 
diff --git a/collect.c b/collect.c
index b380f71..cf32283 100644
--- a/collect.c
+++ b/collect.c
@@ -1,6 +1,5 @@
 /*
  * Live variable collector
- * Copyright (C) 2021, 2023  Gregory James Duck
  * Copyright (C) 2024-2025  Nguyễn Gia Phong
  *
  * This file is part of taosc.
@@ -21,32 +20,39 @@
 
 #include "stdlib.c"
 
-FILE *fout = NULL;
+static uint64_t stack_size;
+static int output_file = -1;
+
+/*
+ * Get an environment variable and parse as a number.
+ * Return 0 on error.
+ */
+uint64_t getenvull(const char *name)
+{
+	const char *const s = getenv(name);
+	if (s == NULL)
+		return 0ULL;
+	errno = 0;
+	const uint64_t u = strtoull(s, NULL, 0);
+	if (errno)
+		return 0ULL;
+	return u;
+}
 
 void init(int argc, const char *const *argv, char **envp)
 {
 	environ = envp;
+	stack_size = getenvull("TAOSC_STACK_SIZE");
 	const char *const path = getenv("TAOSC_OUTPUT");
 	if (path != NULL)
-		fout = fopen(path, "w");
-	if (fout == NULL)
-		fout = stderr;
-	setvbuf(fout, NULL, _IOFBF, 0);
+		output_file = open(path, O_WRONLY | O_CREAT, 0644);
+	if (output_file == -1)
+		output_file = 2; /* stderr */
 }
 
-
 void log(const struct STATE *state)
 {
-	static mutex_t mutex = MUTEX_INITIALIZER;
-	if (mutex_lock(&mutex) < 0)
-		return;
-	clearerr(fout);
-	fprintf(fout, "[begin]\n");
-	const int64_t *const env = (const int64_t *) state;
-	for (unsigned char i = 1; i < sizeof(*state) / sizeof(*env); ++i)
-		fprintf(fout, "%hhu %lld\n", i, env[i]);
-	fprintf(fout, "[end]\n");
-	fflush(fout);
-	fclose(fout); /* FIXME: reopen */
-	mutex_unlock(&mutex);
+	write(output_file, (const char *)state, sizeof(struct STATE));
+	write(output_file, (const char *)state->rsp, stack_size);
+	fsync(output_file);
 }
diff --git a/collection b/collection
deleted file mode 100644
index fa0d3df..0000000
--- a/collection
+++ /dev/null
@@ -1,17 +0,0 @@
-1 r15 int
-2 r14 int
-3 r13 int
-4 r12 int
-5 r11 int
-6 r10 int
-7 r9 int
-8 r8 int
-9 rdi int
-10 rsi int
-11 rbp int
-12 rbx int
-13 rdx int
-14 rcx int
-15 rax int
-16 rsp int
-17 rip int
diff --git a/fix.m4 b/fix.m4
index d3ee7fa..de4e446 100644
--- a/fix.m4
+++ b/fix.m4
@@ -17,7 +17,7 @@
 # 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 -ex -o pipefail
+set -eux -o pipefail
 save_exit_code() {
   set +e
   # TODO: make timeout configurable
@@ -39,63 +39,58 @@ test -x "$binary"
 opts="${@:3}" # TODO: interpolation
 
 test -d "$wd/exploits"
-test ! -z "$(ls -A "$wd/exploits")"
-mkdir -p "$wd/exit-codes"
+test -n "$(ls -A "$wd/exploits")"
 for exploit in "$wd/exploits"/*
 do
-  save_exit_code "$binary" "$opts" "$exploit"
-  echo $exit_code > "$wd/exit-codes/$(basename "$exploit")"
-done
-
-> "$wd/stack-trace"
-for exploit in "$wd/exploits"/*
-do
-  gdb --batch --ex run --ex backtrace --args \
+  gdb --batch --ex run --ex backtrace --args\
     "$binary" "$opts" "$exploit" 2>/dev/null |
     grep '^#[0-9]\+ \+0x[0-9a-f]\+' |
-    awk '!$7 || $7 == bin {print $1, $2}' "bin=$binary" >> "$wd/stack-trace"
-done
+    awk '!$7 || $7 == bin {print $1, $2}' "bin=$binary" |
+    sed 's/^#//'
+done | sort -n | uniq > "$wd/stack-trace"
 
-grep '^#0 0x[0-9a-f]\+$' "$wd/stack-trace" |
-  sed 's/^#0 0x0*//' > "$wd/return-blocks"
+grep '^0 0x[0-9a-f]\+$' "$wd/stack-trace" |
+  sed 's/^0 0x0*//' > "$wd/call-trace"
 # Stack trace contains return addresses, not call addresses:
 # https://devblogs.microsoft.com/oldnewthing?p=96116
-grep -v '^#0 0x[0-9a-f]\+$' "$wd/stack-trace" |
-  sort |
-  sed 's/^#[0-9]\+ 0x0*//' |
-  taosc-trace-call "$binary" >> "$wd/return-blocks"
+grep -v '^0 0x[0-9a-f]\+$' "$wd/stack-trace" |
+  sed 's/^[0-9]\+ 0x0*//' |
+  taosc-trace-call "$binary" >> "$wd/call-trace"
 
-> "$wd/jumps"
+rm -f "$wd/patch-location"
 pushd DATA_DIR > /dev/null
-taosc-scout "$binary" < "$wd/return-blocks" |
-  while read loc destinations
+trap 'popd > /dev/null' EXIT
+taosc-scout "$binary" < "$wd/call-trace" |
+  while read loc destinations && test ! -f "$wd/patch-location"
   do
-    e9tool -100 -M addr=0x$loc -P 'if dest()@jump goto' \
-      -o "$bin.$loc" "$binary"
+    e9tool -100 -M addr=0x$loc -P 'if dest()@jump goto' -o "$bin.jump" "$binary"
+    rm -f "$wd/destinations"
     for dest in $destinations
     do
       for exploit in "$wd/exploits"/*
       do
-        save_exit_code env TAOSC_DEST=0x$dest "$bin.$loc" "$opts" "$exploit"
-        if test $exit_code -ge 124 && test $exit_code -le 127 ||
-          test $exit_code -eq $(< "$wd/exit-codes/$(basename "$exploit")")
+        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
         then
           continue 2 # next destination
         fi
       done
-      echo $loc $dest >> "$wd/jumps"
+      echo $loc > "$wd/patch-location"
+      echo $dest >> "$wd/destinations"
     done
   done 2>&1 1>/dev/null
-exit
+test -s "$wd/patch-location"
+test -s "$wd/destinations"
 
-afl-dyninst -x "$binary" "$bin.fuzzee"
-pushd DATA_DIR > /dev/null
-trap 'popd > /dev/null' EXIT
-e9tool -M addr=$address -P 'log(state)@collect'\
-  -o "$bin.collect" "$binary"
-e9tool -M addr=$address -P 'if dest(state)@patch goto'\
+patch_loc=0x$(< "$wd/patch-location")
+e9tool -M addr=$patch_loc -P 'log(state)@collect' -o "$bin.collect" "$binary"
+e9tool -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"\
diff --git a/measure-stack.cc b/measure-stack.cc
index 5151772..e676735 100644
--- a/measure-stack.cc
+++ b/measure-stack.cc
@@ -67,8 +67,7 @@ immediate (Operand const& operand)
 }
 
 static int64_t
-stack_offset (Instruction const& instruction,
-              Architecture architecture)
+stack_offset (Instruction const& instruction, Architecture architecture)
 {
   switch (instruction.getOperation ().getID ())
     {
@@ -85,6 +84,21 @@ stack_offset (Instruction const& instruction,
     }
 }
 
+static size_t
+stack_offset (char const* buffer, size_t length,
+              RegisterAST::Ptr stack_pointer,
+              Architecture architecture)
+{
+  InstructionDecoder decoder {buffer, length, architecture};
+  size_t offset = 0;
+  for (auto instruction = decoder.decode ();
+       instruction.isValid ();
+       instruction = decoder.decode ())
+    if (instruction.isWritten (stack_pointer))
+      offset += stack_offset (instruction, architecture);
+  return offset;
+}
+
 int
 main (int argc, char** argv)
 {
@@ -108,21 +122,23 @@ main (int argc, char** argv)
       size_t stack_size = 0;
       for (auto* const fun : functions)
         {
-          auto const entry = fun->addr ();
-          auto const* buffer = (char*) cs.getPtrToInstruction (entry);
-          auto const length = fun->entry ()->end () - entry;
-          InstructionDecoder decoder {buffer, length, architecture};
-          size_t s = 0;
-          for (auto insn = decoder.decode ();
-               insn.isValid ();
-               insn = decoder.decode ())
-            if (insn.isWritten (stack_pointer))
-              s += stack_offset (insn, architecture);
-          if (s == 0)
+          size_t offset = 0;
+          for (auto* prologue = fun->entry ();
+               prologue;
+               prologue = (prologue->targets ().size () == 1
+                           ? (*prologue->targets ().begin ())->trg ()
+                           : nullptr))
+            {
+              auto const* const buffer = (char*)
+                cs.getPtrToInstruction (prologue->start ());
+              offset += stack_offset (buffer, prologue->size (),
+                                      stack_pointer, architecture);
+            }
+          if (offset == 0)
             continue;
           if (stack_size == 0)
-            stack_size = s;
-          else if (s != stack_size)
+            stack_size = offset;
+          else if (offset != stack_size)
             die_for (address, "functions with different stack sizes spanning");
         }
       std::cout << stack_size << '\n';