#include // getpid #include #include #include "util/Logger.hpp" #include "util/ElfReader.hpp" #include "experiment.hpp" #include "experimentInfo.hpp" #include "campaign.hpp" #include "psnr.hpp" #include "sal/SALConfig.hpp" #include "sal/SALInst.hpp" #include "sal/Memory.hpp" #include "sal/Listener.hpp" #include "sal/Register.hpp" // you need to have the tracing plugin enabled for this #include "../plugins/tracing/TracingPlugin.hpp" #define PREPARATION 1 #define LOCAL 1 using namespace std; using namespace fail; // Check if configuration dependencies are satisfied: //#if !defined(CONFIG_EVENT_BREAKPOINTS) || !defined(CONFIG_SR_RESTORE) || \ // !defined(CONFIG_SR_SAVE) || !defined(CONFIG_EVENT_TRAP) // #error This experiment needs: breakpoints, traps, save, and restore. Enable these in the configuration. //#endif bool NanoJPEGExperiment::run() { Logger log("nJPEG", true); log << "startup" << endl; #if PREPARATION == 1 // STEP 1: run until main starts, save state, record trace // TODO: store golden run output IOPortListener io(0x3f8, true); simulator.addListenerAndResume(&io); log << "main() reached, saving state" << endl; simulator.save(NANOJPEG_STATE); // record trace log << "restoring state" << endl; simulator.restore(NANOJPEG_STATE); log << "EIP = " << hex << simulator.getCPU(0).getInstructionPointer() << endl; log << "enabling tracing" << endl; TracingPlugin tp; tp.setLogIPOnly(true); ofstream of(NANOJPEG_TRACE); tp.setTraceFile(&of); // this must be done *after* configuring the plugin: simulator.addFlow(&tp); // count instructions simulator.addListener(&io); BPSingleListener step(ANY_ADDR); long counter = 0; while (true) { BaseListener *l = simulator.addListenerAndResume(&step); if (l == &io) { break; } counter++; } log << "golden run took " << dec << counter << " instructions" << endl; simulator.removeFlow(&tp); of.close(); #else // STEP 2: the actual experiment PSNR psnr(NANOJPEG_GOLDEN_PPM); ElfReader elfreader(NANOJPEG_ELF); guest_address_t addr_text_start = elfreader.getSymbol("___TEXT_START__").getAddress(); guest_address_t addr_text_end = elfreader.getSymbol("___TEXT_END__").getAddress(); guest_address_t addr_rodata_start = elfreader.getSymbol("___RODATA_START__").getAddress(); guest_address_t addr_bss_end = elfreader.getSymbol("___BSS_END__").getAddress(); guest_address_t addr_output_image_ptr = elfreader.getSymbol("output_image").getAddress(); guest_address_t addr_output_image_size = elfreader.getSymbol("output_image_size").getAddress(); log << "ELF symbols: text " << hex << addr_text_start << "-" << addr_text_end << " rodata/data/bss " << addr_rodata_start << "-" << addr_bss_end << " output_image ptr @ " << addr_output_image_ptr << ", size @ " << addr_output_image_size << endl; elfreader.~ElfReader(); #if !LOCAL for (int experiment_count = 0; experiment_count < 200 || (m_jc.getNumberOfUndoneJobs() != 0) ; ) { // only do 200 sequential experiments, to prevent swapping #endif // get an experiment parameter set log << "asking job server for experiment parameters" << endl; NanoJPEGExperimentData param; #if !LOCAL if (!m_jc.getParam(param)) { log << "Dying." << endl; simulator.terminate(1); } #else // XXX debug param.msg.set_instr_offset(2000); //param.msg.set_instr_address(12345); param.msg.set_register_id(0); // EAX //param.msg.set_bitmask(0xffffffffULL); param.msg.set_bitmask(0x2); param.msg.set_timeout(NANOJPEG_TIMEOUT); #endif int id = param.getWorkloadID(); int instr_offset = param.msg.instr_offset(); Register *reg = simulator.getCPU(0).getRegister(param.msg.register_id()); uint64_t bitmask = param.msg.bitmask(); int timeout = param.msg.timeout(); for (int bitnr = 0; bitnr < 32; ++bitnr) { if (!((1 << bitnr) & bitmask)) { continue; } #if !LOCAL experiment_count++; #endif log << "ID=" << id << " instr=0x" << hex << instr_offset << " reg=" << reg->getName() << " bitnr=" << dec << bitnr << " timeout=" << timeout << endl; NanoJPEGProtoMsg_Result *result = param.msg.add_result(); result->set_bitnr(bitnr); log << "restoring state" << endl; simulator.restore(NANOJPEG_STATE); // no need to wait if offset is 0 if (instr_offset > 0) { // XXX could be improved with intermediate states (reducing runtime until injection) BPSingleListener bp_fi; bp_fi.setWatchInstructionPointer(ANY_ADDR); bp_fi.setCounter(instr_offset); if (simulator.addListenerAndResume(&bp_fi) != &bp_fi) { log << "WTF?" << endl; simulator.terminate(1); } } // --- fault injection --- uint32_t data = reg->getData(); uint32_t newdata = data ^ (1 << bitnr); reg->setData(newdata); // note at what IP we did it uint32_t injection_ip = simulator.getCPU(0).getInstructionPointer(); param.msg.set_injection_ip(injection_ip); log << "fault injected @ ip " << injection_ip << " reg " << reg->getName() << " 0x" << hex << ((int)data) << " -> 0x" << ((int)newdata) << endl; // sanity check if (param.msg.has_instr_address() && injection_ip != param.msg.instr_address()) { stringstream ss; ss << "SANITY CHECK FAILED: " << injection_ip << " != " << param.msg.instr_address(); log << ss.str() << endl; result->set_resulttype(result->UNKNOWN); result->set_latest_ip(injection_ip); result->set_details(ss.str()); simulator.clearListeners(); continue; } // --- aftermath --- // possible outcomes: // - trap, "crash" // - jump outside text segment // - memory access outside of DATA/BSS // - (XXX unaligned jump inside text segment) // - (XXX weird instructions?) // - reaches the end, PSNR can be calculated // - reaches the end, output image broken // - global timeout // additional info: // - PSNR // normal experiment end: guest system tells us IOPortListener ev_io(0x3f8, true); simulator.addListener(&ev_io); // global timeout TimerListener ev_timeout(timeout); simulator.addListener(&ev_timeout); // catch traps as "extraordinary" ending TrapListener ev_trap(ANY_TRAP); simulator.addListener(&ev_trap); // jump outside text segment BPRangeListener ev_below_text(ANY_ADDR, addr_text_start - 1); BPRangeListener ev_beyond_text(addr_text_end + 1, ANY_ADDR); simulator.addListener(&ev_below_text); simulator.addListener(&ev_beyond_text); // memory access outside of data/bss segment MemAccessListener ev_mem_low(0x0, MemAccessEvent::MEM_READWRITE); ev_mem_low.setWatchWidth(addr_rodata_start); MemAccessListener ev_mem_high(addr_bss_end + 1, MemAccessEvent::MEM_READWRITE); ev_mem_high.setWatchWidth(0xFFFFFFFFU - (addr_bss_end + 1)); simulator.addListener(&ev_mem_low); simulator.addListener(&ev_mem_high); BaseListener *ev = simulator.resume(); // record latest IP regardless of result result->set_latest_ip(simulator.getCPU(0).getInstructionPointer()); if (ev == &ev_io) { log << "Result FINISHED/BROKEN (" << ev_io.getData() << ")" << endl; // retrieve image MemoryManager& mm = simulator.getMemoryManager(); uint32_t output_image_addr; mm.getBytes(addr_output_image_ptr, 4, &output_image_addr); uint32_t output_image_size; mm.getBytes(addr_output_image_size, 4, &output_image_size); log << "image address " << hex << output_image_addr << " size " << dec << output_image_size << endl; if (output_image_size != 3 * psnr.getWidth() * psnr.getHeight()) { log << "odd image size" << endl; result->set_details("odd image size"); result->set_resulttype(result->BROKEN); } else if (output_image_addr < addr_rodata_start || output_image_addr >= addr_bss_end) { log << "odd image address" << endl; result->set_details("odd image address"); result->set_resulttype(result->BROKEN); } else { result->set_resulttype(result->FINISHED); string image; image.resize(output_image_size); mm.getBytes(output_image_addr, output_image_size, &image[0]); // calculate PSNR double psnr_value = psnr.calculate(image); result->set_psnr(psnr_value); log << "PSNR: " << psnr_value << endl; } } else if (ev == &ev_timeout) { log << "Result TIMEOUT" << endl; result->set_resulttype(result->TIMEOUT); } else if (ev == &ev_below_text || ev == &ev_beyond_text) { log << "Result OUTSIDE" << endl; result->set_resulttype(result->OUTSIDE); } else if (ev == &ev_mem_low || ev == &ev_mem_high) { log << "Result OUTSIDEMEM (EIP " << result->latest_ip() << ")" << endl; result->set_resulttype(result->OUTSIDEMEM); } else if (ev == &ev_trap) { log << dec << "Result TRAP #" << ev_trap.getTriggerNumber() << endl; result->set_resulttype(result->TRAP); stringstream ss; ss << ev_trap.getTriggerNumber(); result->set_details(ss.str()); } else { log << "Result WTF?" << endl; result->set_resulttype(result->UNKNOWN); stringstream ss; ss << "eventid " << hex << ((unsigned long) ev) << " EIP " << simulator.getCPU(0).getInstructionPointer(); result->set_details(ss.str()); } } #if !LOCAL m_jc.sendResult(param); } #endif #endif // Explicitly terminate, or the simulator will continue to run. simulator.terminate(); }