added test to repository

This commit is contained in:
Ludwig Lehnert 2025-02-26 18:48:49 +01:00
parent 3ee1405265
commit 30ea9163c9
14 changed files with 521 additions and 0 deletions

3
.clangd Executable file
View File

@ -0,0 +1,3 @@
CompileFlags:
Add:
- "-I/var/home/ludwig/git/windows-binary-fuzzing/irdb-sdk/include"

6
.gitignore vendored Executable file
View File

@ -0,0 +1,6 @@
*.o
*.so
*.a
plugins_install/
.sconsign.dblite

0
.gitmodules vendored Normal file → Executable file
View File

36
SConscript Executable file
View File

@ -0,0 +1,36 @@
import os
#
# import the environment and clone it so we can make changes.
#
Import('env')
irdb_env = env.Clone()
#
# These settings are recommended, but you can choose what you like
#
# be strict about syntax/warnings
irdb_env.Append(CXXFLAGS=" -Wall -Werror -fmax-errors=2 ")
# link against core and transform libraries
irdb_env.Append(LIBS=Split("irdb-core irdb-transform"))
# be able to include the SDK files
irdb_env.Append(CPPPATH=" $IRDB_SDK/include ")
# this is where the libraries are.
irdb_env.Append(LIBPATH=Split(" $IRDB_LIBS "))
# this is where to place plugins.
irdb_env.Replace(INSTALL_PATH=os.environ['PWD']+"/plugins_install")
#
# export the new environment for children sub-conscripts
#
Export('irdb_env')
#
# include the children sconscript files.
#
libs = irdb_env.SConscript("./transforms/SConscript")
#
# And we are done
#
Return('libs')

33
SConstruct Executable file
View File

@ -0,0 +1,33 @@
import os
env = Environment()
pwd = os.getcwd()
env.Replace(TRANSFORMS_HOME=os.path.join(pwd, "transforms"))
env.Replace(IRDB_SDK=os.path.join(pwd, "irdb-sdk"))
env.Replace(IRDB_LIBS=os.environ['IRDB_LIBS'])
env.Replace(debug=ARGUMENTS.get("debug", 0))
env.Append(CXXFLAGS=" -std=c++11 ")
env.Append(LINKFLAGS=" -Wl,-unresolved-symbols=ignore-in-shared-libs ")
if int(env['debug']) == 1:
env.Append(CFLAGS=" -g ")
env.Append(CXXFLAGS=" -g ")
env.Append(LINKFLAGS=" -g ")
env.Append(SHLINKFLAGS=" -g ")
else:
env.Append(CFLAGS=" -O ")
env.Append(CXXFLAGS=" -O ")
env.Append(LINKFLAGS=" -O ")
env.Append(SHLINKFLAGS=" -O ")
Export('env')
SConscript("SConscript", variant_dir='build')

36
build/SConscript Executable file
View File

@ -0,0 +1,36 @@
import os
#
# import the environment and clone it so we can make changes.
#
Import('env')
irdb_env = env.Clone()
#
# These settings are recommended, but you can choose what you like
#
# be strict about syntax/warnings
irdb_env.Append(CXXFLAGS=" -Wall -Werror -fmax-errors=2 ")
# link against core and transform libraries
irdb_env.Append(LIBS=Split("irdb-core irdb-transform"))
# be able to include the SDK files
irdb_env.Append(CPPPATH=" $IRDB_SDK/include ")
# this is where the libraries are.
irdb_env.Append(LIBPATH=Split(" $IRDB_LIBS "))
# this is where to place plugins.
irdb_env.Replace(INSTALL_PATH=os.environ['PWD']+"/plugins_install")
#
# export the new environment for children sub-conscripts
#
Export('irdb_env')
#
# include the children sconscript files.
#
libs = irdb_env.SConscript("./transforms/SConscript")
#
# And we are done
#
Return('libs')

23
build/transforms/SConscript Executable file
View File

