diff options
| -rw-r--r-- | Makefile | 2 | ||||
| -rw-r--r-- | collect.c | 44 | ||||
| -rw-r--r-- | collection | 17 | ||||
| -rw-r--r-- | fix.m4 | 65 | ||||
| -rw-r--r-- | measure-stack.cc | 46 |
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'; |
