|
|
|
|
@ -21,6 +21,7 @@
|
|
|
|
|
#include "config/FailConfig.hpp"
|
|
|
|
|
#include "util/ProtoStream.hpp"
|
|
|
|
|
#include "TracePlugin.pb.h"
|
|
|
|
|
#include "util/gzstream/gzstream.h"
|
|
|
|
|
|
|
|
|
|
#include "l4sys.pb.h"
|
|
|
|
|
|
|
|
|
|
@ -35,8 +36,7 @@ using namespace fail;
|
|
|
|
|
save, and restore. Enable these in the configuration.
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
string output;
|
|
|
|
|
string golden_run;
|
|
|
|
|
//string golden_run;
|
|
|
|
|
extern L4SysConversion l4sysRegisterConversion;
|
|
|
|
|
|
|
|
|
|
string L4SysExperiment::sanitised(const string &in_str) {
|
|
|
|
|
@ -62,13 +62,13 @@ BaseListener* L4SysExperiment::waitIOOrOther(bool clear_output) {
|
|
|
|
|
IOPortListener ev_ioport(0x3F8, true);
|
|
|
|
|
BaseListener* ev = NULL;
|
|
|
|
|
if (clear_output)
|
|
|
|
|
output.clear();
|
|
|
|
|
currentOutput.clear();
|
|
|
|
|
while (true) {
|
|
|
|
|
simulator.addListener(&ev_ioport);
|
|
|
|
|
ev = simulator.resume();
|
|
|
|
|
simulator.removeListener(&ev_ioport);
|
|
|
|
|
if (ev == &ev_ioport) {
|
|
|
|
|
output += ev_ioport.getData();
|
|
|
|
|
currentOutput += ev_ioport.getData();
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
@ -112,6 +112,8 @@ bx_bool L4SysExperiment::fetchInstruction(BX_CPU_C *instance,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void L4SysExperiment::logInjection() {
|
|
|
|
|
// XXX fixme
|
|
|
|
|
#if 0
|
|
|
|
|
// explicit type assignment necessary before sending over output stream
|
|
|
|
|
int id = param->getWorkloadID();
|
|
|
|
|
int instr_offset = param->msg.instr_offset();
|
|
|
|
|
@ -122,9 +124,13 @@ void L4SysExperiment::logInjection() {
|
|
|
|
|
log << "job " << id << " exp_type " << exp_type << endl;
|
|
|
|
|
log << "inject @ ip " << hex << injection_ip << " (offset " << dec << instr_offset
|
|
|
|
|
<< ")" << " bit " << bit_offset << endl;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BaseListener *L4SysExperiment::singleStep(bool preserveAddressSpace) {
|
|
|
|
|
// XXX: fixme
|
|
|
|
|
return 0;
|
|
|
|
|
#if 0
|
|
|
|
|
address_t aspace = (preserveAddressSpace ? L4SYS_ADDRESS_SPACE : ANY_ADDR);
|
|
|
|
|
BPSingleListener singlestepping_event(ANY_ADDR, aspace);
|
|
|
|
|
simulator.addListener(&singlestepping_event);
|
|
|
|
|
@ -143,13 +149,14 @@ BaseListener *L4SysExperiment::singleStep(bool preserveAddressSpace) {
|
|
|
|
|
param->msg.set_resulttype(param->msg.TIMEOUT);
|
|
|
|
|
param->msg.set_resultdata(
|
|
|
|
|
simulator.getCPU(0).getInstructionPointer());
|
|
|
|
|
param->msg.set_output(sanitised(output.c_str()));
|
|
|
|
|
param->msg.set_output(sanitised(currentOutput.c_str()));
|
|
|
|
|
param->msg.set_details("Timed out immediately after injecting");
|
|
|
|
|
|
|
|
|
|
m_jc.sendResult(*param);
|
|
|
|
|
terminate(0);
|
|
|
|
|
}
|
|
|
|
|
return ev;
|
|
|
|
|
#endif
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void L4SysExperiment::injectInstruction(
|
|
|
|
|
@ -195,11 +202,11 @@ void L4SysExperiment::terminate(int reason) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void L4SysExperiment::terminateWithError(string details, int reason) {
|
|
|
|
|
param->msg.set_resulttype(param->msg.UNKNOWN);
|
|
|
|
|
param->msg.set_resultdata(
|
|
|
|
|
simulator.getCPU(0).getInstructionPointer());
|
|
|
|
|
param->msg.set_output(sanitised(output.c_str()));
|
|
|
|
|
param->msg.set_details(details);
|
|
|
|
|
L4SysProtoMsg_Result *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);
|
|
|
|
|
@ -237,7 +244,7 @@ void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener& bp)
|
|
|
|
|
map<address_t, unsigned> times_called_map;
|
|
|
|
|
bool injecting = false;
|
|
|
|
|
|
|
|
|
|
std::ofstream out("trace.pb");
|
|
|
|
|
ogzstream out("trace.pb");
|
|
|
|
|
ProtoOStream *os = new ProtoOStream(&out);
|
|
|
|
|
|
|
|
|
|
while (bp.getTriggerInstructionPointer() != L4SYS_FUNC_EXIT) {
|
|
|
|
|
@ -281,6 +288,7 @@ void L4SysExperiment::collectInstructionTrace(fail::BPSingleListener& bp)
|
|
|
|
|
// the generic *-trace tools
|
|
|
|
|
// XXX: need to log CR3 if we want multiple binaries here
|
|
|
|
|
Trace_Event e;
|
|
|
|
|
e.set_time_delta(1);
|
|
|
|
|
e.set_ip(curr_addr);
|
|
|
|
|
os->writeMessage(&e);
|
|
|
|
|
}
|
|
|
|
|
@ -317,13 +325,14 @@ void L4SysExperiment::goldenRun(fail::BPSingleListener& bp)
|
|
|
|
|
<< simulator.getCPU(0).getInstructionPointer()
|
|
|
|
|
<< endl;
|
|
|
|
|
|
|
|
|
|
std::string golden_run;
|
|
|
|
|
ofstream golden_run_file(L4SYS_CORRECT_OUTPUT);
|
|
|
|
|
bp.setWatchInstructionPointer(L4SYS_FUNC_EXIT);
|
|
|
|
|
simulator.addListener(&bp);
|
|
|
|
|
BaseListener* ev = waitIOOrOther(true);
|
|
|
|
|
if (ev == &bp) {
|
|
|
|
|
golden_run.assign(output.c_str());
|
|
|
|
|
golden_run_file << output.c_str();
|
|
|
|
|
golden_run.assign(currentOutput.c_str());
|
|
|
|
|
golden_run_file << currentOutput.c_str();
|
|
|
|
|
log << "Output successfully logged!" << endl;
|
|
|
|
|
} else {
|
|
|
|
|
log
|
|
|
|
|
@ -364,6 +373,9 @@ bool L4SysExperiment::run() {
|
|
|
|
|
log << "Important data missing - call \"prepare\" first." << endl;
|
|
|
|
|
terminate(10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Read the golden run output for validation purposes
|
|
|
|
|
std::string golden_run;
|
|
|
|
|
ifstream golden_run_file(L4SYS_CORRECT_OUTPUT);
|
|
|
|
|
|
|
|
|
|
if (!golden_run_file.good()) {
|
|
|
|
|
@ -377,12 +389,7 @@ bool L4SysExperiment::run() {
|
|
|
|
|
|
|
|
|
|
golden_run_file.close();
|
|
|
|
|
|
|
|
|
|
//the generated output probably has a similar length
|
|
|
|
|
output.reserve(teststruct.st_size);
|
|
|
|
|
|
|
|
|
|
log << "restoring state" << endl;
|
|
|
|
|
simulator.restore(L4SYS_STATE_FOLDER);
|
|
|
|
|
|
|
|
|
|
// get the experiment parameters
|
|
|
|
|
log << "asking job server for experiment parameters" << endl;
|
|
|
|
|
if (!m_jc.getParam(*param)) {
|
|
|
|
|
log << "Dying." << endl;
|
|
|
|
|
@ -390,339 +397,369 @@ bool L4SysExperiment::run() {
|
|
|
|
|
terminate(1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int instr_offset = param->msg.instr_offset();
|
|
|
|
|
int bit_offset = param->msg.bit_offset();
|
|
|
|
|
int exp_type = param->msg.exp_type();
|
|
|
|
|
int exp_type = param->msg.exp_type();
|
|
|
|
|
int instr_offset = param->msg.fsppilot().injection_instr();
|
|
|
|
|
int regData = param->msg.fsppilot().data_address();
|
|
|
|
|
|
|
|
|
|
log << " got job parameters: offs " << hex << instr_offset
|
|
|
|
|
<< " bit " << bit_offset << " exp " << exp_type << endl;
|
|
|
|
|
int reg, width, offset;
|
|
|
|
|
reg = ((regData >> 8) & 0xF) + 1; // regs start at 1
|
|
|
|
|
width = (regData >> 4) & 0xF;
|
|
|
|
|
offset = regData & 0xF;
|
|
|
|
|
|
|
|
|
|
log << "Inject type " << exp_type << " at instr. offset " << instr_offset
|
|
|
|
|
<< " reg data (" << reg << ", " << width << ", "
|
|
|
|
|
<< offset << ")" << std::endl;
|
|
|
|
|
|
|
|
|
|
/* Each experiment message stands for 8 bits to be tested */
|
|
|
|
|
for (unsigned bit_offset = 0; bit_offset < 8; ++bit_offset) {
|
|
|
|
|
|
|
|
|
|
//the generated output probably has a similar length
|
|
|
|
|
currentOutput.clear();
|
|
|
|
|
currentOutput.reserve(teststruct.st_size);
|
|
|
|
|
simulator.clearListeners();
|
|
|
|
|
|
|
|
|
|
L4SysProtoMsg_Result *result = param->msg.add_result();
|
|
|
|
|
result->set_instr_offset(instr_offset);
|
|
|
|
|
result->set_bit_offset(bit_offset + 8 * offset);
|
|
|
|
|
result->set_register_offset(static_cast<L4SysProtoMsg_RegisterType>(reg));
|
|
|
|
|
|
|
|
|
|
// restore experiment state
|
|
|
|
|
log << "restoring state" << endl;
|
|
|
|
|
simulator.restore(L4SYS_STATE_FOLDER);
|
|
|
|
|
log << "EIP = " << hex << simulator.getCPU(0).getInstructionPointer() << endl;
|
|
|
|
|
|
|
|
|
|
#ifdef L4SYS_FILTER_INSTRUCTIONS
|
|
|
|
|
ifstream instr_list_file(L4SYS_INSTRUCTION_LIST, ios::binary);
|
|
|
|
|
// XXX still needed???
|
|
|
|
|
ifstream instr_list_file(L4SYS_INSTRUCTION_LIST, ios::binary);
|
|
|
|
|
|
|
|
|
|
if (!instr_list_file.good()) {
|
|
|
|
|
log << "Missing instruction trace" << endl;
|
|
|
|
|
terminate(21);
|
|
|
|
|
}
|
|
|
|
|
if (!instr_list_file.good()) {
|
|
|
|
|
log << "Missing instruction trace" << endl;
|
|
|
|
|
terminate(21);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TraceInstr curr_instr;
|
|
|
|
|
instr_list_file.seekg(instr_offset * 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();
|
|
|
|
|
TraceInstr curr_instr;
|
|
|
|
|
instr_list_file.seekg(instr_offset * 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);
|
|
|
|
|
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);
|
|
|
|
|
#else
|
|
|
|
|
bp.setWatchInstructionPointer(ANY_ADDR);
|
|
|
|
|
bp.setCounter(instr_offset);
|
|
|
|
|
bp.setWatchInstructionPointer(ANY_ADDR);
|
|
|
|
|
bp.setCounter(instr_offset);
|
|
|
|
|
#endif
|
|
|
|
|
simulator.addListener(&bp);
|
|
|
|
|
//and log the output
|
|
|
|
|
waitIOOrOther(true);
|
|
|
|
|
simulator.addListener(&bp);
|
|
|
|
|
//and log the output
|
|
|
|
|
waitIOOrOther(true);
|
|
|
|
|
|
|
|
|
|
// note at what IP we will do the injection
|
|
|
|
|
address_t injection_ip =
|
|
|
|
|
// note at what IP we will do the injection
|
|
|
|
|
address_t testIP = param->msg.fsppilot().injection_instr_absolute() & 0xFFFFFFFF;
|
|
|
|
|
address_t injection_ip =
|
|
|
|
|
simulator.getCPU(0).getInstructionPointer();
|
|
|
|
|
param->msg.set_injection_ip(injection_ip);
|
|
|
|
|
result->set_injection_ip(injection_ip);
|
|
|
|
|
log << std::hex << "testIP " << testIP << " <-> " << injection_ip
|
|
|
|
|
<< " inject_ip_abs" << std::endl;
|
|
|
|
|
if (testIP != injection_ip) {
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << std::hex << "Test IP " << testIP << " does not match injection IP "
|
|
|
|
|
<< injection_ip << std::endl;
|
|
|
|
|
terminateWithError(ss.str(), 19);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef L4SYS_FILTER_INSTRUCTIONS
|
|
|
|
|
// only works if we filter instructions
|
|
|
|
|
// sanity check (only works if we're working with an instruction trace)
|
|
|
|
|
if (injection_ip != curr_instr.trigger_addr) {
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << "SANITY CHECK FAILED: " << injection_ip << " != "
|
|
|
|
|
<< curr_instr.trigger_addr;
|
|
|
|
|
log << ss.str() << endl;
|
|
|
|
|
terminateWithError(ss.str(), 20);
|
|
|
|
|
}
|
|
|
|
|
// only works if we filter instructions
|
|
|
|
|
// sanity check (only works if we're working with an instruction trace)
|
|
|
|
|
if (injection_ip != curr_instr.trigger_addr) {
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << "SANITY CHECK FAILED: " << injection_ip << " != "
|
|
|
|
|
<< curr_instr.trigger_addr;
|
|
|
|
|
log << ss.str() << endl;
|
|
|
|
|
terminateWithError(ss.str(), 20);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
// inject
|
|
|
|
|
if (exp_type == param->msg.GPRFLIP) {
|
|
|
|
|
if (!param->msg.has_register_offset()) {
|
|
|
|
|
terminateWithError(
|
|
|
|
|
"Sent package did not contain the injection location (register offset)",
|
|
|
|
|
30);
|
|
|
|
|
}
|
|
|
|
|
int reg_offset = param->msg.register_offset();
|
|
|
|
|
ConcreteCPU& cpu = simulator.getCPU(0);
|
|
|
|
|
Register *reg_target = cpu.getRegister(reg_offset - 1);
|
|
|
|
|
regdata_t data = cpu.getRegisterContent(reg_target);
|
|
|
|
|
regdata_t newdata = data ^ (1 << bit_offset);
|
|
|
|
|
cpu.setRegisterContent(reg_target, newdata);
|
|
|
|
|
// inject
|
|
|
|
|
if (exp_type == param->msg.GPRFLIP) {
|
|
|
|
|
int reg_offset = reg; // XXX redundant
|
|
|
|
|
ConcreteCPU& cpu = simulator.getCPU(0);
|
|
|
|
|
Register *reg_target = cpu.getRegister(reg_offset - 1);
|
|
|
|
|
regdata_t data = cpu.getRegisterContent(reg_target);
|
|
|
|
|
regdata_t newdata = data ^ (1 << (bit_offset + 8 * offset));
|
|
|
|
|
cpu.setRegisterContent(reg_target, newdata);
|
|
|
|
|
|
|
|
|
|
// do the logging in case everything worked out
|
|
|
|
|
logInjection();
|
|
|
|
|
log << "IP " << hex << simulator.getCPU(0).getInstructionPointer()
|
|
|
|
|
<< " register data: 0x" << hex << ((int) data) << " -> 0x"
|
|
|
|
|
<< ((int) newdata) << endl;
|
|
|
|
|
} else if (exp_type == param->msg.IDCFLIP) {
|
|
|
|
|
// this is a twisted one
|
|
|
|
|
// do the logging in case everything worked out
|
|
|
|
|
logInjection();
|
|
|
|
|
log << "IP " << hex << simulator.getCPU(0).getInstructionPointer()
|
|
|
|
|
<< " register data: 0x" << hex << ((int) data) << " -> 0x"
|
|
|
|
|
<< ((int) newdata) << endl;
|
|
|
|
|
}
|
|
|
|
|
// XXX: Fixme to work with database campaign!
|
|
|
|
|
#if 0
|
|
|
|
|
else if (exp_type == param->msg.IDCFLIP) {
|
|
|
|
|
// this is a twisted one
|
|
|
|
|
|
|
|
|
|
// initial definitions
|
|
|
|
|
bxInstruction_c *currInstr = simulator.getCurrentInstruction();
|
|
|
|
|
unsigned length_in_bits = currInstr->ilen() << 3;
|
|
|
|
|
|
|
|
|
|
// get the instruction in plain text and inject the error there
|
|
|
|
|
// Note: we need to fetch some extra bytes into the array
|
|
|
|
|
// in case the faulty instruction is interpreted to be longer
|
|
|
|
|
// than the original one
|
|
|
|
|
Bit8u curr_instr_plain[MAX_INSTR_BYTES];
|
|
|
|
|
const Bit8u *addr = calculateInstructionAddress();
|
|
|
|
|
memcpy(curr_instr_plain, addr, MAX_INSTR_BYTES);
|
|
|
|
|
|
|
|
|
|
// CampaignManager has no idea of the instruction length
|
|
|
|
|
// (neither do we), therefore this small adaption
|
|
|
|
|
bit_offset %= length_in_bits;
|
|
|
|
|
param->msg.set_bit_offset(bit_offset);
|
|
|
|
|
|
|
|
|
|
// do some access calculation
|
|
|
|
|
int byte_index = bit_offset >> 3;
|
|
|
|
|
Bit8u bit_index = bit_offset & 7;
|
|
|
|
|
|
|
|
|
|
// apply the fault
|
|
|
|
|
curr_instr_plain[byte_index] ^= 0x80 >> bit_index;
|
|
|
|
|
|
|
|
|
|
// decode the instruction
|
|
|
|
|
bxInstruction_c bochs_instr;
|
|
|
|
|
memset(&bochs_instr, 0, sizeof(bxInstruction_c));
|
|
|
|
|
fetchInstruction(simulator.getCPUContext(), curr_instr_plain,
|
|
|
|
|
&bochs_instr);
|
|
|
|
|
|
|
|
|
|
// inject it
|
|
|
|
|
injectInstruction(currInstr, &bochs_instr);
|
|
|
|
|
|
|
|
|
|
// do the logging
|
|
|
|
|
logInjection();
|
|
|
|
|
} else if (exp_type == param->msg.RATFLIP) {
|
|
|
|
|
ud_type_t which = UD_NONE;
|
|
|
|
|
unsigned rnd = 0;
|
|
|
|
|
Udis86 udis(injection_ip);
|
|
|
|
|
do {
|
|
|
|
|
// initial definitions
|
|
|
|
|
bxInstruction_c *currInstr = simulator.getCurrentInstruction();
|
|
|
|
|
udis.setInputBuffer(calculateInstructionAddress(), currInstr->ilen());
|
|
|
|
|
if (!udis.fetchNextInstruction()) {
|
|
|
|
|
terminateWithError(
|
|
|
|
|
"Could not decode instruction using UDIS86", 32);
|
|
|
|
|
}
|
|
|
|
|
ud_t _ud = udis.getCurrentState();
|
|
|
|
|
unsigned length_in_bits = currInstr->ilen() << 3;
|
|
|
|
|
|
|
|
|
|
/* start Bjoern Doebel's code (slightly modified) */
|
|
|
|
|
/* ============================================== */
|
|
|
|
|
unsigned opcount = 0;
|
|
|
|
|
unsigned operands[4] = { ~0U, ~0U, ~0U, ~0U };
|
|
|
|
|
enum {
|
|
|
|
|
RAT_IDX_MASK = 0x0FF,
|
|
|
|
|
RAT_IDX_OFFSET = 0x100
|
|
|
|
|
};
|
|
|
|
|
// get the instruction in plain text and inject the error there
|
|
|
|
|
// Note: we need to fetch some extra bytes into the array
|
|
|
|
|
// in case the faulty instruction is interpreted to be longer
|
|
|
|
|
// than the original one
|
|
|
|
|
Bit8u curr_instr_plain[MAX_INSTR_BYTES];
|
|
|
|
|
const Bit8u *addr = calculateInstructionAddress();
|
|
|
|
|
memcpy(curr_instr_plain, addr, MAX_INSTR_BYTES);
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < 3; ++i) {
|
|
|
|
|
/*
|
|
|
|
|
* Case 1: operand is a register
|
|
|
|
|
*/
|
|
|
|
|
if (_ud.operand[i].type == UD_OP_REG) {
|
|
|
|
|
operands[opcount++] = i;
|
|
|
|
|
} else if (_ud.operand[i].type == UD_OP_MEM) {
|
|
|
|
|
// CampaignManager has no idea of the instruction length
|
|
|
|
|
// (neither do we), therefore this small adaption
|
|
|
|
|
bit_offset %= length_in_bits;
|
|
|
|
|
param->msg.set_bit_offset(bit_offset);
|
|
|
|
|
|
|
|
|
|
// do some access calculation
|
|
|
|
|
int byte_index = bit_offset >> 3;
|
|
|
|
|
Bit8u bit_index = bit_offset & 7;
|
|
|
|
|
|
|
|
|
|
// apply the fault
|
|
|
|
|
curr_instr_plain[byte_index] ^= 0x80 >> bit_index;
|
|
|
|
|
|
|
|
|
|
// decode the instruction
|
|
|
|
|
bxInstruction_c bochs_instr;
|
|
|
|
|
memset(&bochs_instr, 0, sizeof(bxInstruction_c));
|
|
|
|
|
fetchInstruction(simulator.getCPUContext(), curr_instr_plain,
|
|
|
|
|
&bochs_instr);
|
|
|
|
|
|
|
|
|
|
// inject it
|
|
|
|
|
injectInstruction(currInstr, &bochs_instr);
|
|
|
|
|
|
|
|
|
|
// do the logging
|
|
|
|
|
logInjection();
|
|
|
|
|
} else if (exp_type == param->msg.RATFLIP) {
|
|
|
|
|
ud_type_t which = UD_NONE;
|
|
|
|
|
unsigned rnd = 0;
|
|
|
|
|
Udis86 udis(injection_ip);
|
|
|
|
|
do {
|
|
|
|
|
bxInstruction_c *currInstr = simulator.getCurrentInstruction();
|
|
|
|
|
udis.setInputBuffer(calculateInstructionAddress(), currInstr->ilen());
|
|
|
|
|
if (!udis.fetchNextInstruction()) {
|
|
|
|
|
terminateWithError(
|
|
|
|
|
"Could not decode instruction using UDIS86", 32);
|
|
|
|
|
}
|
|
|
|
|
ud_t _ud = udis.getCurrentState();
|
|
|
|
|
|
|
|
|
|
/* start Bjoern Doebel's code (slightly modified) */
|
|
|
|
|
/* ============================================== */
|
|
|
|
|
unsigned opcount = 0;
|
|
|
|
|
unsigned operands[4] = { ~0U, ~0U, ~0U, ~0U };
|
|
|
|
|
enum {
|
|
|
|
|
RAT_IDX_MASK = 0x0FF,
|
|
|
|
|
RAT_IDX_OFFSET = 0x100
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
for (unsigned i = 0; i < 3; ++i) {
|
|
|
|
|
/*
|
|
|
|
|
* Case 2: operand is memory op.
|
|
|
|
|
*
|
|
|
|
|
* In this case, we may have 2 registers involved for the
|
|
|
|
|
* index-scale address calculation.
|
|
|
|
|
* Case 1: operand is a register
|
|
|
|
|
*/
|
|
|
|
|
if (_ud.operand[i].base != 0) // 0 if hard-wired mem operand
|
|
|
|
|
if (_ud.operand[i].type == UD_OP_REG) {
|
|
|
|
|
operands[opcount++] = i;
|
|
|
|
|
if (_ud.operand[i].index != 0)
|
|
|
|
|
operands[opcount++] = i + RAT_IDX_OFFSET;
|
|
|
|
|
} else if (_ud.operand[i].type == UD_OP_MEM) {
|
|
|
|
|
/*
|
|
|
|
|
* Case 2: operand is memory op.
|
|
|
|
|
*
|
|
|
|
|
* In this case, we may have 2 registers involved for the
|
|
|
|
|
* index-scale address calculation.
|
|
|
|
|
*/
|
|
|
|
|
if (_ud.operand[i].base != 0) // 0 if hard-wired mem operand
|
|
|
|
|
operands[opcount++] = i;
|
|
|
|
|
if (_ud.operand[i].index != 0)
|
|
|
|
|
operands[opcount++] = i + RAT_IDX_OFFSET;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (opcount == 0) {
|
|
|
|
|
// try the next instruction
|
|
|
|
|
singleStep(true);
|
|
|
|
|
} else {
|
|
|
|
|
// assign the necessary variables
|
|
|
|
|
rnd = rand() % opcount;
|
|
|
|
|
|
|
|
|
|
if (operands[rnd] > RAT_IDX_OFFSET) {
|
|
|
|
|
which = _ud.operand[operands[rnd] - RAT_IDX_OFFSET].index;
|
|
|
|
|
if (opcount == 0) {
|
|
|
|
|
// try the next instruction
|
|
|
|
|
singleStep(true);
|
|
|
|
|
} else {
|
|
|
|
|
which = _ud.operand[operands[rnd]].base;
|
|
|
|
|
// assign the necessary variables
|
|
|
|
|
rnd = rand() % opcount;
|
|
|
|
|
|
|
|
|
|
if (operands[rnd] > RAT_IDX_OFFSET) {
|
|
|
|
|
which = _ud.operand[operands[rnd] - RAT_IDX_OFFSET].index;
|
|
|
|
|
} else {
|
|
|
|
|
which = _ud.operand[operands[rnd]].base;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* ============================================ */
|
|
|
|
|
/* end Bjoern Doebel's code (slightly modified) */
|
|
|
|
|
/* ============================================ */
|
|
|
|
|
/* end Bjoern Doebel's code (slightly modified) */
|
|
|
|
|
|
|
|
|
|
} while (which == UD_NONE &&
|
|
|
|
|
simulator.getCPU(0).getInstructionPointer() != L4SYS_FUNC_EXIT);
|
|
|
|
|
} while (which == UD_NONE &&
|
|
|
|
|
simulator.getCPU(0).getInstructionPointer() != L4SYS_FUNC_EXIT);
|
|
|
|
|
|
|
|
|
|
if (simulator.getCPU(0).getInstructionPointer() == L4SYS_FUNC_EXIT) {
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << "Reached the end of the experiment ";
|
|
|
|
|
ss << "without finding an appropriate instruction";
|
|
|
|
|
|
|
|
|
|
terminateWithError(ss.str(), 33);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// store the real injection point
|
|
|
|
|
param->msg.set_injection_ip(simulator.getCPU(0).getInstructionPointer());
|
|
|
|
|
|
|
|
|
|
// so we are able to flip the associated registers
|
|
|
|
|
// for details on the algorithm, see Bjoern Doebel's SWIFI/RATFlip class
|
|
|
|
|
|
|
|
|
|
// some declarations
|
|
|
|
|
GPRegisterId bochs_reg = Udis86::udisGPRToFailBochsGPR(which);
|
|
|
|
|
param->msg.set_register_offset(
|
|
|
|
|
static_cast<L4SysProtoMsg_RegisterType>(bochs_reg + 1));
|
|
|
|
|
ConcreteCPU &cpu = simulator.getCPU(0);
|
|
|
|
|
Register *bochsRegister = cpu.getRegister(bochs_reg);
|
|
|
|
|
Register *exchangeRegister = NULL;
|
|
|
|
|
|
|
|
|
|
// first, decide if the fault hits a register bound to this thread
|
|
|
|
|
// (ten percent chance)
|
|
|
|
|
if (rand() % 10 == 0) {
|
|
|
|
|
// assure exchange of registers
|
|
|
|
|
unsigned int exchg_reg = rand() % 7;
|
|
|
|
|
if (exchg_reg == bochs_reg)
|
|
|
|
|
exchg_reg++;
|
|
|
|
|
exchangeRegister = cpu.getRegister(exchg_reg);
|
|
|
|
|
param->msg.set_details(l4sysRegisterConversion.output(exchg_reg + 1));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// prepare the fault
|
|
|
|
|
regdata_t data = cpu.getRegisterContent(bochsRegister);
|
|
|
|
|
if (rnd > 0) {
|
|
|
|
|
//input register - do the fault injection here
|
|
|
|
|
regdata_t newdata = 0;
|
|
|
|
|
if (exchangeRegister != NULL) {
|
|
|
|
|
// the data is taken from a process register chosen before
|
|
|
|
|
newdata = cpu.getRegisterContent(exchangeRegister);
|
|
|
|
|
} else {
|
|
|
|
|
// the data comes from an uninitialised register
|
|
|
|
|
newdata = rand();
|
|
|
|
|
if (simulator.getCPU(0).getInstructionPointer() == L4SYS_FUNC_EXIT) {
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << "0x" << hex << newdata;
|
|
|
|
|
param->msg.set_details(ss.str());
|
|
|
|
|
ss << "Reached the end of the experiment ";
|
|
|
|
|
ss << "without finding an appropriate instruction";
|
|
|
|
|
|
|
|
|
|
terminateWithError(ss.str(), 33);
|
|
|
|
|
}
|
|
|
|
|
cpu.setRegisterContent(bochsRegister, newdata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// execute the instruction
|
|
|
|
|
singleStep(true);
|
|
|
|
|
// store the real injection point
|
|
|
|
|
param->msg.set_injection_ip(simulator.getCPU(0).getInstructionPointer());
|
|
|
|
|
|
|
|
|
|
// restore the register if we are still in the thread
|
|
|
|
|
if (rnd == 0) {
|
|
|
|
|
// output register - do the fault injection here
|
|
|
|
|
if (exchangeRegister != NULL) {
|
|
|
|
|
// write the result into the wrong local register
|
|
|
|
|
regdata_t newdata = cpu.getRegisterContent(bochsRegister);
|
|
|
|
|
cpu.setRegisterContent(exchangeRegister, newdata);
|
|
|
|
|
// so we are able to flip the associated registers
|
|
|
|
|
// for details on the algorithm, see Bjoern Doebel's SWIFI/RATFlip class
|
|
|
|
|
|
|
|
|
|
// some declarations
|
|
|
|
|
GPRegisterId bochs_reg = Udis86::udisGPRToFailBochsGPR(which);
|
|
|
|
|
param->msg.set_register_offset(static_cast<L4SysProtoMsg_RegisterType>(bochs_reg + 1));
|
|
|
|
|
ConcreteCPU &cpu = simulator.getCPU(0);
|
|
|
|
|
Register *bochsRegister = cpu.getRegister(bochs_reg);
|
|
|
|
|
Register *exchangeRegister = NULL;
|
|
|
|
|
|
|
|
|
|
// first, decide if the fault hits a register bound to this thread
|
|
|
|
|
// (ten percent chance)
|
|
|
|
|
if (rand() % 10 == 0) {
|
|
|
|
|
// assure exchange of registers
|
|
|
|
|
unsigned int exchg_reg = rand() % 7;
|
|
|
|
|
if (exchg_reg == bochs_reg)
|
|
|
|
|
exchg_reg++;
|
|
|
|
|
exchangeRegister = cpu.getRegister(exchg_reg);
|
|
|
|
|
param->msg.set_details(l4sysRegisterConversion.output(exchg_reg + 1));
|
|
|
|
|
}
|
|
|
|
|
// otherwise, just assume it is stored in an unused register
|
|
|
|
|
}
|
|
|
|
|
// restore the actual value of the register
|
|
|
|
|
// in reality, it would never have been overwritten
|
|
|
|
|
cpu.setRegisterContent(bochsRegister, data);
|
|
|
|
|
|
|
|
|
|
// log the injection
|
|
|
|
|
logInjection();
|
|
|
|
|
// prepare the fault
|
|
|
|
|
regdata_t data = cpu.getRegisterContent(bochsRegister);
|
|
|
|
|
if (rnd > 0) {
|
|
|
|
|
//input register - do the fault injection here
|
|
|
|
|
regdata_t newdata = 0;
|
|
|
|
|
if (exchangeRegister != NULL) {
|
|
|
|
|
// the data is taken from a process register chosen before
|
|
|
|
|
newdata = cpu.getRegisterContent(exchangeRegister);
|
|
|
|
|
} else {
|
|
|
|
|
// the data comes from an uninitialised register
|
|
|
|
|
newdata = rand();
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << "0x" << hex << newdata;
|
|
|
|
|
param->msg.set_details(ss.str());
|
|
|
|
|
}
|
|
|
|
|
cpu.setRegisterContent(bochsRegister, newdata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} else if (exp_type == param->msg.ALUINSTR) {
|
|
|
|
|
static BochsALUInstructions aluInstrObject(aluInstructions, aluInstructionsSize);
|
|
|
|
|
// find the closest ALU instruction after the current IP
|
|
|
|
|
|
|
|
|
|
bxInstruction_c *currInstr;
|
|
|
|
|
while (!aluInstrObject.isALUInstruction(
|
|
|
|
|
currInstr = simulator.getCurrentInstruction()) &&
|
|
|
|
|
simulator.getCPU(0).getInstructionPointer() != L4SYS_FUNC_EXIT) {
|
|
|
|
|
// execute the instruction
|
|
|
|
|
singleStep(true);
|
|
|
|
|
|
|
|
|
|
// restore the register if we are still in the thread
|
|
|
|
|
if (rnd == 0) {
|
|
|
|
|
// output register - do the fault injection here
|
|
|
|
|
if (exchangeRegister != NULL) {
|
|
|
|
|
// write the result into the wrong local register
|
|
|
|
|
regdata_t newdata = cpu.getRegisterContent(bochsRegister);
|
|
|
|
|
cpu.setRegisterContent(exchangeRegister, newdata);
|
|
|
|
|
}
|
|
|
|
|
// otherwise, just assume it is stored in an unused register
|
|
|
|
|
}
|
|
|
|
|
// restore the actual value of the register
|
|
|
|
|
// in reality, it would never have been overwritten
|
|
|
|
|
cpu.setRegisterContent(bochsRegister, data);
|
|
|
|
|
|
|
|
|
|
// log the injection
|
|
|
|
|
logInjection();
|
|
|
|
|
|
|
|
|
|
} else if (exp_type == param->msg.ALUINSTR) {
|
|
|
|
|
static BochsALUInstructions aluInstrObject(aluInstructions, aluInstructionsSize);
|
|
|
|
|
// find the closest ALU instruction after the current IP
|
|
|
|
|
|
|
|
|
|
bxInstruction_c *currInstr;
|
|
|
|
|
while (!aluInstrObject.isALUInstruction(
|
|
|
|
|
currInstr = simulator.getCurrentInstruction()) &&
|
|
|
|
|
simulator.getCPU(0).getInstructionPointer() != L4SYS_FUNC_EXIT) {
|
|
|
|
|
singleStep(true);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (simulator.getCPU(0).getInstructionPointer() == L4SYS_FUNC_EXIT) {
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << "Reached the end of the experiment ";
|
|
|
|
|
ss << "without finding an appropriate instruction";
|
|
|
|
|
|
|
|
|
|
terminateWithError(ss.str(), 34);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// store the real injection point
|
|
|
|
|
param->msg.set_injection_ip(simulator.getCPU(0).getInstructionPointer());
|
|
|
|
|
|
|
|
|
|
// now exchange it with a random equivalent
|
|
|
|
|
bxInstruction_c newInstr;
|
|
|
|
|
string details;
|
|
|
|
|
aluInstrObject.randomEquivalent(newInstr, details);
|
|
|
|
|
if (memcmp(&newInstr, currInstr, sizeof(bxInstruction_c)) == 0) {
|
|
|
|
|
// something went wrong - exit experiment
|
|
|
|
|
terminateWithError(
|
|
|
|
|
"Did not hit an ALU instruction - correct the source code please!",
|
|
|
|
|
40);
|
|
|
|
|
}
|
|
|
|
|
// record information on the new instruction
|
|
|
|
|
param->msg.set_details(details);
|
|
|
|
|
|
|
|
|
|
// inject it
|
|
|
|
|
injectInstruction(currInstr, &newInstr);
|
|
|
|
|
|
|
|
|
|
// do the logging
|
|
|
|
|
logInjection();
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
if (simulator.getCPU(0).getInstructionPointer() == L4SYS_FUNC_EXIT) {
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << "Reached the end of the experiment ";
|
|
|
|
|
ss << "without finding an appropriate instruction";
|
|
|
|
|
// aftermath
|
|
|
|
|
BPSingleListener ev_done(L4SYS_FUNC_EXIT, L4SYS_ADDRESS_SPACE);
|
|
|
|
|
simulator.addListener(&ev_done);
|
|
|
|
|
|
|
|
|
|
terminateWithError(ss.str(), 34);
|
|
|
|
|
}
|
|
|
|
|
unsigned instr_left = L4SYS_TOTINSTR - instr_offset; // XXX offset is in NUMINSTR, TOTINSTR is higher
|
|
|
|
|
BPSingleListener ev_incomplete(ANY_ADDR, L4SYS_ADDRESS_SPACE);
|
|
|
|
|
ev_incomplete.setCounter(static_cast<unsigned>(instr_left * 1.1));
|
|
|
|
|
simulator.addListener(&ev_incomplete);
|
|
|
|
|
|
|
|
|
|
// store the real injection point
|
|
|
|
|
param->msg.set_injection_ip(simulator.getCPU(0).getInstructionPointer());
|
|
|
|
|
TimerListener ev_timeout(calculateTimeout(instr_left));
|
|
|
|
|
simulator.addListener(&ev_timeout);
|
|
|
|
|
|
|
|
|
|
// now exchange it with a random equivalent
|
|
|
|
|
bxInstruction_c newInstr;
|
|
|
|
|
string details;
|
|
|
|
|
aluInstrObject.randomEquivalent(newInstr, details);
|
|
|
|
|
if (memcmp(&newInstr, currInstr, sizeof(bxInstruction_c)) == 0) {
|
|
|
|
|
// something went wrong - exit experiment
|
|
|
|
|
terminateWithError(
|
|
|
|
|
"Did not hit an ALU instruction - correct the source code please!",
|
|
|
|
|
40);
|
|
|
|
|
}
|
|
|
|
|
// record information on the new instruction
|
|
|
|
|
param->msg.set_details(details);
|
|
|
|
|
//do not discard output recorded so far
|
|
|
|
|
BaseListener *ev = waitIOOrOther(false);
|
|
|
|
|
|
|
|
|
|
// inject it
|
|
|
|
|
injectInstruction(currInstr, &newInstr);
|
|
|
|
|
|
|
|
|
|
// do the logging
|
|
|
|
|
logInjection();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// aftermath
|
|
|
|
|
BPSingleListener ev_done(L4SYS_FUNC_EXIT, L4SYS_ADDRESS_SPACE);
|
|
|
|
|
simulator.addListener(&ev_done);
|
|
|
|
|
|
|
|
|
|
unsigned instr_left = L4SYS_TOTINSTR - instr_offset; // XXX offset is in NUMINSTR, TOTINSTR is higher
|
|
|
|
|
BPSingleListener ev_incomplete(ANY_ADDR, L4SYS_ADDRESS_SPACE);
|
|
|
|
|
ev_incomplete.setCounter(
|
|
|
|
|
static_cast<unsigned>(instr_left * 1.1));
|
|
|
|
|
simulator.addListener(&ev_incomplete);
|
|
|
|
|
|
|
|
|
|
TimerListener ev_timeout(calculateTimeout(instr_left));
|
|
|
|
|
simulator.addListener(&ev_timeout);
|
|
|
|
|
|
|
|
|
|
//do not discard output recorded so far
|
|
|
|
|
BaseListener *ev = waitIOOrOther(false);
|
|
|
|
|
|
|
|
|
|
/* 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(output.c_str(), golden_run.c_str()) == 0) {
|
|
|
|
|
log << "Result DONE" << endl;
|
|
|
|
|
param->msg.set_resulttype(param->msg.DONE);
|
|
|
|
|
/* 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) {
|
|
|
|
|
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 {
|
|
|
|
|
log << "Result WRONG" << endl;
|
|
|
|
|
param->msg.set_resulttype(param->msg.WRONG);
|
|
|
|
|
param->msg.set_output(sanitised(output.c_str()));
|
|
|
|
|
log << "Result WTF?" << endl;
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << "eventid " << ev;
|
|
|
|
|
terminateWithError(ss.str(), 50);
|
|
|
|
|
}
|
|
|
|
|
} else if (ev == &ev_incomplete) {
|
|
|
|
|
log << "Result INCOMPLETE" << endl;
|
|
|
|
|
param->msg.set_resulttype(param->msg.INCOMPLETE);
|
|
|
|
|
param->msg.set_resultdata(
|
|
|
|
|
simulator.getCPU(0).getInstructionPointer());
|
|
|
|
|
param->msg.set_output(sanitised(output.c_str()));
|
|
|
|
|
} else if (ev == &ev_timeout) {
|
|
|
|
|
log << "Result TIMEOUT" << endl;
|
|
|
|
|
param->msg.set_resulttype(param->msg.TIMEOUT);
|
|
|
|
|
param->msg.set_resultdata(
|
|
|
|
|
simulator.getCPU(0).getInstructionPointer());
|
|
|
|
|
param->msg.set_output(sanitised(output.c_str()));
|
|
|
|
|
} else {
|
|
|
|
|
log << "Result WTF?" << endl;
|
|
|
|
|
stringstream ss;
|
|
|
|
|
ss << "eventid " << ev;
|
|
|
|
|
terminateWithError(ss.str(), 50);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_jc.sendResult(*param);
|
|
|
|
|
|