@ -0,0 +1,23 @@
# import and create a copy of the environment so we don't change
# anyone else's env.
Import('irdb_env')
myenv = irdb_env.Clone()
#
# set input files and output program name
#
files = Glob(Dir('.').srcnode().abspath+"/*.cpp")
pgm_name = "initialize_stack.exe"
#
# build, install and return the program by default.
#
pgm = irdb_env.Program(pgm_name, files)
install = myenv.Install("$INSTALL_PATH/", pgm)
Default(install)
#
# and we're done
#
Return('install')

Binary file not shown.

3
container Executable file
View File

@ -0,0 +1,3 @@
#!/bin/bash
podman run --rm -it -v $(pwd):/work:z -w /work git.zephyr-software.com:4567/opensrc/zipr/zipr-bin iagree

1
set_env_vars Executable file
View File

@ -0,0 +1 @@
export PSPATH=$PSPATH:$TRANSFORMS_HOME/plugins_install

23
transforms/SConscript Executable file
View File

@ -0,0 +1,23 @@
# import and create a copy of the environment so we don't change
# anyone else's env.
Import('irdb_env')
myenv = irdb_env.Clone()
#
# set input files and output program name
#
files = Glob(Dir('.').srcnode().abspath+"/*.cpp")
pgm_name = "initialize_stack.exe"
#
# build, install and return the program by default.
#
pgm = irdb_env.Program(pgm_name, files)
install = myenv.Install("$INSTALL_PATH/", pgm)
Default(install)
#
# and we're done
#
Return('install')

147
transforms/initialize_stack.cpp Executable file
View File

@ -0,0 +1,147 @@
#include "initialize_stack.hpp"
#include <algorithm>
#include <fstream>
#include <math.h>
#define ALLOF(a) begin(a), end(a)
using namespace std;
using namespace IRDB_SDK;
using namespace InitStack;
//
// constructor
//
InitStack_t::InitStack_t(FileIR_t *p_variantIR,
const string &p_functions_filename, int p_init_value,
bool p_verbose)
: Transform_t(p_variantIR), // initialize the Transform class so things like
// insertAssembly and getFileIR() can be used
m_init_value(p_init_value), // member variable inits, these will vary
// depending on your transform's objectives
m_verbose(p_verbose), m_num_transformed(0) {
// check whether to read in a list of functions to transform
if (p_functions_filename == "") {
cout << "Auto-initialize all functions" << endl;
m_funcs_to_init =
getFileIR()->getFunctions(); // use all functions from the IR
} else {
cout << "Auto-initialize functions specified in: " << p_functions_filename
<< endl;
readFunctionsFromFile(p_functions_filename); // read functions from file
}
}
//
// read list of functions to auto-initialize
//
// post conditions: set of functions to auto-initialize
//
void InitStack_t::readFunctionsFromFile(const string &p_filename) {
// get all functions for readability of the rest of the code
const auto &all_funcs = getFileIR()->getFunctions();
// open input file and check for successful open
ifstream functionsFile(
p_filename); // can't use auto decl here because of lack of copy
// constructor in ifstream class
if (!functionsFile.is_open())
throw runtime_error("Cannot open " + p_filename);
// read each line of the input file.
auto line = string();
while (functionsFile >> line) {
// locate a function with the name read from the file.
const auto func_it = find_if(ALLOF(all_funcs), [&](const Function_t *f) {
return f->getName() == line;
});
// if found, log and insert it into the set to transform
if (func_it != end(all_funcs)) {
auto f = *func_it;
cout << "Adding " << f->getName() << " to function list" << endl;
m_funcs_to_init.insert(f);
}
}
}
//
// Execute the transform by transforming all to-transform functions
//
// 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
//
//
bool InitStack_t::execute() {
// transform all functions
for (auto f : m_funcs_to_init)
initStack(f);
// #ATTRIBUTE is a convention used to help find useful information in log
// files
cout << "#ATTRIBUTE InitStack::num_transformed=" << m_num_transformed << endl;
return m_num_transformed > 0; // true means success
}
//
// preconditions : f is not NULL
// postconditions: stack auto-initialized if stack frame size > 0
//
void InitStack_t::initStack(Function_t *f) {
// preconditions
assert(f != nullptr);
// check that the frame size is in the area we care about
const auto frame_size = f->getStackFrameSize();
// nothing to init.
if (frame_size == 0)
return;
const auto num_locs = static_cast<uint64_t>(ceil(frame_size / 4.0));
// debug output
cout << "Function: " << f->getName()
<< " frame size: " << f->getStackFrameSize() << endl;
// not all functions have an entry point
const auto entry = f->getEntryPoint();
if (!entry)
return;
// log what we are doing
cout << "Function: " << f->getName() << " auto-initialize " << dec << num_locs
<< " stack memory locations (4 bytes at a time) with value = " << hex
<< m_init_value << endl;
// determine the registers to use on x86-32 or x86-64
const auto sp_reg =
getFileIR()->getArchitectureBitWidth() == 64 ? "rsp" : "esp";
const auto scratch_reg =
getFileIR()->getArchitectureBitWidth() == 64 ? "r11" : "ecx";
// Now, do the dirty work of inserting new assembly to initialize the stack.
// Insert these instructions at the start of the function (to initialize the
// stack frame before the function runs) Assume: flags are dead at function
// entry. Future work: Verify this is true using dead register list. Note: we
// spill a scratch register into the red zone at 120 bytes passed the end of
// the frame
const auto newInsns = insertAssemblyInstructionsBefore(
entry,
string() + " mov [%%1 + %%2], %%3\n"
" mov %%3, -%%4\n"
"L1: mov dword [%%1 + %%3 * 4 - 4], %%5\n"
" inc %%3\n"
" jnz 0\n"
" mov %%3, [%%1 + %%2]\n",
{sp_reg, to_string(-f->getStackFrameSize() - 120), scratch_reg,
to_string(num_locs), to_string(m_init_value)});
newInsns[4]->setTarget(newInsns[2]); // Link jnz to L1.
// bump stats
m_num_transformed++;
}

