Merge branch 'tobias' Apply Code cleanup and restructuring with new config file to main branch.
This commit is contained in:
@ -1,97 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Script to automate the preparation steps for an L4Sys experiment
|
||||
#
|
||||
# The L4Sys experiment manual lists four steps that need to be performed
|
||||
# in order to launch an L4Sys campaign. This script automates the final
|
||||
# three steps.
|
||||
#
|
||||
# Usage: Follow the manual. Find injection addresses and CR3 value and
|
||||
# adjust experimentInfo.hpp accordingly. Then run this script.
|
||||
#
|
||||
# TODO: Adjust $FAILDIR to your needs!
|
||||
|
||||
BAK=experimentInfo.hpp.bak
|
||||
CFG=experimentInfo.hpp
|
||||
FAIL_CMD="fail-client -q" # -rc bochs-dbg.rc"
|
||||
FAILDIR=/home/doebel/src/fail
|
||||
DBNAME=fail
|
||||
FAIL_CMD="fail-client" # -rc bochs-dbg.rc"
|
||||
FAIL_ARGS="-f bochsrc-bd -q"
|
||||
FAILDIR=/home/tstumpf/code/fail
|
||||
BUILDDIR=/home/tstumpf/obj/fail
|
||||
DBNAME=failtobias
|
||||
BINDIR=~/local/bin
|
||||
|
||||
if [ -n "$1" ] ; then
|
||||
if [ "$1" = "mem" ]; then
|
||||
TYPE=$1
|
||||
|
||||
echo -e "\033[32;1m=========================================================================================="
|
||||
echo "[$(date)] Prepare FI-Experiment ...."
|
||||
echo -e "==========================================================================================\033[0m"
|
||||
|
||||
if [ -n $TYPE ] ; then
|
||||
if [ $TYPE = "mem" ]; then
|
||||
IMPORTER=MemoryImporter;
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ "$IMPORTER" = "MemoryImporter" ]; then
|
||||
echo "Preparing memory injection experiment."
|
||||
else
|
||||
elif [ $TYPE = "reg" ]; then
|
||||
IMPORTER=RegisterImporter;
|
||||
echo "Preparing register injection experiment."
|
||||
else
|
||||
echo "Specified experiment type not knwon";
|
||||
exit
|
||||
fi
|
||||
else
|
||||
echo "Specify your experiment type (mem/reg)"
|
||||
exit
|
||||
fi
|
||||
|
||||
function buildfail {
|
||||
echo -e "\033[33mCompiling....\033[0m"
|
||||
cd $FAILDIR/build
|
||||
FAIL_BUILD_PARALLEL=16 $FAILDIR/scripts/rebuild-bochs.sh - >/dev/null
|
||||
cd -
|
||||
}
|
||||
|
||||
function BuildNRun {
|
||||
buildfail
|
||||
echo -e "\033[33mRunning...\033[0m"
|
||||
$FAIL_CMD $@
|
||||
}
|
||||
|
||||
# backup experiment config
|
||||
cp $CFG $BAK
|
||||
|
||||
blink_addr=$(nm -C fiasco.image| grep blink | cut -d\ -f 1)
|
||||
longjmp_addr=$(nm -C fiasco.image| grep longjmp | cut -d\ -f 1)
|
||||
|
||||
#echo -e "\033[35;1m[$(date)] ================== Step 0: Getting CR3 =================\033[0m"
|
||||
#cat $BAK | sed -e 's/PREPARATION_STEP.*/PREPARATION_STEP 4/' >$CFG
|
||||
#buildfail
|
||||
#cr3=`$FAIL_CMD -f bochsrc-bd 2>/dev/null | grep CR3 | sed -e 's/ //g' | cut -d\= -f 2`
|
||||
#echo \#defne L4SYS_ADDRESS_SPACE 0x$cr3
|
||||
#cat $BAK | sed -e "s/L4SYS_ADDRESS_SPACE .*/L4SYS_ADDRESS_SPACE 0x$cr3/" >$CFG
|
||||
|
||||
mv $CFG $BAK
|
||||
|
||||
cat $BAK | sed -e "s/L4SYS_BREAK_BLINK .*/L4SYS_BREAK_BLINK 0x$blink_addr/" >$CFG
|
||||
mv $CFG $BAK
|
||||
cat $BAK | sed -e "s/L4SYS_BREAK_LONGJMP .*/L4SYS_BREAK_LONGJMP 0x$longjmp_addr/" >$CFG
|
||||
mv $CFG $BAK
|
||||
|
||||
echo -e "\033[35;1m[$(date)] ================== Step 1: Generating Snapshot =================\033[0m"
|
||||
cat $BAK | sed -e 's/PREPARATION_STEP.*/PREPARATION_STEP 1/' >$CFG
|
||||
BuildNRun -f bochsrc-bd
|
||||
|
||||
echo -e "\033[35;1m[$(date)] ================== Step 2: Get Instruction Count ===============\033[0m"
|
||||
cat $BAK | sed -e 's/PREPARATION_STEP.*/PREPARATION_STEP 2/' >$CFG
|
||||
buildfail
|
||||
|
||||
echo -e "\033[32mRunning...\033[0m"
|
||||
values=`$FAIL_CMD 2>/dev/null | grep instructions\; | sed -e 's/.*after \(.*\) instructions;\(.*\) accepted/\1 \2/'`
|
||||
echo $values
|
||||
filtered_instr=`echo $values | cut -d\ -f 2`
|
||||
total_instr=`echo $values | cut -d\ -f 1`
|
||||
|
||||
echo -e "\033[35;1m[$(date)] ================== Step 3: Golden Run ==========================\033[0m"
|
||||
cat $BAK | sed -e 's/PREPARATION_STEP.*/PREPARATION_STEP 3/' >$CFG
|
||||
BuildNRun
|
||||
|
||||
# now get ready to rumble...
|
||||
echo -e "\033[35;1m[$(date)] ================== Step 4: Build Injection Client ==============\033[0m"
|
||||
cat $BAK | sed -e "s/L4SYS_NUMINSTR.*/L4SYS_NUMINSTR $filtered_instr/" >$BAK.2
|
||||
cat $BAK.2 | sed -e "s/L4SYS_TOTINSTR.*/L4SYS_TOTINSTR $total_instr/" >$BAK.3
|
||||
cat $BAK.3 | sed -e "s/PREPARATION_STEP.*/PREPARATION_STEP 0/">$CFG
|
||||
rm $BAK $BAK.2 $BAK.3
|
||||
buildfail
|
||||
|
||||
echo -e "\033[35;1m[$(date)] ================== Step 5: Trace Import/Prune ==============\033[0m"
|
||||
import-trace --importer $IMPORTER -e fiasco.image -d $DBNAME -t trace.pb
|
||||
prune-trace -d $DBNAME
|
||||
$BINDIR/fail-client -Wf,--step=all -Wf,--blink_addr=$blink_addr -Wf,--longjmp_addr=$longjmp_addr $FAIL_ARGS
|
||||
$BINDIR/import-trace --importer $IMPORTER -e fiasco.image -d $DBNAME -t trace.pb
|
||||
$BINDIR/prune-trace -d $DBNAME
|
||||
|
||||
echo -e "\033[32;1m=========================================================================================="
|
||||
echo "[$(date)] Preparations are finished. Happy injecting...."
|
||||
|
||||
@ -14,14 +14,15 @@ set(MY_CAMPAIGN_SRCS
|
||||
aluinstr.cc
|
||||
experiment.hpp
|
||||
experiment.cc
|
||||
experimentFI.cc
|
||||
experimentParameter.cc
|
||||
experimentPreparation.cc
|
||||
campaign.hpp
|
||||
campaign.cc
|
||||
UDIS86.hpp
|
||||
UDIS86.cc
|
||||
InstructionFilter.hpp
|
||||
InstructionFilter.cc
|
||||
conversion.hpp
|
||||
conversion.cc
|
||||
)
|
||||
|
||||
#### PROTOBUFS ####
|
||||
|
||||
@ -1,164 +1,20 @@
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#include "campaign.hpp"
|
||||
#include "experimentInfo.hpp"
|
||||
#include "conversion.hpp"
|
||||
#include "comm/DatabaseCampaignMessage.pb.h"
|
||||
#include "experiment.hpp"
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "util/Logger.hpp"
|
||||
#include "sal/SALConfig.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
char const * const results_csv = "l4sys.csv";
|
||||
|
||||
extern L4SysConversion l4sysResultConversion;
|
||||
extern L4SysConversion l4sysExperimentConversion;
|
||||
extern L4SysConversion l4sysRegisterConversion;
|
||||
|
||||
#if 0
|
||||
bool L4SysCampaign::run() {
|
||||
Logger log("L4SysCampaign");
|
||||
|
||||
ifstream test(results_csv);
|
||||
if (test.is_open()) {
|
||||
log << results_csv << " already exists" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
ofstream results(results_csv);
|
||||
if (!results.is_open()) {
|
||||
log << "failed to open " << results_csv << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
log << "startup" << endl;
|
||||
|
||||
int count = 0;
|
||||
srand(time(NULL));
|
||||
|
||||
for (int i = 0; i < L4SYS_NUMINSTR; ++i) {
|
||||
for (int r = 1; r < 9; ++r) {
|
||||
for (int b = 0; b < 32; ++b) {
|
||||
L4SysExperimentData *d = new L4SysExperimentData;
|
||||
d->msg.set_exp_type(d->msg.GPRFLIP);
|
||||
d->msg.set_register_offset(static_cast<L4SysProtoMsg_RegisterType>(r));
|
||||
d->msg.set_instr_offset(i);
|
||||
d->msg.set_bit_offset(b);
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < 20000; ++i) {
|
||||
L4SysExperimentData *d = new L4SysExperimentData;
|
||||
d->msg.set_exp_type(d->msg.GPRFLIP);
|
||||
// affect a random register
|
||||
int reg_offset = rand() % 8 + 1;
|
||||
d->msg.set_register_offset(
|
||||
static_cast<L4SysProtoMsg_RegisterType>(reg_offset));
|
||||
// modify for a random instruction
|
||||
int instr_offset = rand() % L4SYS_NUMINSTR;
|
||||
d->msg.set_instr_offset(instr_offset);
|
||||
// modify a random bit
|
||||
int bit_offset = rand() % 32;
|
||||
d->msg.set_bit_offset(bit_offset);
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
for (int i = 0; i < 20000; ++i) {
|
||||
L4SysExperimentData *d = new L4SysExperimentData;
|
||||
d->msg.set_exp_type(d->msg.ALUINSTR);
|
||||
// modify for a random instruction
|
||||
int instr_offset = rand() % L4SYS_NUMINSTR;
|
||||
d->msg.set_instr_offset(instr_offset);
|
||||
// this value is not required for this experiment, so set it to an arbitrary value
|
||||
d->msg.set_bit_offset(0);
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
for (int i = 0; i < 20000; ++i) {
|
||||
L4SysExperimentData *d = new L4SysExperimentData;
|
||||
d->msg.set_exp_type(d->msg.IDCFLIP);
|
||||
// modify for a random instruction
|
||||
int instr_offset = rand() % L4SYS_NUMINSTR;
|
||||
d->msg.set_instr_offset(instr_offset);
|
||||
// modify a random bit - Bochs supports at most 15 bytes of instruction
|
||||
int bit_offset = rand() % 125;
|
||||
d->msg.set_bit_offset(bit_offset);
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
for (int i = 0; i < 20000; ++i) {
|
||||
L4SysExperimentData *d = new L4SysExperimentData;
|
||||
d->msg.set_exp_type(d->msg.RATFLIP);
|
||||
// modify for a random instruction
|
||||
int instr_offset = rand() % L4SYS_NUMINSTR;
|
||||
d->msg.set_instr_offset(instr_offset);
|
||||
// this value is not required for this experiment, so set it to an arbitrary value
|
||||
d->msg.set_bit_offset(0);
|
||||
|
||||
campaignmanager.addParam(d);
|
||||
++count;
|
||||
}
|
||||
|
||||
campaignmanager.noMoreParameters();
|
||||
log << "done enqueueing parameter sets (" << count << ")." << endl;
|
||||
|
||||
// collect results
|
||||
L4SysExperimentData *res;
|
||||
int rescount = 0;
|
||||
results
|
||||
<< "exp_type,injection_ip,register,instr_offset,injection_bit,resulttype,resultdata,output,details"
|
||||
<< endl;
|
||||
while ((res = static_cast<L4SysExperimentData *>(campaignmanager.getDone()))) {
|
||||
rescount++;
|
||||
|
||||
results << l4sysExperimentConversion.output(res->msg.exp_type())
|
||||
<< "," << hex << res->msg.injection_ip() << dec << ",";
|
||||
if (res->msg.has_register_offset())
|
||||
results << l4sysRegisterConversion.output(res->msg.register_offset());
|
||||
else
|
||||
results << "None";
|
||||
results << "," << res->msg.instr_offset() << "," << res->msg.bit_offset()
|
||||
<< ","
|
||||
<< l4sysResultConversion.output(res->msg.resulttype()) << ","
|
||||
<< res->msg.resultdata();
|
||||
if (res->msg.has_output())
|
||||
results << "," << res->msg.output();
|
||||
if (res->msg.has_details())
|
||||
results << "," << res->msg.details();
|
||||
results << endl;
|
||||
delete res;
|
||||
}
|
||||
|
||||
log << "done. sent " << count << " received " << rescount << endl;
|
||||
results.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
using namespace google::protobuf;
|
||||
|
||||
void L4SysCampaign::cb_send_pilot(DatabaseCampaignMessage p)
|
||||
{
|
||||
#if 0
|
||||
int inj_instr = p.injection_instr();
|
||||
int data_addr = p.data_address();
|
||||
int reg = (data_addr >> 8) & 0xF;
|
||||
int width = (data_addr >> 4) & 0xF;
|
||||
int offs = data_addr & 0xF;
|
||||
#endif
|
||||
L4SysExperimentData *d = new L4SysExperimentData;
|
||||
d->msg.mutable_fsppilot()->CopyFrom(p);
|
||||
|
||||
if(!type.compare("mem")) {
|
||||
d->msg.set_exp_type(d->msg.MEM);
|
||||
} else if(!type.compare("reg")) {
|
||||
d->msg.set_exp_type(d->msg.GPRFLIP);
|
||||
//d->msg.set_exp_type(d->msg.MEM);
|
||||
campaignmanager.addParam(d);
|
||||
} else {
|
||||
log << "Specified FI-type not supported" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
fail::campaignmanager.addParam(d);
|
||||
}
|
||||
|
||||
@ -1,11 +1,16 @@
|
||||
#ifndef __L4SYS_CAMPAIGN_HPP__
|
||||
#define __L4SYS_CAMPAIGN_HPP__
|
||||
|
||||
|
||||
#include "cpn/DatabaseCampaign.hpp"
|
||||
#include "comm/ExperimentData.hpp"
|
||||
#include "l4sys.pb.h"
|
||||
#include <google/protobuf/descriptor.h>
|
||||
|
||||
#include "util/Logger.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
class L4SysExperimentData : public fail::ExperimentData {
|
||||
public:
|
||||
L4SysProtoMsg msg;
|
||||
@ -13,15 +18,15 @@ public:
|
||||
};
|
||||
|
||||
class L4SysCampaign : public fail::DatabaseCampaign {
|
||||
#if 0
|
||||
public:
|
||||
virtual bool run();
|
||||
#else
|
||||
virtual const google::protobuf::Descriptor * cb_result_message()
|
||||
{ return google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("L4SysProtoMsg"); }
|
||||
|
||||
virtual void cb_send_pilot(DatabaseCampaignMessage pilot);
|
||||
#endif
|
||||
|
||||
fail::Logger log; //<! the logger
|
||||
|
||||
public:
|
||||
std::string type;
|
||||
};
|
||||
|
||||
#endif // __L4SYS_CAMPAIGN_HPP__
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
#include "conversion.hpp"
|
||||
|
||||
char const *l4sys_output_result_strings[] = { "Unknown", "No effect", "Incomplete execution", "Crash", "Silent data corruption", "Error" };
|
||||
char const *l4sys_output_experiment_strings[] = { "Unknown", "GPR Flip", "RAT Flip", "IDC Flip", "ALU Instr Flip" };
|
||||
char const *l4sys_output_register_strings[] = { "Unknown", "EAX", "ECX", "EDX", "EBX", "ESP", "EBP", "ESI", "EDI" };
|
||||
|
||||
L4SysConversion l4sysResultConversion(
|
||||
l4sys_output_result_strings,
|
||||
sizeof(l4sys_output_result_strings));
|
||||
L4SysConversion l4sysExperimentConversion(
|
||||
l4sys_output_experiment_strings,
|
||||
sizeof(l4sys_output_experiment_strings));
|
||||
L4SysConversion l4sysRegisterConversion(
|
||||
l4sys_output_register_strings,
|
||||
sizeof(l4sys_output_register_strings));
|
||||
|
||||
@ -1,25 +0,0 @@
|
||||
#ifndef __L4SYS_CONVERSION_HPP__
|
||||
#define __L4SYS_CONVERSION_HPP__
|
||||
|
||||
#include "l4sys.pb.h"
|
||||
|
||||
class L4SysConversion {
|
||||
public:
|
||||
L4SysConversion(char const **strings, size_t array_size)
|
||||
: m_Strings(strings)
|
||||
{
|
||||
m_ElementCount = array_size / sizeof(char*);
|
||||
}
|
||||
char const * output(unsigned int res) {
|
||||
if (res == 0 || res >= m_ElementCount) {
|
||||
return m_Strings[0];
|
||||
} else {
|
||||
return m_Strings[res];
|
||||
}
|
||||
}
|
||||
private:
|
||||
char const **m_Strings;
|
||||
size_t m_ElementCount;
|
||||
};
|
||||
|
||||
#endif // __L4SYS_CONVERSION_HPP__
|
||||
File diff suppressed because it is too large
Load Diff
78
src/experiments/l4-sys/experiment.conf
Normal file
78
src/experiments/l4-sys/experiment.conf
Normal file
@ -0,0 +1,78 @@
|
||||
#experiment specific config
|
||||
address_space=0x1fd77000
|
||||
|
||||
# if address_space_trace is not set then we use address_space for tracing
|
||||
#address_space_trace=0x0
|
||||
|
||||
# specifies the range that needs to be captured to log program output properly
|
||||
func_entry=0x20000216
|
||||
func_exit=0x2000029c
|
||||
|
||||
# specifies the range that injections should be carried out on (should be a
|
||||
subset of the above)
|
||||
# if filter entry/exit is not set, then (filter_entry=func_entry) and
|
||||
# (filter_exit = func_exit), the filter range have to be a supset of
|
||||
# the function
|
||||
filter_entry=0x2000022f
|
||||
filter_exit=0x20000252
|
||||
|
||||
#default break_* value is 0
|
||||
#break_blink=0xdead
|
||||
#break_longjmp=0xdead
|
||||
#break_exit=0xdead
|
||||
|
||||
#experiment values calculated during preparation
|
||||
numinstr=16
|
||||
totinstr=58401
|
||||
|
||||
#default config
|
||||
emul_ips=5000000
|
||||
max_instr_bytes=15
|
||||
campain_server=localhost
|
||||
|
||||
#default files
|
||||
state_folder=l4sys.state
|
||||
instruction_list=ip.list
|
||||
golden_run=golden.out
|
||||
filter=filter.list
|
||||
trace=trace.pb
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@ -26,10 +26,9 @@ typedef struct TraceInstrType {
|
||||
unsigned bp_counter;
|
||||
} TraceInstr;
|
||||
|
||||
typedef std::vector<TraceInstr> TraceVector;
|
||||
|
||||
class L4SysExperiment : public fail::ExperimentFlow {
|
||||
private:
|
||||
class L4SysConfig;
|
||||
fail::JobClient m_jc; //!< the job client connecting to the campaign server
|
||||
fail::Logger log; //<! the logger
|
||||
L4SysExperimentData *param; //<! the parameter set currently in use by the client
|
||||
@ -74,63 +73,38 @@ private:
|
||||
* @returns a value for Bochs' eipBiased variable
|
||||
*/
|
||||
Bit32u eipBiased();
|
||||
/**
|
||||
* Parses a raw instruction into a bxInstruction_c structure.
|
||||
* This simple version of the function is taken from Bochs
|
||||
* where it is currently disabled due to the TRACE_CACHE option,
|
||||
* and has been modified to fit the needs of instruction modification.
|
||||
* @param instance a pointer to the current Bochs CPU
|
||||
* @param instr a pointer to the address the instruction is fetched from
|
||||
* @param iStorage an outgoing value which contains the parsed instruction
|
||||
* @returns \a false if the instruction continued on the following page in memory
|
||||
*/
|
||||
bx_bool fetchInstruction(BX_CPU_C *instance, const Bit8u *instr, bxInstruction_c *iStorage);
|
||||
/**
|
||||
* Write out the injection parameters to the logger.
|
||||
*/
|
||||
void logInjection();
|
||||
/**
|
||||
* Proceeds by one single instruction.
|
||||
* @param preserveAddressSpace if set, the address space of the next instruction
|
||||
* must match with the current address space
|
||||
* (for example, this is important when debugging in the kernel)
|
||||
* @returns the listener that was triggered, in case there were more than one
|
||||
*/
|
||||
fail::BaseListener *singleStep(bool preserveAddressSpace);
|
||||
/**
|
||||
* Injects a new instruction into the Bochs instruction stream and restores the previous one
|
||||
* @param oldInstr address of the instruction to be replaced
|
||||
* @param newInstr address of the instruction to replace it with
|
||||
*/
|
||||
void injectInstruction(bxInstruction_c *oldInstr, bxInstruction_c *newInstr);
|
||||
/**
|
||||
* Calculate the timeout of the current workload in milliseconds.
|
||||
*/
|
||||
unsigned calculateTimeout(unsigned instr_left);
|
||||
unsigned calculateTimeout(unsigned instr_left, unsigned ips);
|
||||
/**
|
||||
* Send back the experiment parameter set with a description of the error.
|
||||
*/
|
||||
void terminateWithError(std::string details, int reason, L4SysProtoMsg_Result*);
|
||||
/**
|
||||
* Run until L4SYS_FUNC_ENTRY and save state (experiment preparation,
|
||||
* phase 1)
|
||||
* Run until reaching the entry point of the experiment and save
|
||||
* state.
|
||||
*/
|
||||
void startAndSaveInitState(fail::BPSingleListener* bp);
|
||||
void CR3run(fail::BPSingleListener *bp);
|
||||
void runToStart(fail::BPSingleListener *bp);
|
||||
/**
|
||||
* Collect list of executed instructions, considering instruction
|
||||
* filtering if configured (experiment preparation, phase 2).
|
||||
* filtering if configured.
|
||||
*/
|
||||
void collectInstructionTrace(fail::BPSingleListener* bp);
|
||||
/**
|
||||
* Perform the golden run (experiment preparation, phase 3)
|
||||
* Perform the golden run.
|
||||
*/
|
||||
void goldenRun(fail::BPSingleListener* bp);
|
||||
|
||||
/**
|
||||
* Doing fault injection expiriments.
|
||||
*/
|
||||
void doExperiments(fail::BPSingleListener *bp);
|
||||
|
||||
/**
|
||||
* Check that all required setup has been done before an experiment run.
|
||||
*/
|
||||
void validatePrerequisites();
|
||||
void validatePrerequisites(std::string state, std::string output);
|
||||
|
||||
/**
|
||||
* Load job parameters for an experiment.
|
||||
@ -142,7 +116,7 @@ private:
|
||||
/**
|
||||
* Read the golden run output into the target string.
|
||||
*/
|
||||
void readGoldenRun(std::string& target);
|
||||
void readGoldenRun(std::string& target, std::string golden_run);
|
||||
|
||||
/*
|
||||
* Prepare memory experiment. Creates a breakpoint to run until the
|
||||
@ -167,8 +141,55 @@ private:
|
||||
*/
|
||||
void doRegisterInjection(int regDesc, int bit);
|
||||
|
||||
void setupFilteredBreakpoint(fail::BPSingleListener* bp, int instOffset, std::string instr_list);
|
||||
|
||||
void setupFilteredBreakpoint(fail::BPSingleListener* bp, int instOffset);
|
||||
/**
|
||||
* Updates a parameter of the config file or adds a new one
|
||||
* if the parameter was not specified.
|
||||
*/
|
||||
int updateConfig(std::string parameter, std::string value);
|
||||
|
||||
/**
|
||||
* Adds the value of the config file to the parameter list and
|
||||
* parse the parameter list. This function makes use of the
|
||||
* CommandLine Parser from the fail* framework.
|
||||
*/
|
||||
void parseOptions(L4SysConfig&);
|
||||
|
||||
/**
|
||||
* Configuration Setup.
|
||||
*/
|
||||
class L4SysConfig {
|
||||
public:
|
||||
unsigned long int max_instr_bytes;
|
||||
unsigned long int address_space;
|
||||
unsigned long int address_space_trace;
|
||||
unsigned long int func_entry;
|
||||
unsigned long int func_exit;
|
||||
unsigned long int filter_entry;
|
||||
unsigned long int filter_exit;
|
||||
unsigned long int break_blink;
|
||||
unsigned long int break_longjmp;
|
||||
unsigned long int break_exit;
|
||||
unsigned long int filter_instructions;
|
||||
unsigned long int emul_ips;
|
||||
std::string state_folder;
|
||||
std::string instruction_list;
|
||||
std::string alu_instructions;
|
||||
std::string golden_run;
|
||||
std::string filter;
|
||||
std::string trace;
|
||||
std::string campain_server;
|
||||
|
||||
unsigned long int numinstr;
|
||||
unsigned long int totinstr;
|
||||
|
||||
enum {NO_PREP, GET_CR3, CREATE_CHECKPOINT, COLLECT_INSTR_TRACE, GOLDEN_RUN, FULL_PREPARATION} step;
|
||||
};
|
||||
|
||||
private:
|
||||
L4SysConfig conf;
|
||||
};
|
||||
|
||||
|
||||
#endif // __L4SYS_EXPERIMENT_HPP__
|
||||
|
||||
450
src/experiments/l4-sys/experimentFI.cc
Normal file
450
src/experiments/l4-sys/experimentFI.cc
Normal file
@ -0,0 +1,450 @@
|
||||
#include <iostream>
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "InstructionFilter.hpp"
|
||||
#include "UDIS86.hpp"
|
||||
#include "aluinstr.hpp"
|
||||
#include "campaign.hpp"
|
||||
|
||||
#include <sal/bochs/BochsMemory.hpp>
|
||||
#include "sal/SALConfig.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
string L4SysExperiment::sanitised(const string &in_str) {
|
||||
string result;
|
||||
int in_str_size = in_str.size();
|
||||
result.reserve(in_str_size);
|
||||
for (int idx = 0; idx < in_str_size; idx++) {
|
||||
char cur_char = in_str[idx];
|
||||
unsigned cur_char_value = static_cast<unsigned>(cur_char);
|
||||
// also exclude the delimiter (',')
|
||||
if (cur_char_value < 0x20 || cur_char_value > 0x7E || cur_char_value == ',') {
|
||||
char str_nr[5];
|
||||
sprintf(str_nr, "\\%03o", cur_char_value);
|
||||
result += str_nr;
|
||||
} else {
|
||||
result += cur_char;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
BaseListener* L4SysExperiment::waitIOOrOther(bool clear_output) {
|
||||
IOPortListener ev_ioport(0x3F8, true);
|
||||
BaseListener* ev = NULL;
|
||||
if (clear_output)
|
||||
currentOutput.clear();
|
||||
while (true) {
|
||||
simulator.addListener(&ev_ioport);
|
||||
ev = simulator.resume();
|
||||
//log << "hello " << simulator.getListenerCount() << std::endl;
|
||||
//simulator.removeListener(&ev_ioport);
|
||||
if (ev == &ev_ioport) {
|
||||
currentOutput += ev_ioport.getData();
|
||||
//log << currentOutput << std::endl;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ev;
|
||||
}
|
||||
|
||||
unsigned L4SysExperiment::calculateTimeout(unsigned instr_left, unsigned ips) {
|
||||
// the timeout in seconds, plus one backup second (avoids rounding overhead)
|
||||
// [instr] / [instr / s] = [s]
|
||||
unsigned seconds = instr_left / ips + 1;
|
||||
// 1.1 (+10 percent) * 1000000 mus/s * [s]
|
||||
return 1100000 * seconds;
|
||||
}
|
||||
|
||||
|
||||
BaseListener* L4SysExperiment::afterInjection(L4SysProtoMsg_Result* res)
|
||||
{
|
||||
BaseListener *bl = 0;
|
||||
|
||||
simtime_t t_inject = simulator.getTimerTicks();
|
||||
simtime_t t_bailout;
|
||||
|
||||
ifstream instr_list_file(conf.instruction_list.c_str(), ios::binary);
|
||||
instr_list_file.seekg((1 + res->instr_offset()) * sizeof(TraceInstr));
|
||||
|
||||
RangeSetInstructionFilter filtering(conf.filter.c_str());
|
||||
|
||||
for (;;) {
|
||||
// Step over _all_ instructions in the trace AS
|
||||
BPSingleListener stepListener(ANY_ADDR, conf.address_space_trace);
|
||||
|
||||
TraceInstr curr_instr;
|
||||
instr_list_file.read(reinterpret_cast<char*>(&curr_instr),
|
||||
sizeof(TraceInstr));
|
||||
|
||||
t_bailout = simulator.getTimerTicks();
|
||||
|
||||
// step until next traced instruction
|
||||
simulator.addListener(&stepListener);
|
||||
bl = waitIOOrOther(false);
|
||||
|
||||
// bail out if we hit a listener other than the single step
|
||||
// one -> in this case the experiment is over prematurely
|
||||
if (bl != &stepListener) {
|
||||
// Note, the difference in this case is the diff between the
|
||||
// last correct instruction and the starting point -> this is
|
||||
// useful for TIMEOUT events where the actual time now would be
|
||||
// the complete TIMEOUT whereas we are interested in the time
|
||||
// until execution deviates from the original trace
|
||||
res->set_deviate_steps(t_bailout - t_inject);
|
||||
res->set_deviate_eip(-1);
|
||||
log << "bailing out of single-stepping mode" << endl;
|
||||
break;
|
||||
}
|
||||
|
||||
address_t eip = stepListener.getTriggerInstructionPointer();
|
||||
|
||||
if (!filtering.isValidInstr(eip))
|
||||
continue;
|
||||
|
||||
if (eip != curr_instr.trigger_addr) {
|
||||
// In the case where we see an actual instruction stream deviation, we
|
||||
// want the real diff between NOW and the injection start point
|
||||
t_bailout = simulator.getTimerTicks();
|
||||
log << "got " << hex << eip << " expected "
|
||||
<< curr_instr.trigger_addr << endl;
|
||||
|
||||
log << "mismatch found after " << (t_bailout - t_inject) << " instructions." << endl;
|
||||
res->set_deviate_steps(t_bailout - t_inject);
|
||||
res->set_deviate_eip(eip);
|
||||
|
||||
return waitIOOrOther(false);
|
||||
}
|
||||
}
|
||||
|
||||
log << "left single-stepping mode after " << (t_bailout - t_inject)
|
||||
<< " instructions." << endl;
|
||||
return bl;
|
||||
}
|
||||
|
||||
void L4SysExperiment::getJobParameters()
|
||||
{
|
||||
// get the experiment parameters
|
||||
log << "asking job server for experiment parameters" << endl;
|
||||
if (!m_jc.getParam(*param)) {
|
||||
log << "Dying." << endl;
|
||||
// communicate that we were told to die
|
||||
terminate(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L4SysExperiment::validatePrerequisites(std::string state, std::string output)
|
||||
{
|
||||
struct stat teststruct;
|
||||
if (stat(state.c_str(), &teststruct) == -1 ||
|
||||
stat(output.c_str(), &teststruct) == -1) {
|
||||
log << "Important data missing - call \"prepare\" first." << endl;
|
||||
terminate(10);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void L4SysExperiment::readGoldenRun(std::string& target, std::string golden_run)
|
||||
{
|
||||
ifstream golden_run_file(golden_run.c_str());
|
||||
|
||||
if (!golden_run_file.good()) {
|
||||
log << "Could not open file " << golden_run.c_str() << endl;
|
||||
terminate(20);
|
||||
}
|
||||
|
||||
target.assign((istreambuf_iterator<char>(golden_run_file)),
|
||||
istreambuf_iterator<char>());
|
||||
|
||||
golden_run_file.close();
|
||||
}
|
||||
|
||||
|
||||
void L4SysExperiment::setupFilteredBreakpoint(fail::BPSingleListener* bp, int instOffset, std::string instr_list)
|
||||
{
|
||||
/*
|
||||
* The L4Sys experiment uses instruction filtering to restrict the range
|
||||
* of fault injection to only e.g., kernel instructions.
|
||||
*
|
||||
* To speed up injection, L4Sys furthermore does not use per-instruction
|
||||
* breakpoints but only places a breakpoint on the actually interesting
|
||||
* instruction (e.g., the injection EIP). Hence, we also do not count
|
||||
* instructions from the beginning of the experiment, but we count how
|
||||
* often a certain EIP was hit before the injection.
|
||||
*
|
||||
* To achieve these properties, we use an additional trace file that
|
||||
* provides us with a 'hit counter' of each injection candidate. We use
|
||||
* the global instruction ID (DataBaseCampaign: instruction_offset) to
|
||||
* index into this trace file and determine the value for the breakpoint
|
||||
* counter.
|
||||
*/
|
||||
ifstream instr_list_file(instr_list.c_str(), ios::binary);
|
||||
|
||||
if (!instr_list_file.good()) {
|
||||
log << "Missing instruction trace" << endl;
|
||||
terminate(21);
|
||||
}
|
||||
|
||||
log << "inst offset " << dec << instOffset << " sizeof(TraceInstr) " << sizeof(TraceInstr) << endl;
|
||||
TraceInstr curr_instr;
|
||||
instr_list_file.seekg(instOffset * sizeof(TraceInstr));
|
||||
log << instr_list_file.eof() << " " << instr_list_file.bad() << " "
|
||||
<< instr_list_file.fail() << endl;
|
||||
if (instr_list_file.eof()) {
|
||||
log << "Job parameters indicate position outside the traced instruction list." << endl;
|
||||
terminate(1);
|
||||
}
|
||||
instr_list_file.read(reinterpret_cast<char*>(&curr_instr), sizeof(TraceInstr));
|
||||
instr_list_file.close();
|
||||
|
||||
log << "setting watchpoint at " << hex << curr_instr.trigger_addr << endl;
|
||||
bp->setWatchInstructionPointer(curr_instr.trigger_addr);
|
||||
log << "setting bp counter " << hex << curr_instr.bp_counter << endl;
|
||||
bp->setCounter(curr_instr.bp_counter);
|
||||
}
|
||||
|
||||
|
||||
fail::BPSingleListener*
|
||||
L4SysExperiment::prepareMemoryExperiment(int ip, int offset, int dataAddress)
|
||||
{
|
||||
fail::BPSingleListener *bp = new BPSingleListener(0, conf.address_space_trace);
|
||||
log << "\033[34;1mMemory fault injection\033[0m at instruction " << std::hex << offset
|
||||
<< ", ip " << ip << ", address " << dataAddress << std::endl;
|
||||
|
||||
setupFilteredBreakpoint(bp, offset, conf.instruction_list);
|
||||
assert(bp->getWatchInstructionPointer() == (address_t)(ip & 0xFFFFFFFF));
|
||||
return bp;
|
||||
}
|
||||
|
||||
|
||||
fail::BPSingleListener*
|
||||
L4SysExperiment::prepareRegisterExperiment(int ip, int offset, int dataAddress)
|
||||
{
|
||||
fail::BPSingleListener *bp = new BPSingleListener(0, conf.address_space_trace);
|
||||
|
||||
int reg, regOffset;
|
||||
reg = ((dataAddress >> 4) & 0xF) + 1; // regs start at 1
|
||||
regOffset = dataAddress & 0xF;
|
||||
|
||||
log << "\033[32;1mGPR bitflip\033[0m at instr. offset " << offset
|
||||
<< " reg data (" << reg << ", "
|
||||
<< regOffset << ")" << std::endl;
|
||||
|
||||
setupFilteredBreakpoint(bp, offset, conf.instruction_list);
|
||||
log << bp->getWatchInstructionPointer() << std::endl;
|
||||
log << ip << std::endl;
|
||||
assert(bp->getWatchInstructionPointer() == (address_t)(ip & 0xFFFFFFFF));
|
||||
log << bp->getCounter() << std::endl;
|
||||
return bp;
|
||||
}
|
||||
|
||||
|
||||
bool L4SysExperiment::doMemoryInjection(int address, int bit)
|
||||
{
|
||||
MemoryManager& mm = simulator.getMemoryManager();
|
||||
|
||||
// XXX: evil, but I need to bail out if memory access is invalid
|
||||
host_address_t addr = reinterpret_cast<BochsMemoryManager*>(&mm)->guestToHost(address);
|
||||
if (addr == (host_address_t)ADDR_INV)
|
||||
return false;
|
||||
|
||||
byte_t data = mm.getByte(address);
|
||||
byte_t newdata = data ^ (1 << bit);
|
||||
mm.setByte(address, newdata);
|
||||
log << "[" << std::hex << address << "] " << (int)data
|
||||
<< " -> " << (int)newdata << std::endl;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
void L4SysExperiment::doRegisterInjection(int regDesc, int bit)
|
||||
{
|
||||
int reg, offset;
|
||||
reg = (regDesc >> 4) + 1; // regs start at 1
|
||||
offset = regDesc & 0xF;
|
||||
|
||||
ConcreteCPU& cpu = simulator.getCPU(0);
|
||||
Register *reg_target = cpu.getRegister(reg - 1);
|
||||
regdata_t data = cpu.getRegisterContent(reg_target);
|
||||
regdata_t newdata = data ^ (1 << (bit + 8 * offset));
|
||||
cpu.setRegisterContent(reg_target, newdata);
|
||||
log << "Reg[" << reg << "]: " << std::hex << data << " -> "
|
||||
<< newdata << std::endl;
|
||||
}
|
||||
|
||||
void L4SysExperiment::terminateWithError(string details, int reason,
|
||||
L4SysProtoMsg_Result *r = 0) {
|
||||
L4SysProtoMsg_Result *result;
|
||||
|
||||
if (r)
|
||||
result = r;
|
||||
else
|
||||
result = param->msg.add_result();
|
||||
|
||||
result->set_resulttype(param->msg.UNKNOWN);
|
||||
result->set_resultdata(simulator.getCPU(0).getInstructionPointer());
|
||||
result->set_output(sanitised(currentOutput.c_str()));
|
||||
result->set_details(details);
|
||||
|
||||
m_jc.sendResult(*param);
|
||||
terminate(reason);
|
||||
}
|
||||
|
||||
void L4SysExperiment::doExperiments(fail::BPSingleListener* bp) {
|
||||
// LAST STEP: The actual experiment.
|
||||
validatePrerequisites(conf.state_folder, conf.golden_run);
|
||||
|
||||
// Read the golden run output for validation purposes
|
||||
std::string golden_run;
|
||||
readGoldenRun(golden_run, conf.golden_run);
|
||||
|
||||
getJobParameters();
|
||||
|
||||
int exp_type = param->msg.exp_type();
|
||||
int instr_offset = param->msg.fsppilot().injection_instr();
|
||||
int regData = param->msg.fsppilot().data_address();
|
||||
|
||||
if (exp_type == param->msg.MEM) {
|
||||
bp = prepareMemoryExperiment(param->msg.fsppilot().injection_instr_absolute(),
|
||||
param->msg.fsppilot().injection_instr(),
|
||||
param->msg.fsppilot().data_address());
|
||||
} else if (exp_type == param->msg.GPRFLIP) {
|
||||
bp = prepareRegisterExperiment(param->msg.fsppilot().injection_instr_absolute(),
|
||||
param->msg.fsppilot().injection_instr(),
|
||||
param->msg.fsppilot().data_address());
|
||||
} else {
|
||||
log << "Unsupported experiment type: " << exp_type << std::endl;
|
||||
terminate(1);
|
||||
}
|
||||
|
||||
assert(bp);
|
||||
|
||||
for (unsigned bit = 0; bit < 8; ++bit) {
|
||||
|
||||
L4SysProtoMsg_Result *result = param->msg.add_result();
|
||||
result->set_instr_offset(instr_offset);
|
||||
|
||||
simulator.clearListeners();
|
||||
|
||||
log << "Bit " << bit << ", restoring state." << endl;
|
||||
simulator.restore(conf.state_folder.c_str());
|
||||
log << " ... EIP = " << std::hex << simulator.getCPU(0).getInstructionPointer() << std::endl;
|
||||
|
||||
simulator.addListener(bp);
|
||||
|
||||
simtime_t now = simulator.getTimerTicks();
|
||||
fail::BaseListener *go = waitIOOrOther(true);
|
||||
assert(go == bp);
|
||||
|
||||
log << "Hit BP @ " << hex << bp->getTriggerInstructionPointer() << " " << bp->getWatchInstructionPointer()
|
||||
<< " Start time " << now << ", new time " << simulator.getTimerTicks()
|
||||
<< ", diff = " << simulator.getTimerTicks() - now << std::endl;
|
||||
|
||||
assert(bp->getTriggerInstructionPointer() == bp->getWatchInstructionPointer());
|
||||
result->set_injection_ip(bp->getTriggerInstructionPointer());
|
||||
|
||||
if (exp_type == param->msg.MEM) {
|
||||
result->set_bit_offset(bit);
|
||||
log << "injection addr: "
|
||||
<< std::hex << param->msg.fsppilot().data_address()
|
||||
<< std::endl;
|
||||
result->set_injection_address(param->msg.fsppilot().data_address());
|
||||
if (!doMemoryInjection(param->msg.fsppilot().data_address(), bit))
|
||||
{
|
||||
terminateWithError("invalid mem access", 51, result);
|
||||
}
|
||||
} else if (exp_type == param->msg.GPRFLIP) {
|
||||
int reg = (param->msg.fsppilot().data_address() >> 4) + 1;
|
||||
result->set_register_offset(static_cast<L4SysProtoMsg_RegisterType>(reg));
|
||||
result->set_bit_offset(bit + 8 * (param->msg.fsppilot().data_address() & 0xF));
|
||||
doRegisterInjection(param->msg.fsppilot().data_address(), bit);
|
||||
} else {
|
||||
log << "doing nothing for experiment type " << exp_type << std::endl;
|
||||
}
|
||||
|
||||
BPSingleListener ev_done(conf.func_exit, conf.address_space);
|
||||
simulator.addListener(&ev_done);
|
||||
|
||||
// Well-known bailout point -- if we hit L4SYS_BREAK_BLINK, which
|
||||
// is the entry of Vga::blink_cursor(), we know that we are in some
|
||||
// kind of error handler
|
||||
BPSingleListener ev_blink(conf.break_blink);
|
||||
simulator.addListener(&ev_blink);
|
||||
BPSingleListener ev_longjmp(conf.break_longjmp);
|
||||
simulator.addListener(&ev_longjmp);
|
||||
|
||||
//If we come to our own exit function, we can stop
|
||||
BPSingleListener ev_exit(conf.break_exit);
|
||||
simulator.addListener(&ev_exit);
|
||||
|
||||
unsigned instr_left = conf.totinstr - instr_offset; // XXX offset is in NUMINSTR, TOTINSTR is higher
|
||||
BPSingleListener ev_incomplete(ANY_ADDR, conf.address_space);
|
||||
/*
|
||||
* Use hard-coded value for incomplete counter. We are currently looking at short-running pieces
|
||||
* of code. This means that in the error case, where a lot of data is still to be printed to serial
|
||||
* line, the benchmark does not complete this within <short-time> * <1.x> cycles. Instead, we use
|
||||
* a frame large enough to catch some more output even at the end of a run.
|
||||
*/
|
||||
ev_incomplete.setCounter(2000000);
|
||||
simulator.addListener(&ev_incomplete);
|
||||
|
||||
/*
|
||||
* This timeout will always be at least one second - see calculateTimeout()
|
||||
*/
|
||||
TimerListener ev_timeout(calculateTimeout(instr_left, conf.emul_ips));
|
||||
simulator.addListener(&ev_timeout);
|
||||
log << "continue... (" << simulator.getListenerCount()
|
||||
<< " breakpoints, timeout @ " << ev_timeout.getTimeout()
|
||||
<< std::endl;
|
||||
|
||||
log << "TOListener " << (void*)&ev_timeout << " incompListener "
|
||||
<< (void*)&ev_incomplete << endl;
|
||||
BaseListener *ev = afterInjection(result);
|
||||
log << "afterInj: res.devstep = " << result->deviate_steps() << endl;
|
||||
|
||||
/* copying a string object that contains control sequences
|
||||
* unfortunately does not work with the library I am using,
|
||||
* which is why output is passed on as C string and
|
||||
* the string compare is done on C strings
|
||||
*/
|
||||
if (ev == &ev_done) {
|
||||
if (strcmp(currentOutput.c_str(), golden_run.c_str()) == 0) {
|
||||
log << "Result DONE" << endl;
|
||||
result->set_resulttype(param->msg.DONE);
|
||||
} else {
|
||||
log << "Result WRONG" << endl;
|
||||
result->set_resulttype(param->msg.WRONG);
|
||||
result->set_output(sanitised(currentOutput.c_str()));
|
||||
}
|
||||
} else if ((ev == &ev_incomplete) ||
|
||||
(ev == &ev_blink) ||
|
||||
(ev == &ev_longjmp)) {
|
||||
log << "Result INCOMPLETE" << endl;
|
||||
result->set_resulttype(param->msg.INCOMPLETE);
|
||||
result->set_resultdata(simulator.getCPU(0).getInstructionPointer());
|
||||
result->set_output(sanitised(currentOutput.c_str()));
|
||||
} else if (ev == &ev_timeout) {
|
||||
log << "Result TIMEOUT" << endl;
|
||||
result->set_resulttype(param->msg.TIMEOUT);
|
||||
result->set_resultdata(simulator.getCPU(0).getInstructionPointer());
|
||||
result->set_output(sanitised(currentOutput.c_str()));
|
||||
} else if (ev == &ev_exit) {
|
||||
log << "Result FAILSTOP" << endl;
|
||||
result->set_resulttype(param->msg.FAILSTOP);
|
||||
result->set_resultdata(simulator.getCPU(0).getInstructionPointer());
|
||||
result->set_output(sanitised(currentOutput.c_str()));
|
||||
} else {
|
||||
log << "Result WTF?" << endl;
|
||||
stringstream ss;
|
||||
ss << "eventid " << ev;
|
||||
terminateWithError(ss.str(), 50);
|
||||
}
|
||||
}
|
||||
|
||||
m_jc.sendResult(*param);
|
||||
}
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
#ifndef __L4SYS_EXPERIMENT_INFO_HPP__
|
||||
#define __L4SYS_EXPERIMENT_INFO_HPP__
|
||||
|
||||
// the maximum number of bytes in a Bochs instruction
|
||||
#define MAX_INSTR_BYTES 15
|
||||
|
||||
// the bounds of the program (space, instructions and time)
|
||||
// client
|
||||
#define L4SYS_ADDRESS_SPACE 0x1fd4c000
|
||||
// server
|
||||
#define L4SYS_ADDRESS_SPACE_TRACE L4SYS_ADDRESS_SPACE
|
||||
//#define L4SYS_ADDRESS_SPACE_TRACE 0x1fd4c000
|
||||
|
||||
// FUNC_{ENTRY,EXIT} specifies the range that needs to
|
||||
// be captured to log program output properly
|
||||
#define L4SYS_FUNC_ENTRY 0x20000220
|
||||
#define L4SYS_FUNC_EXIT 0x20000216
|
||||
// FILTER_{ENTRY,EXIT} specifies the range that injections
|
||||
// should be carried out on (should be a subset of the above)
|
||||
// and only works with FILTER_INSTRUCTIONS turned on
|
||||
#define L4SYS_FILTER_ENTRY 0x200002ba
|
||||
#define L4SYS_FILTER_EXIT 0x20000444
|
||||
|
||||
#define L4SYS_BREAK_BLINK 0xf004b800
|
||||
#define L4SYS_BREAK_LONGJMP 0xf004c88e
|
||||
|
||||
// select instruction filtering
|
||||
// XXX: this should be always on and the code should be
|
||||
// reworked to do the non-filtering work with an empty
|
||||
// filter list
|
||||
#define L4SYS_FILTER_INSTRUCTIONS 1
|
||||
|
||||
// kernel: 2377547, userland: 79405472
|
||||
#define L4SYS_NUMINSTR 27025
|
||||
#define L4SYS_TOTINSTR 189122
|
||||
#define L4SYS_BOCHS_IPS 5000000
|
||||
|
||||
// several file names used
|
||||
#define L4SYS_STATE_FOLDER "l4sys.state"
|
||||
#define L4SYS_INSTRUCTION_LIST "ip.list"
|
||||
#define L4SYS_ALU_INSTRUCTIONS "alu.list"
|
||||
#define L4SYS_CORRECT_OUTPUT "golden.out"
|
||||
#define L4SYS_FILTER "filter.list"
|
||||
|
||||
// flags
|
||||
// 0 - preparation complete
|
||||
// >0 - next step to execute
|
||||
#define PREPARATION_STEP 0
|
||||
|
||||
#endif // __L4SYS_EXPERIMENT_INFO_HPP__
|
||||
315
src/experiments/l4-sys/experimentParameter.cc
Normal file
315
src/experiments/l4-sys/experimentParameter.cc
Normal file
@ -0,0 +1,315 @@
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "experiment.hpp"
|
||||
|
||||
#include "util/CommandLine.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
#define EXPERIMENT_CONF "experiment.conf"
|
||||
|
||||
|
||||
static void parameterMissing(fail::Logger log, string c) {
|
||||
log << "Error: Missing config parameter (" << c << ")" << endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
|
||||
int L4SysExperiment::updateConfig(string parameter, string value) {
|
||||
|
||||
bool replaced = false;
|
||||
|
||||
std::list<std::string> buf;
|
||||
|
||||
ifstream is(EXPERIMENT_CONF);
|
||||
|
||||
if (is) {
|
||||
while(!is.eof()) {
|
||||
string tmp;
|
||||
getline(is, tmp);
|
||||
if( !tmp.compare(0, parameter.length() + 1, string(parameter + "=")) ) {
|
||||
buf.push_back(string(parameter + "=" + value) );
|
||||
replaced = true;
|
||||
} else {
|
||||
buf.push_back(tmp);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cerr << "Open config file failed" << endl;
|
||||
return -1;
|
||||
}
|
||||
is.close();
|
||||
|
||||
ofstream os(EXPERIMENT_CONF);
|
||||
|
||||
if (os) {
|
||||
if(!replaced)
|
||||
os << parameter << "=" << value << endl;
|
||||
|
||||
while(!buf.empty()) {
|
||||
os << buf.front() << endl;
|
||||
buf.pop_front();
|
||||
}
|
||||
} else {
|
||||
cerr << "Open config file for update failed" << endl;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void L4SysExperiment::parseOptions(L4SysConfig &conf) {
|
||||
CommandLine &cmd = CommandLine::Inst();
|
||||
|
||||
ifstream fin(EXPERIMENT_CONF);
|
||||
|
||||
//Currently we don't delete the parameterstring to avoid deling ptr in the cmd
|
||||
//Nevertheless, we shouldn't need them, so deleting should be safe
|
||||
|
||||
//We interpret the file content as parameters to our main function, so we don't
|
||||
//need an own parser. Program parameters appear earlier in the parameter list
|
||||
//and will overwrite the parameters from the config file.
|
||||
while(fin.good()) {
|
||||
string buffer;
|
||||
string prefix("--");
|
||||
|
||||
getline(fin, buffer, '\n');
|
||||
|
||||
if( !(strncmp(buffer.c_str(),"#",1) == 0) &&
|
||||
!(strncmp(buffer.c_str()," ",1) == 0 ) &&
|
||||
!(strncmp(buffer.c_str(),"",1) == 0 ) ) {
|
||||
string t(prefix + buffer);
|
||||
//Workarround, if we just alloc t.length some of the arguments values
|
||||
//disappear
|
||||
char *c = (char *) malloc(t.length() * 2);
|
||||
strcpy(c, t.c_str());
|
||||
cmd.add_args( c );
|
||||
}
|
||||
}
|
||||
|
||||
cout << "end of config file" << endl;
|
||||
|
||||
cmd.addOption("", "", Arg::None, "USAGE: fail-client -Wf,[option] -Wf,[option] ... <BochsOptions...>");
|
||||
CommandLine::option_handle HELP =
|
||||
cmd.addOption("h", "help", Arg::None, "-h,--help \tPrint usage and exit");
|
||||
CommandLine::option_handle STEP =
|
||||
cmd.addOption("s", "step", Arg::Optional, "-s,--step \tSpecify preparation step, without this argumnt fail-client start in experiment mode (cr3: get CR3, cc: Create Checkpoint, it: collect instruction trace, gr: golden run, all: do the whole preparation)");
|
||||
|
||||
CommandLine::option_handle OPT_MAX_INSTR_BYTES =
|
||||
cmd.addOption("", "max_instr_bytes", Arg::Optional, "--max_instr_bytes \t define MAX_INSTR_BYTES");
|
||||
CommandLine::option_handle OPT_ADDRESS_SPACE =
|
||||
cmd.addOption("", "address_space", Arg::Optional, "--address_space \t define L4SYS_ADDRESS_SPACE");
|
||||
CommandLine::option_handle OPT_ADDRESS_SPACE_TRACE =
|
||||
cmd.addOption("", "address_space_trace", Arg::Optional, "--address_space_trace \t define L4SYS_ADDRESS_SPACE_TRACE");
|
||||
CommandLine::option_handle OPT_FUNC_ENTRY =
|
||||
cmd.addOption("", "func_entry", Arg::Optional, "--func_entry \t define L4SYS_FUNC_ENTRY");
|
||||
CommandLine::option_handle OPT_FUNC_EXIT =
|
||||
cmd.addOption("", "func_exit", Arg::Optional, "--func_exit \t define L4SYS_FUNC_EXIT");
|
||||
CommandLine::option_handle OPT_FILTER_ENTRY =
|
||||
cmd.addOption("", "filter_entry", Arg::Optional, "--filter_entry \t define L4SYS_FILTER_ENTRY");
|
||||
CommandLine::option_handle OPT_FILTER_EXIT =
|
||||
cmd.addOption("", "filter_exit", Arg::Optional, "--filter_exit \t define L4SYS_FILTER_EXIT");
|
||||
CommandLine::option_handle OPT_BREAK_BLINK =
|
||||
cmd.addOption("", "break_blink", Arg::Optional, "--break_blink \t define L4SYS_BREAK_BLINK");
|
||||
CommandLine::option_handle OPT_BREAK_LONGJMP =
|
||||
cmd.addOption("", "break_longjmp", Arg::Optional, "--break_longjmp \t define L4SYS_BREAK_LONGJMP");
|
||||
CommandLine::option_handle OPT_BREAK_EXIT =
|
||||
cmd.addOption("", "break_exit", Arg::Optional, "--break_exit \t define L4SYS_BREAK_EXIT");
|
||||
CommandLine::option_handle OPT_NUMINSTR =
|
||||
cmd.addOption("", "numinstr", Arg::Optional, "--numinstr \t define L4SYS_NUMINSTR");
|
||||
CommandLine::option_handle OPT_TOTINSTR =
|
||||
cmd.addOption("", "totinstr", Arg::Optional, "--totinstr \t define L4SYS_TOTINSTR");
|
||||
CommandLine::option_handle OPT_EMUL_IPS =
|
||||
cmd.addOption("", "emul_ips", Arg::Optional, "--emul_ips \t define L4SYS_BOCHS_IPS");
|
||||
CommandLine::option_handle OPT_STATE_FOLDER =
|
||||
cmd.addOption("", "state_folder", Arg::Optional, "--state_folder \t define L4SYS_STATE_FOLDER");
|
||||
CommandLine::option_handle OPT_INSTRUCTION_LIST =
|
||||
cmd.addOption("", "instruction_list", Arg::Optional, "--instruction_list \t define L4SYS_INSTRUCTION_LIST");
|
||||
CommandLine::option_handle OPT_CORRECT_OUTPUT =
|
||||
cmd.addOption("", "golden_run", Arg::Optional, "--correct_output \t define L4SYS_CORRECT_OUTPUT");
|
||||
CommandLine::option_handle OPT_FILTER =
|
||||
cmd.addOption("", "filter", Arg::Optional, "--filter \t define L4SYS_FILTER");
|
||||
CommandLine::option_handle OPT_TRACE =
|
||||
cmd.addOption("", "trace", Arg::Optional, "--trace \t define outputfile for trace (default trace.pb)");
|
||||
CommandLine::option_handle OPT_CAMPAIN_SERVER =
|
||||
cmd.addOption("", "campain_server", Arg::Optional, "--campain_server \t specify the hostname of the campain server (default localhost)");
|
||||
|
||||
|
||||
if (!cmd.parse()) {
|
||||
cerr << "Error parsing arguments." << endl;
|
||||
simulator.terminate(1);
|
||||
} else if (cmd[HELP]) {
|
||||
cmd.printUsage();
|
||||
simulator.terminate(0);
|
||||
}
|
||||
|
||||
if (cmd[OPT_MAX_INSTR_BYTES]) {
|
||||
conf.max_instr_bytes = strtol(cmd[OPT_MAX_INSTR_BYTES].arg, NULL, 10);
|
||||
log << "max_instr_bytes: "<< dec << conf.max_instr_bytes << endl;
|
||||
} else {
|
||||
parameterMissing(log, "max_instr_bytes");
|
||||
}
|
||||
|
||||
if (cmd[OPT_ADDRESS_SPACE]) {
|
||||
conf.address_space = strtol(cmd[OPT_ADDRESS_SPACE].arg, NULL, 16);
|
||||
log << "address_space: "<< hex << conf.address_space << endl;
|
||||
|
||||
//Confi address_space=0 means no address space filtering
|
||||
if (conf.address_space == 0)
|
||||
conf.address_space = fail::ANY_ADDR;
|
||||
} else {
|
||||
conf.address_space = fail::ANY_ADDR;
|
||||
}
|
||||
|
||||
if (cmd[OPT_ADDRESS_SPACE_TRACE]) {
|
||||
conf.address_space_trace = strtol(cmd[OPT_ADDRESS_SPACE_TRACE].arg, NULL, 16);
|
||||
if( conf.address_space_trace == 0 )
|
||||
conf.address_space_trace = conf.address_space;
|
||||
else
|
||||
log << "address_space_trace: "<< hex << conf.address_space_trace << endl;
|
||||
} else {
|
||||
conf.address_space_trace = conf.address_space;
|
||||
}
|
||||
|
||||
|
||||
if (conf.address_space_trace == 0)
|
||||
conf.address_space_trace = fail::ANY_ADDR;
|
||||
|
||||
if (cmd[OPT_FUNC_ENTRY]) {
|
||||
conf.func_entry = strtol(cmd[OPT_FUNC_ENTRY].arg, NULL, 16);
|
||||
log << "func_entry: "<< hex << conf.func_entry << endl;
|
||||
} else{
|
||||
parameterMissing(log, "func_entry");
|
||||
}
|
||||
|
||||
if (cmd[OPT_FUNC_EXIT]) {
|
||||
conf.func_exit = strtol(cmd[OPT_FUNC_EXIT].arg, NULL, 16);
|
||||
log << "func_exit: "<< hex << conf.func_exit << endl;
|
||||
} else {
|
||||
parameterMissing(log, "func_exit");
|
||||
}
|
||||
|
||||
if (cmd[OPT_FILTER_ENTRY]) {
|
||||
conf.filter_entry = strtol(cmd[OPT_FILTER_ENTRY].arg, NULL, 16);
|
||||
log << "filter_entry: "<< hex << conf.filter_entry << endl;
|
||||
} else {
|
||||
conf.filter_entry = conf.func_entry;
|
||||
}
|
||||
|
||||
if (cmd[OPT_FILTER_EXIT]) {
|
||||
conf.filter_exit = strtol(cmd[OPT_FILTER_EXIT].arg, NULL, 16);
|
||||
log << "filter_exit: "<< hex << conf.filter_exit << endl;
|
||||
} else {
|
||||
conf.filter_exit = conf.func_exit;
|
||||
}
|
||||
|
||||
if (cmd[OPT_BREAK_BLINK]) {
|
||||
conf.break_blink = strtol(cmd[OPT_BREAK_BLINK].arg, NULL, 16);
|
||||
log << "break_blink: "<< hex << conf.break_blink << endl;
|
||||
} else {
|
||||
conf.break_blink = 0;
|
||||
}
|
||||
|
||||
if (cmd[OPT_BREAK_LONGJMP]) {
|
||||
conf.break_longjmp = strtol(cmd[OPT_BREAK_LONGJMP].arg, NULL, 16);
|
||||
log << "break_longjmp: "<< hex << conf.break_longjmp << endl;
|
||||
} else {
|
||||
conf.break_longjmp = 0;
|
||||
}
|
||||
|
||||
if (cmd[OPT_BREAK_EXIT]) {
|
||||
conf.break_exit = strtol(cmd[OPT_BREAK_EXIT].arg, NULL, 16);
|
||||
log << "break_exit: "<< hex << conf.break_exit << endl;
|
||||
} else {
|
||||
conf.break_exit = 0;
|
||||
}
|
||||
|
||||
if (cmd[OPT_NUMINSTR]) {
|
||||
conf.numinstr = strtol(cmd[OPT_NUMINSTR].arg, NULL, 10);
|
||||
log << "numinstr: "<< dec << conf.numinstr << endl;
|
||||
} else {
|
||||
parameterMissing(log, "numinstr");
|
||||
}
|
||||
|
||||
if (cmd[OPT_TOTINSTR]) {
|
||||
conf.totinstr = strtol(cmd[OPT_TOTINSTR].arg, NULL, 10);
|
||||
log << "totinstr: "<< dec << conf.totinstr << endl;
|
||||
} else {
|
||||
parameterMissing(log, "totinstr");
|
||||
}
|
||||
|
||||
if (cmd[OPT_EMUL_IPS]) {
|
||||
conf.emul_ips = strtol(cmd[OPT_EMUL_IPS].arg, NULL, 10);
|
||||
log << "emul_ips = " << dec << conf.emul_ips << endl;
|
||||
} else {
|
||||
parameterMissing(log, "emul_ips");
|
||||
}
|
||||
|
||||
if (cmd[OPT_STATE_FOLDER]) {
|
||||
conf.state_folder = std::string(cmd[OPT_STATE_FOLDER].arg);
|
||||
log << "state_folder: "<< conf.state_folder << endl;
|
||||
} else {
|
||||
conf.state_folder = "l4sys.state";
|
||||
}
|
||||
|
||||
if (cmd[OPT_INSTRUCTION_LIST]) {
|
||||
conf.instruction_list = std::string(cmd[OPT_INSTRUCTION_LIST].arg);
|
||||
log << "instruction_list: "<< conf.instruction_list << endl;
|
||||
} else {
|
||||
conf.instruction_list = "ip.list";
|
||||
}
|
||||
|
||||
if (cmd[OPT_CORRECT_OUTPUT]) {
|
||||
conf.golden_run = std::string(cmd[OPT_CORRECT_OUTPUT].arg);
|
||||
log << "golden_run: "<< conf.golden_run << endl;
|
||||
} else {
|
||||
conf.golden_run = "golden.out";
|
||||
}
|
||||
|
||||
if (cmd[OPT_FILTER]) {
|
||||
conf.filter = std::string(cmd[OPT_FILTER].arg);
|
||||
log << "filter: "<< conf.filter << endl;
|
||||
} else {
|
||||
conf.filter = "filter.list";
|
||||
}
|
||||
|
||||
if (cmd[OPT_TRACE]) {
|
||||
conf.trace = std::string(cmd[OPT_TRACE].arg);
|
||||
log << "trace: "<< conf.trace << endl;
|
||||
} else {
|
||||
conf.trace = "trace.pb";
|
||||
}
|
||||
|
||||
if (cmd[OPT_CAMPAIN_SERVER]) {
|
||||
conf.campain_server = std::string(cmd[OPT_CAMPAIN_SERVER].arg);
|
||||
log << "campain_server: "<< conf.campain_server << endl;
|
||||
} else {
|
||||
conf.campain_server = "localhost";
|
||||
}
|
||||
|
||||
if (cmd[STEP]) {
|
||||
if (!std::string("cr3").compare(cmd[STEP].arg) ) {
|
||||
log << "calculate cr3" << endl;
|
||||
conf.step = L4SysConfig::GET_CR3;
|
||||
} else if (!std::string("cc").compare(cmd[STEP].arg) ) {
|
||||
log << "Create Checkpoint" << endl;
|
||||
conf.step = L4SysConfig::CREATE_CHECKPOINT;
|
||||
} else if (!std::string("it").compare(cmd[STEP].arg) ) {
|
||||
log << "collect instruction trace" << endl;
|
||||
conf.step = L4SysConfig::COLLECT_INSTR_TRACE;
|
||||
} else if (!std::string("gr").compare(cmd[STEP].arg) ) {
|
||||
log << "golden run" << endl;
|
||||
conf.step = L4SysConfig::GOLDEN_RUN;
|
||||
} else if (!std::string("all").compare(cmd[STEP].arg) ) {
|
||||
log << "do all preparation steps" << endl;
|
||||
conf.step = L4SysConfig::FULL_PREPARATION;
|
||||
} else {
|
||||
cerr << "Wrong argument for option '--step'" << endl;
|
||||
simulator.terminate(1);
|
||||
}
|
||||
} else {
|
||||
conf.step = L4SysConfig::NO_PREP;
|
||||
}
|
||||
}
|
||||
240
src/experiments/l4-sys/experimentPreparation.cc
Normal file
240
src/experiments/l4-sys/experimentPreparation.cc
Normal file
@ -0,0 +1,240 @@
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "experiment.hpp"
|
||||
#include "InstructionFilter.hpp"
|
||||
#include "aluinstr.hpp"
|
||||
|
||||
#include "sal/SALConfig.hpp"
|
||||
#include "util/gzstream/gzstream.h"
|
||||
#include "util/ProtoStream.hpp"
|
||||
#include "TracePlugin.pb.h"
|
||||
#include "sal/Listener.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace fail;
|
||||
|
||||
Bit32u L4SysExperiment::eipBiased() {
|
||||
BX_CPU_C *cpu_context = simulator.getCPUContext();
|
||||
Bit32u EIP = cpu_context->gen_reg[BX_32BIT_REG_EIP].dword.erx;
|
||||
return EIP + cpu_context->eipPageBias;
|
||||
}
|
||||
|
||||
const Bit8u *L4SysExperiment::calculateInstructionAddress() {
|
||||
// pasted in from various nested Bochs functions and macros - I hope
|
||||
// they will not change too soon (as do the Bochs developers, probably)
|
||||
BX_CPU_C *cpu_context = simulator.getCPUContext();
|
||||
const Bit8u *result = cpu_context->eipFetchPtr + eipBiased();
|
||||
return result;
|
||||
}
|
||||
|
||||
void L4SysExperiment::runToStart(fail::BPSingleListener *bp)
|
||||
{
|
||||
bp->setWatchInstructionPointer(conf.func_entry);
|
||||
|
||||
log << "run until ip reaches 0x" << hex << conf.func_entry << endl;
|
||||
|
||||
simulator.addListenerAndResume(bp);
|
||||
|
||||
log << "test function entry reached, saving state" << endl;
|
||||
log << "EIP: expected " << hex << bp->getTriggerInstructionPointer()
|
||||
<< " and actually got "
|
||||
<< simulator.getCPU(0).getInstructionPointer()
|
||||
<< endl;
|
||||
log << "check the source code if the two instruction pointers are not equal" << endl;
|
||||
|
||||
if(conf.address_space == conf.address_space_trace) {
|
||||
conf.address_space_trace = BX_CPU(0)->cr3;
|
||||
}
|
||||
|
||||
conf.address_space = BX_CPU(0)->cr3;
|
||||
|
||||
char tmp[20];
|
||||
sprintf(tmp, "0x%lx", BX_CPU(0)->cr3);
|
||||
updateConfig("address_space", tmp );
|
||||
}
|
||||
|
||||
|
||||
void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener* bp)
|
||||
{
|
||||
fail::MemAccessListener ML(ANY_ADDR, MemAccessEvent::MEM_READWRITE);
|
||||
ogzstream out(conf.trace.c_str());
|
||||
ProtoOStream *os = new ProtoOStream(&out);
|
||||
|
||||
size_t count = 0, inst_accepted = 0, mem = 0, mem_valid = 0;
|
||||
simtime_t prevtime = 0, currtime;
|
||||
simtime_diff_t deltatime;
|
||||
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(conf.state_folder.c_str());
|
||||
currtime = simulator.getTimerTicks();
|
||||
|
||||
log << "EIP = " << hex
|
||||
<< simulator.getCPU(0).getInstructionPointer()
|
||||
<< endl;
|
||||
|
||||
if (!simulator.addListener(&ML)) {
|
||||
log << "did not add memory listener..." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
if (!simulator.addListener(bp)) {
|
||||
log << "did not add breakpoint listener..." << std::endl;
|
||||
exit(1);
|
||||
}
|
||||
|
||||
ofstream instr_list_file(conf.instruction_list.c_str(), ios::binary);
|
||||
RangeSetInstructionFilter filtering(conf.filter.c_str());
|
||||
bp->setWatchInstructionPointer(ANY_ADDR);
|
||||
|
||||
map<address_t, unsigned> times_called_map;
|
||||
bool injecting = false;
|
||||
|
||||
while (bp->getTriggerInstructionPointer() != conf.func_exit) {
|
||||
fail::BaseListener *res = simulator.resume();
|
||||
address_t curr_addr = 0;
|
||||
|
||||
// XXX: See the API problem below!
|
||||
if (res == &ML) {
|
||||
curr_addr = ML.getTriggerInstructionPointer();
|
||||
simulator.addListener(&ML);
|
||||
if ((conf.address_space_trace != ANY_ADDR) && (BX_CPU(0)->cr3 != conf.address_space_trace)) {
|
||||
continue;
|
||||
}
|
||||
++mem;
|
||||
} else if (res == bp) {
|
||||
curr_addr = bp->getTriggerInstructionPointer();
|
||||
assert(curr_addr == simulator.getCPU(0).getInstructionPointer());
|
||||
simulator.addListener(bp);
|
||||
++count;
|
||||
}
|
||||
|
||||
currtime = simulator.getTimerTicks();
|
||||
deltatime = currtime - prevtime;
|
||||
|
||||
if (curr_addr == conf.filter_entry) {
|
||||
injecting = true;
|
||||
}
|
||||
|
||||
if (curr_addr == conf.filter_exit) {
|
||||
injecting = false;
|
||||
}
|
||||
|
||||
// Only trace if:
|
||||
// 1) we are between FILTER_ENTRY and FILTER_EXIT, and
|
||||
// 2) we have a valid instruction according to filter rules, and
|
||||
// 3) we are in the TRACE address space
|
||||
if (!injecting or
|
||||
!filtering.isValidInstr(curr_addr, reinterpret_cast<char const*>(calculateInstructionAddress()))
|
||||
or
|
||||
(BX_CPU(0)->cr3 != conf.address_space_trace)
|
||||
) {
|
||||
//log << "connt..." << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (res == &ML) {
|
||||
#if 0
|
||||
log << "Memory event IP " << std::hex << ML.getTriggerInstructionPointer()
|
||||
<< " @ " << ML.getTriggerAddress() << "("
|
||||
<< ML.getTriggerAccessType() << "," << ML.getTriggerWidth()
|
||||
<< ")" << std::endl;
|
||||
#endif
|
||||
++mem_valid;
|
||||
|
||||
Trace_Event te;
|
||||
if (deltatime != 0) { te.set_time_delta(1); };
|
||||
te.set_ip(curr_addr);
|
||||
te.set_memaddr(ML.getTriggerAddress());
|
||||
te.set_accesstype( (ML.getTriggerAccessType() & MemAccessEvent::MEM_READ) ? te.READ : te.WRITE );
|
||||
te.set_width(ML.getTriggerWidth());
|
||||
os->writeMessage(&te);
|
||||
} else if (res == bp) {
|
||||
unsigned times_called = times_called_map[curr_addr];
|
||||
++times_called;
|
||||
times_called_map[curr_addr] = times_called;
|
||||
|
||||
//log << "breakpoint event" << std::endl;
|
||||
// now check if we want to add the instruction for fault injection
|
||||
++inst_accepted;
|
||||
|
||||
// 1) The 'old' way of logging instructions -> DEPRECATE soon
|
||||
// BUT: we are currently using the bp_counter stored in this
|
||||
// file!
|
||||
TraceInstr new_instr;
|
||||
//log << "writing IP " << hex << curr_addr << " counter "
|
||||
// << dec << times_called << "(" << hex << BX_CPU(0)->cr3 << ")"
|
||||
// << endl;
|
||||
new_instr.trigger_addr = curr_addr;
|
||||
new_instr.bp_counter = times_called;
|
||||
|
||||
instr_list_file.write(reinterpret_cast<char*>(&new_instr), sizeof(TraceInstr));
|
||||
|
||||
// 2) The 'new' way -> generate Events that can be processed by
|
||||
// the generic *-trace tools
|
||||
// XXX: need to log CR3 if we want multiple binaries here
|
||||
Trace_Event e;
|
||||
if (deltatime != 0) { e.set_time_delta(1); };
|
||||
e.set_ip(curr_addr);
|
||||
os->writeMessage(&e);
|
||||
} else {
|
||||
printf("Unknown res? %p\n", res);
|
||||
}
|
||||
prevtime = currtime;
|
||||
|
||||
//short sanity check
|
||||
//log << "continue..." << std::endl;
|
||||
}
|
||||
log << "saving instructions triggered during normal execution" << endl;
|
||||
instr_list_file.close();
|
||||
log << "test function calculation position reached after "
|
||||
<< dec << count << " instructions; " << inst_accepted << " accepted" << endl;
|
||||
log << "mem accesses: " << mem << ", valid: " << mem_valid << std::endl;
|
||||
|
||||
conf.numinstr = inst_accepted;
|
||||
conf.totinstr = count;
|
||||
|
||||
//Write new values into config file
|
||||
char numimstr_str[20];
|
||||
|
||||
sprintf(numimstr_str, "%li", inst_accepted);
|
||||
updateConfig("numinstr", numimstr_str );
|
||||
|
||||
char totinstr_str[20];
|
||||
sprintf(totinstr_str, "%li", count);
|
||||
updateConfig("totinstr", totinstr_str);
|
||||
|
||||
delete bp;
|
||||
}
|
||||
|
||||
void L4SysExperiment::goldenRun(fail::BPSingleListener* bp)
|
||||
{
|
||||
log << "restoring state" << endl;
|
||||
simulator.restore(conf.state_folder.c_str());
|
||||
log << "EIP = " << hex
|
||||
<< simulator.getCPU(0).getInstructionPointer()
|
||||
<< endl;
|
||||
|
||||
std::string golden_run;
|
||||
ofstream golden_run_file(conf.golden_run.c_str());
|
||||
bp->setWatchInstructionPointer(conf.func_exit);
|
||||
simulator.addListener(bp);
|
||||
BaseListener* ev = waitIOOrOther(true);
|
||||
if (ev == bp) {
|
||||
golden_run.assign(currentOutput.c_str());
|
||||
golden_run_file << currentOutput.c_str();
|
||||
log << "Output successfully logged!" << endl;
|
||||
} else {
|
||||
log
|
||||
<< "Obviously, there is some trouble with"
|
||||
<< " the events registered - aborting simulation!"
|
||||
<< endl;
|
||||
golden_run_file.close();
|
||||
terminate(10);
|
||||
}
|
||||
|
||||
log << "saving output generated during normal execution" << endl;
|
||||
golden_run_file.close();
|
||||
delete bp;
|
||||
}
|
||||
@ -31,6 +31,7 @@ message L4SysProtoMsg {
|
||||
TIMEOUT = 3;
|
||||
WRONG = 4;
|
||||
UNKNOWN = 5;
|
||||
FAILSTOP = 6;
|
||||
}
|
||||
|
||||
required DatabaseCampaignMessage fsppilot = 1;
|
||||
|
||||
@ -3,10 +3,36 @@
|
||||
|
||||
#include "cpn/CampaignManager.hpp"
|
||||
#include "campaign.hpp"
|
||||
#include "util/CommandLine.hpp"
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
|
||||
L4SysCampaign c;
|
||||
|
||||
fail::CommandLine &cmd = fail::CommandLine::Inst();
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
if(strncmp(argv[i], "--type=", 7) == 0)
|
||||
c.type = std::string(std::string(argv[i]), 7);
|
||||
else
|
||||
cmd.add_args(argv[i]);
|
||||
}
|
||||
|
||||
if (c.type.empty()) {
|
||||
std::cerr << "You have to specify FI type" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if(!c.type.compare("mem")) {
|
||||
std::cout << "We will do memory FI" << std::endl;
|
||||
} else if(!c.type.compare("reg")) {
|
||||
std::cout << "We will do register FI" << std::endl;
|
||||
} else {
|
||||
std::cout << "Specified FI-type not supported" << std::endl;
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
if (fail::campaignmanager.runCampaign(&c)) {
|
||||
return 0;
|
||||
} else {
|
||||
|
||||
Binary file not shown.
@ -17,7 +17,8 @@
|
||||
%opening
|
||||
\title{L4Sys Fault Injection Campaign -- User Manual}
|
||||
\author{Martin Unzner (\href{mailto:munzner@os.inf.tu-dresden.de}{munzner@os.inf.tu-dresden.de}), \\
|
||||
Björn Döbel (\href{mailto:doebel@os.inf.tu-dresden.de}{doebel@os.inf.tu-dresden.de})}
|
||||
Björn Döbel (\href{mailto:doebel@os.inf.tu-dresden.de}{doebel@os.inf.tu-dresden.de}), \\
|
||||
Tobias Stumpf (\href{mailto:tstumpf@os.inf.tu-dresden.de}{tstumpf@os.inf.tu-dresden.de})}
|
||||
|
||||
\begin{document}
|
||||
|
||||
@ -26,7 +27,7 @@
|
||||
\begin{abstract}
|
||||
This document describes how to use the L4Sys experiment suite.
|
||||
However, this is not a complete documentation. When in doubt,
|
||||
please read the source code or contact me. Still, I would like
|
||||
please read the source code or contact us. Still, we would like
|
||||
you to read this whole document before investigating further.
|
||||
\end{abstract}
|
||||
|
||||
@ -37,14 +38,10 @@ The framework builds on Fail* and provides means to perform fault injection
|
||||
experiments for applications running on top of the Fiasco.OC/L4Re
|
||||
microkernel-based operating system as well as the underlying microkernel.
|
||||
|
||||
\noindent \lfs{} provides four experiment types:
|
||||
\noindent \lfs{} provides two experiment types:
|
||||
\begin{enumerate}[topsep=0em,itemsep=0em]
|
||||
\item \emph{GPRFlip} simulates bit flips in general purpose registers.
|
||||
\item \emph{RATFlip} simulates errors in the association between the
|
||||
physical register file and general purpose registers.
|
||||
\item \emph{IDCFlip} simulates errors occurring during instruction decoding.
|
||||
\item \emph{ALUInstrFlip} simulates errors in the processor's arithmetic
|
||||
logic unit.
|
||||
\item \emph{GPRFLIP} simulates bit flips in general purpose registers.
|
||||
\item \emph{MEM} simulates memorry bit flipps.
|
||||
\end{enumerate}
|
||||
|
||||
\noindent \lfs{} currently works for x86/32 running in Fail/Bochs only.
|
||||
@ -72,7 +69,7 @@ The following CMake flags need to be set:
|
||||
|
||||
Enabling \verb+CONFIG_FAST_BREAKPOINTS+ may speed up the experiment clients
|
||||
significantly. Enabling \verb+CONFIG_BOCHS_NO_ABORT+ is necessary to detect
|
||||
whether Bochs stopped because of a bad instruction induced by IDCFlip.
|
||||
whether Bochs stopped because of a bad instruction (currently not used).
|
||||
Keep in mind that this implies the risk of a deadlock
|
||||
in the campaign system, because packets (i.e. experiment descriptions)
|
||||
are resent if the client does not answer and finally, all clients
|
||||
@ -80,7 +77,7 @@ might fail because they tried to execute the same faulty instruction.
|
||||
|
||||
\section{Emulator Setup}
|
||||
|
||||
The next step is to prepare an L4Re application setup to run in Bochs. To
|
||||
The next step is to prepare a L4Re application setup to run in Bochs. To
|
||||
setup your system, first, you need a dedicated \texttt{bochsrc} file. It has
|
||||
proven useful to have a Bochs resource file or an independent Bochs instance
|
||||
with GUI enabled for the initial testing, however the experiments are intended
|
||||
@ -102,12 +99,15 @@ injection campaign. This requires three (+ one optional) steps:
|
||||
\begin{enumerate}[topsep=0em,itemsep=0em]
|
||||
\item \emph{OPTIONAL:} If we want to perform a campaign that only targets
|
||||
a single application, we need to determine this application's address
|
||||
space ID.
|
||||
space ID. Choosing a different link address (add
|
||||
\verb+DEFAULT_RELOC = 0x20000000+ in your applications Makefile)
|
||||
helps you to figure out your address space by stopping at a unique
|
||||
instruction pointer.
|
||||
\item \emph{REQUIRED:} We perform an initial run of our setup in Bochs until
|
||||
the point where Bochs is booted and the application in question
|
||||
starts. At this point we take a snapshot of the emulator so that we
|
||||
can skip everything upfront in the remaining runs.
|
||||
\item \emph{REQUIRED:} The \lfs{} campaign uses \verb+L4SYS_NUM_INSTR+ to
|
||||
\item \emph{REQUIRED:} The \lfs{} campaign uses the number of instructions to
|
||||
determine the set of instructions to inject faults in. We need to
|
||||
perform one run of our setup to determine this number.
|
||||
\item \emph{REQUIRED:} We need to perform a \emph{golden run} without any
|
||||
@ -115,146 +115,103 @@ injection campaign. This requires three (+ one optional) steps:
|
||||
\end{enumerate}
|
||||
|
||||
All parameters of the \lfs{} experiment can be configured via file
|
||||
\texttt{experimentInfo.hpp}. Normally, it should not be necessary to change
|
||||
the program flow directly. However, the interested reader is invited to take a
|
||||
look at \texttt{experiment.cc}, too.
|
||||
\texttt{experiment.conf} or can be given to \verb+fail-client+ as command
|
||||
line parameter.
|
||||
|
||||
\subsection{Constants}
|
||||
|
||||
Some values are constant throughout all steps of the preparation and also when
|
||||
the workload program is run. The most important constant is
|
||||
\verb+L4SYS_BOCHS_IPS+, which has to be consistent with your \texttt{bochsrc}
|
||||
Some configuration values are constant throughout all steps of the preparation
|
||||
and also when the workload program is run. The most important configuration
|
||||
parameter is \verb+emul_ipc+, which has to be consistent with your \texttt{bochsrc}
|
||||
setting and is used for several timely calculations in the client.
|
||||
|
||||
\subsection{Step 0: Determine the address space}
|
||||
\subsection{Preparation}
|
||||
|
||||
First, we need to find the start and end instruction addresses for our
|
||||
workload program and our given experiment. For this purpose, use a
|
||||
disassembler, such as \texttt{objdump} or \emph{IDA Pro}. Determine the first
|
||||
and last instruction for your campaign and set \verb+L4SYS_FUNC_ENTRY+ and
|
||||
\verb+L4SYS_FUNC_EXIT+ in the header file accordingly. \verb+L4SYS_NUMINSTR+
|
||||
is determined automatically in a later preparation step and can be ignored for
|
||||
now.
|
||||
and last instruction for your campaign and set \verb+func_entry+ and
|
||||
\verb+func_exit+ in the configuration file accordingly. Additionally, you
|
||||
can limit the range for FI by setting \verb+filter_entry+ and \verb+filter_exit+.
|
||||
|
||||
If you want your campaign only to affect a specific address space (e.g.,
|
||||
because you are only interested in faults at the application level), \lfs{}
|
||||
leverages Fail*'s address space filtering mechanism. To determine the address
|
||||
space identifier, you will have to use Bochs'
|
||||
\href{http://bochs.sourceforge.net/doc/docbook/user/internal-debugger.html}{internal
|
||||
debugger} and perform the following actions:
|
||||
\begin{enumerate}[topsep=0em,itemsep=0em]
|
||||
\item Compile Bochs with support for the internal debugger. This can either
|
||||
be done by configuring and rebuilding the fail client accordingly or
|
||||
using a separate Bochs installation - we don't need Fail*
|
||||
functionality here.\footnote{BD: I saw differing values when using
|
||||
another Bochs installation, though. Perhaps it's safer to use the same
|
||||
Bochs build for testing and injection.}
|
||||
\item Boot your system in Bochs. The debugger prompt (or window) will
|
||||
appear. Use the \verb+lbreak+ command to set an instruction breakpoint
|
||||
to an address in your application. (Hint: Remember you already figured
|
||||
out \verb+L4SYS_FUNC_ENTRY+ previously.)
|
||||
\item Run Bochs until the breakpoint is hit. Verify that you are in the
|
||||
right address space (instruction pointers may be similar in different
|
||||
applications as L4's BID links all programs to the same starting
|
||||
address by default).
|
||||
\item Use the \verb+creg+ command to look at the current control registers.
|
||||
Set \verb+L4SYS_ADDRESS_SPACE+ to the value of the CR3 (page table
|
||||
control) register.
|
||||
\end{enumerate}
|
||||
All preparations steps can be done together or step-by-step. To start
|
||||
preparation call fail-client with the parameter \verb+-Wf,--step=OPTION+.
|
||||
Use \verb+OPTION=all+ for doing all preparations steps together.
|
||||
|
||||
\subsubsection{Step 0: Determine the address space (OPTION=cr3)}
|
||||
|
||||
If you are not interested in address space filtering, you may set
|
||||
\verb+L4SYS_ADDRESS_SPACE+ to \verb+ANY_ADDR+. Note that in this case you will
|
||||
\verb+address_space+ to \verb+0+. Note that in this case you will
|
||||
probably encounter instruction pointers across various address spaces and may
|
||||
not get the unique results you want.
|
||||
|
||||
\subsection{Step 1: Save the initial state of the machine}
|
||||
During this step, the option \verb+address_space+ of your configuration
|
||||
file is is updated.
|
||||
|
||||
Make sure \verb+PREPARATION_STEP+ is still set to \texttt{1}, and
|
||||
you have set \verb+L4SYS_ADDRESS_SPACE+ accordingly.
|
||||
|
||||
\subsubsection{Step 1: Save the initial state of the machine}
|
||||
|
||||
Make sure you have set \verb+address_space+ accordingly.
|
||||
Now recompile and execute the framework code again, this time with the graphical
|
||||
user interface disabled. The experiment client runs until
|
||||
\verb+L4SYS_FUNC_ENTRY+ is reached and then saves
|
||||
the complete configuration.
|
||||
\verb+func_entry+ is reached and then saves the complete configuration.
|
||||
|
||||
\subsection{Step 2: Determine the instructions to execute}
|
||||
\subsubsection{Step 2: Determine the instructions to execute}
|
||||
|
||||
For this part, it depends on how you want to conduct the injection
|
||||
experiments. Setting \verb+L4SYS_FILTER_INSTRUCTIONS+
|
||||
stores all instructions by default, and
|
||||
enables the filter functionality to store only those
|
||||
instructions that match the filter.
|
||||
Each instruction in the trace requires
|
||||
an address plus an unsigned breakpoint counter,
|
||||
which means~8 bytes per instruction on a 32-bit system
|
||||
and 12~bytes per instruction on a 64-bit system.
|
||||
|
||||
If \verb+L4SYS_FILTER_INSTRUCTIONS+ is not set, the instruction
|
||||
to perform the fault injection at is determined by single-stepping
|
||||
through the program from the beginning, which is quite slow.
|
||||
I only recommend it for long programs, where a complete
|
||||
instruction trace would require several hundred megabytes of data.
|
||||
|
||||
No matter which method you choose, the default implementation
|
||||
of the campaign server reads the total instruction count
|
||||
from \verb+L4SYS_NUMINSTR+. Thus, it is mandatory to set this
|
||||
value to the number of instructions available.
|
||||
|
||||
To obtain this number and optionally the instruction trace,
|
||||
set \verb+PREPARATION_STEP+ to \texttt{2} and recompile, then execute
|
||||
the experiment client. You do not have to pass parameters to Bochs
|
||||
any more, because the configuration is overwritten with the
|
||||
state saved in step~1.
|
||||
For this part the filter-file (\verb+filter.list+ by default) is
|
||||
read to filter out instruction which are not in any of the specified
|
||||
ranges.
|
||||
|
||||
After the program has finished, you will get a summary on the
|
||||
total of instructions executed.
|
||||
|
||||
If you have
|
||||
\verb+L4SYS_FILTER_INSTRUCTIONS+ enabled, this is not the
|
||||
value you look for; it merely claims how many
|
||||
instructions have been processed at all.
|
||||
To set \verb+L4SYS_NUMINSTR+ correctly, you need to look for the
|
||||
number before the word \emph{accepted}, which points out how many
|
||||
instructions have been accepted by the applied filter. Of course,
|
||||
if no filtering is selected, these two figures should be equal.
|
||||
Please contact me if that is not the case.
|
||||
\subsubsection{Step 3: Determine the correct output}
|
||||
|
||||
If \verb+L4SYS_FILTER_INSTRUCTIONS+ is disabled, you should
|
||||
get a statistical output on how many of the instructions
|
||||
were executed in userland and kernel space, respectively,
|
||||
but the interesting figure in this case is of course the overall
|
||||
sum of executed instructions.
|
||||
|
||||
\subsection{Step 3: Determine the correct output}
|
||||
|
||||
This is the easiest step: Set \verb+PREPARATION_STEP+ to \texttt{3},
|
||||
recompile the client and execute it in the target directory.
|
||||
It runs the complete program and logs the output. You can
|
||||
The framework runs the complete program and logs the output. You can
|
||||
check the resulting file (by default \texttt{golden.out}),
|
||||
and if it does not comply with your expectations of a valid
|
||||
run, you should correct the entry and exit point, the address space
|
||||
or, in the worst case, your Bochs settings.
|
||||
|
||||
\subsubsection{Step 4: Fill the database with your experiments}
|
||||
|
||||
Use the tools \verb+import-trace+ and \verb+prune-trace+ to
|
||||
import the data and reducing the amount of necessary experiments.
|
||||
|
||||
You can call both applications with parameters specifying your
|
||||
database setup or using a default config file (\verb+~/.my.cnf+)
|
||||
defining username, password and database name. For \verb+import-trace+
|
||||
you have to specify the following parameters:
|
||||
|
||||
\begin{itemize}
|
||||
\item \verb+--importer MemoryImport|RegisterImporter+
|
||||
\item \verb+-e+ YourImageName
|
||||
\item \verb+-t+ NameOfYourTraceFile (trace.pb by default)
|
||||
\end{itemize}
|
||||
|
||||
\section{Campaign Setup}
|
||||
|
||||
To setup the actual campaign, you need to edit \texttt{campaign.cc}.
|
||||
The full language capabilities of \texttt{AspectC++} are at your hand to define
|
||||
the course of your experiments; a sample covering all experiment
|
||||
types at random is already provided. In the experiment client,
|
||||
set \verb+PREPARATION_STEP+ to \texttt{0}, which means there is nothing more
|
||||
to prepare.
|
||||
To start a FI-experiment, you need to start both the campaign server
|
||||
(\verb+l4-sys-server+) and the experiment client (texttt{l4-sys-server})
|
||||
needs a parameter to specify the experiment type.
|
||||
|
||||
After you have successfully compiled both programs, you need to
|
||||
start both the campaign server (\texttt{l4-sys-server})
|
||||
and the experiment client. By default, they should run on the
|
||||
same machine, but you can adapt the \texttt{L4SysExperiment}
|
||||
constructor in \texttt{experiment.cc} to connect the \texttt{JobClient}
|
||||
to a remote server instead of \texttt{localhost}. Each experiment client processes
|
||||
exactly one experiment and exits. To complete your campaign,
|
||||
you should use the \texttt{client.sh} script in the \texttt{scripts}
|
||||
subdirectory of Fail*.
|
||||
If \verb+l4-sys-server+ and \verb+fail-client+ are running on
|
||||
different core then the config parameter \verb+campain_server+ should
|
||||
be added to specify the host name or ip addr. of the machine running
|
||||
\verb+l4-sys-server+.
|
||||
|
||||
\section{Format of the result file}
|
||||
Each experiment client processes exactly one experiment
|
||||
and exits. To complete your campaign, you should use the \verb+client.sh+
|
||||
script in the \texttt{scripts} subdirectory of Fail*.
|
||||
|
||||
|
||||
\section{Get your results}
|
||||
|
||||
Your results are stored in your database. Look at the table \verb+result_L4SysProtoMsg+
|
||||
to see your results.
|
||||
|
||||
\iffalse
|
||||
When the campaign is finished, the campaign server generates a report
|
||||
file (by default called \texttt{lfsys.csv}) in a primitive CSV dialect.
|
||||
The only syntax rules are that the columns are separated by commas,
|
||||
@ -342,17 +299,11 @@ the campaign server, from left to right.
|
||||
provide the opcode of the new instruction.
|
||||
\end{enumerate}
|
||||
|
||||
\fi
|
||||
\section{Known bugs}
|
||||
|
||||
If you need support for more than one processor,
|
||||
you will have to extend the code accordingly:
|
||||
at the moment, when in doubt, it uses the first CPU.
|
||||
|
||||
\section{To Be Continued}
|
||||
|
||||
This is everything I consider important so far. If you still encounter
|
||||
problems you may
|
||||
contact me and I will try to set the record straight.
|
||||
Happy experimenting! :)
|
||||
|
||||
\end{document}
|
||||
|
||||
Reference in New Issue
Block a user