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
|
#!/bin/bash
|
||||||
|
|
||||||
# Script to automate the preparation steps for an L4Sys experiment
|
# 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
|
FAIL_CMD="fail-client" # -rc bochs-dbg.rc"
|
||||||
CFG=experimentInfo.hpp
|
FAIL_ARGS="-f bochsrc-bd -q"
|
||||||
FAIL_CMD="fail-client -q" # -rc bochs-dbg.rc"
|
FAILDIR=/home/tstumpf/code/fail
|
||||||
FAILDIR=/home/doebel/src/fail
|
BUILDDIR=/home/tstumpf/obj/fail
|
||||||
DBNAME=fail
|
DBNAME=failtobias
|
||||||
|
BINDIR=~/local/bin
|
||||||
|
|
||||||
if [ -n "$1" ] ; then
|
TYPE=$1
|
||||||
if [ "$1" = "mem" ]; then
|
|
||||||
IMPORTER=MemoryImporter;
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if [ "$IMPORTER" = "MemoryImporter" ]; then
|
echo -e "\033[32;1m=========================================================================================="
|
||||||
echo "Preparing memory injection experiment."
|
echo "[$(date)] Prepare FI-Experiment ...."
|
||||||
|
echo -e "==========================================================================================\033[0m"
|
||||||
|
|
||||||
|
if [ -n $TYPE ] ; then
|
||||||
|
if [ $TYPE = "mem" ]; then
|
||||||
|
IMPORTER=MemoryImporter;
|
||||||
|
echo "Preparing memory injection experiment."
|
||||||
|
elif [ $TYPE = "reg" ]; then
|
||||||
|
IMPORTER=RegisterImporter;
|
||||||
|
echo "Preparing register injection experiment."
|
||||||
|
else
|
||||||
|
echo "Specified experiment type not knwon";
|
||||||
|
exit
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
IMPORTER=RegisterImporter;
|
echo "Specify your experiment type (mem/reg)"
|
||||||
echo "Preparing register injection experiment."
|
exit
|
||||||
fi
|
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)
|
blink_addr=$(nm -C fiasco.image| grep blink | cut -d\ -f 1)
|
||||||
longjmp_addr=$(nm -C fiasco.image| grep longjmp | 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
|
$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
|
||||||
cat $BAK | sed -e "s/L4SYS_BREAK_BLINK .*/L4SYS_BREAK_BLINK 0x$blink_addr/" >$CFG
|
$BINDIR/prune-trace -d $DBNAME
|
||||||
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
|
|
||||||
|
|
||||||
echo -e "\033[32;1m=========================================================================================="
|
echo -e "\033[32;1m=========================================================================================="
|
||||||
echo "[$(date)] Preparations are finished. Happy injecting...."
|
echo "[$(date)] Preparations are finished. Happy injecting...."
|
||||||
|
|||||||
@ -14,14 +14,15 @@ set(MY_CAMPAIGN_SRCS
|
|||||||
aluinstr.cc
|
aluinstr.cc
|
||||||
experiment.hpp
|
experiment.hpp
|
||||||
experiment.cc
|
experiment.cc
|
||||||
|
experimentFI.cc
|
||||||
|
experimentParameter.cc
|
||||||
|
experimentPreparation.cc
|
||||||
campaign.hpp
|
campaign.hpp
|
||||||
campaign.cc
|
campaign.cc
|
||||||
UDIS86.hpp
|
UDIS86.hpp
|
||||||
UDIS86.cc
|
UDIS86.cc
|
||||||
InstructionFilter.hpp
|
InstructionFilter.hpp
|
||||||
InstructionFilter.cc
|
InstructionFilter.cc
|
||||||
conversion.hpp
|
|
||||||
conversion.cc
|
|
||||||
)
|
)
|
||||||
|
|
||||||
#### PROTOBUFS ####
|
#### PROTOBUFS ####
|
||||||
|
|||||||
@ -1,164 +1,20 @@
|
|||||||
#include <iostream>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
#include "campaign.hpp"
|
#include "campaign.hpp"
|
||||||
#include "experimentInfo.hpp"
|
#include "experiment.hpp"
|
||||||
#include "conversion.hpp"
|
|
||||||
#include "comm/DatabaseCampaignMessage.pb.h"
|
|
||||||
#include "cpn/CampaignManager.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)
|
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;
|
L4SysExperimentData *d = new L4SysExperimentData;
|
||||||
d->msg.mutable_fsppilot()->CopyFrom(p);
|
d->msg.mutable_fsppilot()->CopyFrom(p);
|
||||||
d->msg.set_exp_type(d->msg.GPRFLIP);
|
|
||||||
//d->msg.set_exp_type(d->msg.MEM);
|
if(!type.compare("mem")) {
|
||||||
campaignmanager.addParam(d);
|
d->msg.set_exp_type(d->msg.MEM);
|
||||||
|
} else if(!type.compare("reg")) {
|
||||||
|
d->msg.set_exp_type(d->msg.GPRFLIP);
|
||||||
|
} else {
|
||||||
|
log << "Specified FI-type not supported" << std::endl;
|
||||||
|
exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
fail::campaignmanager.addParam(d);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
#ifndef __L4SYS_CAMPAIGN_HPP__
|
#ifndef __L4SYS_CAMPAIGN_HPP__
|
||||||
#define __L4SYS_CAMPAIGN_HPP__
|
#define __L4SYS_CAMPAIGN_HPP__
|
||||||
|
|
||||||
|
|
||||||
#include "cpn/DatabaseCampaign.hpp"
|
#include "cpn/DatabaseCampaign.hpp"
|
||||||
#include "comm/ExperimentData.hpp"
|
#include "comm/ExperimentData.hpp"
|
||||||
#include "l4sys.pb.h"
|
#include "l4sys.pb.h"
|
||||||
#include <google/protobuf/descriptor.h>
|
#include <google/protobuf/descriptor.h>
|
||||||
|
|
||||||
|
#include "util/Logger.hpp"
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
class L4SysExperimentData : public fail::ExperimentData {
|
class L4SysExperimentData : public fail::ExperimentData {
|
||||||
public:
|
public:
|
||||||
L4SysProtoMsg msg;
|
L4SysProtoMsg msg;
|
||||||
@ -13,15 +18,15 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
class L4SysCampaign : public fail::DatabaseCampaign {
|
class L4SysCampaign : public fail::DatabaseCampaign {
|
||||||
#if 0
|
|
||||||
public:
|
|
||||||
virtual bool run();
|
|
||||||
#else
|
|
||||||
virtual const google::protobuf::Descriptor * cb_result_message()
|
virtual const google::protobuf::Descriptor * cb_result_message()
|
||||||
{ return google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("L4SysProtoMsg"); }
|
{ return google::protobuf::DescriptorPool::generated_pool()->FindMessageTypeByName("L4SysProtoMsg"); }
|
||||||
|
|
||||||
virtual void cb_send_pilot(DatabaseCampaignMessage pilot);
|
virtual void cb_send_pilot(DatabaseCampaignMessage pilot);
|
||||||
#endif
|
|
||||||
|
fail::Logger log; //<! the logger
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::string type;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // __L4SYS_CAMPAIGN_HPP__
|
#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;
|
unsigned bp_counter;
|
||||||
} TraceInstr;
|
} TraceInstr;
|
||||||
|
|
||||||
typedef std::vector<TraceInstr> TraceVector;
|
|
||||||
|
|
||||||
class L4SysExperiment : public fail::ExperimentFlow {
|
class L4SysExperiment : public fail::ExperimentFlow {
|
||||||
private:
|
private:
|
||||||
|
class L4SysConfig;
|
||||||
fail::JobClient m_jc; //!< the job client connecting to the campaign server
|
fail::JobClient m_jc; //!< the job client connecting to the campaign server
|
||||||
fail::Logger log; //<! the logger
|
fail::Logger log; //<! the logger
|
||||||
L4SysExperimentData *param; //<! the parameter set currently in use by the client
|
L4SysExperimentData *param; //<! the parameter set currently in use by the client
|
||||||
@ -74,63 +73,38 @@ private:
|
|||||||
* @returns a value for Bochs' eipBiased variable
|
* @returns a value for Bochs' eipBiased variable
|
||||||
*/
|
*/
|
||||||
Bit32u eipBiased();
|
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.
|
* 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.
|
* Send back the experiment parameter set with a description of the error.
|
||||||
*/
|
*/
|
||||||
void terminateWithError(std::string details, int reason, L4SysProtoMsg_Result*);
|
void terminateWithError(std::string details, int reason, L4SysProtoMsg_Result*);
|
||||||
/**
|
/**
|
||||||
* Run until L4SYS_FUNC_ENTRY and save state (experiment preparation,
|
* Run until reaching the entry point of the experiment and save
|
||||||
* phase 1)
|
* state.
|
||||||
*/
|
*/
|
||||||
void startAndSaveInitState(fail::BPSingleListener* bp);
|
void runToStart(fail::BPSingleListener *bp);
|
||||||
void CR3run(fail::BPSingleListener *bp);
|
|
||||||
/**
|
/**
|
||||||
* Collect list of executed instructions, considering instruction
|
* Collect list of executed instructions, considering instruction
|
||||||
* filtering if configured (experiment preparation, phase 2).
|
* filtering if configured.
|
||||||
*/
|
*/
|
||||||
void collectInstructionTrace(fail::BPSingleListener* bp);
|
void collectInstructionTrace(fail::BPSingleListener* bp);
|
||||||
/**
|
/**
|
||||||
* Perform the golden run (experiment preparation, phase 3)
|
* Perform the golden run.
|
||||||
*/
|
*/
|
||||||
void goldenRun(fail::BPSingleListener* bp);
|
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.
|
* 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.
|
* Load job parameters for an experiment.
|
||||||
@ -142,7 +116,7 @@ private:
|
|||||||
/**
|
/**
|
||||||
* Read the golden run output into the target string.
|
* 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
|
* Prepare memory experiment. Creates a breakpoint to run until the
|
||||||
@ -166,9 +140,56 @@ private:
|
|||||||
* combination.
|
* combination.
|
||||||
*/
|
*/
|
||||||
void doRegisterInjection(int regDesc, int bit);
|
void doRegisterInjection(int regDesc, int bit);
|
||||||
|
|
||||||
|
|
||||||
void setupFilteredBreakpoint(fail::BPSingleListener* bp, int instOffset);
|
void setupFilteredBreakpoint(fail::BPSingleListener* bp, int instOffset, std::string instr_list);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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__
|
#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;
|
TIMEOUT = 3;
|
||||||
WRONG = 4;
|
WRONG = 4;
|
||||||
UNKNOWN = 5;
|
UNKNOWN = 5;
|
||||||
|
FAILSTOP = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
required DatabaseCampaignMessage fsppilot = 1;
|
required DatabaseCampaignMessage fsppilot = 1;
|
||||||
|
|||||||
@ -3,10 +3,36 @@
|
|||||||
|
|
||||||
#include "cpn/CampaignManager.hpp"
|
#include "cpn/CampaignManager.hpp"
|
||||||
#include "campaign.hpp"
|
#include "campaign.hpp"
|
||||||
|
#include "util/CommandLine.hpp"
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
|
|
||||||
L4SysCampaign c;
|
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)) {
|
if (fail::campaignmanager.runCampaign(&c)) {
|
||||||
return 0;
|
return 0;
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
Binary file not shown.
@ -17,7 +17,8 @@
|
|||||||
%opening
|
%opening
|
||||||
\title{L4Sys Fault Injection Campaign -- User Manual}
|
\title{L4Sys Fault Injection Campaign -- User Manual}
|
||||||
\author{Martin Unzner (\href{mailto:munzner@os.inf.tu-dresden.de}{munzner@os.inf.tu-dresden.de}), \\
|
\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}
|
\begin{document}
|
||||||
|
|
||||||
@ -26,7 +27,7 @@
|
|||||||
\begin{abstract}
|
\begin{abstract}
|
||||||
This document describes how to use the L4Sys experiment suite.
|
This document describes how to use the L4Sys experiment suite.
|
||||||
However, this is not a complete documentation. When in doubt,
|
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.
|
you to read this whole document before investigating further.
|
||||||
\end{abstract}
|
\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
|
experiments for applications running on top of the Fiasco.OC/L4Re
|
||||||
microkernel-based operating system as well as the underlying microkernel.
|
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]
|
\begin{enumerate}[topsep=0em,itemsep=0em]
|
||||||
\item \emph{GPRFlip} simulates bit flips in general purpose registers.
|
\item \emph{GPRFLIP} simulates bit flips in general purpose registers.
|
||||||
\item \emph{RATFlip} simulates errors in the association between the
|
\item \emph{MEM} simulates memorry bit flipps.
|
||||||
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.
|
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
\noindent \lfs{} currently works for x86/32 running in Fail/Bochs only.
|
\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
|
Enabling \verb+CONFIG_FAST_BREAKPOINTS+ may speed up the experiment clients
|
||||||
significantly. Enabling \verb+CONFIG_BOCHS_NO_ABORT+ is necessary to detect
|
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
|
Keep in mind that this implies the risk of a deadlock
|
||||||
in the campaign system, because packets (i.e. experiment descriptions)
|
in the campaign system, because packets (i.e. experiment descriptions)
|
||||||
are resent if the client does not answer and finally, all clients
|
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}
|
\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
|
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
|
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
|
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]
|
\begin{enumerate}[topsep=0em,itemsep=0em]
|
||||||
\item \emph{OPTIONAL:} If we want to perform a campaign that only targets
|
\item \emph{OPTIONAL:} If we want to perform a campaign that only targets
|
||||||
a single application, we need to determine this application's address
|
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
|
\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
|
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
|
starts. At this point we take a snapshot of the emulator so that we
|
||||||
can skip everything upfront in the remaining runs.
|
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
|
determine the set of instructions to inject faults in. We need to
|
||||||
perform one run of our setup to determine this number.
|
perform one run of our setup to determine this number.
|
||||||
\item \emph{REQUIRED:} We need to perform a \emph{golden run} without any
|
\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}
|
\end{enumerate}
|
||||||
|
|
||||||
All parameters of the \lfs{} experiment can be configured via file
|
All parameters of the \lfs{} experiment can be configured via file
|
||||||
\texttt{experimentInfo.hpp}. Normally, it should not be necessary to change
|
\texttt{experiment.conf} or can be given to \verb+fail-client+ as command
|
||||||
the program flow directly. However, the interested reader is invited to take a
|
line parameter.
|
||||||
look at \texttt{experiment.cc}, too.
|
|
||||||
|
|
||||||
\subsection{Constants}
|
\subsection{Constants}
|
||||||
|
|
||||||
Some values are constant throughout all steps of the preparation and also when
|
Some configuration values are constant throughout all steps of the preparation
|
||||||
the workload program is run. The most important constant is
|
and also when the workload program is run. The most important configuration
|
||||||
\verb+L4SYS_BOCHS_IPS+, which has to be consistent with your \texttt{bochsrc}
|
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.
|
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
|
First, we need to find the start and end instruction addresses for our
|
||||||
workload program and our given experiment. For this purpose, use a
|
workload program and our given experiment. For this purpose, use a
|
||||||
disassembler, such as \texttt{objdump} or \emph{IDA Pro}. Determine the first
|
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
|
and last instruction for your campaign and set \verb+func_entry+ and
|
||||||
\verb+L4SYS_FUNC_EXIT+ in the header file accordingly. \verb+L4SYS_NUMINSTR+
|
\verb+func_exit+ in the configuration file accordingly. Additionally, you
|
||||||
is determined automatically in a later preparation step and can be ignored for
|
can limit the range for FI by setting \verb+filter_entry+ and \verb+filter_exit+.
|
||||||
now.
|
|
||||||
|
|
||||||
If you want your campaign only to affect a specific address space (e.g.,
|
All preparations steps can be done together or step-by-step. To start
|
||||||
because you are only interested in faults at the application level), \lfs{}
|
preparation call fail-client with the parameter \verb+-Wf,--step=OPTION+.
|
||||||
leverages Fail*'s address space filtering mechanism. To determine the address
|
Use \verb+OPTION=all+ for doing all preparations steps together.
|
||||||
space identifier, you will have to use Bochs'
|
|
||||||
\href{http://bochs.sourceforge.net/doc/docbook/user/internal-debugger.html}{internal
|
\subsubsection{Step 0: Determine the address space (OPTION=cr3)}
|
||||||
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}
|
|
||||||
|
|
||||||
If you are not interested in address space filtering, you may set
|
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
|
probably encounter instruction pointers across various address spaces and may
|
||||||
not get the unique results you want.
|
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
|
Now recompile and execute the framework code again, this time with the graphical
|
||||||
user interface disabled. The experiment client runs until
|
user interface disabled. The experiment client runs until
|
||||||
\verb+L4SYS_FUNC_ENTRY+ is reached and then saves
|
\verb+func_entry+ is reached and then saves the complete configuration.
|
||||||
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
|
For this part the filter-file (\verb+filter.list+ by default) is
|
||||||
to perform the fault injection at is determined by single-stepping
|
read to filter out instruction which are not in any of the specified
|
||||||
through the program from the beginning, which is quite slow.
|
ranges.
|
||||||
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.
|
|
||||||
|
|
||||||
After the program has finished, you will get a summary on the
|
After the program has finished, you will get a summary on the
|
||||||
total of instructions executed.
|
total of instructions executed.
|
||||||
|
|
||||||
If you have
|
\subsubsection{Step 3: Determine the correct output}
|
||||||
\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.
|
|
||||||
|
|
||||||
If \verb+L4SYS_FILTER_INSTRUCTIONS+ is disabled, you should
|
The framework runs the complete program and logs the output. You can
|
||||||
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
|
|
||||||
check the resulting file (by default \texttt{golden.out}),
|
check the resulting file (by default \texttt{golden.out}),
|
||||||
and if it does not comply with your expectations of a valid
|
and if it does not comply with your expectations of a valid
|
||||||
run, you should correct the entry and exit point, the address space
|
run, you should correct the entry and exit point, the address space
|
||||||
or, in the worst case, your Bochs settings.
|
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}
|
\section{Campaign Setup}
|
||||||
|
|
||||||
To setup the actual campaign, you need to edit \texttt{campaign.cc}.
|
To start a FI-experiment, you need to start both the campaign server
|
||||||
The full language capabilities of \texttt{AspectC++} are at your hand to define
|
(\verb+l4-sys-server+) and the experiment client (texttt{l4-sys-server})
|
||||||
the course of your experiments; a sample covering all experiment
|
needs a parameter to specify the experiment type.
|
||||||
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.
|
|
||||||
|
|
||||||
After you have successfully compiled both programs, you need to
|
If \verb+l4-sys-server+ and \verb+fail-client+ are running on
|
||||||
start both the campaign server (\texttt{l4-sys-server})
|
different core then the config parameter \verb+campain_server+ should
|
||||||
and the experiment client. By default, they should run on the
|
be added to specify the host name or ip addr. of the machine running
|
||||||
same machine, but you can adapt the \texttt{L4SysExperiment}
|
\verb+l4-sys-server+.
|
||||||
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*.
|
|
||||||
|
|
||||||
\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
|
When the campaign is finished, the campaign server generates a report
|
||||||
file (by default called \texttt{lfsys.csv}) in a primitive CSV dialect.
|
file (by default called \texttt{lfsys.csv}) in a primitive CSV dialect.
|
||||||
The only syntax rules are that the columns are separated by commas,
|
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.
|
provide the opcode of the new instruction.
|
||||||
\end{enumerate}
|
\end{enumerate}
|
||||||
|
|
||||||
|
\fi
|
||||||
\section{Known bugs}
|
\section{Known bugs}
|
||||||
|
|
||||||
If you need support for more than one processor,
|
If you need support for more than one processor,
|
||||||
you will have to extend the code accordingly:
|
you will have to extend the code accordingly:
|
||||||
at the moment, when in doubt, it uses the first CPU.
|
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}
|
\end{document}
|
||||||
|
|||||||
Reference in New Issue
Block a user