61
transforms/initialize_stack.hpp Executable file
View File

@ -0,0 +1,61 @@
#pragma once
#include <irdb-core>
#include <irdb-transform>
//
// Put the transform in its own namespace
// just to keep the header files easy to read.
// This is not an IRDB transform requirement, just good coding practice.
//
namespace InitStack {
using namespace std;
using namespace IRDB_SDK;
//
// This class handles initializing stack frames to a constant value
//
// Note: Using private inheritence here for "principle of minimum access",
// but you can choose what's best for your needs.
//
class InitStack_t : private Transform_t {
public:
// construct an object
InitStack_t(
FileIR_t *p_variantIR, // the FileIR object to transform
const string &p_function_filename, // the name of a file with functions to
// transform. "" -> no file and
// transform all functions
int init_value = 0, // the value to write when initializing the stack
bool p_verbose = false // use verbose logging?
);
// execute the transform
// input: m_funcs_to_init the set of functions to transform, the fileIR to
// transform output: the transformed fileIR, with extra instructions to init
// stack frames return value: true -> success, false -> fail
bool execute();
private:
// methods
// read in the given file full of function names to transform (called from
// constructor) input: the filename and FileIR to transform output:
// m_funcs_to_init with the functions listed in the file
void readFunctionsFromFile(const string &p_filename);
// initialize the stack for a given function
// input: the fileIR to transform
// output: the transformed fileIR
void initStack(Function_t *f);
// data
set<Function_t *> m_funcs_to_init; // the functions whose stacks this object
// should initialize
int m_init_value; // the value with which to init the stack.
bool m_verbose; // do verbose logging
int m_num_transformed; // stats about how many functions that this object has
// transformed
};
} // namespace InitStack

View File

