diff --git a/.gitignore b/.gitignore index 18f3af526f613fcef9c38093cc5acb022359f4c3..909a827a011796b266e5d3b138ad212431106e44 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,8 @@ install/ +build/ manifest.txt.config +libzafl/src/*.so +libzafl/src/*.os +manifest.txt.config +.sconsign.dblite +*.swp diff --git a/zfuzz/README.md b/zfuzz/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ff2fbddf863a55b75ec96e1cf24ef7774c17accb --- /dev/null +++ b/zfuzz/README.md @@ -0,0 +1 @@ +All things related to fuzzing at Zephyr \ No newline at end of file diff --git a/zfuzz/afl_transforms/.gitignore b/zfuzz/afl_transforms/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..3afee3475a10291c108cdf6a4681978e0ac638b5 --- /dev/null +++ b/zfuzz/afl_transforms/.gitignore @@ -0,0 +1,2 @@ +build +.sconsign.dblite diff --git a/zfuzz/afl_transforms/SConscript b/zfuzz/afl_transforms/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..6d2b16becae3add613347f79ac97f9cee0b8eaa9 --- /dev/null +++ b/zfuzz/afl_transforms/SConscript @@ -0,0 +1,32 @@ +import shutil +import os +import tarfile + +Import('env') + +(sysname, nodename, release, version, machine)=os.uname() + +env.Append(CXXFLAGS=" -Wall -Werror") +env.Append(LINKFLAGS=" -Wall -Werror -Wl,-unresolved-symbols=ignore-in-shared-libs ") + +env['BASE_IRDB_LIBS']="irdb-core" + + +# pebliss requires iconv, which needs to be explicit on cygwin. +if "CYGWIN" in sysname: + # add tuple of 1 item! + env['BASE_IRDB_LIBS']=env['BASE_IRDB_LIBS']+("iconv",) + +Export('env') + +libs=SConscript("tools/SConscript", variant_dir='scons_build/tools') + +pedi = Command( target = "./testoutput", + source = "./SConscript", + action = "cd "+os.environ['SECURITY_TRANSFORMS_HOME']+" ; " +os.environ['PEDI_HOME']+"/pedi -m manifest.txt ; cd -" ) + +Depends(pedi,libs) + + +Default( pedi ) + diff --git a/zfuzz/afl_transforms/SConstruct b/zfuzz/afl_transforms/SConstruct new file mode 100644 index 0000000000000000000000000000000000000000..0fadf113a8aa7183159e564f5186366602b37169 --- /dev/null +++ b/zfuzz/afl_transforms/SConstruct @@ -0,0 +1,45 @@ +import os +import sys + + + +(sysname, nodename, release, version, machine)=os.uname() + +env=Environment() + + +# default build options +env.Replace(CFLAGS="-fPIC -Wall ") +env.Replace(CXXFLAGS="-fPIC -Wall ") +env.Replace(LINKFLAGS="-fPIC -Wall ") + +# parse arguments +env.Replace(SECURITY_TRANSFORMS_HOME=os.environ['SECURITY_TRANSFORMS_HOME']) +env.Replace(SMPSA_HOME=os.environ['SMPSA_HOME']) +env.Replace(IRDB_SDK=os.environ['IRDB_SDK']) +env.Replace(AFL_TRANSFORMS=os.environ['AFL_TRANSFORMS']) +env.Replace(debug=ARGUMENTS.get("debug",0)) + + +if int(env['debug']) == 1: + print "Setting debug mode" + env.Append(CFLAGS=" -g ") + env.Append(CXXFLAGS=" -g ") + env.Append(LINKFLAGS=" -g ") +else: + print "Setting release mode" + env.Append(CFLAGS=" -O3 ") + env.Append(CXXFLAGS=" -O3 ") + env.Append(LINKFLAGS=" -O3 ") + + +# add extra flag for solaris. +if sysname == "SunOS": + env.Append(LINKFLAGS=" -L/opt/csw/lib -DSOLARIS ") + env.Append(CFLAGS=" -I/opt/csw/include -DSOLARIS ") + env.Append(CXXFLAGS=" -I/opt/csw/include -DSOLARIS ") + + +Export('env') +SConscript("SConscript", variant_dir='build') + diff --git a/zfuzz/afl_transforms/tools/SConscript b/zfuzz/afl_transforms/tools/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..f30fe37c68d95dc0ba828dbc50077427b2f3f562 --- /dev/null +++ b/zfuzz/afl_transforms/tools/SConscript @@ -0,0 +1,18 @@ +import os + +Import('env') + +dirs=''' + laf + zax + ''' + + +nobuild_dirs=''' + ''' + +tools=[] +for i in Split(dirs): + tools=tools+SConscript(os.path.join(i,"SConscript")) + +Return('tools') diff --git a/zfuzz/afl_transforms/tools/laf/SConscript b/zfuzz/afl_transforms/tools/laf/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..6e620a5caee8574ea01071979e8a4f4fd7f4b6dd --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/SConscript @@ -0,0 +1,34 @@ +import os + + + +Import('env') + +# import and create a copy of the environment so we don't screw up anyone elses env. +myenv=env.Clone() + +cpppath=''' + $IRDB_SDK/include + $SECURITY_TRANSFORMS_HOME/libtransform/include + $SECURITY_TRANSFORMS_HOME/libMEDSannotation/include + $SECURITY_TRANSFORMS_HOME/libIRDB/include + $SMPSA_HOME/include + ''' + + +files=Glob( Dir('.').srcnode().abspath+"/*.cpp") + + +pgm="laf.exe" + +LIBPATH="$SECURITY_TRANSFORMS_HOME/lib" +LIBS=Split("stars "+ env.subst('$BASE_IRDB_LIBS')+ " irdb-cfg irdb-util irdb-transform irdb-deep MEDSannotation jsoncpp ") +myenv=myenv.Clone(CPPPATH=Split(cpppath)) +myenv.Append(CXXFLAGS = " -std=c++11 -Wall -g ") +pgm=myenv.Program(pgm, files, LIBPATH=LIBPATH, LIBS=LIBS) +install=myenv.Install("$SECURITY_TRANSFORMS_HOME/plugins_install/", pgm) +Default(install) + + + +Return('install') diff --git a/zfuzz/afl_transforms/tools/laf/SConstruct b/zfuzz/afl_transforms/tools/laf/SConstruct new file mode 100644 index 0000000000000000000000000000000000000000..c96332f0422ad5df0853931209219d9a2e20bc17 --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/SConstruct @@ -0,0 +1,6 @@ + + + +env=Environment() +Export('env') +lib=SConscript("SConscript") diff --git a/zfuzz/afl_transforms/tools/laf/laf.cpp b/zfuzz/afl_transforms/tools/laf/laf.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8f94168a43117438a28504bb464530efcbc59aea --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/laf.cpp @@ -0,0 +1,464 @@ +/*************************************************************************** + * Copyright (c) 2018-2019 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT INFO + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include "laf.hpp" + +using namespace std; +using namespace IRDB_SDK; +using namespace Laf; +using namespace MEDS_Annotation; + +#define ALLOF(a) begin(a),end(a) +#define FIRSTOF(a) (*(begin(a))) + +Laf_t::Laf_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_variantIR, bool p_verbose) + : + Transform(p_variantIR), + m_dbinterface(p_dbinterface), + m_verbose(p_verbose) +{ + m_split_compare = true; + m_split_branch = true; + + auto deep_analysis=DeepAnalysis_t::factory(getFileIR()); + leaf_functions = deep_analysis->getLeafFunctions(); + dead_registers = deep_analysis->getDeadRegisters(); + + m_blacklist.insert("init"); + m_blacklist.insert("_init"); + m_blacklist.insert("start"); + m_blacklist.insert("_start"); + m_blacklist.insert("fini"); + m_blacklist.insert("_fini"); + m_blacklist.insert("register_tm_clones"); + m_blacklist.insert("deregister_tm_clones"); + m_blacklist.insert("frame_dummy"); + m_blacklist.insert("__do_global_ctors_aux"); + m_blacklist.insert("__do_global_dtors_aux"); + m_blacklist.insert("__libc_csu_init"); + m_blacklist.insert("__libc_csu_fini"); + m_blacklist.insert("__libc_start_main"); + m_blacklist.insert("__gmon_start__"); + m_blacklist.insert("__cxa_atexit"); + m_blacklist.insert("__cxa_finalize"); + m_blacklist.insert("__assert_fail"); + m_blacklist.insert("free"); + m_blacklist.insert("fnmatch"); + m_blacklist.insert("readlinkat"); + m_blacklist.insert("malloc"); + m_blacklist.insert("calloc"); + m_blacklist.insert("realloc"); + m_blacklist.insert("argp_failure"); + m_blacklist.insert("argp_help"); + m_blacklist.insert("argp_state_help"); + m_blacklist.insert("argp_error"); + m_blacklist.insert("argp_parse"); + + m_skip_byte_cmp = 0; +} + +RegisterSet_t Laf_t::getDeadRegs(Instruction_t* insn) const +{ + auto it = dead_registers -> find(insn); + if(it != dead_registers->end()) + return it->second; + return RegisterSet_t(); +} + +// return intersection of candidates and allowed general-purpose registers +RegisterSet_t Laf_t::getFreeRegs(const RegisterSet_t& candidates, const RegisterSet_t& allowed) const +{ + RegisterIDSet_t free_regs; + set_intersection(ALLOF(candidates), ALLOF(allowed), std::inserter(free_regs,free_regs.begin())); + return free_regs; +} + +bool Laf_t::isBlacklisted(Function_t *p_func) const +{ + return (p_func->getName()[0] == '.' || + p_func->getName().find("@plt") != string::npos || + p_func->getName().find("__libc_") != string::npos || + m_blacklist.find(p_func->getName())!=m_blacklist.end()); +} + +void Laf_t::setSplitCompare(bool p_val) +{ + m_split_compare = p_val; +} + +void Laf_t::setSplitBranch(bool p_val) +{ + m_split_branch = p_val; +} + +bool Laf_t::getSplitCompare() const +{ + return m_split_compare; +} + +bool Laf_t::getSplitBranch() const +{ + return m_split_branch; +} + +bool Laf_t::hasLeafAnnotation(Function_t* fn) const +{ + auto it = leaf_functions -> find(fn); + return (it != leaf_functions->end()); +} + +int Laf_t::execute() +{ + if (getSplitCompare()) + doSplitCompare(); + + return 1; +} + +// handle comparisons of the form: +// c: cmp [rbp - 4], 0x12345678 or c: cmp reg, 0x12345678 +// jcc: jne foobar +void Laf_t::doSplitCompare(Instruction_t* p_instr, bool p_honor_red_zone) +{ + const auto d_cp = DecodedInstruction_t::factory(p_instr); + const auto &d_c = *d_cp; + + const auto immediate = d_c.getImmediate(); + auto jcc = p_instr->getFallthrough(); + const auto d_cbrp = DecodedInstruction_t::factory(jcc); + const auto &d_cbr = *d_cbrp; + const auto is_jne = (d_cbr.getMnemonic() == "jne"); + const auto is_je = !is_jne; + auto orig_jcc_fallthrough = jcc->getFallthrough(); + auto orig_jcc_target = jcc->getTarget(); + auto allowed_regs = RegisterSet_t({rn_RAX, rn_RBX, rn_RCX, rn_RDX, rn_R8, rn_R9, rn_R10, rn_R11, rn_R12, rn_R13, rn_R14, rn_R15}); + + cout << "found comparison: " << p_instr->getDisassembly() << " immediate: " << hex << immediate << " " << jcc->getDisassembly() << endl; + + const auto dead_regs = getDeadRegs(p_instr); + + if (d_c.getOperand(0)->isRegister()) + { + const auto r = d_c.getOperand(0)->getString(); + const auto reg = Register::getRegister(r); + allowed_regs.erase(reg); + } + +// auto save_temp = true; + auto free_regs = getFreeRegs(dead_regs, allowed_regs); + auto free_reg = string(); + + if (free_regs.size() > 0) + { +// save_temp = false; + const auto first_free_register = FIRSTOF(free_regs); + free_regs.erase(first_free_register); + free_reg = registerToString(convertRegisterTo32bit(first_free_register)); + } + + // for now, skip if no free register + if (free_regs.size() == 0) + { + cout << "no free register, skipping: " << p_instr->getBaseID() << ": " << p_instr->getDisassembly() << endl; + return; + } + + if (p_honor_red_zone) + p_instr = insertAssemblyBefore(p_instr, "lea rsp, [rsp-128]"); + + string s; + + if (is_jne) + cout << "is jump not equal" << endl; + else + cout << "is jump equal" << endl; + + auto byte0 = immediate >> 24; // high byte + auto byte1 = (immediate&0xff0000) >> 16; + auto byte2 = (immediate&0xff00) >> 8; + auto byte3 = immediate&0xff; // low byte + + if (byte0 == 0x0 && byte1 == 0x0 && byte2 == 0x0) + { + cout << "skip as immediate is only 1 byte: 0x" << immediate << endl; + return; + } + + cout <<" bytes: 0x" << hex << byte0 << byte1 << byte2 << byte3 << endl; + + + auto init_sequence = string(); + + if (d_c.getOperand(0)->isRegister()) + { + // cmp eax, 0x12345678 + stringstream ss; + ss << "mov " << free_reg << ", " << d_c.getOperand(0)->getString(); + init_sequence = ss.str(); + } + else + { + // cmp dword [rbp - 4], 0x12345678 + const auto memopp = d_c.getOperand(0); + const auto &memop = *memopp; + init_sequence = "mov " + free_reg + ", dword [ " + memop.getString() + " ]"; + } + + // @todo: save/restore free register r15d + cout << "decompose sequence: assume free register: " << free_reg << endl; + cout << "init sequence is: " << init_sequence << endl; + + +/* + mov eax, dword [rbp - 4] + sar eax, 0x18 + cmp eax, 0x12 + jne j +*/ + stringstream ss; + auto t = (Instruction_t*) NULL; + + s = init_sequence; + getFileIR()->registerAssembly(p_instr, s); + cout << s << endl; + + s = "sar " + free_reg + ", 0x18"; + t = insertAssemblyAfter(p_instr, s); + cout << s << endl; + + ss.str(""); + ss << "cmp " << free_reg << ", 0x" << hex << byte0; + s = ss.str(); + t = insertAssemblyAfter(t, s); + cout << s << endl; + + s = "jne 0"; + t = insertAssemblyAfter(t, s); + if (is_je) { + t->setTarget(orig_jcc_fallthrough); + cout << "target: original fallthrough "; + } + else { + t->setTarget(orig_jcc_target); + cout << "target: original target "; + } + + cout << s << endl; + +/* + mov eax, dword [rbp - 4] + and eax, 0xff0000 + sar eax, 0x10 + cmp eax, 0x34 + jne xxx +*/ + s = init_sequence; + t = insertAssemblyAfter(t, s); + cout << s << endl; + + s = "and " + free_reg + ", 0xff0000"; + t = insertAssemblyAfter(t, s); + cout << s << endl; + + s = "sar " + free_reg + ", 0x10"; + t = insertAssemblyAfter(t, s); + cout << s << endl; + + ss.str(""); + ss << "cmp " << free_reg << ", 0x" << hex << byte1; + s = ss.str(); + t = insertAssemblyAfter(t, s); + cout << s << endl; + + s = "jne 0"; + t = insertAssemblyAfter(t, s); + if (is_je) { + t->setTarget(orig_jcc_fallthrough); + cout << "target: original fallthrough "; + } + else { + t->setTarget(orig_jcc_target); + cout << "target: original target "; + } + cout << s << endl; + +/* + mov eax,DWORD PTR [rbp-0x4] + and eax,0xff00 + sar eax,0x8 + cmp eax,0x56 + jne xxx +*/ + s = init_sequence; + t = insertAssemblyAfter(t, s); + cout << s << endl; + + s = "and " + free_reg + ", 0xff00"; + t = insertAssemblyAfter(t, s); + cout << s << endl; + + s = "sar " + free_reg + ", 0x8"; + t = insertAssemblyAfter(t, s); + cout << s << endl; + + ss.str(""); + ss << "cmp " << free_reg << ", 0x" << hex << byte2; + s = ss.str(); + t = insertAssemblyAfter(t, s); + cout << s << endl; + + s = "jne 0"; + t = insertAssemblyAfter(t, s); + if (is_je) { + t->setTarget(orig_jcc_fallthrough); + cout << "target: original fallthrough "; + } + else { + t->setTarget(orig_jcc_target); + cout << "target: original target "; + } + cout << s << endl; + +/* + 4005e4: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] + 4005e7: 0f b6 c0 movzx eax,al + 4005ea: 83 f8 78 cmp eax,0x78 + 4005ed: 75 07 jne 4005f6 <main+0x80> +*/ + s = init_sequence; + t = insertAssemblyAfter(t, s); + cout << s << endl; + + s = "and " + free_reg + ", 0xff"; + t = insertAssemblyAfter(t, s); + cout << s << endl; + + ss.str(""); + ss << "cmp " << free_reg << ", 0x" << hex << byte3; + s = ss.str(); + t = insertAssemblyAfter(t, s); + cout << s << endl; + + if (p_honor_red_zone) + t = insertAssemblyAfter(t, "lea rsp, [rsp+128]"); + + if (is_je) + s = "je 0"; + else + s = "jne 0"; + t = insertAssemblyAfter(t, s); + t->setTarget(orig_jcc_target); + t->setFallthrough(orig_jcc_fallthrough); + cout << "target: original target fallthrough: original fallthrough "; + cout << s << endl; + +} + +int Laf_t::doSplitCompare() +{ +/* + // look for cmp against constant +0000000000400526 <main>: + 400535: 81 7d fc 78 56 34 12 cmp DWORD PTR [rbp-0x4],0x12345678 + 40053c: 75 0a jne 400548 <main+0x22> + 40054e: c3 ret + + // decompose into series of 1-byte comparisons + 4005b9: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] + 4005bc: c1 f8 18 sar eax,0x18 + 4005bf: 83 f8 12 cmp eax,0x12 + 4005c2: 75 32 jne 4005f6 <main+0x80> + 4005c4: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] + 4005c7: 25 00 00 ff 00 and eax,0xff0000 + 4005cc: c1 f8 10 sar eax,0x10 + 4005cf: 83 f8 34 cmp eax,0x34 + 4005d2: 75 22 jne 4005f6 <main+0x80> + 4005d4: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] + 4005d7: 25 00 ff 00 00 and eax,0xff00 + 4005dc: c1 f8 08 sar eax,0x8 + 4005df: 83 f8 56 cmp eax,0x56 + 4005e2: 75 12 jne 4005f6 <main+0x80> + 4005e4: 8b 45 fc mov eax,DWORD PTR [rbp-0x4] + 4005e7: 0f b6 c0 movzx eax,al + 4005ea: 83 f8 78 cmp eax,0x78 + 4005ed: 75 07 jne 4005f6 <main+0x80> + 4005ef: b8 01 00 00 00 mov eax,0x1 + 4005f4: eb 05 jmp 4005fb <main+0x85> + 4005f6: b8 00 00 00 00 mov eax,0x0 + 4005fb: c9 leave + 4005fc: c3 ret +*/ + + auto to_split_compare = vector<Instruction_t*>(); + for(auto func : getFileIR()->getFunctions()) + { + if (isBlacklisted(func)) + continue; + + for(auto i : func->getInstructions()) + { + const auto dp = DecodedInstruction_t::factory(i); + const auto &d = *dp; + if (d.getMnemonic()!="cmp") continue; + if (d.getOperands().size()!=2) continue; + if (!d.getOperand(1)->isConstant()) continue; + if (d.getOperand(0)->getArgumentSizeInBytes()!=4) continue; + if (!i->getFallthrough()) continue; + const auto fp = DecodedInstruction_t::factory(i->getFallthrough()); + const auto &f = *fp; + if (f.getMnemonic() != "je" && f.getMnemonic() !="jeq" && f.getMnemonic() !="jne") continue; + + // these values are easy for fuzzer to guess + const auto imm = d.getImmediate(); + if (imm == 0 || imm == 1 || imm == -1 || imm == 0xff || imm == 0xffff) + continue; + + // nothing to split if it's just a 1-byte compare + const auto byte0 = imm >> 24; // high byte + const auto byte1 = (imm&0xff0000) >> 16; + const auto byte2 = (imm&0xff00) >> 8; +// const auto byte3 = imm&0xff; // low byte + const auto byteCmp = (byte0 == 0x0 && byte1 == 0x0 && byte2 == 0x0); + if (byteCmp) + { + m_skip_byte_cmp++; + continue; + } + + // we now have a cmp instruction to decompose + if (d.getOperand(0)->isRegister() || d.getOperand(0)->isMemory()) + to_split_compare.push_back(i); + }; + + }; + + // transform each comparison that needs to be decomposed + for(auto c : to_split_compare) + { + auto honorRedZone = true; + honorRedZone = hasLeafAnnotation(c->getFunction()); + doSplitCompare(c, honorRedZone); + } + + return 1; // true means success +} diff --git a/zfuzz/afl_transforms/tools/laf/laf.hpp b/zfuzz/afl_transforms/tools/laf/laf.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1a2f7b408267e8d5833e9197a0a41e106203986d --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/laf.hpp @@ -0,0 +1,50 @@ +#ifndef _ZAFL_LAF_H +#define _ZAFL_LAF_H + +#include <memory> + +#include <irdb-core> +#include <irdb-transform> +#include <irdb-util> +#include <irdb-deep> +#include <libMEDSAnnotation.h> + +namespace Laf +{ + using RegisterSet_t = IRDB_SDK::RegisterIDSet_t; +// the actual transform. +class Laf_t : public IRDB_SDK::Transform +{ +public: + // explicitly disable default and copy constructors + Laf_t() = delete; + Laf_t(const Laf::Laf_t&) = delete; + Laf_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_variantIR, bool p_verbose=false); + int execute(); + void setSplitCompare(bool); + void setSplitBranch(bool); + bool getSplitCompare() const; + bool getSplitBranch() const; + +private: + RegisterSet_t getDeadRegs(IRDB_SDK::Instruction_t* insn) const; + RegisterSet_t getFreeRegs(const RegisterSet_t& candidates, const RegisterSet_t& allowed) const; + void doSplitCompare(IRDB_SDK::Instruction_t*, bool p_honor_red_zone); + int doSplitCompare(); + bool isBlacklisted(IRDB_SDK::Function_t*) const; + bool hasLeafAnnotation(IRDB_SDK::Function_t* fn) const; + +private: + IRDB_SDK::pqxxDB_t &m_dbinterface; + std::unique_ptr<IRDB_SDK::FunctionSet_t> leaf_functions; + std::unique_ptr<IRDB_SDK::DeadRegisterMap_t> dead_registers; + bool m_verbose; + bool m_split_compare; + bool m_split_branch; + std::set<std::string> m_blacklist; + size_t m_skip_byte_cmp; +}; + +} + +#endif diff --git a/zfuzz/afl_transforms/tools/laf/laf_driver.cpp b/zfuzz/afl_transforms/tools/laf/laf_driver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f069b3bc9e65fe765ffc5192deec5e64cbd03d54 --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/laf_driver.cpp @@ -0,0 +1,163 @@ +/*************************************************************************** + * Copyright (c) 2018 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT INFO + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <getopt.h> + +#include "laf.hpp" + +using namespace std; +using namespace IRDB_SDK; +using namespace Laf; + +void usage(char* name) +{ + cerr<<"Usage: "<<name<<" <variant_id>\n"; + cerr<<"\t[--verbose | -v] Verbose mode "<<endl; + cerr<<"\t[--enable-split-compare | -c] Enable split compare "<<endl; + cerr<<"\t[--disable-split-compare | -c] Disable split compare "<<endl; + cerr<<"\t[--enable-split-branch | -b] Enable split branch "<<endl; + cerr<<"\t[--disable-split-branch | -B] Disable split branch "<<endl; + cerr<<"[--help,--usage,-?,-h] Display this message "<<endl; +} + +int main(int argc, char **argv) +{ + if(argc < 2) + { + usage(argv[0]); + exit(1); + } + + string programName(argv[0]); + auto variantID = atoi(argv[1]); + auto verbose=false; + auto split_compare = true; + auto split_branch = true; + + // Parse some options for the transform + static struct option long_options[] = { + {"verbose", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"usage", no_argument, 0, '?'}, + {"enable-split-compare", no_argument, 0, 'c'}, + {"disable-split-compare", no_argument, 0, 'C'}, + {"enable-split-branch", no_argument, 0, 'b'}, + {"disable-split-branch", no_argument, 0, 'B'}, + {0,0,0,0} + }; + + const char* short_opts="v?h"; + while(true) + { + int index = 0; + int c = getopt_long(argc, argv, short_opts, long_options, &index); + if(c == -1) + break; + switch(c) + { + case 'v': + verbose=true; + break; + case '?': + case 'h': + usage(argv[0]); + exit(1); + break; + case 'c': + split_compare=true; + break; + case 'C': + split_compare=false; + break; + case 'b': + split_branch=true; + break; + case 'B': + split_branch=false; + break; + default: + break; + } + } + + + /* setup the interface to the sql server */ + auto pqxx_interface=pqxxDB_t::factory(); + BaseObj_t::setInterface(pqxx_interface.get()); + + auto pidp=VariantID_t::factory(variantID); + assert(pidp->isRegistered()==true); + + bool one_success = false; + for(set<File_t*>::iterator it=pidp->getFiles().begin(); + it!=pidp->getFiles().end(); + ++it) + { + File_t* this_file = *it; + auto firp = FileIR_t::factory(pidp.get(), this_file); + + cout<<"Transforming "<<this_file->getURL()<<endl; + + assert(firp && pidp); + + try + { + Laf_t laf(*pqxx_interface, firp.get(), verbose); + laf.setSplitCompare(split_compare); + laf.setSplitBranch(split_branch); + + int success=laf.execute(); + + if (success) + { + cout<<"Writing changes for "<<this_file->getURL()<<endl; + one_success = true; + firp->writeToDB(); + } + else + { + cout<<"Skipping (no changes) "<<this_file->getURL()<<endl; + } + } + catch (DatabaseError_t pnide) + { + cerr << programName << ": Unexpected database error: " << pnide << "file url: " << this_file->getURL() << endl; + exit(1); + } + catch (...) + { + cerr << programName << ": Unexpected error file url: " << this_file->getURL() << endl; + exit(1); + } + } // end file iterator + + // if any transforms for any files succeeded, we commit + if (one_success) + { + cout<<"Commiting changes...\n"; + pqxx_interface->commit(); + } + + return 0; +} + diff --git a/zfuzz/afl_transforms/tools/laf/test/Makefile b/zfuzz/afl_transforms/tools/laf/test/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b023fbca013ef749c3e65eaa90032be7a0b36184 --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/test/Makefile @@ -0,0 +1,19 @@ +all: test0.laf test0.laf.zafl + +test_afl: test4.c + g++ test4.c -o $@ + +test_afl.laf: test_afl + $(PSZ) test_afl $@ --critical-step laf=on -s meds_static=off -s rida=on + +test0.exe: test0.c + g++ test0.c -o $@ + +test0.laf: test0.exe + $(PSZ) test0.exe $@ --critical-step laf=on -s meds_static=off -s rida=on + +test0.laf.zafl: test0.exe + zafl.sh $< $@ --enable-split-compare + +clean: + rm -fr peasoup_exec* *.o test_afl test_afl.laf test0.exe test0.laf a.out a.exe diff --git a/zfuzz/afl_transforms/tools/laf/test/run_tests.sh b/zfuzz/afl_transforms/tools/laf/test/run_tests.sh new file mode 100755 index 0000000000000000000000000000000000000000..f0c5982a7ef9a6226774221e2334e7670431a4b6 --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/test/run_tests.sh @@ -0,0 +1,46 @@ +make clean test0.laf.zafl + +cleanup() { + rm test0.*.out + make clean +} + +report_failure() { + echo "Test $1: FAIL" + cleanup + exit 1 +} + +report_success() { + echo "Test $1: PASS" + +} + +echo "1" | ./test0.exe > test0.exe.out +echo "1" | ./test0.laf.zafl > test0.laf.out +diff test0.exe.out test0.laf.out +if [ ! $? -eq 0 ]; then + report_failure "equality" +else + report_success "equality" +fi + +echo "2" | ./test0.exe > test0.exe.out +echo "2" | ./test0.laf.zafl > test0.laf.out +if [ ! $? -eq 0 ]; then + report_failure "inequality" +else + report_success "inequality" +fi + +echo "4" | ./test0.exe > test0.exe.out +echo "4" | ./test0.laf.zafl > test0.laf.out +if [ ! $? -eq 0 ]; then + report_failure "inequality" +else + report_success "inequality" +fi + +echo Sanity tests passed +cleanup +exit 0 diff --git a/zfuzz/afl_transforms/tools/laf/test/test.c b/zfuzz/afl_transforms/tools/laf/test/test.c new file mode 100644 index 0000000000000000000000000000000000000000..4fd335a46c6a027dcb3c170dcd0efe7009c8a1e5 --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/test/test.c @@ -0,0 +1,12 @@ +#include <iostream> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char **argv) +{ + int x = 0; + std::cin >> std::hex >> x; + if (x == 0x01000000) + abort(); + return 0; +} diff --git a/zfuzz/afl_transforms/tools/laf/test/test0.c b/zfuzz/afl_transforms/tools/laf/test/test0.c new file mode 100644 index 0000000000000000000000000000000000000000..a9f5dc637a3fbbb479b885b279af98547d16d79f --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/test/test0.c @@ -0,0 +1,41 @@ +#include <unistd.h> +#include <iostream> +#include <stdio.h> +#include <stdlib.h> + +#define MAX_CHARS 1024 + +// #define K 1234567 +#define K 2 + +int half(int i) { + return i / 2; +} + +int main(int argc, char **argv) +{ + int x; + volatile int y; + char buf[MAX_CHARS+1]; + + fgets(buf, MAX_CHARS, stdin); + x = atoi(buf); + if (x == K) { + fprintf(stdout, "x(%d) is equal to K(%d)\n", x, K); + } + + if (x != K) { + fprintf(stdout, "x(%d) is not equal to K(%d)\n", x, K); + } + + y = half(x); + + if (y == K) { + fprintf(stdout, "y(%d) is equal to K(%d)\n", y, K); + } + + if (y != K) { + fprintf(stdout, "y(%d) is not equal to K(%d)\n", y, K); + } + return 0; +} diff --git a/zfuzz/afl_transforms/tools/laf/test/test2.c b/zfuzz/afl_transforms/tools/laf/test/test2.c new file mode 100644 index 0000000000000000000000000000000000000000..6c3740ea211d420951c30be35f41b4aec7af32de --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/test/test2.c @@ -0,0 +1,12 @@ +#include <iostream> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char **argv) +{ + int x = 0; + std::cin >> std::hex >> x; + if (x != 0x01000000) + abort(); + return 0; +} diff --git a/zfuzz/afl_transforms/tools/laf/test/test4.c b/zfuzz/afl_transforms/tools/laf/test/test4.c new file mode 100644 index 0000000000000000000000000000000000000000..6b242c872436d39efba023459ed9922901d9c639 --- /dev/null +++ b/zfuzz/afl_transforms/tools/laf/test/test4.c @@ -0,0 +1,20 @@ +#include <unistd.h> +#include <iostream> +#include <stdio.h> +#include <stdlib.h> + +int main(int argc, char **argv) +{ + int x; + + read(0, &x, 4); +// if (x == 33620225) // 0x02010101 +// if (x == 3791716609) // 0xe2010101 +// if (x == 16843057) +// if (x == 16851249) +// if (x == 0x7c3f) + if (x == 0x237c3f) +// if (x == 0x3f7c1234) + abort(); + return 0; +} diff --git a/zfuzz/afl_transforms/tools/zax/SConscript b/zfuzz/afl_transforms/tools/zax/SConscript new file mode 100644 index 0000000000000000000000000000000000000000..31be205feb583a2da4f072e29648bd22bd2cf3ac --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/SConscript @@ -0,0 +1,32 @@ +import os + + + +Import('env') + +# import and create a copy of the environment so we don't screw up anyone elses env. +myenv=env.Clone() + +cpppath=''' + $IRDB_SDK/include + $SECURITY_TRANSFORMS_HOME/include + $SMPSA_HOME/include + ''' + + +files=Glob( Dir('.').srcnode().abspath+"/*.cpp") + + +pgm="zax.exe" + +LIBPATH="$SECURITY_TRANSFORMS_HOME/lib" +LIBS=Split("stars "+ env.subst('$BASE_IRDB_LIBS')+ " irdb-cfg irdb-util irdb-transform irdb-elfdep irdb-deep ") +myenv=myenv.Clone(CPPPATH=Split(cpppath)) +myenv.Append(CXXFLAGS = " -std=c++11 -Wall -g -fmax-errors=2 ") +pgm=myenv.Program(pgm, files, LIBPATH=LIBPATH, LIBS=LIBS) +install=myenv.Install("$SECURITY_TRANSFORMS_HOME/plugins_install/", pgm) +Default(install) + + + +Return('install') diff --git a/zfuzz/afl_transforms/tools/zax/SConstruct b/zfuzz/afl_transforms/tools/zax/SConstruct new file mode 100644 index 0000000000000000000000000000000000000000..c96332f0422ad5df0853931209219d9a2e20bc17 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/SConstruct @@ -0,0 +1,6 @@ + + + +env=Environment() +Export('env') +lib=SConscript("SConscript") diff --git a/zfuzz/afl_transforms/tools/zax/critical_edge_breaker.cpp b/zfuzz/afl_transforms/tools/zax/critical_edge_breaker.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0f40b0071a9440be5a576da855eab87d3cb8dcfb --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/critical_edge_breaker.cpp @@ -0,0 +1,137 @@ +/*************************************************************************** + * Copyright (c) 2018-2019 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT INFO + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <irdb-cfg> +#include <irdb-transform> +#include "critical_edge_breaker.hpp" + +using namespace std; +using namespace IRDB_SDK; +using namespace Zafl; + +CriticalEdgeBreaker_t::CriticalEdgeBreaker_t(IRDB_SDK::FileIR_t *p_IR, set<string> p_blacklist, const bool p_verbose) : + m_IR(p_IR), + m_blacklist(p_blacklist), + m_verbose(p_verbose), + m_extra_nodes(0) +{ + breakCriticalEdges(); +} + +unsigned CriticalEdgeBreaker_t::getNumberExtraNodes() const +{ + return m_extra_nodes; +} + +// iterate over each function and break critical edges +void CriticalEdgeBreaker_t::breakCriticalEdges() +{ + auto is_blacklisted = [this](const Function_t* f) -> bool + { + const auto fname = f->getName(); + return (fname[0] == '.' || fname.find("@plt") != string::npos || m_blacklist.find(fname)!=m_blacklist.end()); + }; + + for ( auto &f : m_IR->getFunctions() ) + { + if (!f) continue; + if (is_blacklisted(f)) continue; + + if (f->getEntryPoint()) + m_extra_nodes += breakCriticalEdges(f); + } +} + +// +// break critical edges by inserting an afl-instrumented dummy node +// if A --> B is a critical edge, break critical edge by adding C to yield: +// A --> C --> B +// +unsigned CriticalEdgeBreaker_t::breakCriticalEdges(Function_t* p_func) +{ + auto cfgp = ControlFlowGraph_t::factory(p_func); + auto &cfg = *cfgp; + + auto ceap = CriticalEdges_t::factory(cfg, false); + auto &cea = *ceap; + + const auto critical_edges = cea.getAllCriticalEdges(); + auto num_critical_edges_instrumented = 0; + + cout << endl; + cout << "Breaking critical edges for function: " << p_func->getName(); + cout << " - " << critical_edges.size() << " critical edges detected" << endl; + + if (m_verbose) + { + cout << "Original CFG: " << endl; + cout << cfg << endl; + } + + for (const auto &edge : critical_edges) + { + auto source_block = get<0>(edge); + auto target_block = get<1>(edge); + + auto last_instruction_in_source_block = source_block->getInstructions()[source_block->getInstructions().size()-1]; + auto first_instruction_in_target_block = target_block->getInstructions()[0]; + + if (source_block->endsInConditionalBranch()) + { + const auto func = last_instruction_in_source_block->getFunction(); + + if (last_instruction_in_source_block->getTarget() == first_instruction_in_target_block) + { + auto jmp=m_IR->addNewInstruction(nullptr,func); + setInstructionAssembly(m_IR, jmp, "jmp 0", nullptr, first_instruction_in_target_block); + jmp->setComment("break_critical_edge_jmp"); + + last_instruction_in_source_block->setTarget(jmp); + num_critical_edges_instrumented++; + } + else if (last_instruction_in_source_block->getFallthrough() == first_instruction_in_target_block) + { + auto jmp=m_IR->addNewInstruction(nullptr,func); + setInstructionAssembly(m_IR, jmp, "jmp 0", nullptr, first_instruction_in_target_block); + jmp->setComment("break_critical_edge_fallthrough"); + + last_instruction_in_source_block->setFallthrough(jmp); + num_critical_edges_instrumented++; + } + } + } + + + if (m_verbose) + { + m_IR->assembleRegistry(); + cout << "Number critical edge instrumented: " << num_critical_edges_instrumented << endl; + auto post_cfgp = ControlFlowGraph_t::factory(p_func); + auto &post_cfg = *post_cfgp; + cout << "Post CFG: " << endl; + cout << post_cfg << endl; + } + return num_critical_edges_instrumented; +} + + diff --git a/zfuzz/afl_transforms/tools/zax/critical_edge_breaker.hpp b/zfuzz/afl_transforms/tools/zax/critical_edge_breaker.hpp new file mode 100644 index 0000000000000000000000000000000000000000..bbeeebcdd1e2f5ddb5b29b55ef4651dbbbd1bb7c --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/critical_edge_breaker.hpp @@ -0,0 +1,33 @@ +#ifndef _LIBTRANSFORM_CRITICAL_EDGE_BREAKER_H +#define _LIBTRANSFORM_CRITICAL_EDGE_BREAKER_H + +#include <irdb-core> + +namespace Zafl +{ + using namespace IRDB_SDK; + + // + // Break critical edges + // + class CriticalEdgeBreaker_t + { + public: + CriticalEdgeBreaker_t(FileIR_t *p_variantIR, set<string> p_blacklist=set<string>(), const bool p_verbose=false); + unsigned getNumberExtraNodes() const; + + protected: + void breakCriticalEdges(); + + private: + unsigned breakCriticalEdges(Function_t*); + + private: + FileIR_t* m_IR; + const set<string> m_blacklist; + const bool m_verbose; + unsigned m_extra_nodes; + }; +} + +#endif diff --git a/zfuzz/afl_transforms/tools/zax/test/test_context.c b/zfuzz/afl_transforms/tools/zax/test/test_context.c new file mode 100644 index 0000000000000000000000000000000000000000..6e43960a11c49a0d468c728879f8bb2619e70ca2 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/test/test_context.c @@ -0,0 +1,34 @@ +#include <stdio.h> +volatile int bar(int a) +{ + if (a == 2) + return 5; + else + return 3; +} + +volatile int foo(int a) +{ + if (a==3) + return bar(a); + else + return bar(2); + +} + +volatile int bob(int a) +{ + a = bar(7); + if (a==2) + return 1; + else return 2; + +} + +int main(int argc, char **argv) +{ + int x = foo(argc); + int y = bar(argc); + int z = bob(argc); + printf("out=%d\n", x+y+z); +} diff --git a/zfuzz/afl_transforms/tools/zax/test/test_context.sh b/zfuzz/afl_transforms/tools/zax/test/test_context.sh new file mode 100755 index 0000000000000000000000000000000000000000..7505a722b9b7c82cbbcaadc975e007deb7bea232 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/test/test_context.sh @@ -0,0 +1,106 @@ +cd $(dirname $(realpath $0) ) + +PUT=test_context.exe +ZAFL_PUT="$PUT.zafl $PUT.zafl.context_sensitive" +MYARG="a" + +log_msg() +{ + echo "TEST PASS: $1" +} + +log_error() +{ + echo "TEST FAIL: $1" + exit 1 +} + +check_afl() +{ + which afl-showmap >/dev/null 2>&1 + if [ ! $? -eq 0 ]; then + log_error "AFL doesn't seem to be installed. Try: 'sudo apt install afl' before proceeding or download/build afl directly from source" + fi +} + +build_one() +{ + local orig=$1 + local zafl=$2 + shift + shift + zafl.sh $orig $zafl $@ + if [ $? -eq 0 ]; then + log_msg "build $zafl" + else + log_error "build $zafl" + fi +} + +build_all() +{ + g++ test_context.c -o $PUT +} + +zafl_all() +{ + for p in $* + do + build_one $p $p.zafl -v -t $p.analysis + build_one $p $p.zafl.context_sensitive --enable-context-sensitivity function -v -t $p.analysis.context_sensitive + done +} + +clean_all() +{ + rm -fr ${PUT}* +} + +verify_output() +{ + local arg=$1 + shift + local orig_zafl=$1 + shift + local all_configs=$* + + ./$orig_zafl $arg > $orig_zafl.output.orig + + for p in $all_configs + do + echo "Program under test: $p" + ./${p} $arg > $p.output + diff $orig_zafl.output.orig $p.output + if [ ! $? -eq 0 ]; then + log_error "output verification failure: $p.output" + fi + done + + log_msg "output verified for $orig_zafl" +} + +clean_all +check_afl + +build_all + +zafl_all $PUT +verify_output $MYARG $PUT $ZAFL_PUT + +zafl_map=${PUT}.zafl.map +afl-showmap -o $zafl_map -- ./${PUT}.zafl +count_zafl=$(wc -l ${zafl_map} | cut -d' ' -f1) + +zafl_cs_map=${PUT}.zafl.map +afl-showmap -o $zafl_cs_map -- ./${PUT}.zafl.context_sensitive +count_zafl_cs=$(wc -l ${zafl_cs_map} | cut -d' ' -f1) + +# difference must be exactly 2 +let diff=$count_zafl_cs-$count_zafl +if [ $diff -eq 2 ]; then + log_msg "context sensitive map has +2 entries over baseline zafl map" +else + log_error "context sensitive map does not have expected number of entries (should be +2): map_size(zafl):$count_zafl map_size(zafl_context_sensitive):$count_zafl_cs" +fi + +clean_all diff --git a/zfuzz/afl_transforms/tools/zax/test/test_context_recursion.sh b/zfuzz/afl_transforms/tools/zax/test/test_context_recursion.sh new file mode 100755 index 0000000000000000000000000000000000000000..fd50dc643e03bac5d59dc831cbad7b926e6b6590 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/test/test_context_recursion.sh @@ -0,0 +1,118 @@ +cd $(dirname $(realpath $0) ) + +PUT=test_fib.exe +ZAFL_PUT="$PUT.zafl $PUT.zafl.context_sensitive" +MYARG=8 + +log_msg() +{ + echo "TEST PASS: $1" +} + +log_error() +{ + echo "TEST FAIL: $1" + exit 1 +} + +check_afl() +{ + which afl-showmap >/dev/null 2>&1 + if [ ! $? -eq 0 ]; then + log_error "AFL doesn't seem to be installed. Try: 'sudo apt install afl' before proceeding or download/build afl directly from source" + fi +} + +build_one() +{ + local orig=$1 + local zafl=$2 + shift + shift + zafl.sh $orig $zafl $@ + if [ $? -eq 0 ]; then + log_msg "build $zafl" + else + log_error "build $zafl" + fi +} + +build_all() +{ + g++ test_fib.c -o $PUT +} + +zafl_all() +{ + for p in $* + do + build_one $p $p.zafl -v -t $p.analysis + build_one $p $p.zafl.context_sensitive --enable-context-sensitivity function -v -t $p.analysis.context_sensitive + done +} + +clean_all() +{ + rm -fr ${PUT}* +} + +verify_output() +{ + local arg=$1 + shift + local orig_zafl=$1 + shift + local all_configs=$* + + ./$orig_zafl $arg > $orig_zafl.output.orig + + for p in $all_configs + do + echo "Program under test: $p" + ./${p} $arg > $p.output + diff $orig_zafl.output.orig $p.output + if [ ! $? -eq 0 ]; then + log_error "output verification failure: $p.output" + fi + done + + log_msg "output verified for $orig_zafl" +} + +clean_all +check_afl + +build_all + +zafl_all $PUT +verify_output $MYARG $PUT $ZAFL_PUT + +zafl_map15=${PUT}.zafl.map +afl-showmap -o $zafl_map15 -- ./${PUT}.zafl 15 +count_zafl15=$(wc -l ${zafl_map15} | cut -d' ' -f1) + +zafl_cs_map8=${PUT}.zafl.cs.map.8 +afl-showmap -o $zafl_cs_map8 -- ./${PUT}.zafl.context_sensitive 8 +count_zafl_cs8=$(wc -l ${zafl_cs_map8} | cut -d' ' -f1) + +zafl_cs_map20=${PUT}.zafl.cs.map.20 +afl-showmap -o $zafl_cs_map20 -- ./${PUT}.zafl.context_sensitive 20 +count_zafl_cs20=$(wc -l ${zafl_cs_map20} | cut -d' ' -f1) + +# make sure we have more entries with context sensitivity +let diff=$count_zafl_cs8-$count_zafl15 +if [ $diff -gt 5 ]; then + log_msg "vanilla zafl with deep recursion should have fewer entries than context sensitive version" +else + log_error "vanilla zafl with deep recursion should have fewer entries than context sensitive version" +fi + +# make sure we don't blow out the map with deep recursion +let diff=$count_zafl_cs20-$count_zafl_cs8 +if [ $diff -eq 0 ]; then + log_msg "recursion level of 8 or 20 should have same number of entries in the trace map" +else + log_error "recursion level of 8 or 20 should have same number of entries in the trace map" +fi + +clean_all diff --git a/zfuzz/afl_transforms/tools/zax/test/test_fib.c b/zfuzz/afl_transforms/tools/zax/test/test_fib.c new file mode 100644 index 0000000000000000000000000000000000000000..0c0e160652b2e2d3b89e8e715f9165f23d80791e --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/test/test_fib.c @@ -0,0 +1,22 @@ +#include <stdio.h> +#include <stdlib.h> + +volatile int fib(int a) +{ + if (a == 0) return 0; + if (a == 1) return 1; + if (a == 2) return 1; + return fib(a-1) + fib(a-2); +} + +int main(int argc, char **argv) +{ + if (argc <= 1) return 1; + int x = atoi(argv[1]); + if (x < 0) + return 2; + if (x > 50) + return 3; + int f = fib(x); + printf("fibonacci(%d) = %d\n", x, f); +} diff --git a/zfuzz/afl_transforms/tools/zax/test/test_graph.sh b/zfuzz/afl_transforms/tools/zax/test/test_graph.sh new file mode 100755 index 0000000000000000000000000000000000000000..b87836f4ce045a87a7c87e62de011a850b842ab5 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/test/test_graph.sh @@ -0,0 +1,148 @@ +cd $(dirname $(realpath $0) ) + +PUT=test_mystrlen.exe +# make arg 16 chars sharp as it falls on an afl boundary for the hit count +MYARG="0123456789abcdef" + +PUT2=test_mystrlen2.exe +MYARG2="0123456789abcdefaadsf" + +PUT3=test_running.exe +MYARG3="000aaaaaaaa" +MYARG3A="000aaaaaaaaaaaaaaaaaaaaaaaaaaaa" + +ZAFL_PUT="$PUT.zafl $PUT.zafl.c $PUT.zafl.g $PUT.zafl.d $PUT.zafl.d.g $PUT.zafl.c.d.g" +ZAFL_PUT2="$PUT2.zafl $PUT2.zafl.c $PUT2.zafl.g $PUT2.zafl.d $PUT2.zafl.d.g $PUT2.zafl.c.d.g" +ZAFL_PUT3="$PUT3.zafl $PUT3.zafl.c $PUT3.zafl.g $PUT3.zafl.d $PUT3.zafl.d.g $PUT3.zafl.c.d.g" + +log_msg() +{ + echo "TEST PASS: $1" +} + +log_error() +{ + echo "TEST FAIL: $1" + exit 1 +} + +check_afl() +{ + which afl-showmap >/dev/null 2>&1 + if [ ! $? -eq 0 ]; then + log_error "AFL doesn't seem to be installed. Try: 'sudo apt install afl' before proceeding or download/build afl directly from source" + fi +} + +build_one() +{ + local orig=$1 + local zafl=$2 + shift + shift + zafl.sh $orig $zafl $@ + if [ $? -eq 0 ]; then + log_msg "build $zafl" + else + log_error "build $zafl" + fi +} + +build_all() +{ + g++ test_mystrlen.cpp -o $PUT + g++ test_mystrlen2.cpp -o $PUT2 + g++ test_running.cpp -o $PUT3 +} + +zafl_all() +{ + for p in $* + do + build_one $p $p.zafl -v -t $p.analysis + build_one $p $p.zafl.c -c -v -t $p.analysis.c + build_one $p $p.zafl.g -g -v -t $p.analysis.g + build_one $p $p.zafl.d -d -v -t $p.analysis.d + build_one $p $p.zafl.d.g -d -g -v -t $p.analysis.d.g + build_one $p $p.zafl.c.d.g -d -g -v -t $p.analysis.c.d.g + done +} + +clean_all() +{ + rm -fr ${PUT}* ${PUT2}* ${PUT3}* +} + +verify_output() +{ + local arg=$1 + shift + local orig_zafl=$1 + shift + local all_configs=$* + + ./$orig_zafl $arg > $orig_zafl.output.orig + + for p in $all_configs + do + echo "Program under test: $p" + ./${p} $arg > $p.output + diff $orig_zafl.output.orig $p.output + if [ ! $? -eq 0 ]; then + log_error "output verification failure: $p.output" + fi + done + + log_msg "output verified for $orig_zafl" +} + +verify_afl_map() +{ + local arg=$1 + shift + local orig_zafl=$1 + shift + local all_configs=$* + for p in $all_configs + do + echo "Computing trace maps for input $MYARG" + afl-showmap -o $p.map -- ./$p $arg + cut -d':' -f2 $p.map | sort -r | head -n 1 > $p.max_count + done + + for p in $all_configs + do + diff $orig_zafl.zafl.max_count $p.max_count >/dev/null 2>&1 + if [ $? -eq 0 ]; then + max=$(cat $orig_zafl.zafl.max_count) + log_msg "maximum afl edge value for $orig_zafl.zafl and $p match ($max)" + else + echo -n "Maximum count for $orig_zafl: " + cat $orig_zafl.zafl.max_count + echo -n "Maximum count for $p: " + cat $p.max_count + log_error "maximum afl edge value does not match for $orig_zafl.zafl and $p" + fi + done +} + +clean_all +check_afl + +build_all + +zafl_all $PUT +verify_output $MYARG $PUT $ZAFL_PUT +verify_afl_map $MYARG $PUT $ZAFL_PUT + +zafl_all $PUT2 +verify_output $MYARG2 $PUT2 $ZAFL_PUT2 +verify_afl_map $MYARG2 $PUT2 $ZAFL_PUT2 + +zafl_all $PUT3 +verify_output $MYARG3 $PUT3 $ZAFL_PUT3 +verify_afl_map $MYARG3 $PUT3 $ZAFL_PUT3 +verify_output $MYARG3A $PUT3 $ZAFL_PUT3 +verify_afl_map $MYARG3A $PUT3 $ZAFL_PUT3 + +clean_all diff --git a/zfuzz/afl_transforms/tools/zax/test/test_mystrlen.cpp b/zfuzz/afl_transforms/tools/zax/test/test_mystrlen.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5bc0f485305ef44047245817145633b28a3b9ace --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/test/test_mystrlen.cpp @@ -0,0 +1,34 @@ +#include <iostream> + +using namespace std; + +int x = 0; + +volatile int identity(int y) +{ + if (y % 39 == 0) + x = 1; + else + x = 2; + return y; +} + +size_t my_strlen(char *arg) +{ + int count = 0; + while (*arg!='\0') + { + count++; + arg++; + count = identity(count); + } + + return count; +} + +int main(int argc, char **argv) +{ + if (argc > 1) + cout << "length= " << my_strlen(argv[1]) << endl; + cout << "x= " << x << endl; +} diff --git a/zfuzz/afl_transforms/tools/zax/test/test_mystrlen2.cpp b/zfuzz/afl_transforms/tools/zax/test/test_mystrlen2.cpp new file mode 100644 index 0000000000000000000000000000000000000000..559b408cc8fa15e8d90327800cb765bdb994d483 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/test/test_mystrlen2.cpp @@ -0,0 +1,34 @@ +#include <iostream> + +using namespace std; + +int x = 0; + +volatile int identity(int x) +{ + return x; +} + +size_t my_strlen(char *arg) +{ + int count = 0; + while (*arg!='\0') + { + if (count % 99) + x *= count; + else if (count % 98) + x += count; + + count++; + arg++; + } + + return count; +} + +int main(int argc, char **argv) +{ + if (argc > 1) + cout << "length: " << my_strlen(argv[1]) << endl; + cout << "x: " << x <<endl; +} diff --git a/zfuzz/afl_transforms/tools/zax/test/test_running.cpp b/zfuzz/afl_transforms/tools/zax/test/test_running.cpp new file mode 100644 index 0000000000000000000000000000000000000000..59d04bd247e560f9777f296925352115434c43d1 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/test/test_running.cpp @@ -0,0 +1,34 @@ +#include <iostream> +#include <string.h> + +using namespace std; + +int num_running_a = 0; + +size_t my_strlen(char *arg) +{ + int i = 0; + bool a_detected = false; + + while(arg[i]) + { + if (arg[i]=='a') { + num_running_a++; + a_detected = true; + } + else{ + if (a_detected) + goto out; + } + i++; + } +out: + return i; +} + +int main(int argc, char **argv) +{ + if (argc > 1) + cout << "length: " << my_strlen(argv[1]) << endl; + cout << "num running a: " << num_running_a <<endl; +} diff --git a/zfuzz/afl_transforms/tools/zax/zax.cpp b/zfuzz/afl_transforms/tools/zax/zax.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bc1fe097a97691ada8dacc1c020743339303119c --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/zax.cpp @@ -0,0 +1,410 @@ +/*************************************************************************** + * Copyright (c) 2018-2019 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT INFO + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include "zax.hpp" + +using namespace std; +using namespace IRDB_SDK; +using namespace Zafl; + +Zax_t::Zax_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_variantIR, string p_forkServerEntryPoint, set<string> p_exitPoints, bool p_use_stars, bool p_autozafl) : ZaxBase_t(p_dbinterface, p_variantIR, p_forkServerEntryPoint, p_exitPoints, p_use_stars, p_autozafl) +{ +} + +/* + * Return random block id + * Try 100x to avoid duplicate ids + */ +ZaflBlockId_t Zax_t::getBlockId(const unsigned p_max) +{ + auto counter = 0; + auto blockid = 0; + + // only try getting new block id 100 times + // avoid returning duplicate if we can help it + while (counter++ < 100) { + blockid = rand() % p_max; + if (m_used_blockid.find(blockid) == m_used_blockid.end()) + { + m_used_blockid.insert(blockid); + return blockid; + } + } + return blockid; +} + +/* + Original afl instrumentation: + block_id = <random>; + zafl_trace_bits[zafl_prev_id ^ block_id]++; + zafl_prev_id = block_id >> 1; + + CollAfl optimization when (#predecessors==1) (goal of CollAfl is to reduce collisions): + block_id = <some unique value for this block> + zafl_trace_bits[block_id]++; + zafl_prev_id = block_id >> 1; +*/ +void Zax_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_honorRedZone, const bool p_collafl_optimization) +{ + char buf[8192]; + auto live_flags = true; + char *reg_temp = NULL; + char *reg_temp32 = NULL; + char *reg_temp16 = NULL; + char *reg_trace_map = NULL; + char *reg_prev_id = NULL; + char *reg_context = NULL; + char *reg_context16 = NULL; + auto save_temp = true; + auto save_trace_map = true; + auto save_prev_id = true; + auto save_context = (getContextSensitivity() != ContextSensitivity_None) ? true : false; + auto block_record=BBRecord_t(); + + // if fixed address, only need 1 register + // if not, need up to 4 registers + auto num_free_regs_desired = save_context ? 4 : 3; + if (useFixedAddresses()) + num_free_regs_desired = 1; + + auto instr = getInstructionToInstrument(p_bb, num_free_regs_desired); + if (!instr) throw; + + // don't try to reserve the trace_map reg if we aren't using it. + if(useFixedAddresses()) + { + save_trace_map=false; + save_prev_id=false; + save_context=false; + } + + block_record.push_back(instr); + + // If we are using stars, try to assign rax, rcx, and rdx to their + // most desireable position in the instrumentation. + if (m_use_stars) + { + auto regset = getDeadRegs(instr); + live_flags = regset.find(IRDB_SDK::rn_EFLAGS)==regset.end(); + + const auto allowed_regs = RegisterSet_t({rn_RAX, rn_RBX, rn_RCX, rn_RDX, rn_R8, rn_R9, rn_R10, rn_R11, rn_R12, rn_R13, rn_R14, rn_R15}); + auto free_regs = getFreeRegs(regset, allowed_regs); + + for (auto r : regset) + { + if (r == rn_RAX) + { + reg_temp = strdup("rax"); reg_temp32 = strdup("eax"); reg_temp16 = strdup("ax"); + save_temp = false; + free_regs.erase(r); + } + else if (r == rn_RCX && save_trace_map) + { + reg_trace_map = strdup("rcx"); + save_trace_map = false; + free_regs.erase(r); + } + else if (r == rn_RDX && save_prev_id) + { + reg_prev_id = strdup("rdx"); + save_prev_id = false; + free_regs.erase(r); + } + else if (r == rn_R8 && save_context) + { + reg_context=strdup("r8"); + reg_context16=strdup("r8w"); + save_context = false; + free_regs.erase(r); + } + } + + // if we failed to do the assignment, check for any other register to fill the assignment. + if (save_temp && free_regs.size() >= 1) + { + auto r = *free_regs.begin(); + auto r32 = convertRegisterTo32bit(r); assert(isValidRegister(r32)); + auto r16 = convertRegisterTo16bit(r); assert(isValidRegister(r16)); + reg_temp = strdup(registerToString(r).c_str()); + reg_temp32 = strdup(registerToString(r32).c_str()); + reg_temp16 = strdup(registerToString(r16).c_str()); + save_temp = false; + free_regs.erase(r); + } + + if (save_trace_map && free_regs.size() >= 1) + { + auto r = *free_regs.begin(); + reg_trace_map = strdup(registerToString(r).c_str()); + save_trace_map = false; + free_regs.erase(r); + } + + if (save_prev_id && free_regs.size() >= 1) + { + auto r = *free_regs.begin(); + reg_prev_id = strdup(registerToString(r).c_str()); + save_prev_id = false; + free_regs.erase(r); + } + + if (getContextSensitivity() != ContextSensitivity_None) + { + if (save_context && free_regs.size() >= 1) + { + auto r = *free_regs.begin(); + auto r16 = convertRegisterTo16bit(r); + reg_context = strdup(registerToString(r).c_str()); + reg_context16 = strdup(registerToString(r16).c_str()); + save_context = false; + free_regs.erase(r); + } + } + } + + // In the event we couldn't find a free register, or we aren't using stars + // to identify free registers, use the default registers. We will save and + // restore these later. + if (!reg_temp) reg_temp = strdup("rax"); + if (!reg_temp32) reg_temp32 = strdup("eax"); + if (!reg_temp16) reg_temp16 = strdup("ax"); + if (!reg_trace_map) reg_trace_map = strdup("rcx"); + if (!reg_prev_id) reg_prev_id = strdup("rdx"); + if (!reg_context) reg_context = strdup("r8"); + if (!reg_context16) reg_context16 = strdup("r8w"); + + if (m_verbose) + { + cout << "save_temp: " << save_temp << " save_trace_map: " << save_trace_map << " save_prev_id: " << save_prev_id << " live_flags: " << live_flags << endl; + cout << "reg_temp: " << reg_temp << " " << reg_temp32 << " " << reg_temp16 + << " reg_trace_map: " << reg_trace_map + << " reg_prev_id: " << reg_prev_id + << " reg_context: " << reg_context << endl; + } + + // warning: first instrumentation must use insertAssemblyBefore + // others use insertAssemblyAfter. + // we declare a macro-like lambda function to do the lifting for us. + auto inserted_before = false; + auto tmp = instr; + const auto do_insert=[&](const string& insn_str) -> void + { + if (inserted_before) + { + tmp = insertAssemblyAfter(tmp, insn_str); + block_record.push_back(tmp); + } + else + { + const auto orig = insertAssemblyBefore(tmp, insn_str); + inserted_before = true; + block_record.push_back(orig); + } + }; + + // get some IDs which we can use as to generate custom labels + const auto blockid = getBlockId(); + const auto labelid = getLabelId(); + + if (m_verbose) + { + cout << "labelid: " << labelid << " baseid: " << instr->getBaseID() << " address: 0x" + << hex << instr->getAddress()->getVirtualOffset() << dec << " instruction: " + << instr->getDisassembly(); + } + + // Emit the instrumentation register-saving phase. + // Omit saving any registers we don't need to save because we + // were able to locate a free register. + if (p_honorRedZone) do_insert("lea rsp, [rsp-128]"); + if (save_temp) do_insert("push rax"); + if (save_trace_map) do_insert("push rcx"); + if (save_prev_id) do_insert("push rdx"); + if (save_context) do_insert("push r8"); + if (live_flags) do_insert("pushf"); + + const auto live_flags_str = live_flags ? "live" : "dead"; + if (m_verbose) cout << " flags are " << live_flags_str << endl; + + +/* + 0: mov rdx,QWORD PTR [rip+0x0] # load previous block id. + 7: mov rcx,QWORD PTR [rip+0x0] # load trace map address. + +<no collafl optimization> + e: movzx eax,WORD PTR [rdx] # hash prev-block-id with this-block id (0x1234) + 11: xor ax,0x1234 + 15: movzx eax,ax + +<collafl-style optimization> +<noaddr> mov eax, <blockid> # faster hash -- ignore previous id. + + 18: add rax,QWORD PTR [rcx] # generate address of trace map to bump + ib: add BYTE PTR [rax],0x1 # bump the entr + 1e: mov eax,0x91a # write this block ID back to the prev-id variable. + 23: mov WORD PTR [rdx],ax +*/ + + // load the previous block ID. + // 0: mov rdx,QWORD PTR [rip+0x0] # 7 <f+0x7> +// FIXME: Why are we doing this if we aren't bothering to hash the previous block ID? + + if (!useFixedAddresses()) + { + sprintf(buf, "P%d: mov %s, QWORD [rel P%d]", labelid, reg_prev_id, labelid); // rdx + do_insert(buf); + create_got_reloc(getFileIR(), m_prev_id, tmp); + } + + if (getContextSensitivity() != ContextSensitivity_None) + { + if (!useFixedAddresses()) + { + sprintf(buf, "C%d: mov %s, QWORD [rel C%d]", labelid, reg_context, labelid); + do_insert(buf); + create_got_reloc(getFileIR(), m_context_id, tmp); + + sprintf(buf,"mov %s, [%s]", reg_context, reg_context); + do_insert(buf); + } + } + + // if we are using a variable address trace map, generate the address. + if(!useFixedAddresses()) + { + // 7: mov rcx,QWORD PTR [rip+0x0] # e <f+0xe> + sprintf(buf, "T%d: mov %s, QWORD [rel T%d]", labelid, reg_trace_map, labelid); + do_insert(buf); + create_got_reloc(getFileIR(), m_trace_map, tmp); + } + + // compute index into trace map + // do the calculation to has the previouus block ID with this block ID + // in the faster or slower fashion depending on the requested technique. + if (!p_collafl_optimization) + { + // e: movzx eax,WORD PTR [rdx] + if (useFixedAddresses()) + { + sprintf(buf,"movzx %s,WORD [0x%lx]", reg_temp32, getFixedAddressPrevId()); + } + else + { + sprintf(buf,"movzx %s,WORD [%s]", reg_temp32, reg_prev_id); + } + do_insert(buf); + + // 11: xor ax,0x1234 + sprintf(buf, "xor %s,0x%x", reg_temp16, blockid); + do_insert(buf); + + // hash with calling context value + if (getContextSensitivity() != ContextSensitivity_None) + { + if (useFixedAddresses()) + { + sprintf(buf, "xor %s,WORD [0x%lx]", reg_temp16, getFixedAddressContext()); + do_insert(buf); + } + else + { + // xor ax, <context_id_register> + sprintf(buf, "xor %s,%s", reg_temp16, reg_context16); + do_insert(buf); + } + } + + // 15: movzx eax,ax + sprintf(buf,"movzx %s,%s", reg_temp32, reg_temp16); + do_insert(buf); + } + else + { + // <noaddr> mov rax, <blockid> + sprintf(buf,"mov %s,0x%x", reg_temp, blockid); + do_insert(buf); + } + + // write into the trace map. + if(useFixedAddresses()) + { + // do it the fast way with the fixed-adresss trace map + // 1b: 80 00 01 add BYTE PTR [rax],0x1 + sprintf(buf,"add BYTE [%s + 0x%lx],0x1", reg_temp, getFixedAddressMap()); + do_insert(buf); + } + else + { + // do it the slow way with the variable-adresss trace map + // 18: add rax,QWORD PTR [rcx] + sprintf(buf,"add %s,QWORD [%s]", reg_temp, reg_trace_map); + do_insert(buf); + + // 1b: add BYTE PTR [rax],0x1 + sprintf(buf,"add BYTE [%s],0x1", reg_temp); + do_insert(buf); + } + + // write out block id into zafl_prev_id for the next instrumentation. + // 1e: mov eax,0x91a + if (!p_collafl_optimization) + { + if (useFixedAddresses()) + { + sprintf(buf, "mov WORD [0x%lx], 0x%x", getFixedAddressPrevId(), blockid >> 1); + do_insert(buf); + } + else + { + sprintf(buf, "mov %s, 0x%x", reg_temp32, blockid >> 1); + do_insert(buf); + + // store prev_id + // 23: mov WORD PTR [rdx],ax + sprintf(buf, "mov WORD [%s], %s", reg_prev_id, reg_temp16); + do_insert(buf); + } + } + + // finally, restore any flags/registers so that the program can execute. + if (live_flags) do_insert("popf"); + if (save_context) do_insert("pop r8"); + if (save_prev_id) do_insert("pop rdx"); + if (save_trace_map) do_insert("pop rcx"); + if (save_temp) do_insert("pop rax"); + if (p_honorRedZone) do_insert("lea rsp, [rsp+128]"); + + m_modifiedBlocks[blockid] = block_record; + + +// FIXME: use strings instead of strdup and buffers everywhere. +// there is no chance the mallocs/frees in this function match properly. + free(reg_temp); + free(reg_temp32); + free(reg_temp16); + free(reg_trace_map); + free(reg_prev_id); + free(reg_context); + free(reg_context16); +} + diff --git a/zfuzz/afl_transforms/tools/zax/zax.hpp b/zfuzz/afl_transforms/tools/zax/zax.hpp new file mode 100644 index 0000000000000000000000000000000000000000..9f281bc7faefe436b1f3570b3b653df3415eb836 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/zax.hpp @@ -0,0 +1,30 @@ +#ifndef _LIBTRANSFORM_ZAX_H +#define _LIBTRANSFORM_ZAX_H + +#include "zax_base.hpp" + +namespace Zafl +{ + // + // Implements afl-style edge coverage instrumentation + // + class Zax_t : public ZaxBase_t + { + public: + // explicitly disable default and copy constructors + Zax_t() = delete; + Zax_t(const Zafl::Zax_t&) = delete; + Zax_t(pqxxDB_t &p_dbinterface, FileIR_t *p_variantIR, string p_entry, set<string> p_exits, bool p_use_stars=false, bool p_autozafl=false); + virtual ~Zax_t() {}; + + protected: + virtual ZaflBlockId_t getBlockId(const unsigned p_maxid=0xFFFF); + virtual void instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_hasLeafAnnotation, const bool p_collafl_optimization=false); + + private: + set<ZaflBlockId_t> m_used_blockid; // internal bookkeeping to keep track of used block ids + }; + +} + +#endif diff --git a/zfuzz/afl_transforms/tools/zax/zax_base.cpp b/zfuzz/afl_transforms/tools/zax/zax_base.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8cfb7655619d4977ddaecaea5d812a2885bff7ac --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/zax_base.cpp @@ -0,0 +1,1299 @@ +/*************************************************************************** + * Copyright (c) 2018-2019 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT INFO + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <stdlib.h> +#include <string.h> +#include <algorithm> +#include <cctype> +#include <sstream> +#include <fstream> +#include <irdb-cfg> +#include <irdb-transform> +#include <irdb-elfdep> +#include <irdb-deep> + +#include "zax_base.hpp" +#include "critical_edge_breaker.hpp" + +using namespace std; +using namespace IRDB_SDK; +using namespace Zafl; + +#define ALLOF(a) begin(a),end(a) +#define FIRSTOF(a) (*(begin(a))) + +void create_got_reloc(FileIR_t* fir, pair<DataScoop_t*,int> wrt, Instruction_t* i) +{ + (void)fir->addNewRelocation(i,wrt.second, "pcrel", wrt.first); +} + +RegisterSet_t ZaxBase_t::getDeadRegs(Instruction_t* insn) const +{ + auto it = dead_registers -> find(insn); + if(it != dead_registers->end()) + return it->second; + return RegisterSet_t(); +} + +// return intersection of candidates and allowed general-purpose registers +RegisterSet_t ZaxBase_t::getFreeRegs(const RegisterSet_t& candidates, const RegisterSet_t& allowed) const +{ + RegisterIDSet_t free_regs; + set_intersection(ALLOF(candidates), ALLOF(allowed), std::inserter(free_regs,free_regs.begin())); + return free_regs; +} + +bool ZaxBase_t::hasLeafAnnotation(Function_t* fn) const +{ + auto it = leaf_functions -> find(fn); + return (it != leaf_functions->end()); +} + +bool ZaxBase_t::BB_isPaddingNop(const BasicBlock_t *p_bb) const +{ + return p_bb->getInstructions().size()==1 && + p_bb->getPredecessors().size()==0 && + p_bb->getSuccessors().size()==1 && + p_bb->getInstructions()[0]->getDisassembly().find("nop")!=string::npos; +} + +bool ZaxBase_t::BB_isPushJmp(const BasicBlock_t *p_bb) const +{ + return p_bb->getInstructions().size()==2 && + p_bb->getInstructions()[0]->getDisassembly().find("push")!=string::npos && + p_bb->getInstructions()[1]->getDisassembly().find("jmp")!=string::npos; +} + +ZaxBase_t::ZaxBase_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_variantIR, string p_forkServerEntryPoint, set<string> p_exitPoints, bool p_use_stars, bool p_autozafl) + : + Transform(p_variantIR), + m_dbinterface(p_dbinterface), + m_use_stars(p_use_stars), + m_autozafl(p_autozafl), + m_graph_optimize(false), + m_domgraph_optimize(false), + m_forkserver_enabled(true), + m_breakupCriticalEdges(false), + m_fork_server_entry(p_forkServerEntryPoint), + m_exitpoints(p_exitPoints) +{ + if (m_use_stars) { + cout << "Use STARS analysis engine" << endl; + auto deep_analysis=DeepAnalysis_t::factory(getFileIR()); + leaf_functions = deep_analysis -> getLeafFunctions(); + dead_registers = deep_analysis -> getDeadRegisters(); + } + + auto ed=ElfDependencies_t::factory(getFileIR()); + if (p_autozafl) + { + cout << "autozafl library is on" << endl; + (void)ed->prependLibraryDepedencies("libautozafl.so"); + } + else + { + cout << "autozafl library is off" << endl; + (void)ed->prependLibraryDepedencies("libzafl.so"); + } + + // bind to external symbols declared in libzafl.so + m_plt_zafl_initAflForkServer=ed->appendPltEntry("zafl_initAflForkServer"); + m_trace_map = ed->appendGotEntry("zafl_trace_map"); + m_prev_id = ed->appendGotEntry("zafl_prev_id"); + m_context_id = ed->appendGotEntry("zafl_context"); + + // let's not instrument these functions ever + // see isBlacklisted() for other blacklisted functions + m_blacklist.insert("init"); + m_blacklist.insert("_init"); + m_blacklist.insert("start"); + m_blacklist.insert("_start"); + m_blacklist.insert("fini"); + m_blacklist.insert("_fini"); + m_blacklist.insert("register_tm_clones"); + m_blacklist.insert("deregister_tm_clones"); + m_blacklist.insert("frame_dummy"); + m_blacklist.insert("__do_global_ctors_aux"); + m_blacklist.insert("__do_global_dtors_aux"); + m_blacklist.insert("__libc_csu_init"); + m_blacklist.insert("__libc_csu_fini"); + m_blacklist.insert("__libc_start_main"); + m_blacklist.insert("__gmon_start__"); + m_blacklist.insert("__cxa_atexit"); + m_blacklist.insert("__cxa_finalize"); + m_blacklist.insert("__assert_fail"); + m_blacklist.insert("free"); + m_blacklist.insert("fnmatch"); + m_blacklist.insert("readlinkat"); + m_blacklist.insert("malloc"); + m_blacklist.insert("calloc"); + m_blacklist.insert("realloc"); + m_blacklist.insert("argp_failure"); + m_blacklist.insert("argp_help"); + m_blacklist.insert("argp_state_help"); + m_blacklist.insert("argp_error"); + m_blacklist.insert("argp_parse"); + + m_verbose = false; + m_bb_float_instrumentation = false; + + setContextSensitivity(ContextSensitivity_None); + + m_entry_point = nullptr; + + m_labelid = 0; + m_blockid = 0; + + // stats + m_num_bb = 0; + m_num_bb_instrumented = 0; + m_num_bb_skipped = 0; + m_num_bb_skipped_pushjmp = 0; + m_num_bb_skipped_nop_padding = 0; + m_num_bb_skipped_cbranch = 0; + m_num_style_collafl = 0; + m_num_bb_float_instrumentation = 0; + m_num_bb_float_regs_saved = 0; + m_num_domgraph_blocks_elided = 0; + m_num_exit_blocks_elided = 0; + m_num_entry_blocks_elided = 0; + m_num_single_block_function_elided = 0; + m_num_contexts = 0; + + // fixed addresses (must match libzafl) + const auto trace_map_fixed_addr_s = getenv("ZAFL_TRACE_MAP_FIXED_ADDRESS"); + m_do_fixed_addr_optimization = (trace_map_fixed_addr_s!=nullptr); + + if (m_do_fixed_addr_optimization) { + cout << "fixed address optimization enabled" << endl; + m_trace_map_fixed_addr = strtoul(trace_map_fixed_addr_s,nullptr,0); + // must match values in libzafl.so + // @todo: include libzafl.hpp + const auto trace_map_size = 65536; // power of 2 + const auto gap = 4096; // page, multiple of 4K + const auto previd_offset = 32; // word aligned + const auto context_offset = 64; // word aligned (make sure no overlap with previd) + m_previd_fixed_addr = m_trace_map_fixed_addr + trace_map_size + gap + previd_offset; + m_context_fixed_addr = m_trace_map_fixed_addr + trace_map_size + gap + context_offset; + cout << hex; + cout << "tracemap fixed at: 0x" << m_trace_map_fixed_addr << endl; + cout << "prev_id fixed at : 0x" << m_previd_fixed_addr << endl; + cout << "context fixed at : 0x" << m_context_fixed_addr << endl; + cout << dec; + } + else + { + m_trace_map_fixed_addr = 0; + m_previd_fixed_addr = 0; + m_context_fixed_addr = 0; + } +} + +bool ZaxBase_t::useFixedAddresses() const +{ + return m_do_fixed_addr_optimization; +} + +unsigned long ZaxBase_t::getFixedAddressMap() const +{ + return m_trace_map_fixed_addr; +} + +unsigned long ZaxBase_t::getFixedAddressPrevId() const +{ + return m_previd_fixed_addr; +} + +unsigned long ZaxBase_t::getFixedAddressContext() const +{ + return m_context_fixed_addr; +} + +void ZaxBase_t::setVerbose(bool p_verbose) +{ + m_verbose = p_verbose; +} + +void ZaxBase_t::setBasicBlockOptimization(bool p_bb_graph_optimize) +{ + m_graph_optimize = p_bb_graph_optimize; + const auto enabled = m_graph_optimize ? "enable" : "disable"; + cout << enabled << " basic block optimization" << endl ; +} + +void ZaxBase_t::setDomgraphOptimization(bool p_domgraph_optimize) +{ + m_domgraph_optimize = p_domgraph_optimize; + const auto enabled = m_domgraph_optimize ? "enable" : "disable"; + cout << enabled << " dominator graph optimization" << endl ; +} + + +void ZaxBase_t::setEnableForkServer(bool p_forkserver_enabled) +{ + m_forkserver_enabled = p_forkserver_enabled; +} + +void ZaxBase_t::setBreakupCriticalEdges(bool p_breakupEdges) +{ + m_breakupCriticalEdges = p_breakupEdges; + m_breakupCriticalEdges ? + cout << "enable breaking of critical edges" << endl : + cout << "disable breaking of critical edges" << endl; +} + +void ZaxBase_t::setBasicBlockFloatingInstrumentation(bool p_float) +{ + m_bb_float_instrumentation = p_float; + m_bb_float_instrumentation ? + cout << "enable floating instrumentation" << endl : + cout << "disable floating instrumentation" << endl; +} + +bool ZaxBase_t::getBasicBlockFloatingInstrumentation() const +{ + return m_bb_float_instrumentation; +} + +void ZaxBase_t::setContextSensitivity(ContextSensitivity_t p_context_style) +{ + m_context_sensitivity = p_context_style; + switch (m_context_sensitivity) + { + case ContextSensitivity_None: + cout << "disable context sensitivity" << endl; + break; + case ContextSensitivity_Function: + cout << "enable context sensitivity (style: function)" << endl; + break; + case ContextSensitivity_Callsite: + cout << "enable context sensitivity (style: callsite)" << endl; + break; + } +} + +ContextSensitivity_t ZaxBase_t::getContextSensitivity() const +{ + return m_context_sensitivity; +} + +/* + * Only allow instrumentation in whitelisted functions/instructions + * Each line in file is either a function name or address + */ +void ZaxBase_t::setWhitelist(const string& p_whitelist) +{ + std::ifstream whitelistFile(p_whitelist); + if (!whitelistFile.is_open()) + throw std::runtime_error("Could not open file " + p_whitelist); + std::string line; + while(whitelistFile >> line) + { + cout <<"Adding " << line << " to white list" << endl; + m_whitelist.insert(line); + } + whitelistFile.close(); +} + +/* + * Disallow instrumentation in blacklisted functions/instructions + * Each line in file is either a function name or address + */ +void ZaxBase_t::setBlacklist(const string& p_blackList) +{ + std::ifstream blackListFile(p_blackList); + if (!blackListFile.is_open()) + throw; + std::string line; + while(blackListFile >> line) + { + cout <<"Adding " << line << " to black list" << endl; + m_blacklist.insert(line); + } + blackListFile.close(); +} + +ZaflLabelId_t ZaxBase_t::getLabelId(const unsigned p_max) +{ + return m_labelid++; +} + +ZaflBlockId_t ZaxBase_t::getBlockId(const unsigned p_max) +{ + m_blockid = (m_blockid+1) % p_max; + return m_blockid; +} + +ZaflContextId_t ZaxBase_t::getContextId(const unsigned p_max) +{ + auto counter = 0; + auto contextid = 0; + + // only try getting new context id 100 times + // avoid returning duplicate if we can help it + while (counter++ < 100) { + contextid = rand() % p_max; + if (m_used_contextid.find(contextid) == m_used_contextid.end()) + { + m_used_contextid.insert(contextid); + return contextid; + } + } + return contextid % p_max; +} + +void ZaxBase_t::insertExitPoint(Instruction_t *p_inst) +{ + assert(p_inst->getAddress()->getVirtualOffset()); + + if (p_inst->getFunction()) + cout << "in function: " << p_inst->getFunction()->getName() << " "; + + stringstream ss; + ss << hex << p_inst->getAddress()->getVirtualOffset(); + m_blacklist.insert(ss.str()); + + cout << "insert exit point at: 0x" << ss.str() << endl; + + auto tmp = p_inst; + insertAssemblyBefore(tmp, "xor edi, edi"); // rdi=0 + tmp = insertAssemblyAfter(tmp, "mov eax, 231"); // 231 = __NR_exit_group from <asm/unistd_64.h> + tmp = insertAssemblyAfter(tmp, "syscall"); // sys_exit_group(edi) +} + +void ZaxBase_t::insertForkServer(Instruction_t* p_entry) +{ + assert(p_entry); + + stringstream ss; + ss << "0x" << hex << p_entry->getAddress()->getVirtualOffset(); + cout << "inserting fork server code at address: " << ss.str() << dec << endl; + assert(p_entry->getAddress()->getVirtualOffset()); + + if (p_entry->getFunction()) { + cout << " function: " << p_entry->getFunction()->getName(); + cout << " ep instr: " << p_entry->getDisassembly() << endl; + } + cout << endl; + + // blacklist insertion point + cout << "Blacklisting entry point: " << ss.str() << endl; + m_blacklist.insert(ss.str()); + + m_entry_point = p_entry; + + // insert the instrumentation + auto tmp=p_entry; + const auto regs = vector<string>({ "rdi", "rsi", "rbp", "rdx", "rcx", "rbx", "rax", "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"}); + + // red zone + (void)insertAssemblyBefore(tmp, "lea rsp, [rsp-128]"); + // save flags and registrers + tmp = insertAssemblyAfter(tmp, "pushf ") ; + for (vector<string>::const_iterator rit = regs.begin(); rit != regs.end(); ++rit) + tmp = insertAssemblyAfter(tmp, " push " + *rit); + // call fork server initialization routine (in external library) + tmp = insertAssemblyAfter(tmp, "call 0 ", m_plt_zafl_initAflForkServer) ; + // restore registers and flags + for (vector<string>::const_reverse_iterator rit = regs.rbegin(); rit != regs.rend(); ++rit) + tmp = insertAssemblyAfter(tmp, " pop " + *rit) ; + tmp = insertAssemblyAfter(tmp, "popf ") ; + // red zome + tmp = insertAssemblyAfter(tmp, "lea rsp, [rsp+128]"); +} + +void ZaxBase_t::insertForkServer(string p_forkServerEntry) +{ + assert(p_forkServerEntry.size() > 0); + + cout << "looking for fork server entry point: " << p_forkServerEntry << endl; + + if (std::isdigit(p_forkServerEntry[0])) + { + // find instruction to insert fork server based on address + const auto voffset = (VirtualOffset_t) std::strtoul(p_forkServerEntry.c_str(), NULL, 16); + auto instructions=find_if(getFileIR()->getInstructions().begin(), getFileIR()->getInstructions().end(), [&](const Instruction_t* i) { + return i->getAddress()->getVirtualOffset()==voffset; + }); + + if (instructions==getFileIR()->getInstructions().end()) + { + cerr << "Error: could not find address to insert fork server: " << p_forkServerEntry << endl; + throw; + } + + insertForkServer(*instructions); + } + else + { + // find entry point of specified function to insert fork server + auto entryfunc=find_if(getFileIR()->getFunctions().begin(), getFileIR()->getFunctions().end(), [&](const Function_t* f) { + return f->getName()==p_forkServerEntry; + }); + + + if(entryfunc==getFileIR()->getFunctions().end()) + { + cerr << "Error: could not find function to insert fork server: " << p_forkServerEntry << endl; + throw; + } + + cout << "inserting fork server code at entry point of function: " << p_forkServerEntry << endl; + auto entrypoint = (*entryfunc)->getEntryPoint(); + + if (!entrypoint) + { + cerr << "Could not find entry point for: " << p_forkServerEntry << endl; + throw; + } + insertForkServer(entrypoint); + } +} + +void ZaxBase_t::setupForkServer() +{ + if (m_fork_server_entry.size()>0) + { + // user has specified entry point + insertForkServer(m_fork_server_entry); + } + else + { + // try to insert fork server at main + const auto &all_funcs=getFileIR()->getFunctions(); + const auto main_func_it=find_if(all_funcs.begin(), all_funcs.end(), [&](const Function_t* f) { return f->getName()=="main";}); + if(main_func_it!=all_funcs.end()) + { + insertForkServer("main"); + } + + } + + // it's ok not to have a fork server at all, e.g. libraries + + getFileIR()->assembleRegistry(); + getFileIR()->setBaseIDS(); + +} + +void ZaxBase_t::insertExitPoints() +{ + for (auto exitp : m_exitpoints) + { + if (std::isdigit(exitp[0])) + { + // find instruction to insert fork server based on address + const auto voffset = (VirtualOffset_t) std::strtoul(exitp.c_str(), NULL, 16); + auto instructions=find_if(getFileIR()->getInstructions().begin(), getFileIR()->getInstructions().end(), [&](const Instruction_t* i) { + return i->getAddress()->getVirtualOffset()==voffset; + }); + + if (instructions==getFileIR()->getInstructions().end()) + { + cerr << "Error: could not find address to insert exit point: " << exitp << endl; + throw; + } + + insertExitPoint(*instructions); + } + else + { + // find function by name + auto func_iter=find_if(getFileIR()->getFunctions().begin(), getFileIR()->getFunctions().end(), [&](const Function_t* f) { + return f->getName()==exitp; + }); + + + if(func_iter==getFileIR()->getFunctions().end()) + { + cerr << "Error: could not find function to insert exit points: " << exitp << endl; + throw; + } + + cout << "inserting exit code at return points of function: " << exitp << endl; + for (auto i : (*func_iter)->getInstructions()) + { + if (i->getBaseID() >= 0) + { + const auto d=DecodedInstruction_t::factory(i); + + // if it's a return instruction, add exit point + if (d->isReturn()) + { + insertExitPoint(i); + } + } + } + } + } + + getFileIR()->assembleRegistry(); + getFileIR()->setBaseIDS(); +} + +// blacklist functions: +// - in blacklist +// - that start with '.' +// - that end with @plt +bool ZaxBase_t::isBlacklisted(const Function_t *p_func) const +{ + return (p_func->getName()[0] == '.' || + p_func->getName().find("@plt") != string::npos || + m_blacklist.find(p_func->getName())!=m_blacklist.end()); +} + +bool ZaxBase_t::isWhitelisted(const Function_t *p_func) const +{ + if (m_whitelist.size() == 0) return true; + return (m_whitelist.find(p_func->getName())!=m_whitelist.end()); +} + +bool ZaxBase_t::isBlacklisted(const Instruction_t *p_inst) const +{ + stringstream ss; + ss << "0x" << hex << p_inst->getAddress()->getVirtualOffset(); + return (m_blacklist.count(ss.str()) > 0 || isBlacklisted(p_inst->getFunction()) || p_inst == m_entry_point); +} + +bool ZaxBase_t::isWhitelisted(const Instruction_t *p_inst) const +{ + if (m_whitelist.size() == 0) return true; + + stringstream ss; + ss << "0x" << hex << p_inst->getAddress()->getVirtualOffset(); + return (m_whitelist.count(ss.str()) > 0 || isWhitelisted(p_inst->getFunction())); +} + +void ZaxBase_t::setup() +{ + if (m_forkserver_enabled) + setupForkServer(); + else + cout << "Fork server has been disabled" << endl; + + insertExitPoints(); +} + +void ZaxBase_t::teardown() +{ + dumpAttributes(); + dumpMap(); +} + +// in: control flow graph for a given function +// out: set of basic blocks to instrument +BasicBlockSet_t ZaxBase_t::getBlocksToInstrument(const ControlFlowGraph_t &cfg) +{ + static int bb_debug_id=-1; + + if (m_verbose) + cout << cfg << endl; + + auto keepers = BasicBlockSet_t(); + + for (auto &bb : cfg.getBlocks()) + { + assert(bb->getInstructions().size() > 0); + + bb_debug_id++; + + // already marked as a keeper + if (keepers.find(bb) != keepers.end()) + continue; + + // if whitelist specified, only allow instrumentation for functions/addresses in whitelist + if (!isWhitelisted(bb->getInstructions()[0])) + continue; + + if (isBlacklisted(bb->getInstructions()[0])) + continue; + + // debugging support + if (getenv("ZAFL_LIMIT_BEGIN")) + { + if (bb_debug_id < atoi(getenv("ZAFL_LIMIT_BEGIN"))) + continue; + } + + // debugging support + if (getenv("ZAFL_LIMIT_END")) + { + if (bb_debug_id >= atoi(getenv("ZAFL_LIMIT_END"))) + continue; + } + + // make sure we're not trying to instrument code we just inserted, e.g., fork server, added exit points + if (bb->getInstructions()[0]->getBaseID() < 0) + { + if (m_verbose) + cout << "Base ID < 0" << endl; + continue; + } + + // push/jmp pair, don't bother instrumenting + if (BB_isPushJmp(bb)) + { + m_num_bb_skipped_pushjmp++; + continue; + } + + keepers.insert(bb); + } + return keepers; +} + +void ZaxBase_t::filterPaddingNOP(BasicBlockSet_t& p_in_out) +{ + auto copy=p_in_out; + for(auto block : copy) + { + if (BB_isPaddingNop(block)) + { + p_in_out.erase(block); + m_num_bb_skipped_nop_padding++; + } + } +} + +void ZaxBase_t::filterEntryBlock(BasicBlockSet_t& p_in_out, BasicBlock_t* p_entry) +{ + + if (!m_graph_optimize) + return; + + if (p_entry->getSuccessors().size() != 1) + return; + + if (p_in_out.find(p_entry) == p_in_out.end()) + return; + + if (p_in_out.find(*(p_entry->getSuccessors().begin())) == p_in_out.end()) + return; + + // both entry and successor are in <p_in_out> + // entry block has single successor + p_in_out.erase(p_entry); + m_num_entry_blocks_elided++; + if (m_verbose) { + cout << "Eliding entry block" << endl; + } +} + +void ZaxBase_t::filterExitBlocks(BasicBlockSet_t& p_in_out) +{ + if (!m_graph_optimize) + return; + + auto copy=p_in_out; + for(auto block : copy) + { + if (!block->getIsExitBlock()) + continue; + + if (block->getInstructions()[0]->getIndirectBranchTargetAddress()) + continue; + + if (block->getPredecessors().size() != 1) + continue; + + if (copy.find(*block->getPredecessors().begin()) == copy.end()) + continue; + + const auto last_instruction_index = block->getInstructions().size() - 1; + if (block->getInstructions()[last_instruction_index]->getDisassembly().find("ret")==string::npos) + continue; + + // must be an exit block (ret) + // exit block is not an ibta + // only 1 predecessor + // predecessor in <p_in_out> + p_in_out.erase(block); + m_num_exit_blocks_elided++; + if (m_verbose) { + cout << "Eliding exit block" << endl; + } + } +} + +void ZaxBase_t::filterConditionalBranches(BasicBlockSet_t& p_in_out) +{ + if (!m_graph_optimize) + return; + auto copy=p_in_out; + for(auto block : copy) + { + const auto successors_have_unique_preds = + find_if(ALLOF(block->getSuccessors()), [](const BasicBlock_t* s) + { + return s->getPredecessors().size() > 1; + }) == block->getSuccessors().end(); + + const auto successor_with_ibta = + find_if(ALLOF(block->getSuccessors()), [](const BasicBlock_t* s) + { + return s->getInstructions()[0]->getIndirectBranchTargetAddress(); + }) != block->getSuccessors().end(); + + const auto all_successors_kept = + find_if(ALLOF(block->getSuccessors()), [p_in_out](BasicBlock_t* s) + { + return p_in_out.find(s) == p_in_out.end(); + }) == block->getSuccessors().end(); + + if (block->endsInConditionalBranch() && + all_successors_kept && + successors_have_unique_preds && + !successor_with_ibta) + { + // block ends in conditional branch + // successors are in <p_in_out> + // successors have unique predecessors + // no successor is an ibta + if (m_verbose) + cout << "Eliding conditional branch -- keeping successors" << endl; + p_in_out.erase(block); + m_num_bb_skipped_cbranch++; + continue; + } + } +} + +void ZaxBase_t::filterBlocksByDomgraph(BasicBlockSet_t& p_in_out, const DominatorGraph_t* dg) +{ + if(!m_domgraph_optimize) + return; + + if(m_verbose) + { + cout<<"And the Dominator graph is:" <<endl; + cout<<*dg<<endl; + } + + auto copy=p_in_out; + for(auto block : copy) + { + const auto &dominates = dg->getDominated(block); + + const auto is_dg_leaf = dominates.size()==1; // leaf in the dom tree -- we dominate ourselves. + // this is leaf of cfg: successors.size() == 0; + + const auto is_dominated= + [&](const BasicBlock_t* successor) -> bool + { + const auto &dominators = dg->getDominators(successor); + return dominators.find(block) != end(dominators); + }; + const auto is_non_dominated= [&](const BasicBlock_t* successor) -> bool + { + return !is_dominated(successor); + }; + + auto &successors = block->getSuccessors(); + auto non_dominator_successor_it = find_if(ALLOF(successors), is_non_dominated); + const auto has_non_dominator_successor = non_dominator_successor_it != end(successors); + const auto keep = (is_dg_leaf || has_non_dominator_successor); + if(!keep) + { + p_in_out.erase(block); + m_num_domgraph_blocks_elided++; + if(m_verbose) + { + cout<<"Eliding instrumentation in block id = " << dec << block->getInstructions()[0]->getBaseID() << endl; + cout<<"is_dg_leaf = " << boolalpha << is_dg_leaf << endl; + cout<<"has_non_dom_successor = " << boolalpha << has_non_dominator_successor << endl; + } + } + else + { + if(m_verbose) + { + cout<<"Instrumenting block id = " << dec << block->getInstructions()[0]->getBaseID() << endl; + cout<<"is_dg_leaf = " << boolalpha << is_dg_leaf << endl; + cout<<"has_non_dom_successor = " << boolalpha << has_non_dominator_successor << endl; + } + } + } +} + +// by default, return the first instruction in block +Instruction_t* ZaxBase_t::getInstructionToInstrument(const BasicBlock_t *p_bb, const unsigned p_num_free_regs_desired) +{ + if (!p_bb) + return nullptr; + + const auto first_instruction = p_bb->getInstructions()[0]; + + // no STARS (i.e., no dead reg annotations) + // or floating instrumentation turned off + if (!getBasicBlockFloatingInstrumentation() || !m_use_stars) + { + for (auto i : p_bb->getInstructions()) + { + if (i->getBaseID()) + return i; + } + + // fallback: return the first instruction + return first_instruction; + } + + // scan basic block looking for instruction with requested number of free regs + const auto allowed_regs = RegisterSet_t({rn_RAX, rn_RBX, rn_RCX, rn_RDX, rn_R8, rn_R9, rn_R10, rn_R11, rn_R12, rn_R13, rn_R14, rn_R15}); + // auto &ap = m_stars_analysis_engine.getAnnotations(); + auto best_i = first_instruction; + auto max_free_regs = 0U; + auto num_free_regs_best_i = 0U; + auto num_free_regs_first_instruction = 0U; + + for (auto i : p_bb->getInstructions()) + { + if (isBlacklisted(i)) + continue; + + const auto dead_regs = getDeadRegs(i); + const auto num_free_regs = getFreeRegs(dead_regs, allowed_regs).size(); + + if (i == first_instruction) + num_free_regs_first_instruction = num_free_regs; + + if (num_free_regs >= p_num_free_regs_desired) + { + // found instruction with requested number of free registers + m_num_bb_float_instrumentation++; + if (i != first_instruction) + { + const auto num_saved = std::max(static_cast<long unsigned>(p_num_free_regs_desired), num_free_regs) - num_free_regs_first_instruction; + m_num_bb_float_regs_saved += num_saved; + } + return i; + } + + // keep track of the best thus far + if (num_free_regs > max_free_regs) + { + max_free_regs = num_free_regs; + best_i = i; + num_free_regs_best_i = num_free_regs; + } + } + + if (best_i != first_instruction) + { + const auto num_saved = std::max(p_num_free_regs_desired, num_free_regs_best_i) - num_free_regs_first_instruction; + m_num_bb_float_regs_saved += num_saved; + m_num_bb_float_instrumentation++; + } + + return best_i; +} + +void ZaxBase_t::dumpAttributes() +{ + cout << "#ATTRIBUTE num_bb=" << dec << m_num_bb << endl; + cout << "#ATTRIBUTE num_bb_instrumented=" << m_num_bb_instrumented << endl; + cout << "#ATTRIBUTE num_bb_skipped=" << m_num_bb_skipped << endl; + cout << "#ATTRIBUTE num_bb_skipped_pushjmp=" << m_num_bb_skipped_pushjmp << endl; + cout << "#ATTRIBUTE num_bb_skipped_nop_padding=" << m_num_bb_skipped_nop_padding << endl; + cout << "#ATTRIBUTE num_bb_float_instrumentation=" << m_num_bb_float_instrumentation << endl; + cout << "#ATTRIBUTE num_bb_float_register_saved=" << m_num_bb_float_regs_saved << endl; + cout << "#ATTRIBUTE graph_optimize=" << boolalpha << m_graph_optimize << endl; + cout << "#ATTRIBUTE num_bb_skipped_cond_branch=" << m_num_bb_skipped_cbranch << endl; + cout << "#ATTRIBUTE num_style_collafl=" << m_num_style_collafl << endl; + cout << "#ATTRIBUTE num_domgraph_blocks_elided=" << m_num_domgraph_blocks_elided << endl; + cout << "#ATTRIBUTE num_entry_blocks_elided=" << m_num_entry_blocks_elided << endl; + cout << "#ATTRIBUTE num_exit_blocks_elided=" << m_num_exit_blocks_elided << endl; + cout << "#ATTRIBUTE num_single_block_function_elided=" << m_num_single_block_function_elided << endl; + cout << "#ATTRIBUTE num_contexts=" << m_num_contexts << endl; +} + +// file dump of modified basic block info +void ZaxBase_t::dumpMap() +{ + getFileIR()->setBaseIDS(); // make sure instructions have IDs + getFileIR()->assembleRegistry(); // make sure to assemble all instructions + + std::ofstream mapfile("zax.map"); + + mapfile << "# BLOCK_ID ID_EP:size ID_OLDEP:size (ID_INSTRUMENTATION:size)*" << endl; + for (auto &mb : m_modifiedBlocks) + { + const auto blockid = mb.first; + mapfile << dec << blockid << " "; + for (auto &entry : mb.second) + { + mapfile << hex << entry->getBaseID() << ":" << dec << entry->getDataBits().size() << " "; + } + mapfile << endl; + } +} + +void ZaxBase_t::addContextSensitivity_Callsite(const ControlFlowGraph_t& cfg) +{ + assert(0); +} + +// update calling context hash at entry point +// revert calling context hash on exit +void ZaxBase_t::addContextSensitivity_Function(const ControlFlowGraph_t& cfg) +{ + bool inserted_before = false; + + // don't bother with single block functions + if (cfg.getBlocks().size() == 1) + return; + + m_num_contexts++; + + // + // entry_point + // context = prev_context % RANDOM_CONTEXT_ID + // + // exit point (returns) + // context = prev_context % RANDOM_CONTEXT_ID + // + const auto do_insert=[&](Instruction_t* instr, const string& insn_str) -> Instruction_t* + { + if (inserted_before) + { + instr = insertAssemblyAfter(instr, insn_str); + return instr; + } + else + { + insertAssemblyBefore(instr, insn_str); + inserted_before = true; + return instr; + } + }; + + auto compute_hash_chain = [&](ZaflContextId_t contextid, Instruction_t * instr, string reg_context, string reg_temp) -> Instruction_t* + { + auto labelid = getLabelId(); + auto tmp = instr; + + // fast way with fixed addresses: + // xor [regc], <context_id> + // + // inefficient way: + // E: mov regc, QWORD[rel E] + // mov rtmp, [regc] + // xor rtmp, <context_id> + // mov [regc], rtmp + // + // + if (useFixedAddresses()) + { + const auto xor_context = string("xor WORD [0x") + to_hex_string(getFixedAddressContext()) + "]" + "," + to_string(contextid); + tmp = do_insert(tmp, xor_context); + } + else + { + const auto hash_context_reloc = string("E") + to_string(labelid) + ": mov " + reg_context + ", QWORD [rel E" + to_string(labelid) + "]"; + tmp = do_insert(tmp, hash_context_reloc); + create_got_reloc(getFileIR(), m_context_id, tmp); + + const auto deref_context = string("mov ") + reg_temp + ", [" + reg_context + "]"; + tmp = do_insert(tmp, deref_context); + + const auto hash_chain = string("xor ") + reg_temp + ", " + to_string(contextid); + tmp = do_insert(tmp, hash_chain); + + const auto store_context = string("mov [") + reg_context + "]" + "," + reg_temp; + tmp = do_insert(tmp, store_context); + } + + return tmp; + }; + + auto add_hash_context_instrumentation = [&](ZaflContextId_t contextid, Instruction_t* i, const bool honor_red_zone) + { + inserted_before = false; + + // look for instruction in entry block with at least 1 free reg + auto reg_context = string("r14"); + auto reg_temp = string("r15"); + bool save_context = true; + bool save_temp = true; + + if (useFixedAddresses()) + { + save_context = false; + save_temp = false; + } + + const auto allowed_regs = RegisterSet_t({rn_RAX, rn_RBX, rn_RCX, rn_RDX, rn_R8, rn_R9, rn_R10, rn_R11, rn_R12, rn_R13, rn_R14, rn_R15}); + const auto dead_regs = getDeadRegs(i); + const auto live_flags = dead_regs.find(IRDB_SDK::rn_EFLAGS)==dead_regs.end(); + auto free_regs = getFreeRegs(dead_regs, allowed_regs); + + if (honor_red_zone) + i = do_insert(i, "lea rsp, [rsp-128]"); + + if (save_context && free_regs.size() > 0) + { + reg_context = registerToString(FIRSTOF(free_regs)); + free_regs.erase(FIRSTOF(free_regs)); + save_context = false; + } + + if (save_temp && free_regs.size() > 0) + { + reg_temp = registerToString(FIRSTOF(free_regs)); + free_regs.erase(FIRSTOF(free_regs)); + save_temp = false; + } + + if (save_context) + i = do_insert(i, "push " + reg_context); + + if (save_temp) + i = do_insert(i, "push " + reg_temp); + + if (live_flags) + i = do_insert(i, "pushf"); + + // compute new hash chain value + i = compute_hash_chain(contextid, i, reg_context, reg_temp); + + if (live_flags) + i = do_insert(i, "popf"); + + if (save_temp) + i = do_insert(i, "pop " + reg_temp); + + if (save_context) + i = do_insert(i, "pop " + reg_context); + + if (honor_red_zone) + i = do_insert(i, "lea rsp, [rsp+128]"); + }; + + bool honor_red_zone = true; + if (m_use_stars) + honor_red_zone = hasLeafAnnotation(cfg.getFunction()); + + auto contextid = getContextId(); + auto entry_block = cfg.getEntry(); + inserted_before = false; + add_hash_context_instrumentation(contextid, entry_block->getInstructions()[0], honor_red_zone); + + // find all exit blocks with returns + auto find_exits = BasicBlockSet_t(); + copy_if(ALLOF(cfg.getBlocks()), inserter(find_exits, find_exits.begin()), [entry_block](BasicBlock_t* bb) { + if (bb == entry_block) return false; + if (!bb->getIsExitBlock()) return false; + const auto last_instruction_index = bb->getInstructions().size() - 1; + return (bb->getInstructions()[last_instruction_index]->getDisassembly().find("ret")!=string::npos); + }); + + for (const auto &bb: find_exits) + { + const auto last_instruction_index = bb->getInstructions().size()-1; + inserted_before = false; + add_hash_context_instrumentation(contextid, bb->getInstructions()[last_instruction_index], honor_red_zone); + } +} + +void ZaxBase_t::addContextSensitivity(const ControlFlowGraph_t& cfg) +{ + if (m_entry_point && m_entry_point->getFunction()) + { + cout << "cfg.func: " << cfg.getFunction()->getName() << " ep.func: " << m_entry_point->getFunction()->getName() << endl; + if (m_entry_point->getFunction()->getName() == cfg.getFunction()->getName()) + { + cout << "Do not setup calling context in same function as entry point for fork server" << endl; + return; + } + } + + if (getContextSensitivity() == ContextSensitivity_Callsite) + addContextSensitivity_Callsite(cfg); + else if (getContextSensitivity() == ContextSensitivity_Function) + addContextSensitivity_Function(cfg); + else if (getContextSensitivity() == ContextSensitivity_None) + return; + else + throw; +} + +/* + * Execute the transform. + * + * preconditions: the FileIR is read as from the IRDB. valid file listing functions to auto-initialize + * postcondition: instructions added to auto-initialize stack for each specified function + * + */ +int ZaxBase_t::execute() +{ + if (m_breakupCriticalEdges) + { + CriticalEdgeBreaker_t ceb(getFileIR(), m_blacklist, m_verbose); + cout << "#ATTRIBUTE num_bb_extra_blocks=" << ceb.getNumberExtraNodes() << endl; + + getFileIR()->setBaseIDS(); + getFileIR()->assembleRegistry(); + } + + setup(); + + // for all functions + // build cfg and extract basic blocks + // for all basic blocks, figure out whether should be kept + // for all kept basic blocks + // add afl-compatible instrumentation + + struct BaseIDSorter + { + bool operator()( const Function_t* lhs, const Function_t* rhs ) const + { + assert(lhs->getBaseID() != BaseObj_t::NOT_IN_DATABASE); + assert(rhs->getBaseID() != BaseObj_t::NOT_IN_DATABASE); + return lhs->getBaseID() < rhs->getBaseID(); + } + }; + auto sortedFuncs=set<Function_t*, BaseIDSorter>( ALLOF(getFileIR()->getFunctions())); + for(auto f : sortedFuncs) + { + if (f == nullptr ) continue; + // skip instrumentation for blacklisted functions + if (isBlacklisted(f)) continue; + // skip if function has no entry point + if (!f->getEntryPoint()) continue; + + bool honorRedZone = true; + if (m_use_stars) + honorRedZone = hasLeafAnnotation(f); + + const auto cfgp = ControlFlowGraph_t::factory(f); + const auto &cfg = *cfgp; + const auto num_blocks_in_func = cfg.getBlocks().size(); + m_num_bb += num_blocks_in_func; + + if (m_graph_optimize && num_blocks_in_func == 1) + { + m_num_single_block_function_elided++; + m_num_bb_skipped++; + continue; + } + + const auto dom_graphp=DominatorGraph_t::factory(cfgp.get()); + const auto has_domgraph_warnings = dom_graphp -> hasWarnings(); + + const auto entry_block = cfg.getEntry(); + auto keepers = getBlocksToInstrument(cfg); + + if (m_verbose) + cout << "num blocks to keep (baseline): " << keepers.size() << endl; + + if(has_domgraph_warnings) + { + if(m_verbose) + { + cout << " Domgraph has warnings, eliding domgraph filter" << endl; + cout << " And the domgraph is: " << endl; + cout << *dom_graphp << endl; + } + } + filterBlocksByDomgraph(keepers,dom_graphp.get()); + + if (m_verbose) + cout << "num blocks to keep (after filter dom): " << keepers.size() << " / " << cfgp->getBlocks().size() << endl; + + if (m_graph_optimize) + { + filterConditionalBranches(keepers); + if (m_verbose) + cout << "num blocks to keep (after filter conditional branches): " << keepers.size() << endl; + filterEntryBlock(keepers, entry_block); + if (m_verbose) + cout << "num blocks to keep (after filter entry): " << keepers.size() << endl; + + filterExitBlocks(keepers); + if (m_verbose) + cout << "num blocks to keep (after filter exits): " << keepers.size() << endl; + } + + filterPaddingNOP(keepers); + + struct BBSorter + { + bool operator()( const BasicBlock_t* lhs, const BasicBlock_t* rhs ) const + { + const auto lhs_insns=lhs->getInstructions(); + const auto rhs_insns=rhs->getInstructions(); + assert(lhs_insns[0]->getBaseID() != BaseObj_t::NOT_IN_DATABASE); + assert(rhs_insns[0]->getBaseID() != BaseObj_t::NOT_IN_DATABASE); + return lhs_insns[0]->getBaseID() < rhs_insns[0]->getBaseID(); + } + }; + auto sortedBasicBlocks = set<BasicBlock_t*, BBSorter> (ALLOF(keepers)); + for (auto &bb : sortedBasicBlocks) + { + auto collAflSingleton = false; + // for collAfl-style instrumentation, we want #predecessors==1 + // if the basic block entry point is an IBTA, we don't know the #predecessors + if (m_graph_optimize && + bb->getPredecessors().size() == 1 && + !bb->getInstructions()[0]->getIndirectBranchTargetAddress() + ) + { + if (m_verbose) + cout << "Doing collAfl style" << endl; + collAflSingleton = true; + m_num_style_collafl++; + } + + instrumentBasicBlock(bb, honorRedZone, collAflSingleton); + } + + m_num_bb_instrumented += keepers.size(); + m_num_bb_skipped += (num_blocks_in_func - keepers.size()); + + if (getContextSensitivity() != ContextSensitivity_None) + { + // this handles inserting the calling context sensitivity value + // at entry and exits of functions (todo: or at call sites a la Angora) + getFileIR()->assembleRegistry(); + getFileIR()->setBaseIDS(); + auto cs_cfg=ControlFlowGraph_t::factory(f); + addContextSensitivity(*cs_cfg); + } + + if (m_verbose) + { + getFileIR()->assembleRegistry(); + getFileIR()->setBaseIDS(); + cout << "Post transformation CFG for " << f->getName() << ":" << endl; + auto post_cfg=ControlFlowGraph_t::factory(f); + cout << *post_cfg << endl; + } + + cout << "Function " << f->getName() << ": " << dec << keepers.size() << "/" << num_blocks_in_func << " basic blocks instrumented." << endl; + }; + + + teardown(); + + return 1; +} + diff --git a/zfuzz/afl_transforms/tools/zax/zax_base.hpp b/zfuzz/afl_transforms/tools/zax/zax_base.hpp new file mode 100644 index 0000000000000000000000000000000000000000..772fc82a025d543cbcf8ffb84191602215a1224a --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/zax_base.hpp @@ -0,0 +1,152 @@ +#ifndef _LIBTRANSFORM_ZAXBASE_H +#define _LIBTRANSFORM_ZAXBASE_H + +#include <irdb-core> +#include <irdb-cfg> +#include <irdb-transform> +#include <irdb-util> +#include <irdb-deep> + +// utility functions +// @todo: move these functions into other libs for reuse +extern void create_got_reloc(IRDB_SDK::FileIR_t* fir, std::pair<IRDB_SDK::DataScoop_t*,int> wrt, IRDB_SDK::Instruction_t* i); + +namespace Zafl +{ + using namespace IRDB_SDK; + using namespace std; + + using ZaflBlockId_t = uint32_t; + using ZaflLabelId_t = uint32_t; + using ZaflContextId_t = uint32_t; + using BBRecord_t = vector<Instruction_t*>; + using RegisterSet_t = IRDB_SDK::RegisterIDSet_t; + enum ContextSensitivity_t {ContextSensitivity_None, ContextSensitivity_Callsite, ContextSensitivity_Function}; + + /* + * Base class for afl-compatible instrumentation: + * - fork server + * - trace map + */ + class ZaxBase_t : public Transform + { + public: + ZaxBase_t() = delete; + ZaxBase_t(const Zafl::ZaxBase_t&) = delete; + virtual ~ZaxBase_t() {}; + virtual int execute(); + void setWhitelist(const string& p_filename); + void setBlacklist(const string& p_filename); + void setVerbose(bool); + void setBasicBlockOptimization(bool); + void setDomgraphOptimization(bool); + void setBasicBlockFloatingInstrumentation(bool); + void setEnableForkServer(bool); + void setBreakupCriticalEdges(bool); + void setContextSensitivity(ContextSensitivity_t); + void filterPaddingNOP(BasicBlockSet_t& p_in_out); + void filterBlocksByDomgraph(BasicBlockSet_t& in_out, const DominatorGraph_t * dg ); + void filterConditionalBranches(BasicBlockSet_t& p_in_out); + void filterEntryBlock(BasicBlockSet_t& in_out, BasicBlock_t* p_entry); + void filterExitBlocks(BasicBlockSet_t& in_out); + void addContextSensitivity(const ControlFlowGraph_t&); + + protected: + ZaxBase_t(pqxxDB_t &p_dbinterface, FileIR_t *p_variantIR, string p_entry, set<string> p_exits, bool p_use_stars=false, bool p_autozafl=false); + + virtual void instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_hasLeafAnnotation, const bool p_collafl_optimization=false) = 0; + + virtual ZaflLabelId_t getLabelId(const unsigned p_maxid=0xFFFFFF); + virtual ZaflBlockId_t getBlockId(const unsigned p_maxid=0xFFFF); + virtual ZaflContextId_t getContextId(const unsigned p_maxid=0xFFFF); + virtual BasicBlockSet_t getBlocksToInstrument (const ControlFlowGraph_t& cfg); + virtual Instruction_t* getInstructionToInstrument(const BasicBlock_t *, const unsigned p_num_free_regs_desired = 0); + virtual void setup(); + virtual void teardown(); + virtual void dumpAttributes(); + virtual void dumpMap(); + + void insertExitPoint(Instruction_t *inst); + void insertForkServer(Instruction_t* p_entry); + void insertForkServer(string p_forkServerEntry); + void setupForkServer(); + void insertExitPoints(); + bool isBlacklisted(const Function_t*) const; + bool isWhitelisted(const Function_t*) const; + bool isBlacklisted(const Instruction_t*) const; + bool isWhitelisted(const Instruction_t*) const; + bool BB_isPushJmp(const BasicBlock_t *) const; + bool BB_isPaddingNop(const BasicBlock_t *) const; + bool getBasicBlockFloatingInstrumentation() const; + ContextSensitivity_t getContextSensitivity() const; + bool hasLeafAnnotation(Function_t* fn) const; + RegisterSet_t getDeadRegs(Instruction_t* insn) const; + RegisterSet_t getFreeRegs(const RegisterSet_t& candidates, const RegisterSet_t& allowed) const; + void addContextSensitivity_Callsite(const ControlFlowGraph_t&); + void addContextSensitivity_Function(const ControlFlowGraph_t&); + + bool useFixedAddresses() const; + unsigned long getFixedAddressMap() const; + unsigned long getFixedAddressPrevId() const; + unsigned long getFixedAddressContext() const; + + protected: + pqxxDB_t& m_dbinterface; + unique_ptr<FunctionSet_t> leaf_functions; + unique_ptr<DeadRegisterMap_t> dead_registers; + + bool m_use_stars; // use STARS to have access to dead register info + bool m_autozafl; // link in library w/ auto fork server + bool m_graph_optimize; // skip basic blocks based on graph + bool m_domgraph_optimize; // skip basic blocks based on dominator graph + bool m_forkserver_enabled; // fork server enabled? + bool m_breakupCriticalEdges; + bool m_bb_float_instrumentation; // skip basic blocks based on graph + bool m_verbose; + pair<DataScoop_t*,int> m_trace_map; // afl shared memory trace map + pair<DataScoop_t*,int> m_prev_id; // id of previous block + pair<DataScoop_t*,int> m_context_id; // calling context variable + Instruction_t* m_plt_zafl_initAflForkServer; // plt entry for afl fork server initialization routine + + set<ZaflContextId_t> m_used_contextid; // internal bookkeeping to keep track of used block ids + map<ZaflBlockId_t, BBRecord_t> m_modifiedBlocks; // keep track of modified blocks + + ContextSensitivity_t m_context_sensitivity; // none, @callsite, @function + + + + // stats + size_t m_num_bb; + size_t m_num_bb_instrumented; + size_t m_num_bb_skipped; + size_t m_num_bb_skipped_pushjmp; + size_t m_num_bb_skipped_nop_padding; + size_t m_num_bb_skipped_cbranch; + size_t m_num_bb_float_instrumentation; + size_t m_num_bb_float_regs_saved; + size_t m_num_style_collafl; + size_t m_num_domgraph_blocks_elided; + size_t m_num_entry_blocks_elided; + size_t m_num_exit_blocks_elided; + size_t m_num_single_block_function_elided; + size_t m_num_contexts; + + private: + string m_fork_server_entry; // string to specify fork server entry point + set<string> m_exitpoints; // set of strings to specify exit points + set<string> m_whitelist; // whitelisted functions and/or instructions + set<string> m_blacklist; // blacklisted functions and/or instructions + ZaflLabelId_t m_labelid; // internal bookkeeping to generate labels + ZaflBlockId_t m_blockid; // internal bookkeeping to generate labels + Instruction_t* m_entry_point; // entry point where fork server was inserted + + // fixed address mode + bool m_do_fixed_addr_optimization; + unsigned long m_trace_map_fixed_addr; + unsigned long m_previd_fixed_addr; + unsigned long m_context_fixed_addr; + + }; +} + +#endif diff --git a/zfuzz/afl_transforms/tools/zax/zax_driver.cpp b/zfuzz/afl_transforms/tools/zax/zax_driver.cpp new file mode 100644 index 0000000000000000000000000000000000000000..7dc32dfdb270129d1d2f2c2d6b56ac4c61289e15 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/zax_driver.cpp @@ -0,0 +1,295 @@ +/*************************************************************************** + * Copyright (c) 2018-2019 Zephyr Software LLC. All rights reserved. + * + * This software is furnished under a license and/or other restrictive + * terms and may be used and copied only in accordance with such terms + * and the inclusion of the above copyright notice. This software or + * any other copies thereof may not be provided or otherwise made + * available to any other person without the express written consent + * of an authorized representative of Zephyr Software LCC. Title to, + * ownership of, and all rights in the software is retained by + * Zephyr Software LCC. + * + * Zephyr Software LLC. Proprietary Information + * + * Unless otherwise specified, the information contained in this + * directory, following this legend, and/or referenced herein is + * Zephyr Software LLC. (Zephyr) Proprietary Information. + * + * CONTACT INFO + * + * E-mail: jwd@zephyr-software.com + **************************************************************************/ + +#include <stdlib.h> +#include <getopt.h> +#include <unistd.h> + +#include "zax.hpp" +#include "zuntracer.hpp" + +using namespace std; +using namespace IRDB_SDK; +using namespace Zafl; + +static void usage(char* name) +{ + cerr<<"Usage: "<<name<<" <variant_id>\n"; + cerr<<"\t[--verbose | -v] Verbose mode "<<endl; + cerr<<"\t[--help,--usage,-?,-h] Display this message "<<endl; + cerr<<"\t[--entrypoint|-e {<funcName>|<hex_address>}] Specify where to insert fork server (defaults to main if found)"<<endl; + cerr<<"\t[--exitpoint|-E {<funcName>|<hex_address>}] Specify where to insert exit"<<endl; + cerr<<"\t[--whitelist|-w whitelistFile] Specify list of functions/addresses to instrument"<<endl; + cerr<<"\t[--blacklist|-b blacklistFile] Specify list of functions/addresses to omit"<<endl; + cerr<<"\t[--stars|-s] Enable STARS optimizations "<<endl; + cerr<<"\t[--enable-bb-graph-optimization|-g] Elide instrumentation if basic block has 1 successor"<<endl; + cerr<<"\t[--disable-bb-graph-optimization|-G] Elide instrumentation if basic block has 1 successor"<<endl; + cerr<<"\t[--autozafl|-a] Auto-initialize fork server (incompatible with --entrypoint)"<<endl; + cerr<<"\t[--untracer|-u] Untracer-style block-coverage instrumentation"<<endl; + cerr<<"\t[--enable-critical-edge-breakup|-c] Breakup critical edges"<<endl; + cerr<<"\t[--disable-critical-edge-breakup|-C] Do not breakup critical edges (default)"<<endl; + cerr<<"\t[--enable-floating-instrumentation|-i] Select best instrumentation within basic block"<<endl; + cerr<<"\t[--disable-floating-instrumentation|-I] Instrument first instruction in basic blocks"<<endl; + cerr<<"\t[--enable-context-sensitivity <style>] Use calling context sensitivity, style={callsite,function}"<<endl; + cerr<<"\t[--disable-context-sensitivity] Disable calling context sensitivity"<<endl; + cerr<<"\t[--random-seed|r <value>] Specify random seed"<<endl; +} + +int main(int argc, char **argv) +{ + if(argc < 2) + { + usage(argv[0]); + exit(1); + } + + string programName(argv[0]); + auto entry_fork_server = string(); + auto variantID = atoi(argv[1]); + auto verbose=false; + auto use_stars=false; + auto autozafl=false; + auto whitelistFile=string(); + auto blacklistFile=string(); + auto bb_graph_optimize=false; + auto domgraph_optimize=false; + auto forkserver_enabled=true; + auto untracer_mode=false; + auto breakup_critical_edges=false; + auto floating_instrumentation=false; + auto context_sensitivity=ContextSensitivity_None; + auto random_seed = 0U; + set<string> exitpoints; + + srand(getpid()+time(NULL)); + + // Parse some options for the transform + static struct option long_options[] = { + {"verbose", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"usage", no_argument, 0, '?'}, + {"stars", no_argument, 0, 's'}, + {"entrypoint", required_argument, 0, 'e'}, + {"exitpoint", required_argument, 0, 'E'}, + {"whitelist", required_argument, 0, 'w'}, + {"blacklist", required_argument, 0, 'b'}, + {"autozafl", no_argument, 0, 'a'}, + {"enable-bb-graph-optimization", no_argument, 0, 'g'}, + {"disable-bb-graph-optimization", no_argument, 0, 'G'}, + {"enable-domgraph-optimization", no_argument, 0, 'd'}, + {"disable-domgraph-optimization", no_argument, 0, 'D'}, + {"enable-forkserver", no_argument, 0, 'f'}, + {"disable-forkserver", no_argument, 0, 'F'}, + {"untracer", no_argument, 0, 'u'}, + {"enable-critical-edge-breakup", no_argument, 0, 'c'}, + {"disable-critical-edge-breakup", no_argument, 0, 'C'}, + {"enable-floating-instrumentation", no_argument, 0, 'i'}, + {"disable-floating-instrumentation", no_argument, 0, 'I'}, + {"enable-context-sensitivity", required_argument, 0, 'z'}, + {"random-seed", required_argument, 0, 'r'}, + {0,0,0,0} + }; + const char* short_opts="r:z:e:E:w:sv?hagGdDfFucCiI"; + + while(true) + { + int index = 0; + int c = getopt_long(argc, argv, short_opts, long_options, &index); + if(c == -1) + break; + switch(c) + { + case 'v': + verbose=true; + break; + case '?': + case 'h': + usage(argv[0]); + exit(1); + break; + case 's': + use_stars=true; + cout << "STARS optimization enabled" << endl; + break; + case 'e': + entry_fork_server = optarg; + break; + case 'E': + exitpoints.insert(optarg); + break; + case 'w': + whitelistFile=optarg; + break; + case 'b': + blacklistFile=optarg; + break; + case 'a': + autozafl=true; + break; + case 'g': + bb_graph_optimize=true; + break; + case 'G': + bb_graph_optimize=false; + break; + case 'd': + domgraph_optimize=true; + break; + case 'D': + domgraph_optimize=false; + break; + case 'f': + forkserver_enabled=true; + break; + case 'F': + forkserver_enabled=false; + break; + case 'u': + untracer_mode=true; + break; + case 'c': + breakup_critical_edges=true; + break; + case 'C': + breakup_critical_edges=false; + break; + case 'i': + floating_instrumentation=true; + break; + case 'I': + floating_instrumentation=false; + break; + case 'z': + if (optarg == string("callsite")) + context_sensitivity=ContextSensitivity_Callsite; // Angora fuzzer style + else if (optarg == string("function")) + context_sensitivity=ContextSensitivity_Function; + else + context_sensitivity=ContextSensitivity_None; + break; + case 'r': + random_seed = strtoul(optarg, NULL, 0); + srand(random_seed); + cout << "Setting random seed to: " << random_seed << endl; + break; + default: + break; + } + } + + if (entry_fork_server.size() > 0 && autozafl) + { + cerr << "--entrypoint and --autozafl are incompatible options" << endl; + usage(argv[0]); + exit(1); + } + + if (floating_instrumentation && !use_stars) + { + cerr << "STARS must be turned on when using floating instrumentation" << endl; + usage(argv[0]); + exit(1); + } + + + /* setup the interface to the sql server */ + auto pqxx_interface=pqxxDB_t::factory(); + BaseObj_t::setInterface(pqxx_interface.get()); + + auto pidp=VariantID_t::factory(variantID); + assert(pidp->isRegistered()==true); + + bool one_success = false; + for(set<File_t*>::iterator it=pidp->getFiles().begin(); + it!=pidp->getFiles().end(); + ++it) + { + File_t* this_file = *it; + auto firp = FileIR_t::factory(pidp.get(), this_file); + + cout<<"Transforming "<<this_file->getURL()<<endl; + + assert(firp && pidp); + + try + { + ZaxBase_t* zax_raw; + if (untracer_mode) + zax_raw = new ZUntracer_t(*pqxx_interface, firp.get(), entry_fork_server, exitpoints, use_stars, autozafl); + else + zax_raw = new Zax_t(*pqxx_interface, firp.get(), entry_fork_server, exitpoints, use_stars, autozafl); + auto zax = unique_ptr<ZaxBase_t>(zax_raw); + + if (whitelistFile.size()>0) + zax->setWhitelist(whitelistFile); + if (blacklistFile.size()>0) + zax->setBlacklist(blacklistFile); + + zax->setVerbose(verbose); + zax->setBasicBlockOptimization(bb_graph_optimize); + zax->setDomgraphOptimization(domgraph_optimize); + zax->setBasicBlockFloatingInstrumentation(floating_instrumentation); + zax->setEnableForkServer(forkserver_enabled); + zax->setBreakupCriticalEdges(breakup_critical_edges); + zax->setContextSensitivity(context_sensitivity); + + int success=zax->execute(); + + if (success) + { + cout<<"Writing changes for "<<this_file->getURL()<<endl; + one_success = true; + firp->writeToDB(); + } + else + { + cout<<"Skipping (no changes) "<<this_file->getURL()<<endl; + } + } + catch (const DatabaseError_t pnide) + { + cerr << programName << ": Unexpected database error: " << pnide << "file url: " << this_file->getURL() << endl; + exit(1); + } + catch (const std::exception &e) + { + cerr << programName << ": Error: " << e.what() << endl; + exit(1); + } + catch (...) + { + cerr << programName << ": Unexpected error file url: " << this_file->getURL() << endl; + exit(1); + } + } // end file iterator + + // if any transforms for any files succeeded, we commit + if (one_success) + { + cout<<"Commiting changes...\n"; + pqxx_interface->commit(); + } + + return 0; +} + diff --git a/zfuzz/afl_transforms/tools/zax/zuntracer.cpp b/zfuzz/afl_transforms/tools/zax/zuntracer.cpp new file mode 100644 index 0000000000000000000000000000000000000000..82cb301283b458de1c339445b32ed3e5452ff7c8 --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/zuntracer.cpp @@ -0,0 +1,227 @@ +#include "zuntracer.hpp" + +using namespace std; +using namespace IRDB_SDK; +using namespace Zafl; + +ZUntracer_t::ZUntracer_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_variantIR, string p_forkServerEntryPoint, set<string> p_exitPoints, bool p_use_stars, bool p_autozafl) : ZaxBase_t(p_dbinterface, p_variantIR, p_forkServerEntryPoint, p_exitPoints, p_use_stars, p_autozafl) +{ +} + +void ZUntracer_t::instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_redZoneHint, const bool p_collafl_optimization) +{ + if (!p_bb) throw; + + const auto trace_map_fixed_addr = getenv("ZAFL_TRACE_MAP_FIXED_ADDRESS"); + const auto do_fixed_addr_optimization = (trace_map_fixed_addr!=nullptr); + + if (do_fixed_addr_optimization) + _instrumentBasicBlock_fixed(p_bb, trace_map_fixed_addr); + else + _instrumentBasicBlock(p_bb, p_redZoneHint); +} + +// Highly efficient instrumentation using known address for tracemap +void ZUntracer_t::_instrumentBasicBlock_fixed(BasicBlock_t *p_bb, char* p_tracemap_addr) +{ + // 1st instruction in block record is the new entry point of the block (instr) + // 2nd instruction in block record is where the instruction at the old entry point is now at (orig) + auto instr = getInstructionToInstrument(p_bb); + if (!instr) throw; + + const auto blockid = getBlockId(); + BBRecord_t block_record; + block_record.push_back(instr); + + // e.g.: mov BYTE [ 0x10000 + blockid ], 0x1 + const auto s = string("mov BYTE [") + p_tracemap_addr + "+" + to_string(blockid) + "], 0x1"; + const auto orig = insertAssemblyBefore(instr, s); + block_record.push_back(orig); + + m_modifiedBlocks[blockid] = block_record; +} + +void ZUntracer_t::_instrumentBasicBlock(BasicBlock_t *p_bb, const bool p_redZoneHint) +{ + /* + Original afl instrumentation: + block_id = <random>; + zafl_trace_bits[zafl_prev_id ^ block_id]++; + zafl_prev_id = block_id >> 1; + + Zuntracer instrumentation (simple block coverage) + zafl_trace_bits[block_id] = 1; + */ + + const auto num_free_regs_desired = 1; + auto instr = getInstructionToInstrument(p_bb, num_free_regs_desired); + if (!instr) throw; + + auto tmp = instr; + auto tracemap_reg = string(); + auto found_tracemap_free_register = false; + + // 1st instruction in block record is the new entry point of the block (instr) + // 2nd instruction in block record is where the instruction at the old entry point is now at (orig) + BBRecord_t block_record; + + block_record.push_back(instr); + + const auto blockid = getBlockId(); + const auto labelid = getLabelId(); + + if (m_verbose) + cout << "working with blockid: " << blockid << " labelid: " << labelid << endl; + + if (m_use_stars) + { + const auto allowed_regs = RegisterSet_t({rn_RAX, rn_RBX, rn_RCX, rn_RDX, rn_R8, rn_R9, rn_R10, rn_R11, rn_R12, rn_R13, rn_R14, rn_R15}); + const auto dead_regs = getDeadRegs(instr); + const auto free_regs = getFreeRegs(dead_regs, allowed_regs); + if (free_regs.size() > 0) + { + auto r = *free_regs.begin(); + tracemap_reg = registerToString(r); + found_tracemap_free_register = true; + } + } + + auto inserted_before = false; + auto honorRedZone = p_redZoneHint; + + const auto do_insert=[&](const string& insn_str) -> void + { + if (inserted_before) + { + tmp = insertAssemblyAfter(tmp, insn_str); + block_record.push_back(tmp); + } + else + { + const auto orig = insertAssemblyBefore(tmp, insn_str); + inserted_before = true; + block_record.push_back(orig); + } + }; + + + // if we have a free register, we don't muck with the stack ==> no need to honor red zone + if (found_tracemap_free_register) + { + honorRedZone = false; + } + + if (honorRedZone) + { + do_insert("lea rsp, [rsp-128]"); + } + + // we did not find a free register, save rcx and then use it for the tracemap + if (!found_tracemap_free_register) + { + tracemap_reg = "rcx"; + do_insert("push " + tracemap_reg); + } + + assert(tracemap_reg.size()>0); + + // load address trace map into rcx: T123: mov rcx, QWORD [rel T123] + const auto load_trace_map = "T" + to_string(labelid) + ": mov " + tracemap_reg + " , QWORD [ rel T" + to_string(labelid) + "]"; + do_insert(load_trace_map); + create_got_reloc(getFileIR(), m_trace_map, tmp); + + // update trace map: e.g.: mov rcx, [rcx] + do_insert("mov " + tracemap_reg + ", [" + tracemap_reg + "]"); + + // set counter to 1: mov BYTE [rcx+1234], 1 + do_insert("mov BYTE [" + tracemap_reg + "+" + to_string(blockid) + "], 1"); + + // restore register + if (!found_tracemap_free_register) + { + do_insert("pop " + tracemap_reg); + } + + // red zone + if (honorRedZone) + { + do_insert("lea rsp, [rsp+128]"); + } + + m_modifiedBlocks[blockid] = block_record; +} + +set<BasicBlock_t*> ZUntracer_t::getBlocksToInstrument(ControlFlowGraph_t &cfg) +{ + static int bb_z_debug_id=-1; + + if (m_verbose) + cout << cfg << endl; + + auto keepers = set<BasicBlock_t*>(); + + for (auto &bb : cfg.getBlocks()) + { + bb_z_debug_id++; + + // already marked as a keeper + if (keepers.find(bb) != keepers.end()) + continue; + + // if whitelist specified, only allow instrumentation for functions/addresses in whitelist + if (!isWhitelisted(bb->getInstructions()[0])) + continue; + + if (isBlacklisted(bb->getInstructions()[0])) + continue; + + // debugging support + if (getenv("ZAFL_LIMIT_BEGIN")) + { + if (bb_z_debug_id < atoi(getenv("ZAFL_LIMIT_BEGIN"))) + continue; + } + + // debugging support + if (getenv("ZAFL_LIMIT_END")) + { + if (bb_z_debug_id >= atoi(getenv("ZAFL_LIMIT_END"))) + continue; + } + + // make sure we're not trying to instrument code we just inserted, e.g., fork server, added exit points + if (bb->getInstructions()[0]->getBaseID() < 0) + continue; + + // push/jmp pair, don't bother instrumenting + if (BB_isPushJmp(bb)) + { + m_num_bb_skipped_pushjmp++; + continue; + } + + // padding nop, don't bother + if (BB_isPaddingNop(bb)) + { + m_num_bb_skipped_nop_padding++; + continue; + } + + // optimization: + // @warning @todo: + // very experimental! + // elide instrumentation for conditional branches + // + if (m_graph_optimize) + { + if (bb->getSuccessors().size() == 2 && bb->endsInConditionalBranch()) + { + m_num_bb_skipped_cbranch++; + continue; + } + } + + keepers.insert(bb); + } + return keepers; +} diff --git a/zfuzz/afl_transforms/tools/zax/zuntracer.hpp b/zfuzz/afl_transforms/tools/zax/zuntracer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..eba33a8874d490d4fdaefd6f393e0e8676f50ecb --- /dev/null +++ b/zfuzz/afl_transforms/tools/zax/zuntracer.hpp @@ -0,0 +1,31 @@ +#ifndef _LIBTRANSFORM_ZUNTRACER_H +#define _LIBTRANSFORM_ZUNTRACER_H + +#include "zax_base.hpp" + +namespace Zafl +{ + + using namespace IRDB_SDK; + + // Block-level instrumentation for Untracer + class ZUntracer_t : public ZaxBase_t + { + public: + ZUntracer_t() = delete; + ZUntracer_t(const ZUntracer_t&) = delete; + ZUntracer_t(IRDB_SDK::pqxxDB_t &p_dbinterface, IRDB_SDK::FileIR_t *p_variantIR, string p_entry, set<string> p_exits, bool p_use_stars=false, bool p_autozafl=false); + virtual ~ZUntracer_t() {}; + + protected: + virtual void instrumentBasicBlock(BasicBlock_t*, const bool p_hasLeafAnnotation, const bool p_collafl_optimization=false); + virtual set<BasicBlock_t*> getBlocksToInstrument(ControlFlowGraph_t&); + + private: + void _instrumentBasicBlock_fixed(BasicBlock_t*, char* p_tracemap_addr); + void _instrumentBasicBlock(BasicBlock_t*, const bool p_redZoneHint); + }; + +} + +#endif diff --git a/zfuzz/bin/zafl.sh b/zfuzz/bin/zafl.sh new file mode 100755 index 0000000000000000000000000000000000000000..904cf0ad340a4a88726919138edbfc2e14d4d99d --- /dev/null +++ b/zfuzz/bin/zafl.sh @@ -0,0 +1,407 @@ +#!/bin/bash + +# +# Invoke underlying Zipr toolchain with Zafl step and parameters +# + +if [ ! -z $TERM ] && [ "$TERM" != "dumb" ]; then + RED=`tput setaf 1` + GREEN=`tput setaf 2` + YELLOW=`tput setaf 3` + ORANGE=`tput setaf 214` + MAGENTA=`tput setaf 5` + NC=`tput sgr0` +fi + +usage() +{ + echo + echo "zafl.sh <input_binary> <output_zafl_binary> [options]" + echo + echo "options:" + echo " -s, --stars Use STARS (default)" + echo " -S, --no-stars Do not use STARS" + echo " -g, --graph-optimization Use control flow graph optimizations" + echo " -G, --no-graph-optimization Do not use control flow graph optimizations (default)" + echo " -d, --domgraph-optimization Use Dominator graph optimizations" + echo " -D, --no-domgraph-optimization Do not use Dominator graph optimizations (default)" + echo " -t, --tempdir <dir> Specify location of analysis results directory" + echo " -e, --entry Specify fork server entry point" + echo " -E, --exit Specify fork server exit point(s)" + echo " -u, --untracer Specify untracer instrumentation" + echo " -c, --enable-breakup-critical-edges Breakup critical edges" + echo " -C, --disable-breakup-critical-edges Do not breakup critical edges (default)" + echo " -f, --fork-server-only Fork server only" + echo " -m, --enable-fixed-map [<address>] Use fixed address for tracing map (<address> must be hex and page-aligned, e.g., 0x10000)" + echo " -M, --disable-fixed-map Disable fixed address tracing map" + echo " -i, --enable-floating-instrumentation Select best instrumentation point within basic block (default)" + echo " -I, --disable-floating-instrumentation Use first instruction for instrumentation in basic blocks" + echo " --enable-context-sensitivity <style> style={callsite,function} only function supported currently (off by default)" + echo " -r, --random-seed <value> Specify random seed" +# echo " -l, --enable-locality Maintain code locality (best effort) when instrumenting binary" +# echo " -L, --disable-locality Randomized layout when instrumenting binary" + echo " -w, --whitelist <file> Specify function whitelist (one function per line)" + echo " -b, --blacklist <file> Specify function blacklist (one function per line)" + echo " --enable-split-compare enable laf-intel split compare for integer constants" + echo " --disable-split-compare disable laf-intel split compare for integer constants" + echo " --enable-split-branch enable laf-intel split branch for integer constants" + echo " --disable-split-branch disable laf-intel split branch for integer constants" + echo " -v Verbose mode" + echo +} + +ida_or_rida_opt=" -c rida " +stars_opt=" -o zax:--stars " +zax_opt="" +other_args="" +#float_opt="" +float_opt=" -o zax:--enable-floating-instrumentation " +context_sensitivity_opt="" +trace_opt="" +zipr_opt="" +random_seed="" +laf_opt="" + + +me=$(whoami) +tmp_dir=/tmp/${me}/$$ +mkdir -p $tmp_dir + +# by default, use fixed address for map +trace_map_address="0x10000" +ZAFL_TM_ENV="" + +cleanup() +{ + if [ ! -z "$tmp_dir" ]; then + rm -fr $tmp_dir + fi +} + +log_msg() +{ + echo -e "${GREEN}Zafl: $1 ${NC}" +} + +log_warning() +{ + echo -e "${ORANGE}Zafl: $1 ${NC}" +} + +log_error() +{ + echo -e "${RED}Zafl: $1 ${NC}" +} + +log_error_exit() +{ + log_error "$1" + cleanup + exit 1 +} + +parse_args() +{ + PARAMS="" + while (( "$#" )); do + key="$1" + + case $key in + -h|--help) + usage + exit 0 + ;; + --ida) + ida_or_rida_opt=" -c meds_static=on -s rida=off " + shift + ;; + --rida) + ida_or_rida_opt=" -s meds_static=off -c rida=on " + shift + ;; + -s | --stars) + stars_opt=" -o zax:--stars " + shift + ;; + -S | --no-stars) + stars_opt=" " + float_opt=" -o zax:--disable-floating-instrumentation " + shift + ;; + -g | --graph-optimization) + zax_opt=" $zax_opt -o zax:-g " + shift + ;; + -G | --no-graph-optimization) + zax_opt=" $zax_opt -o zax:-G " + shift + ;; + -d | --domgraph-optimization) + zax_opt=" $zax_opt -o zax:-d " + shift + ;; + -D | --no-domgraph-optimization) + zax_opt=" $zax_opt -o zax:-D " + shift + ;; + -e | --entry) + shift + entry_opt=" -o zax:\"-e $1\" " + shift + ;; + -E | --exit) + shift + exit_opt=" $exit_opt -o zax:\"-E $1\" " + shift + ;; + -v | --verbose) + verbose_opt=" -o zax:-v " + shift + ;; + -t | --tempdir) + shift + other_args=" --tempdir $1" + shift + ;; + -w | --whitelist) + shift + zax_opt=" $zax_opt -o zax:\"--whitelist $(realpath $1) \"" + shift + ;; + -b | --blacklist) + shift + zax_opt=" $zax_opt -o zax:\"--blacklist $(realpath $1) \"" + shift + ;; + -u | --untracer) + zax_opt=" $zax_opt -o zax:--untracer " + shift + ;; + -c | --enable-breakup-critical-edges) + zax_opt=" $zax_opt -o zax:-c " + shift + ;; + -C | --disable-breakup-critical-edges) + zax_opt=" $zax_opt -o zax:-C " + shift + ;; + -f | --fork-server-only) + ZAFL_LIMIT_END=0 + export ZAFL_LIMIT_END + log_warning "Fork Server Only mode: no block-level instrumentation will be performed" + shift + ;; + -m | --enable-fixed-map) + shift + case $1 in + 0x*) + trace_map_address="$1" + shift + ;; + esac + ZAFL_TM_ENV="ZAFL_TRACE_MAP_FIXED_ADDRESS=$trace_map_address" + ;; + -M | --disable-fixed-map) + unset ZAFL_TRACE_MAP_FIXED_ADDRESS + ZAFL_TM_ENV="" + shift + ;; + -i | --enable-floating-instrumentation) + float_opt=" -o zax:--enable-floating-instrumentation " + shift + ;; + -I | --disable-floating-instrumentation) + float_opt=" -o zax:--disable-floating-instrumentation " + shift + ;; + --enable-context-sensitivity) + shift + case $1 in + function) + zax_opt=" -o zax:\"--enable-context-sensitivity function\" " + shift + ;; + callsite) + zax_opt=" -o zax:\"--enable-context-sensitivity callsite\" " + echo "Error: context sensitivity <callsite> currently unsupported" + exit 1 + ;; + *) + echo "Error: must specify function or callsite for context sensitivity" + exit 1 + ;; + esac + ;; + -r | --random-seed) + shift + random_seed="$1" + zax_opt=" $zax_opt -o zax:\"--random-seed $random_seed\" " + zipr_opt=" $zipr_opt --step-option zipr:\"--zipr:seed $random_seed\" " + shift + ;; + -l | --enable-locality) + trace_opt=" --step-option zipr:--traceplacement:on --step-option zipr:true " + shift + ;; + -L | --disable-locality) + trace_opt="" + shift + ;; + --enable-split-compare) + laf_opt=" $laf_opt -o laf:--enable-split-compare " + shift + ;; + --disable-split-compare) + laf_opt=" $laf_opt -o laf:--disable-split-compare " + shift + ;; + --enable-split-branch) + laf_opt=" $laf_opt -o laf:--enable-split-branch " + shift + ;; + --disable-split-branch) + laf_opt=" $laf_opt -o laf:--disable-split-branch " + shift + ;; + -*|--*=) # unsupported flags + echo "Error: Unsupported flag $1" >&2 + exit 1 + ;; + *) # preserve positional arguments + PARAMS="$PARAMS $1" + shift + ;; + esac + done + + eval set -- "$PARAMS" + positional=($PARAMS) + input_binary=${positional[0]} + output_zafl_binary=${positional[1]} + + if [ -z $input_binary ]; then + usage + log_error_exit "You must specify an input binary to be protected" + fi + + if [ -z $output_zafl_binary ]; then + usage + log_error_exit "You must specify the name of the output binary" + fi + + input_binary=$(realpath $input_binary) +} + +find_main() +{ + main_addr="" + tmp_objdump=$tmp_dir/tmp.objdump + tmp_main=$tmp_dir/tmp.main + + objdump -d $input_binary > $tmp_objdump + + grep "<main>:" $tmp_objdump > $tmp_main + + if [ $? -eq 0 ]; then + main_addr=$(cut -d' ' -f1 $tmp_main) + log_msg "Detected main at: 0x$main_addr" + options=" $options -o zax:'-e 0x$main_addr'" + else + grep -B1 "libc_start_main@" $tmp_objdump >/dev/null 2>&1 + if [ $? -eq 0 ]; then + grep -B1 start_main $tmp_objdump | grep rdi | grep rip >/dev/null 2>&1 + if [ $? -eq 0 ]; then + ep=$(readelf -h $input_binary | grep -i "entry point" | cut -d'x' -f2) + if [ ! -z $ep ]; then + log_msg "main exec is PIE... use entry point address (0x$ep) for fork server" + options=" $options -o zax:'-e 0x$ep'" + else + log_error_exit "error finding entry point address" + fi + else + grep "libc_start_main" $tmp_objdump | grep ">:" | grep -v -e "@plt" -e "jmp" >/dev/null 2>&1 + if [ $? -eq 0 ]; then + log_msg "Detected libc: no main" + rm $tmp_objdump + return + fi + + main_addr=$(grep -B1 libc_start_main@plt $tmp_objdump | grep mov | grep rdi | cut -d':' -f2 | cut -d'm' -f2 | cut -d',' -f1 | cut -d'x' -f2) + if [ "$main_addr" = "" ]; then + log_error_exit "error inferring main" + fi + + log_msg "inferring main to be at: 0x$main_addr" + options=" $options -o zax:'-e 0x$main_addr'" + fi + else + log_warning "no main() detected, probably a library ==> no automated insertion of fork server" + fi + fi + rm $tmp_objdump >/dev/null 2>&1 +} + +verify_zafl_symbols() +{ + # verify library dependency set + ldd $1 | grep -e libzafl -e libautozafl >/dev/null 2>&1 + if [ $? -eq 0 ]; then + log_msg "success. Output file is: $1" + else + ldd $1 + log_error_exit "output binary does not show a dependence on the Zafl support library" + fi + + # sanity check symbols in zafl library resolve + ldd -d $1 | grep symbol | grep 'not defined' >/dev/null 2>&1 + if [ $? -eq 0 ]; then + log_error_exit "something went wrong in resolving Zafl symbols" + fi +} + +parse_args $* +if [ -z "$entry_opt" ]; then + find_main +else + options=" $options $entry_opt " +fi + +if [ ! -z "$exit_opt" ]; then + options=" $options $exit_opt " +fi + +# +# Execute Zipr toolchain with Zafl options +# +log_msg "Transforming input binary $input_binary into $output_zafl_binary" + +optional_step="" +if [ ! -z "$laf_opt" ]; +then + optional_step=" -c laf=on $laf_opt " +fi + +zax_opt=" $zax_opt $float_opt " +cmd="$ZAFL_TM_ENV $PSZ $input_binary $output_zafl_binary $ida_or_rida_opt -c move_globals=on $optional_step -c zax=on -o move_globals:--elftables-only $stars_opt $zax_opt $verbose_opt $options $other_args $trace_opt $zipr_opt" + + +if [ ! -z "$ZAFL_TM_ENV" ]; then + log_msg "Trace map will be expected at fixed address" + if [ -z $ZAFL_TRACE_MAP_FIXED_ADDRESS ]; then + log_warning "When running afl-fuzz, make sure that the environment variable ZAFL_TRACE_MAP_FIXED_ADDRESS is exported and set properly to (otherwise the instrumented binary will crash):" + log_warning "export $ZAFL_TM_ENV" + fi +else + if [ ! -z $ZAFL_TRACE_MAP_FIXED_ADDRESS ]; then + log_msg "Trace map will be at fixed address: $ZAFL_TRACE_MAP_FIXED_ADDRESS" + fi +fi + +log_msg "Issuing command: $cmd" +eval $cmd +if [ $? -eq 0 ]; then + verify_zafl_symbols $output_zafl_binary +else + log_error_exit "error transforming input program" +fi + diff --git a/zfuzz/build-all.sh b/zfuzz/build-all.sh new file mode 100755 index 0000000000000000000000000000000000000000..845b062ca636439d6abe848729c82b77c7263e07 --- /dev/null +++ b/zfuzz/build-all.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +echo +echo "Building Fuzzing Support" +echo + +SCONSDEBUG="" +if [[ "$*" =~ "--debug" ]]; then + SCONSDEBUG=" debug=1 " +fi + +cd $AFL_TRANSFORMS +scons $SCONSDEBUG -j 3 || exit + +cd $ZFUZZ_HOME/libzafl +scons +cp lib/* $ZEST_RUNTIME/lib64/ + + diff --git a/zfuzz/clean-all.sh b/zfuzz/clean-all.sh new file mode 100755 index 0000000000000000000000000000000000000000..54fcb50cef3afeff6d75ab354d56a1cfe4dc75b9 --- /dev/null +++ b/zfuzz/clean-all.sh @@ -0,0 +1,11 @@ +#!/bin/bash + +cd $AFL_TRANSFORMS +scons -c || exit + +cd $ZFUZZ_HOME/libzafl/src +scons -c || exit +scons autozafl=1 -c || exit + +cd $ZFUZZ_HOME +rm -fr afl diff --git a/zfuzz/get-packages.sh b/zfuzz/get-packages.sh new file mode 100755 index 0000000000000000000000000000000000000000..42bc16e296de1bb6e01686f4e0dd033f3e587945 --- /dev/null +++ b/zfuzz/get-packages.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +args="$@" +if [[ "$args" == "" ]]; then + args="all" +fi + +for arg in $args; do + case $arg in + build) + # do nothing + ;; + + all | deploy | test) + which apt-get 1> /dev/null 2> /dev/null + if [ $? -eq 0 ]; then + sudo apt-get install -y --ignore-missing afl + else + sudo yum install -y --skip-broken afl + fi + ;; + + *) + echo "arg not recognized. Recognized args: all, build, test, deploy" + exit 1 + ;; + esac +done diff --git a/zfuzz/libzafl b/zfuzz/libzafl new file mode 160000 index 0000000000000000000000000000000000000000..5bcb4a8dbae1af977384ceda3b92e402f3c7f67d --- /dev/null +++ b/zfuzz/libzafl @@ -0,0 +1 @@ +Subproject commit 5bcb4a8dbae1af977384ceda3b92e402f3c7f67d diff --git a/zfuzz/manifest.txt b/zfuzz/manifest.txt new file mode 100644 index 0000000000000000000000000000000000000000..445b1c8c73767510674eda1da672a40d879a6eea --- /dev/null +++ b/zfuzz/manifest.txt @@ -0,0 +1,5 @@ +# env vars. +file set_env_vars zfuzz +file get-packages.sh zfuzz +directory bin zfuzz +directory test zfuzz diff --git a/zfuzz/set_env_vars b/zfuzz/set_env_vars new file mode 100644 index 0000000000000000000000000000000000000000..7f210602174452a34543d9343e9c835e587189d8 --- /dev/null +++ b/zfuzz/set_env_vars @@ -0,0 +1,5 @@ +export ZFUZZ_HOME=`pwd` +export AFL_TRANSFORMS=$ZFUZZ_HOME/afl_transforms +export PATH=$PATH:$ZFUZZ_HOME/bin +export AFL_SKIP_BIN_CHECK=1 +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib diff --git a/zfuzz/test/bc.fixed_map/test_bc.sh b/zfuzz/test/bc.fixed_map/test_bc.sh new file mode 100755 index 0000000000000000000000000000000000000000..c62e0e0618c49341198d381479c0c9759bc933ca --- /dev/null +++ b/zfuzz/test/bc.fixed_map/test_bc.sh @@ -0,0 +1,107 @@ +export AFL_TIMEOUT=15 +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib/:. + +user=$(whoami) +session=/tmp/tmp.${user}.zafl.bc.fixed_map.$$ + +cleanup() +{ + rm -fr $session +} + +log_error() +{ + echo "TEST FAIL: $1" + cleanup + exit 1 +} + +log_message() +{ + echo "TEST MSG: $1" +} + +log_success() +{ + echo "TEST PASS: $1" +} + +fuzz_with_zafl() +{ + bc_zafl=$1 + + env | grep ZAFL + + # setup AFL directories + mkdir zafl_in + echo "1" > zafl_in/1 + + if [ -d zafl_out ]; then + rm -fr zafl_out + fi + + # run for 30 seconds + timeout $AFL_TIMEOUT afl-fuzz -i zafl_in -o zafl_out -- $bc_zafl + if [ $? -eq 124 ]; then + if [ ! -e zafl_out/fuzzer_stats ]; then + log_error "$bc_zafl: something went wrong with afl -- no fuzzer stats file" + fi + + cat zafl_out/fuzzer_stats + execs_per_sec=$( grep execs_per_sec zafl_out/fuzzer_stats ) + log_success "$bc_zafl: $execs_per_sec" + else + log_error "$bc_zafl: unable to run with afl" + fi + +} + +mkdir -p $session +pushd $session + +# Fix map at 0x10000 +# Should match value in env. var ZAFL_TRACE_MAP_FIXED_ADDRESS +trace_map_address="0x10000" + +# build ZAFL version of bc executable with fixed map + +zafl.sh `which bc` bc.fixed.zafl -m $trace_map_address --tempdir analysis.bc.fixed.zafl +if [ $? -eq 0 ]; then + log_success "build bc.fixed.zafl" +else + log_error "build bc.fixed.zafl" +fi +grep ATTR analysis.bc.fixed.zafl/logs/zax.log +log_message "Fuzz for $AFL_TIMEOUT secs" +fuzz_with_zafl $(realpath ./bc.fixed.zafl) + +# build ZAFL version of readline shared library +readline=$( ldd `which bc` | grep libreadline | cut -d'>' -f2 | cut -d'(' -f1 ) +readline_basename=$( basename $readline ) +readline_realpath=$( realpath $readline ) +echo "basename: $readline_basename realpath: $readline_realpath" +zafl.sh $readline_realpath $readline_basename -m $trace_map_address +if [ $? -eq 0 ]; then + log_success "build zafl version of $readline_basename at $readline_realpath" +else + log_error "build zafl version of $readline_basename at $readline_realpath" +fi + +ldd bc.fixed.zafl + +log_message "Fuzz for $AFL_TIMEOUT secs (with readline library zafl'ed)" +fuzz_with_zafl $(realpath ./bc.fixed.zafl) + +# test functionality +echo "2+3" | `which bc` > out.bc.orig +echo "2+3" | ./bc.fixed.zafl > out.bc.fixed.zafl +diff out.bc.orig out.bc.fixed.zafl >/dev/null 2>&1 +if [ $? -eq 0 ]; then + log_success "bc.fixed.zafl basic functionality" +else + log_error "bc.fixed.zafl basic functionality" +fi + +popd + +cleanup diff --git a/zfuzz/test/bc/test_bc.sh b/zfuzz/test/bc/test_bc.sh new file mode 100755 index 0000000000000000000000000000000000000000..09358674e59976ed25915e76c4ff002d6dab0774 --- /dev/null +++ b/zfuzz/test/bc/test_bc.sh @@ -0,0 +1,95 @@ +export AFL_TIMEOUT=15 +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib/:. + +user=$(whoami) +session=/tmp/tmp.${user}.zafl.bc.$$ + +cleanup() +{ + rm -fr $session +} + +log_error() +{ + echo "TEST FAIL: $1" + cleanup + exit 1 +} + +log_message() +{ + echo "TEST MSG: $1" +} + +log_success() +{ + echo "TEST PASS: $1" +} + +fuzz_with_zafl() +{ + bc_zafl=$1 + + # setup AFL directories + mkdir zafl_in + echo "1" > zafl_in/1 + + if [ -d zafl_out ]; then + rm -fr zafl_out + fi + + # run for 30 seconds + timeout $AFL_TIMEOUT afl-fuzz -i zafl_in -o zafl_out -- $bc_zafl + if [ $? -eq 124 ]; then + if [ ! -e zafl_out/fuzzer_stats ]; then + log_error "$bc_zafl: something went wrong with afl -- no fuzzer stats file" + fi + + cat zafl_out/fuzzer_stats + execs_per_sec=$( grep execs_per_sec zafl_out/fuzzer_stats ) + log_success "$bc_zafl: $execs_per_sec" + else + log_error "$bc_zafl: unable to run with afl" + fi + +} + +mkdir -p $session +pushd $session + +# build with graph optimization +zafl.sh `which bc` bc.stars.zafl.d.g.r.cs -d -g --tempdir analysis.bc.stars.zafl.d.g.r.cs -r 123 --enable-context-sensitivity function +if [ $? -eq 0 ]; then + log_success "build bc.stars.zafl.d.g.r.cs" +else + log_error "build bc.stars.zafl.d.g.r.cs" +fi +log_message "Fuzz for $AFL_TIMEOUT secs" +fuzz_with_zafl $(realpath ./bc.stars.zafl.d.g.r.cs) + +# build ZAFL version of readline shared library +readline=$( ldd `which bc` | grep libreadline | cut -d'>' -f2 | cut -d'(' -f1 ) +readline_basename=$( basename $readline ) +readline_realpath=$( realpath $readline ) +echo "basename: $readline_basename realpath: $readline_realpath" +#$PSZ $readline_realpath $readline_basename -c move_globals=on -c zafl=on -o move_globals:--elftables -o zipr:--traceplacement:on -o zipr:true -o zafl:--stars +zafl.sh $readline_realpath $readline_basename --random-seed 42 -d +if [ $? -eq 0 ]; then + log_success "build zafl version of $readline_basename at $readline_realpath" +else + log_error "build zafl version of $readline_basename at $readline_realpath" +fi + +# test functionality +echo "2+3" | `which bc` > out.bc.orig +echo "2+3" | ./bc.stars.zafl.d.g.r.cs > out.bc.stars.zafl.d.g.r.cs +diff out.bc.orig out.bc.stars.zafl.d.g.r.cs >/dev/null 2>&1 +if [ $? -eq 0 ]; then + log_success "bc.stars.zafl.d.g.r.cs basic functionality" +else + log_error "bc.stars.zafl.d.g.r.cs basic functionality" +fi + +popd + +cleanup diff --git a/zfuzz/test/bench/binutils.spec b/zfuzz/test/bench/binutils.spec new file mode 100755 index 0000000000000000000000000000000000000000..26ef326726a0aa003317098dfb21064f18eb6c69 --- /dev/null +++ b/zfuzz/test/bench/binutils.spec @@ -0,0 +1,20 @@ +#tools can be aflgcc zafl qemu dyninst +tools="aflgcc zafl qemu" + +#binutils_binaries="objdump size readelf strings cxxfilt nm-new strip-new ar" +binutils_binaries="objdump readelf nm-new" + +# specify how to run under afl +declare -A fuzz_map +fuzz_map["size"]="@@" +fuzz_map["objdump"]="-d @@" +fuzz_map["readelf"]="-a @@" +fuzz_map["strings"]="" +fuzz_map["cxxfilt"]="" +fuzz_map["nm-new"]="-a @@" +fuzz_map["strip-new"]="@@" +fuzz_map["gzip"]="-f" + +# additional afl params to pass in +declare -A afl_args +afl_args["objdump"]=" -m 150 " diff --git a/zfuzz/test/bench/build_aflgcc.sh b/zfuzz/test/bench/build_aflgcc.sh new file mode 100755 index 0000000000000000000000000000000000000000..2dfd7254ab47a51c9c12ed91bd121bba3aa00ac8 --- /dev/null +++ b/zfuzz/test/bench/build_aflgcc.sh @@ -0,0 +1,17 @@ +SCRIPT=$(readlink -f $0) + +cd $(dirname $SCRIPT) +source binutils.spec + +echo "Build afl-gcc version of $binutils_binaries" + +for b in $binutils_binaries +do + aflgcc_dir="${b}_aflgcc" + if [ ! -d $aflgcc_dir ]; + then + mkdir ${b}_aflgcc + fi + + cp binutils-2.30/binutils/$b ${b}_aflgcc/${b}.aflgcc +done diff --git a/zfuzz/test/bench/build_dyninst.sh b/zfuzz/test/bench/build_dyninst.sh new file mode 100755 index 0000000000000000000000000000000000000000..e7454ed340c937df62c3cbc84ca72ef63608f2cf --- /dev/null +++ b/zfuzz/test/bench/build_dyninst.sh @@ -0,0 +1,24 @@ +source binutils.spec +echo "Build dyninst version of $binutils_binaries" + +AFL_DYNINST_DIR=/home/an7s/aware/zfuzz/afl-dyninst/ +AFL_DYNINST=$AFL_DYNINST_DIR/afl-dyninst + +for b in $binutils_binaries +do + dyninst_dir="${b}_dyninst" + if [ ! -d $dyninst_dir ]; + then + mkdir ${b}_dyninst + fi + + cp binutils-2.30/binutils/$b ${b}_dyninst + + pushd $dyninst_dir + echo "Building Zafl version of $b" + ln -s $AFL_DYNINST_DIR/libAflDyninst.so . + $AFL_DYNINST -f -i $b -o ${b}.dyninst + mkdir in + echo "1" > in/1 + popd +done diff --git a/zfuzz/test/bench/build_qemu.sh b/zfuzz/test/bench/build_qemu.sh new file mode 100755 index 0000000000000000000000000000000000000000..05defdba7aafea96d9079f6ecaa0ece50ff528bf --- /dev/null +++ b/zfuzz/test/bench/build_qemu.sh @@ -0,0 +1,13 @@ +source binutils.spec +echo "Build qemu version of $binutils_binaries" + +for b in $binutils_binaries +do + qemu_dir="${b}_qemu" + if [ ! -d $qemu_dir ]; + then + mkdir ${b}_qemu + fi + + cp binutils-2.30/binutils/$b ${b}_qemu/${b}.qemu +done diff --git a/zfuzz/test/bench/build_zafl.sh b/zfuzz/test/bench/build_zafl.sh new file mode 100755 index 0000000000000000000000000000000000000000..f7499d9f334a50cf650b5f317a06f3c61b062641 --- /dev/null +++ b/zfuzz/test/bench/build_zafl.sh @@ -0,0 +1,29 @@ +SCRIPT=$(readlink -f $0) + +cd $(dirname $SCRIPT) +source binutils.spec + +echo "Zafl $binutils_binaries" + +for b in $binutils_binaries +do + zafl_dir="${b}_zafl" + if [ ! -d $zafl_dir ]; + then + mkdir ${b}_zafl + fi + + cp binutils-2.30/binutils/$b ${b}_zafl/ + + pushd $zafl_dir + echo "Remove any remnants of previous analysis runs" + rm -fr peasoup_exec* + echo "Building Zafl version of $b" + zafl.sh ./$b ${b}.zafl + + if [ ! -d in ]; then + mkdir in + fi + echo "1" > in/1 + popd +done diff --git a/zfuzz/test/bench/download.sh b/zfuzz/test/bench/download.sh new file mode 100755 index 0000000000000000000000000000000000000000..8583614f0e5dfc4617a59a479abb5f88202e2df0 --- /dev/null +++ b/zfuzz/test/bench/download.sh @@ -0,0 +1,9 @@ +wget http://ftp.gnu.org/gnu/gzip/gzip-1.9.tar.gz +tar -zxf gzip*.tar.gz + +wget http://ftp.gnu.org/gnu/bc/bc-1.07.tar.gz +tar -zxf bc*.tar.gz + +wget http://ftp.gnu.org/gnu/binutils/binutils-2.30.tar.gz +tar -zxf binut*.tar.gz + diff --git a/zfuzz/test/bench/run-all.sh b/zfuzz/test/bench/run-all.sh new file mode 100755 index 0000000000000000000000000000000000000000..9974a28d240eaff798fb368d90ff9500277c32cd --- /dev/null +++ b/zfuzz/test/bench/run-all.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +SCRIPT=$(readlink -f $0) +MYDIR=$(dirname $SCRIPT) + +cd $MYDIR + +source binutils.spec + +echo "Evaluate $binutils_binaries" +AFL_TIMEOUT=3600 +EPOCH_TIMEOUT=3800 + +./setup_afl.sh + +for b in $binutils_binaries +do + echo "fuzz_map for $b: ${fuzz_map[$b]}" + + cd $MYDIR/${b}_aflgcc + nohup timeout $AFL_TIMEOUT afl-fuzz ${afl_args[$b]} -i in -o out -- ./${b}.aflgcc ${fuzz_map[$b]} & + + cd $MYDIR/${b}_zafl + nohup timeout $AFL_TIMEOUT afl-fuzz ${afl_args[$b]} -i in -o out -- ./${b}.zafl ${fuzz_map[$b]} & + + cd $MYDIR/${b}_dyninst + nohup timeout $AFL_TIMEOUT afl-fuzz ${afl_args[$b]} -i in -o out -- ./${b}.dyninst ${fuzz_map[$b]} & + + cd $MYDIR/${b}_qemu + nohup timeout $AFL_TIMEOUT afl-fuzz ${afl_args[$b]} -i in -o out -Q -- ./${b}.qemu ${fuzz_map[$b]} & + + sleep $EPOCH_TIMEOUT + + pkill afl-fuzz >/dev/null 2>&1 + pkill afl-qemu-trace >/dev/null 2>&1 + + grep execs ${b}_*/out/fuzzer_stats +done diff --git a/zfuzz/test/bench/run-zafl-all.sh b/zfuzz/test/bench/run-zafl-all.sh new file mode 100755 index 0000000000000000000000000000000000000000..27a8a11e22f20f5c0f2e99655352611592c4ec8a --- /dev/null +++ b/zfuzz/test/bench/run-zafl-all.sh @@ -0,0 +1,65 @@ +#!/bin/bash + +AFL_TIMEOUT=3600 +EPOCH_TIMEOUT=3800 + +SCRIPT=$(readlink -f $0) +MYDIR=$(dirname $SCRIPT) + +cd $MYDIR + +source binutils.spec + +echo +echo "Build and compare different Zafl configurations" +echo + +binaries=$binutils_binaries +trials="a b c" + +# build all the zafl configurations we care about +for b in $binaries +do + cd $MYDIR/${b}_zafl + echo building in $PWD + if [ ! -e $b.zafl ]; then + $PSZ $b $b.zafl -c move_globals=on -c zax=on -o move_globals:--elftables -o zipr:'--zipr:seed 123' + fi +# if [ ! -e $b.zafl.stars ]; then +# $PSZ $b $b.zafl.stars -c move_globals=on -c zax=on -o move_globals:--elftables -o zafl:--stars -o zipr:'--zipr:seed 123' +# fi +# if [ ! -e $b.zafl.trace ]; then +# $PSZ $b $b.zafl.trace -c move_globals=on -c zax=on -o move_globals:--elftables -o zipr:--traceplacement:on -o zipr:'--zipr:seed 123' +# fi +# if [ ! -e $b.zafl.stars.trace ]; then +# $PSZ $b $b.zafl.stars.trace -c move_globals=on -c zax=on -o move_globals:--elftables -o zipr:--traceplacement:on -o zipr:true -o zafl:--stars -o zipr:'--zipr:seed 123' +# fi +# if [ ! -e $b.zafl.stars.relax ]; then +# $PSZ $b $b.zafl.stars.relax -c move_globals=on -c zax=on -o move_globals:--elftables -o zipr:--relax:on -o zafl:--stars -o zipr:'--zipr:seed 123' +# fi +done + +for t in $trials +do + for b in $binaries + do + cd $MYDIR/${b}_zafl + + echo "fuzz_map for $b: ${fuzz_map[$b]}" + + # create input seed directory + mkdir in >/dev/null 2>&1 + echo "1" > in/1 + + rm -fr out.$t.zafl + nohup timeout $AFL_TIMEOUT afl-fuzz -i in -o out.$t.zafl -- ./$b.zafl ${fuzz_map[$b]} & +# nohup timeout $AFL_TIMEOUT afl-fuzz -i in -o out.$t.zafl.stars -- ./$b.zafl.stars ${fuzz_map[$b]} & +# nohup timeout $AFL_TIMEOUT afl-fuzz -i in -o out.$t.zafl.trace -- ./$b.zafl.trace ${fuzz_map[$b]} & +# nohup timeout $AFL_TIMEOUT afl-fuzz -i in -o out.$t.zafl.stars.trace -- ./$b.zafl.stars.trace ${fuzz_map[$b]} & +# nohup timeout $AFL_TIMEOUT afl-fuzz -i in -o out.$t.zafl.stars.relax -- ./$b.zafl.stars.relax ${fuzz_map[$b]} & + + sleep $EPOCH_TIMEOUT + + pkill afl-fuzz >/dev/null 2>&1 + done +done diff --git a/zfuzz/test/bench/setup_afl.sh b/zfuzz/test/bench/setup_afl.sh new file mode 100755 index 0000000000000000000000000000000000000000..95a41ff712e6662c1b352c6b4c09b6265f8a47a7 --- /dev/null +++ b/zfuzz/test/bench/setup_afl.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +SCRIPT=$(readlink -f $0) +MYDIR=$(dirname $SCRIPT) + +cd $MYDIR + +source binutils.spec + +for b in $binutils_binaries +do + for t in $tools + do + pushd ${b}_${t} + if [ ! -d in ]; then + mkdir in + fi + echo "1" > in/1 + + if [ -d out ]; then + rm -fr out + fi + popd + done +done diff --git a/zfuzz/test/bench/setup_aflgcc.sh b/zfuzz/test/bench/setup_aflgcc.sh new file mode 100755 index 0000000000000000000000000000000000000000..4429cf27503efab167fab22c993420e1880797f2 --- /dev/null +++ b/zfuzz/test/bench/setup_aflgcc.sh @@ -0,0 +1,8 @@ +pushd binutils-2.30 +make clean distclean +rm -fr config.cache +rm -fr */config.cache +rm -fr */*/config.cache +CC=afl-gcc ./configure +make clean all +popd diff --git a/zfuzz/test/bench/setup_binutils.sh b/zfuzz/test/bench/setup_binutils.sh new file mode 100755 index 0000000000000000000000000000000000000000..74adaff01b65bb63a557183d233b8a5e7e8969d5 --- /dev/null +++ b/zfuzz/test/bench/setup_binutils.sh @@ -0,0 +1,8 @@ +pushd binutils-2.30 +make clean distclean +rm -fr config.cache +rm -fr */config.cache +rm -fr */*/config.cache +./configure +make clean all +popd diff --git a/zfuzz/test/gzip/test_gzip.sh b/zfuzz/test/gzip/test_gzip.sh new file mode 100755 index 0000000000000000000000000000000000000000..28b661595e7e4d9737c53a1e1b6a370da3540e46 --- /dev/null +++ b/zfuzz/test/gzip/test_gzip.sh @@ -0,0 +1,134 @@ +AFL_TIMEOUT=30 +session=/tmp/tmp.gzip.$$ +TMP_FILE_1="${session}/gzip.tmp.$$" +TMP_FILE_2="${session}/gzip.tmp.$$" + +mkdir -p $session + +cleanup() +{ + rm -fr /tmp/gzip.tmp* gzip*.zafl peasoup_exec*.gzip* zafl_in zafl_out ${session} +} + +log_error() +{ + echo "TEST FAIL: $1" + exit 1 +} + +log_message() +{ + echo "TEST MSG: $1" +} + +log_success() +{ + echo "TEST PASS: $1" +} + +setup() +{ + echo "hello" > $TMP_FILE_1 + echo "hello" > $TMP_FILE_2 +} + +build_zafl() +{ + gzip_zafl=$1 + shift + $PSZ `which gzip` $gzip_zafl -c move_globals=on -c zax=on -o move_globals:--elftables -o zipr:--traceplacement:on -o zipr:true $* --tempdir analysis.${gzip_zafl} + if [ ! $? -eq 0 ]; then + log_error "$gzip_zafl: unable to generate zafl version" + else + log_message "$gzip_zafl: built successfully" + fi + + grep ATTR analysis.${gzip_zafl}/logs/zax.log +} + +test_zafl() +{ + gzip_zafl=$( realpath $1 ) + shift + + LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib/ $gzip_zafl $* $TMP_FILE_1 + if [ ! $? -eq 0 ]; then + log_error "$gzip_zafl $*: unable to gzip file using zafl version" + fi + + LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib/ $gzip_zafl -d ${TMP_FILE_1}.gz + diff $TMP_FILE_1 $TMP_FILE_2 + if [ $? -eq 0 ]; then + log_success "$gzip_zafl $*: after zipping and unzipping, we get the same file back. yeah!" + else + log_error "$gzip_zafl $*: after zipping and unzipping, we get a diferent file" + fi +} + +fuzz_with_zafl() +{ + gzip_zafl=$1 + + # setup AFL directories + mkdir zafl_in + echo "1" > zafl_in/1 + + if [ -d zafl_out ]; then + rm -fr zafl_out + fi + + # run for 30 seconds + LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib/ timeout $AFL_TIMEOUT afl-fuzz -i zafl_in -o zafl_out -- $gzip_zafl -f + if [ $? -eq 124 ]; then + if [ ! -e zafl_out/fuzzer_stats ]; then + log_error "$gzip_zafl: something went wrong with afl -- no fuzzer stats file" + fi + + cat zafl_out/fuzzer_stats + execs_per_sec=$( grep execs_per_sec zafl_out/fuzzer_stats ) + log_success "$gzip_zafl: ran zafl binary: $execs_per_sec" + else + log_error "$gzip_zafl: unable to run with afl" + fi + +} + +pushd ${session} +setup + + +file /bin/gzip | grep "shared object" >/dev/null 2>&1 +if [ $? -eq 0 ]; then + echo "PIE detected -- this test only designed for non-PIE" + exit 1 +fi + +# test setting of entry point via address +ep=$( objdump -Mintel -d /bin/gzip | grep text | grep -v -i disassembly | cut -d' ' -f1 | sed 's/^00000000//g' ) +build_zafl gzip.stars.entrypoint.${ep}.zafl -o zax:--stars -o "zax:--entrypoint=$ep" +test_zafl ./gzip.stars.entrypoint.${ep}.zafl --fast + +# test setting of entry point via function name +build_zafl gzip.entrypoint.zafl -o "zax:--entrypoint=main" +test_zafl ./gzip.entrypoint.zafl --best + +# test non-STARS version +build_zafl gzip.nostars.zafl +test_zafl ./gzip.nostars.zafl +test_zafl ./gzip.nostars.zafl --fast +test_zafl ./gzip.nostars.zafl --best + +# test STARS version +build_zafl gzip.stars.zafl -o zax:--stars +test_zafl ./gzip.stars.zafl +test_zafl ./gzip.stars.zafl --fast +test_zafl ./gzip.stars.zafl --best + +# test STARS version on AFL +log_message "Fuzz for $AFL_TIMEOUT seconds" +fuzz_with_zafl ./gzip.stars.zafl + +log_success "all tests passed: zafl instrumentation operational on gzip" + +cleanup +popd diff --git a/zfuzz/test/ls.zuntracer/test_ls.sh b/zfuzz/test/ls.zuntracer/test_ls.sh new file mode 100755 index 0000000000000000000000000000000000000000..eb4b6ef93844b18061a1014f12a57df1d64a50b0 --- /dev/null +++ b/zfuzz/test/ls.zuntracer/test_ls.sh @@ -0,0 +1,127 @@ +AFL_TIMEOUT=15 +session=/tmp/tmp.ls.$$ +TMP_FILE_1="${session}/ls.tmp.$$" + +mkdir -p $session + +cleanup() +{ + rm -fr /tmp/ls.tmp* ls*.zafl peasoup_exec*.ls* zafl_in zafl_out ${session} +} + +log_error() +{ + echo "TEST FAIL: $1" + exit 1 +} + +log_message() +{ + echo "TEST MSG: $1" +} + +log_success() +{ + echo "TEST PASS: $1" +} + +setup() +{ + echo "hello" > $TMP_FILE_1 +} + +build_zuntracer() +{ + ls_zafl=$1 + shift + zafl.sh `which ls` $ls_zafl --untracer --tempdir analysis.${ls_zafl} $* + if [ ! $? -eq 0 ]; then + log_error "$ls_zafl: unable to generate zafl version" + else + log_message "$ls_zafl: built successfully" + fi + + grep ATTR analysis.${ls_zafl}/logs/zax.log + if [ ! $? -eq 0 ]; then + log_error "$ls_zafl: no attributes or zax.log file produced" + fi + + if [ ! -f analysis.${ls_zafl}/zax.map ]; then + log_error "$ls_zafl: zax.map file not found" + fi +} + +test_zuntracer() +{ + ls_zafl=$( realpath $1 ) + shift + $ls_zafl $* $TMP_FILE_1 + if [ ! $? -eq 0 ]; then + log_error "$ls_zafl $*: unable to ls file using zafl version" + fi +} + +fuzz_with_zafl() +{ + ls_zafl=$1 + shift + + # setup AFL directories + mkdir zafl_in + echo "1" > zafl_in/1 + + if [ -d zafl_out ]; then + rm -fr zafl_out + fi + + # run for 30 seconds + LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib/ timeout $AFL_TIMEOUT afl-fuzz -i zafl_in -o zafl_out -- $ls_zafl $* + if [ $? -eq 124 ]; then + if [ ! -e zafl_out/fuzzer_stats ]; then + log_error "$ls_zafl: something went wrong with afl -- no fuzzer stats file" + fi + + cat zafl_out/fuzzer_stats + execs_per_sec=$( grep execs_per_sec zafl_out/fuzzer_stats ) + log_success "$ls_zafl $*: ran zafl binary: $execs_per_sec" + else + log_error "$ls_zafl $*: unable to run with afl" + fi + +} + +pushd ${session} +setup + +# zuntracer, don't break critical edges +build_zuntracer ls.untracer.no_critical_edge -C +test_zuntracer ./ls.untracer.no_critical_edge -lt + +# zuntracer, do break critical edges +build_zuntracer ls.untracer.critical_edge -c +test_zuntracer ./ls.untracer.critical_edge -lt + +# zuntracer, do break critical edges, optimize graph +build_zuntracer ls.untracer.critical_edge.graph -c -g -M +test_zuntracer ./ls.untracer.critical_edge.graph -lt + +log_message "Fuzz zuntracer (basic block coverage) for $AFL_TIMEOUT seconds" +fuzz_with_zafl ./ls.untracer.no_critical_edge -lt + +log_message "Fuzz zuntracer (break critical edges) for $AFL_TIMEOUT seconds" +fuzz_with_zafl ./ls.untracer.critical_edge -lt + +log_message "Fuzz zuntracer (break critical edges + graph optimization) for $AFL_TIMEOUT seconds" +fuzz_with_zafl ./ls.untracer.critical_edge.graph -lt + +build_zuntracer ls.untracer.fixed -m +test_zuntracer ./ls.untracer.fixed -lt +build_zuntracer ls.untracer.fixed.0x10000 -m 0x10000 +test_zuntracer ./ls.untracer.fixed.0x10000 -lt + +fuzz_with_zafl ./ls.untracer.fixed.0x10000 -lt + +log_success "all tests passed: zafl/zuntracer instrumentation operational on ls" + +cleanup +popd diff --git a/zfuzz/test/od/test_od.sh b/zfuzz/test/od/test_od.sh new file mode 100755 index 0000000000000000000000000000000000000000..67d2f75d0aceabbb799d8cf356ab845b75fbb1d5 --- /dev/null +++ b/zfuzz/test/od/test_od.sh @@ -0,0 +1,71 @@ +export AFL_TIMEOUT=15 +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib/:. + +session=/tmp/tmp.od.$$ + +cleanup() +{ + rm -fr /tmp/od.tmp* od*.zafl peasoup_exec*.od* zafl_in zafl_out $session +} + +log_error() +{ + echo "TEST FAIL: $1" + exit 1 +} + +log_message() +{ + echo "TEST MSG: $1" +} + +log_success() +{ + echo "TEST PASS: $1" +} + +fuzz_with_zafl() +{ + od_zafl=$1 + + # setup AFL directories + mkdir zafl_in + echo "1" > zafl_in/1 + + if [ -d zafl_out ]; then + rm -fr zafl_out + fi + + # run for 30 seconds + timeout $AFL_TIMEOUT afl-fuzz -i zafl_in -o zafl_out -- $od_zafl @@ + if [ $? -eq 124 ]; then + if [ ! -e zafl_out/fuzzer_stats ]; then + log_error "$od_zafl: something went wrong with afl -- no fuzzer stats file" + fi + + cat zafl_out/fuzzer_stats + execs_per_sec=$( grep execs_per_sec zafl_out/fuzzer_stats ) + log_success "$od_zafl: $execs_per_sec" + else + log_error "$od_zafl: unable to run with afl" + fi + +} + +mkdir $session +pushd $session + +# build ZAFL (with graph optimizations) version of od executable +zafl.sh `which od` od.zafl.d.g -d -g --tempdir analysis.od.zafl.d.g +if [ $? -eq 0 ]; then + log_success "build od.zafl.d.g" +else + log_error "build od.zafl.d.g" +fi +grep ATTR analysis.od.zafl.d.g/logs/zax.log + +log_message "Fuzz rida.zafl for $AFL_TIMEOUT secs" +fuzz_with_zafl $(realpath ./od.zafl.d.g) + +cleanup +popd diff --git a/zfuzz/test/sha256sum/test_sha256sum.sh b/zfuzz/test/sha256sum/test_sha256sum.sh new file mode 100755 index 0000000000000000000000000000000000000000..5fccb52600cd9b3f7bcaa05c275a91e8b075d9c5 --- /dev/null +++ b/zfuzz/test/sha256sum/test_sha256sum.sh @@ -0,0 +1,89 @@ +export AFL_TIMEOUT=15 +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib/:. + +session=/tmp/tmp.sha256sum.$$ + +cleanup() +{ + rm -fr /tmp/sha256sum.tmp* sha256sum*.zafl peasoup_exec*.sha256sum* zafl_in zafl_out $session +} + +log_error() +{ + echo "TEST FAIL: $1" + exit 1 +} + +log_message() +{ + echo "TEST MSG: $1" +} + +log_success() +{ + echo "TEST PASS: $1" +} + +fuzz_with_zafl() +{ + sha256sum_zafl=$1 + + # setup AFL directories + mkdir zafl_in + echo "1" > zafl_in/1 + + if [ -d zafl_out ]; then + rm -fr zafl_out + fi + + # run for 30 seconds + timeout $AFL_TIMEOUT afl-fuzz -i zafl_in -o zafl_out -- $sha256sum_zafl + if [ $? -eq 124 ]; then + if [ ! -e zafl_out/fuzzer_stats ]; then + log_error "$sha256sum_zafl: something went wrong with afl -- no fuzzer stats file" + fi + + cat zafl_out/fuzzer_stats + execs_per_sec=$( grep execs_per_sec zafl_out/fuzzer_stats ) + log_success "$sha256sum_zafl: $execs_per_sec" + else + log_error "$sha256sum_zafl: unable to run with afl" + fi + +} + +mkdir $session +pushd $session + +# build ZAFL (no Ida) version of sha256sum executable +zafl.sh `which sha256sum` sha256sum.rida.zafl --tempdir analysis.sha256sum +if [ $? -eq 0 ]; then + log_success "build sha256sum.rida.zafl" +else + log_error "build sha256sum.rida.zafl" +fi +grep ATTR analysis.sha256sum/logs/zax.log +if [ ! $? -eq 0 ]; then + log_error "zax transform did not run" +fi + +log_message "Fuzz rida.zafl for $AFL_TIMEOUT secs" +fuzz_with_zafl $(realpath ./sha256sum.rida.zafl) + +# build ZAFL (with Ida) version of sha256sum executable +zafl.sh `which sha256sum` sha256sum.ida.zafl --tempdir analysis.sha256sum.ida +if [ $? -eq 0 ]; then + log_success "build sha256sum.ida.zafl" +else + log_error "build sha256sum.ida.zafl" +fi +grep ATTR analysis.sha256sum.ida/logs/zax.log +if [ ! $? -eq 0 ]; then + log_error "zax transform did not run" +fi + +log_message "Fuzz ida.zafl for $AFL_TIMEOUT secs" +fuzz_with_zafl $(realpath ./sha256sum.ida.zafl) + +cleanup +popd diff --git a/zfuzz/test/spec2006/test_spec.sh b/zfuzz/test/spec2006/test_spec.sh new file mode 100755 index 0000000000000000000000000000000000000000..b792652d3a49d0f4a3c8c233ba168d222af190a2 --- /dev/null +++ b/zfuzz/test/spec2006/test_spec.sh @@ -0,0 +1,240 @@ +#!/bin/bash + +# the bad boys +#benchmarks=" +# 400.perlbench +# 403.gcc +# 445.gobmk +# 450.soplex +# 453.povray +# 458.sjeng +# 464.h264ref +# 465.tonto +# 471.omnetpp +# 481.wrf +# 482.sphinx3 +# 483.xalancbmk +# " + +# all +all_benchmarks="400.perlbench 401.bzip2 403.gcc 410.bwaves 416.gamess 429.mcf 433.milc 434.zeusmp 435.gromacs 436.cactusADM 437.leslie3d 444.namd 445.gobmk 450.soplex 453.povray 454.calculix 456.hmmer 458.sjeng 459.GemsFDTD 462.libquantum 464.h264ref 465.tonto 470.lbm 471.omnetpp 473.astar 481.wrf 482.sphinx3 483.xalancbmk" +#all_benchmarks=" 403.gcc " +#all_benchmarks=" 401.bzip2 " + + +# should be 3 for reportable run +number=1 + +setup() +{ + + if [ ! -d spec2006 ]; then + #svn co ^/spec2006/trunk spec2006 + git clone --depth 1 http://git.zephyr-software.com/allzp/spec2006.git spec2006 + fi + + if [[ ! -f /usr/bin/gfortran ]]; then + sudo apt-get install gfortran -y + fi + + cd spec2006/ + if [ ! -L bin ]; then + ln -s bin.power/ bin + fi + source shrc + bin/relocate +} + + +run_test() +{ + config_name=$1 + config=$2 + benchmarks="$3" + cd $SPEC + if [ ! -d result.$config_name ]; then + dropdb $PGDATABASE + createdb $PGDATABASE + $PEASOUP_HOME/tools/db/pdb_setup.sh + rm -Rf result/* + runspec --action scrub --config $config $benchmarks + + echo + echo "**************************************************************************" + echo "Starting test of $config_name" + echo "**************************************************************************" + echo + runspec --action validate --config $config -n $number $benchmarks + cp benchspec/CPU2006/*/exe/* result + mv result result.$config_name + for bench in $benchmarks + do + mv benchspec/CPU2006/$bench/run/build*/peasoup*/logs result.$config_name/$bench.log + done + fi + +} + +get_size_result() +{ + bench=$1 + if [ -e $bench ]; then + size=$(stat --printf="%s" $bench) + #echo -n "$size" + #LC_ALL= numfmt --grouping $size + #LC_ALL= printf "%'d" $size + #LC_NUMERIC=en_US printf "%'d" $size + #LC_NUMERIC=en_US printf "%'f" $size + #LC_NUMERIC=en_US printf "%'.f" $size + #LC_NUMERIC=en_US printf "%'10.10f" $size + #LC_NUMERIC=en_US /usr/bin/printf "%'d" $size + echo $size + else + echo -n "0" + fi +} + +get_result() +{ + bench=$1 + config=$2 + + results=$(cat $SPEC/result.$config/CPU2006.002.log|grep Success|grep $bench|grep ratio=|sed 's/.*ratio=//'|sed 's/,.*//') + + sum=0 + count=0 + for res in $results + do + sum=$(echo $sum + $res | bc) + count=$(echo $count + 1 | bc) + done + #echo sum=$sum + #echo count=$count + res=$(echo "scale=2; $sum / $count" | bc 2> /dev/null ) + + count=$(echo $res|wc -w) + + if [ $count = 1 ]; then + echo -n $res + else + echo -n "0" + fi + +} + + +get_raw_results() +{ + get_raw_perf_results "$@" + get_raw_size_results "$@" + #get_raw_fde_results "$@" +} + +get_raw_perf_results() +{ + configs=$* + first_config=$1 + echo "--------------------------------------------------------------" + echo "Performance results are:" + echo "--------------------------------------------------------------" + echo benchmark $configs + for bench in $benchmarks + do + echo -n "$bench " + for config in $* + do + get_result $bench $config + echo -n " " + done + echo + done +} + +get_raw_size_results() +{ + echo "--------------------------------------------------------------" + echo "Size results are:" + echo "--------------------------------------------------------------" + configs=$* + echo benchmark $configs + for bench in $SPEC/result.$first_config/*_base.amd64-m64-gcc42-nn + do + echo -n "$(basename $bench _base.amd64-m64-gcc42-nn) " + for config in $* + do + if [[ $config == "baseline" ]]; then + file="$SPEC/result.$config/$(basename $bench)" + cp $file /tmp/foo.exe + strip /tmp/foo.exe + file="/tmp/foo.exe" + else + file="$SPEC/result.$config/$(basename $bench)" + fi + res=$(get_size_result $file) + + #printf "%15s" $res + echo -n " $res" + done + echo + done + +} + +get_raw_fde_results() +{ + echo "--------------------------------------------------------------" + echo "FDE results are:" + echo "--------------------------------------------------------------" + configs=$* + echo benchmark $configs + for bench in $SPEC/result.$first_config/*_base.amd64-m64-gcc42-nn + do + #printf "%-20s" $(basename $bench _base.amd64-m64-gcc42-nn) + echo -n $(basename $bench _base.amd64-m64-gcc42-nn) + for config in $* + do + file="$SPEC/result.$config/$(basename $bench)" + res=$(readelf -w $file |grep FDE|wc -l ) + #if [[ $config == "baseline" ]]; then + #else + #fi + + #printf "%15s" $res + echo -n " $res" + done + echo + done + +} + +main() +{ + zipr_flags=" --backend zipr --step-option zipr:--add-sections --step-option zipr:true" + trace_flags=" --step-option zipr:--traceplacement:on --step-option zipr:true" + relax_flags=" --step-option zipr:--relax:on --step-option zipr:true --step-option zipr:--unpin:on --step-option zipr:false" + nounpin_flags=" --step-option zipr:--unpin:on --step-option zipr:false" + split_flags=" --step-option fill_in_indtargs:--split-eh-frame " + icall_flags=" --step-option fix_calls:--no-fix-icalls " + p1flags=" -c p1transform=on " + zafl_flags=" --backend zipr -s meds_static=off -s rida=on -c move_globals=on -c zax=on -o move_globals:--elftables-only " + zafl_opt_flags="--backend zipr -s meds_static=off -s rida=on -c move_globals=on -c zax=on -o move_globals:--elftables-only -o zipr:--traceplacement:on -o zax:--stars " + start_dir=$(pwd) + setup + + # baseline + run_test baseline $SPEC/config/ubuntu14.04lts-64bit.cfg "$all_benchmarks" + + # should be 100% success, tested by jdh on 4/11/18 as 100% success. + PSOPTS="$zipr_flags " run_test zipr $SPEC/config/ubuntu14.04lts-64bit-withps.cfg "$all_benchmarks" + + PSOPTS="$zafl_flags " run_test zafl $SPEC/config/ubuntu14.04lts-64bit-withps.cfg "$all_benchmarks" + +# PSOPTS="$zafl_opt_flags " run_test zafl $SPEC/config/ubuntu14.04lts-64bit-withps.cfg "$all_benchmarks" + + get_raw_results baseline zipr zafl +} + +main "$@" + + + diff --git a/zfuzz/test/spec2017/test_spec17.sh b/zfuzz/test/spec2017/test_spec17.sh new file mode 100755 index 0000000000000000000000000000000000000000..97a4daf85965ae910704779ada7a3a2548d3b869 --- /dev/null +++ b/zfuzz/test/spec2017/test_spec17.sh @@ -0,0 +1,234 @@ +#!/bin/bash + +run_size="test" + +all_benchmarks=" +600.perlbench_s 602.gcc_s 603.bwaves_s 605.mcf_s 607.cactuBSSN_s 619.lbm_s 620.omnetpp_s 621.wrf_s 623.xalancbmk_s 625.x264_s 627.cam4_s 628.pop2_s 631.deepsjeng_s 638.imagick_s 641.leela_s 644.nab_s 648.exchange2_s 649.fotonik3d_s 654.roms_s 657.xz_s 996.specrand_fs 998.specrand_is +" + +all_benchmarks=" +502.gcc_s 625.x264_s +" + + + +number=1 + +setup() +{ + + if [ ! -d spec2017 ]; then + #svn co ^/spec2017/trunk spec2017 + git clone --depth 1 git@git.zephyr-software.com:allzp/spec2017.git spec2017 + fi + + if [[ ! -f /usr/bin/gfortran ]]; then + sudo apt-get install gfortran gcc g++ -y + fi + + cd spec2017/ + source shrc +} + + +run_test() +{ + local config_name="$1" + local config="$2" + local benchmarks="$3" + + tests_that_ran="$tests_that_ran $config_name" + + cd $SPEC + if [ ! -d result.$config_name ]; then + bash -x $PEASOUP_UMBRELLA_DIR/postgres_reset.sh + rm -Rf result/* + runcpu --action scrub --config $config $benchmarks + + echo + echo "**************************************************************************" + echo "Starting test of $config_name" + echo "**************************************************************************" + echo + #runspec --action validate --config $config -n $number $benchmarks + runcpu --config $config --iterations $number --size $run_size --copies=8 --parallel_test_workload $run_size --noreportable $benchmarks + + cp benchspec/CPU/*/exe/* result + mv result result.$config_name + for bench in $benchmarks + do + mv benchspec/CPU/$bench/build/build*/peasoup*/logs result.$config_name/$bench.log + done + fi + +} + +get_size_result() +{ + bench=$1 + if [ -e $bench ]; then + size=$(stat --printf="%s" $bench) + #echo -n "$size" + #LC_ALL= numfmt --grouping $size + #LC_ALL= printf "%'d" $size + #LC_NUMERIC=en_US printf "%'d" $size + #LC_NUMERIC=en_US printf "%'f" $size + #LC_NUMERIC=en_US printf "%'.f" $size + #LC_NUMERIC=en_US printf "%'10.10f" $size + #LC_NUMERIC=en_US /usr/bin/printf "%'d" $size + echo $size + else + echo -n "0" + fi +} + +get_result() +{ + bench=$1 + config=$2 + + results=$(cat $SPEC/result.$config/CPU2017.002.log|grep Success|grep $bench|grep ratio=|sed 's/.*ratio=//'|sed 's/,.*//') + + sum=0 + count=0 + for res in $results + do + sum=$(echo $sum + $res | bc) + count=$(echo $count + 1 | bc) + done + #echo sum=$sum + #echo count=$count + res=$(echo "scale=2; $sum / $count" | bc 2> /dev/null ) + + count=$(echo $res|wc -w) + + if [ $count = 1 ]; then + echo -n $res + else + echo -n "0" + fi + +} + + +get_raw_results() +{ + get_raw_perf_results $tests_that_ran + get_raw_size_results $tests_that_ran + #get_raw_fde_results $tests_that_ran +} + +get_raw_perf_results() +{ + configs="$*" + first_config=$1 + + + echo "--------------------------------------------------------------" + echo "Performance results are:" + echo "--------------------------------------------------------------" + echo benchmark $configs + for bench in $all_benchmarks + do + echo -n "$bench " + for config in $* + do + get_result $bench $config + echo -n " " + done + echo + done +} + +get_raw_size_results() +{ + echo "--------------------------------------------------------------" + echo "Size results are:" + echo "--------------------------------------------------------------" + configs=$* + echo benchmark $configs + for bench in $SPEC/result.$first_config/*mytest-m64 + do + echo -n "$(basename $bench _base.mytest-m64) " + for config in $* + do + if [[ $config == "baseline" ]]; then + file="$SPEC/result.$config/$(basename $bench)" + cp $file /tmp/foo.exe + strip /tmp/foo.exe + file="/tmp/foo.exe" + else + file="$SPEC/result.$config/$(basename $bench)" + fi + res=$(get_size_result $file) + + #printf "%15s" $res + echo -n " $res" + done + echo + done + +} + +get_raw_fde_results() +{ + echo "--------------------------------------------------------------" + echo "FDE results are:" + echo "--------------------------------------------------------------" + configs=$* + echo benchmark $configs + for bench in $SPEC/result.$first_config/*mytest-m64 + do + #printf "%-20s" $(basename $bench _base.amd64-m64-gcc42-nn) + echo -n $(basename $bench _base.amd64-m64-gcc42-nn) + for config in $* + do + file="$SPEC/result.$config/$(basename $bench)" + res=$(readelf -w $file |grep FDE|wc -l ) + #if [[ $config == "baseline" ]]; then + #else + #fi + + #printf "%15s" $res + echo -n " $res" + done + echo + done + +} + +main() +{ + local zipr_flags=" --backend zipr --step-option zipr:--add-sections --step-option zipr:true" + local rida_flags=" -c rida " + local trace_flags=" --step-option zipr:--traceplacement:on --step-option zipr:true" + local relax_flags=" --step-option zipr:--relax:on --step-option zipr:true --step-option zipr:--unpin:on --step-option zipr:false" + local nounpin_flags=" --step-option zipr:--unpin:on --step-option zipr:false" + local split_flags=" --step-option fill_in_indtargs:--split-eh-frame " + local icall_flags=" --step-option fix_calls:--no-fix-icalls " + local p1flags=" -c p1transform=on " + local zafl_flags=" " + local zafl_domgraphflags=" -d " + + # sets $SPEC + setup + + local nops_config=$SPEC/config/ubuntu14.cfg + local withps_config=$SPEC/config/ubuntu14_withps.cfg + + + # baseline + run_test baseline $SPEC/config/ubuntu14.cfg "$all_benchmarks" + + PSOPTS="$zipr_flags $rida_flags " run_test zipr.rida $withps_config "$all_benchmarks" + + PS="zafl.sh" PSOPTS="$zafl_flags " run_test zafl.vanilla $withps_config "$all_benchmarks" + PS="zafl.sh" PSOPTS="$zafl_domgraph_flags " run_test zafl.domgraph $withps_config "$all_benchmarks" + + get_raw_results +} + +main "$@" + + + diff --git a/zfuzz/test/strings/test_strings.sh b/zfuzz/test/strings/test_strings.sh new file mode 100755 index 0000000000000000000000000000000000000000..513e8b3fa6d0f30431db2dbbf9c55876c0e9ad9f --- /dev/null +++ b/zfuzz/test/strings/test_strings.sh @@ -0,0 +1,78 @@ +export AFL_TIMEOUT=15 +export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$SECURITY_TRANSFORMS_HOME/lib/:. + +session=/tmp/tmp.$(whoami).zafl.strings.$$ + +cleanup() +{ + rm -fr $session +} + +log_error() +{ + echo "TEST FAIL: $1" + cleanup + exit 1 +} + +log_message() +{ + echo "TEST MSG: $1" +} + +log_success() +{ + echo "TEST PASS: $1" +} + +fuzz_with_zafl() +{ + strings_zafl=$1 + + # setup AFL directories + mkdir zafl_in + echo "1" > zafl_in/1 + + if [ -d zafl_out ]; then + rm -fr zafl_out + fi + + # run for 30 seconds + timeout $AFL_TIMEOUT afl-fuzz -i zafl_in -o zafl_out -- $strings_zafl + if [ $? -eq 124 ]; then + if [ ! -e zafl_out/fuzzer_stats ]; then + log_error "$strings_zafl: something went wrong with afl -- no fuzzer stats file" + fi + + cat zafl_out/fuzzer_stats + execs_per_sec=$( grep execs_per_sec zafl_out/fuzzer_stats ) + log_success "$strings_zafl: $execs_per_sec" + else + log_error "$strings_zafl: unable to run with afl" + fi + +} + +loc_afl=$(which afl-fuzz) +if [ -z "$loc_afl" ]; then + which afl-fuzz + log_error "afl-fuzz not found" +fi + +mkdir $session +pushd $session + +# build ZAFL version of strings executable +zafl.sh `which strings` strings.zafl.d.g -d -g --tempdir analysis.strings.zafl +if [ $? -eq 0 ]; then + log_success "build strings.zafl.d.g" +else + log_error "build strings.zafl.d.g" +fi +grep ATTR analysis.strings.zafl.d.g/logs/zax.log + +log_message "Fuzz for $AFL_TIMEOUT secs" +fuzz_with_zafl $(realpath ./strings.zafl.d.g) + +cleanup +popd diff --git a/zfuzz/util/afl_setup_core_pattern.sh b/zfuzz/util/afl_setup_core_pattern.sh new file mode 100755 index 0000000000000000000000000000000000000000..7b0ddd6d6002ba2db467d46dcdaf76e5f183d0c2 --- /dev/null +++ b/zfuzz/util/afl_setup_core_pattern.sh @@ -0,0 +1,3 @@ +#!/bin/sh + +echo core >/proc/sys/kernel/core_pattern diff --git a/zfuzz/util/setup-afl.sh b/zfuzz/util/setup-afl.sh new file mode 100755 index 0000000000000000000000000000000000000000..67393044df7a2282b2c5f293da47a02ee4bf0304 --- /dev/null +++ b/zfuzz/util/setup-afl.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +echo +echo "Building Fuzzing Support ($ZFUZZ_HOME)" +echo + +if [ -z "$ZFUZZ_HOME" ]; then + echo "error: environment var $ZFUZZ_HOME is undefined" + exit 1 +fi + +cd $ZFUZZ_HOME + +afl_loc=$(which afl-fuzz) +if [ -z "$afl_loc" ]; then + echo + echo Setup AFL + echo + wget http://lcamtuf.coredump.cx/afl/releases/afl-latest.tgz + tar -xzvf afl-latest.tgz && rm afl-latest.tgz + if [ -d afl ]; then + rm -fr afl + fi + mv afl-* afl + cd afl + make + sudo make install +# cd qemu_mode && ./build_qemu_support.sh + + # afl wants this + sudo $ZFUZZ_HOME/util/afl_setup_core_pattern.sh +fi