@ -0,0 +1,149 @@
#include "initialize_stack.hpp"
#include <getopt.h>
using namespace std;
using namespace IRDB_SDK;
using namespace InitStack;
//
// Print usage info
//
void usage(char *p_name) {
cerr << "Usage: " << p_name << " <variant_id>\n";
cerr << "\t[--functions <file> | -f <file>] Read in the functions to "
"auto-initialize "
<< endl;
cerr << "\t[--initvalue <value> | -i <value>] Specify stack "
"initialization value (default=0)"
<< endl;
cerr << "\t[--verbose | -v] Verbose mode "
" "
<< endl;
cerr << "\t[--help,--usage,-?,-h] Display this message "
" "
<< endl;
}
//
// The entry point for a stand-alone executable transform.
// Note: Thanos-enabled transforms are easier to write, faster to execute, and
// generally preferred. Stand-alone transforms may be useful if the transform
// has issues with memory leaks and/or memory errors. Memory issues in a stand
// alone transform cannot affect correctness of other transforms.
//
int main(int argc, char **argv) {
//
// Sanity check that the command line has at least a variant ID, otherwise we
// won't know what variant to operate on.
//
if (argc < 2) {
usage(argv[0]);
exit(1);
}
// constant parameters read from argv
const auto program_name = string(argv[0]);
const auto variantID = atoi(argv[1]);
// initial values of parameters to parse
auto verbose = false;
auto funcs_filename = string();
auto init_value = 0;
// declare some options for the transform
const char *short_opts = "f:i:v?h";
struct option long_options[] = {{"functions", required_argument, 0, 'f'},
{"initvalue", required_argument, 0, 'i'},
{"verbose", no_argument, 0, 'v'},
{"help", no_argument, 0, 'h'},
{"usage", no_argument, 0, '?'},
{0, 0, 0, 0}};
// parse the options in a standard getopts_long loop
while (true) {
int c = getopt_long(argc, argv, short_opts, long_options, nullptr);
if (c == -1)
break;
switch (c) {
case 'f':
funcs_filename = optarg;
cout << "Reading file with function specifiers: " << funcs_filename
<< endl;
break;
case 'i':
init_value = strtoll(optarg, NULL, 0);
cout << " Stack initialization value: " << hex << init_value << endl;
break;
case 'v':
verbose = true;
break;
case '?':
case 'h':
usage(argv[0]);
exit(1);
break;
default:
break;
}
}
// stand alone transforms must setup the interface to the sql server
auto pqxx_interface = pqxxDB_t::factory();
BaseObj_t::setInterface(pqxx_interface.get());
// stand alone transforms must create and read a variant ID from the database
auto pidp = VariantID_t::factory(variantID);
assert(pidp->isRegistered() == true);
// stand alone transforms must create and read the main file's IR from the
// datatbase
auto this_file = pidp->getMainFile();
auto url = this_file->getURL();
// declare for later so we can return the right value
auto success = false;
// now try to load the IR and execute a transform
try {
// Create and download the file's IR.
// Note: this is achieved differently with thanos-enabled plugins
auto firp = FileIR_t::factory(pidp.get(), this_file);
// sanity
assert(firp && pidp);
// log
cout << "Transforming " << this_file->getURL() << endl;
// create and invoke the transform
InitStack_t is(firp.get(), funcs_filename, init_value, verbose);
success = is.execute();
// conditionally write the IR back to the database on success
if (success) {
cout << "Writing changes for " << url << endl;
// Stand alone trnasforms must manually write the IR back to the IRDB and
// commit the transactions
firp->writeToDB();
// and commit the the transaction to postgres
pqxx_interface->commit();
} else {
cout << "Skipping write back on failure. " << url << endl;
}
} catch (const DatabaseError_t &db_error) {
// log any databse errors that might come up in the transform process
cerr << program_name << ": Unexpected database error: " << db_error
<< "file url: " << url << endl;
} catch (...) {
// log any other errors
cerr << program_name << ": Unexpected error file url: " << url << endl;
}
//
// return success code to driver (as a shell-style return value). 0=success,
// 1=warnings, 2=errors
//
return success ? 0 : 2;
}