6 * Fault observer for fault injection experiments
8 * (c) 2011-2013 Björn Döbel <doebel@os.inf.tu-dresden.de>,
9 * economic rights: Technische Universität Dresden (Germany)
10 * This file is part of TUD:OS and distributed under the terms of the
11 * GNU General Public License 2.
12 * Please see the COPYING-GPL-2 file for details.
15 #include "../asmjit/Assembler.h"
16 #include "../asmjit/MemoryManager.h"
17 #include "observers.h"
18 #include "debugging.h"
19 #include "../emulation"
20 #include "../app_loading"
22 /***************************************************************************
23 * ASMJit instruction wrappers. Generate an ASMJit instruction using the *
24 * input operands from a given UDIS86 disassembled instruction. *
25 ***************************************************************************/
31 * SEU simulation base class
33 * The flips are based on a common model:
34 * - place an INT3 on the instruction to target
35 * - upon the debug interrupt, revert the breakpoint and prepare the injection
37 * - single-step or continue until next INT3
38 * - upon next INT1/INT3 perform post-exec operations
41 class Flipper : public Emulator_base
44 l4_addr_t _flip_eip; // stores the original EIP that was flipped
45 Romain::App_model *_am;
46 unsigned _when; // XXX
47 unsigned _hitcount; // XXX
48 unsigned _interval; // XXX
52 Flipper(L4vcpu::Vcpu *vcpu,
53 Romain::App_model *am,
54 Romain::App_instance *inst)
55 : Emulator_base(vcpu, new Romain::AppModelAddressTranslator(am, inst)),
56 _flip_eip(vcpu->r()->ip), _am(am),
57 _when(0), _hitcount(0), _interval(1), _repeat(false)
60 _local_ip = _translator->translate(ip());
69 l4_addr_t prev_eip() { return _flip_eip; }
70 l4_addr_t next_eip() { return _flip_eip + ilen(); }
73 * Prepare flipped instruction
75 * Returns: true if we want to single-step afterwards, false if not.
77 virtual bool flip() = 0;
78 virtual void revert() = 0;
85 static AsmJit::GPReg ud_to_jit_reg(ud_type t)
87 #define MAP(x, y) case UD_R_##x: return AsmJit::y;
89 MAP(EAX, eax); MAP(EBX, ebx); MAP(ECX, ecx); MAP(EDX, edx);
90 MAP(ESI, esi); MAP(EDI, edi); MAP(ESP, esp);
91 MAP(AX, ax); MAP(BX, bx); MAP(CX, cx); MAP(DX, dx);
92 MAP(SI, si); MAP(DI, di); MAP(SP, sp);
93 MAP(AH, ah); MAP(BH, bh); MAP(CH, ch); MAP(DH, dh);
94 MAP(AL, al); MAP(BL, bl); MAP(CL, cl); MAP(DL, dl);
96 ERROR() << "I don't know a mapping for " << (unsigned)t << " yet.";
97 enter_kdebug("mapping");
100 return AsmJit::no_reg;
104 void disassemble_mem(l4_addr_t start, l4_addr_t remote, unsigned size)
108 count += Romain::InstructionPrinter(start + count, remote + count).ilen();
111 void flip_bit_in_register(AsmJit::Assembler& as, AsmJit::GPReg& reg)
113 unsigned i = random() % 32;
114 as.xor_(reg, (1 << i));
117 void commit_asm(AsmJit::Assembler& as, Romain::App_model *_am,
120 as.int3(); // last instruction is an INT3 which will trigger the replica
121 // to return to the master
122 void *ptr = as.make();
123 unsigned len = as.getOffset();
124 MSG() << "Created asm code @ " << ptr << ", size is " << len << " bytes";
125 _check(!ptr, "code generation failed");
126 Romain::dump_mem(ptr, len);
128 memcpy((void*)_am->trampoline_local(), ptr, len);
129 _vcpu->r()->ip = _am->trampoline_remote();
130 disassemble_mem(_am->trampoline_local(), _am->trampoline_remote(), len);
132 AsmJit::MemoryManager::getGlobal()->free(ptr);
136 #define FUNC1(name, op) \
138 void operator() (AsmJit::Assembler& as, AsmJit::GPReg& r) \
141 FUNC1(Decrement, dec);
142 FUNC1(Increment, inc);
145 #define FUNC2(name, op) \
147 void operator() (AsmJit::Assembler& as, AsmJit::GPReg& op1, \
148 AsmJit::GPReg& op2) { \
150 void operator() (AsmJit::Assembler& as, AsmJit::GPReg& op1, \
154 FUNC2(Addition, add);
155 FUNC2(Subtraction, sub);
159 FUNC2(ShiftLeft, shl);
160 FUNC2(ShiftRight, shr);
162 template <typename FN>
163 void UnaryOperation(ud_t ud, AsmJit::Assembler& as)
165 _check(ud.operand[0].scale != 0, "scale?");
166 AsmJit::GPReg op = AsmJitUser::ud_to_jit_reg(ud.operand[0].base);
171 template <typename FN>
172 void BinaryOperation(ud_t ud, AsmJit::Assembler& as)
174 AsmJit::GPReg op1, op2;
176 MSG() << "BinaryOp " << (unsigned)ud.operand[0].type
177 << " " << (unsigned)ud.operand[1].type
178 << " " << (unsigned)UD_OP_CONST;
180 _check(ud.operand[0].type != UD_OP_REG, "1st op must be reg");
181 _check(ud.operand[0].scale != 0, "scaling in reg??");
182 _check((ud.operand[1].type != UD_OP_REG) &&
183 (ud.operand[1].type != UD_OP_CONST) &&
184 (ud.operand[1].type != UD_OP_IMM), "2nd must be reg or immediate");
186 op1 = AsmJitUser::ud_to_jit_reg(ud.operand[0].base);
188 if (ud.operand[1].type == UD_OP_REG) {
189 _check(ud.operand[1].scale != 0, "scaling in reg??");
190 op2 = AsmJitUser::ud_to_jit_reg(ud.operand[1].base);
192 } else if ((ud.operand[1].type == UD_OP_IMM) ||
193 (ud.operand[1].type == UD_OP_CONST)) {
194 MSG() << "op size " << (int)ud.operand[1].size;
195 switch(ud.operand[1].size) {
196 case 8: imm = ud.operand[1].lval.sbyte; break;
197 case 16: imm = ud.operand[1].lval.sword; break;
198 case 32: imm = ud.operand[1].lval.sdword; break;
199 case 64: enter_kdebug("64bit!"); break;
201 // XXX: sign extension bug -> some of the opcodes
202 // have immediates that are sign extended...
203 MSG() << "Immediate: " << std::hex << imm;
211 * Emulate bit flips within general-purpose registers
213 class GPRFlipEmulator : public Flipper
219 ax, bx, cx, dx, flags,
224 char const* reg_to_str(targetRegs r)
226 #define CASE(reg) case reg: return #reg;
228 CASE(ax); CASE(bx); CASE(cx); CASE(dx); CASE(flags);
229 CASE(si); CASE(di); CASE(bp); CASE(ip); CASE(sp)
236 targetRegs str_to_reg(char const *r)
238 #define CASE(reg) if (strcmp(r, #reg) == 0) return reg;
239 CASE(ax); CASE(bx); CASE(cx); CASE(dx); CASE(flags);
240 CASE(si); CASE(di); CASE(bp); CASE(ip); CASE(sp);
246 unsigned _target_reg;
247 unsigned _target_bit;
250 GPRFlipEmulator(L4vcpu::Vcpu *vcpu,
251 Romain::App_model *am,
252 Romain::App_instance *inst)
253 : Flipper(vcpu, am, inst)
255 char const *reg = ConfigStringValue("swifi:register", "none");
256 _target_reg = str_to_reg(reg);
257 _target_bit = ConfigIntValue("swifi:bit", -1);
259 DEBUG() << "REG: " << reg << "(" << _target_reg
260 << ") BIT: " << _target_bit;
263 void flip_reg(unsigned reg, unsigned bit)
266 case pos: _vcpu->r()->pos ^= (1 << bit); break;
269 FLIP(ax); FLIP(bx); FLIP(cx); FLIP(dx); FLIP(flags);
270 FLIP(si); FLIP(di); FLIP(bp); FLIP(ip); FLIP(sp);
277 unsigned reg = (_target_reg == MAX_REG) ?
278 random() % MAX_REG : _target_reg;
279 unsigned bit = (_target_bit == -1) ?
280 random() % 32 : _target_bit;
282 MSG() << "selected (register, bit): (" << reg_to_str((targetRegs)reg)
283 << ", " << bit << ")";
290 virtual void revert() { }
295 * Instruction emulator that is used to flip a bit in instruction code
296 * (thereby emulating an SEU in the instruction decoder) and later on
299 class InstrFlipEmulator : public Flipper
301 l4_addr_t _flip_addr;
305 InstrFlipEmulator(L4vcpu::Vcpu *vcpu,
306 Romain::App_model *am,
307 Romain::App_instance *inst)
308 : Flipper(vcpu, am, inst),
309 _flip_addr(0), _flip_bit(0)
315 unsigned len = ilen();
318 _flip_bit = random() % len;
319 _flip_addr = local();
324 InstructionPrinter(_flip_addr, _flip_eip);
328 void virtual revert()
331 InstructionPrinter(_flip_addr, _flip_eip);
336 l4_addr_t start = _flip_addr;
337 l4_addr_t bit = _flip_bit;
338 MSG() << std::hex << "-1- addr: " << start;
339 MSG() << std::hex << "-2- bit: " << bit;
340 unsigned byte = bit >> 3;
345 MSG() << std::hex << "-3- flip byte: " << start;
346 MSG() << std::hex << "-4- flip bit: " << bit;
347 *(unsigned char*)start ^= (1 << bit);
353 * Inject ALU bitflips
355 * This class randomly choses from three potential errors:
356 * - instruction SEU -> the ALU performs an operation different
357 * from what it is supposed to do
358 * - input SEU -> one of the input operands encounters
359 * a bit flip while being processed
360 * - output SEU -> the output encounters a bit flip
361 * before being written back to the target
363 class ALUFlipEmulator : public Flipper,
364 public Romain::AsmJitUser
373 ALUError _mode; // the selected ALU error mode
374 ud_operand_t _target; // flip output: determined target
376 bool mnemonic_supported(unsigned mnemonic)
379 case UD_Iinc: case UD_Idec: case UD_Iadd: case UD_Isub:
380 case UD_Ishr: case UD_Ishl: case UD_Iand: case UD_Ior:
381 case UD_Isar: case UD_Ixor: case UD_Ineg:
389 ALUFlipEmulator(L4vcpu::Vcpu *vcpu,
390 Romain::App_model *am,
391 Romain::App_instance *inst)
392 : Flipper(vcpu, am, inst)
394 _mode = e_flip_instr; //(ALUError)(random() % 3);
396 if (!mnemonic_supported(_ud.mnemonic)) {
397 ERROR() << "Mnemonic " << _ud.mnemonic << " not yet supported.";
398 enter_kdebug("mnemonic");
407 case e_flip_instr: flip_instruction(); MSG() << "ALU::flip_instr()"; return false;
408 case e_flip_input: flip_input(); MSG() << "ALU::flip_in()"; return false;
409 case e_flip_output: flip_output(); MSG() << "ALU::flip_out()"; return true;
412 enter_kdebug("ALU::flip");
418 * Reverting only happens in the flip_output case. Otherwise,
419 * we leave the trampoline as is and the caller is responsible
420 * for setting the vCPU's EIP to the next valid instruction.
422 virtual void revert()
424 unsigned bit = random() % 32;
426 if (_mode == e_flip_output) {
427 MSG() << "flipping " << bit;
428 _vcpu->print_state();
429 switch(_target.base) {
430 case UD_R_AX: case UD_R_AL: case UD_R_AH: case UD_R_EAX:
431 _vcpu->r()->ax ^= (1 << bit); break;
432 case UD_R_BX: case UD_R_BL: case UD_R_BH: case UD_R_EBX:
433 _vcpu->r()->bx ^= (1 << bit); break;
434 case UD_R_CX: case UD_R_CL: case UD_R_CH: case UD_R_ECX:
435 _vcpu->r()->cx ^= (1 << bit); break;
436 case UD_R_DX: case UD_R_DL: case UD_R_DH: case UD_R_EDX:
437 _vcpu->r()->dx ^= (1 << bit); break;
439 _vcpu->r()->sp ^= (1 << bit); break;
441 _vcpu->r()->si ^= (1 << bit); break;
443 _vcpu->r()->di ^= (1 << bit); break;
445 ERROR() << "unhandled flip reg: " << _target.base;
453 * Flip an instruction.
455 * Look at the original instruction and randomly select an alternative
456 * one using the same input parameters. This maps unary operations to
457 * unary operations and binary ops to binary ops again.
460 void flip_instruction()
463 AsmJit::Assembler as;
465 if (_ud.operand[1].type == UD_NONE) { // single operand
469 #define CASE1(num, _class) case num: { UnaryOperation<_class>(_ud, as); } break;
470 CASE1(0, Decrement); CASE1(1, Increment); CASE1(2, Negate);
476 #define CASE2(num, _class) case num: { BinaryOperation<_class>(_ud, as); } break;
477 CASE2(0, Addition); CASE2(1, Subtraction);
478 CASE2(2, ShiftLeft); CASE2(3, ShiftRight);
479 CASE2(4, Or); CASE2(5, And); CASE2(6, Xor);
483 commit_asm(as, _am, _vcpu);
488 * Flip one input value.
490 * Selects one of the input values and generates code to flip a random bit
496 AsmJit::Assembler as;
497 unsigned bit = random() % 32;
500 * Step 1: Determine operand.
502 if (_ud.operand[1].type == UD_NONE) { // single operand
505 int i = random() % 2;
507 p = _ud.operand[1-i]; // the other reg
511 * Step 2: generate XOR() operation to flip bit.
513 if (o.type == UD_OP_REG) { // easy: simply xor with constant
514 _check(o.index != UD_NONE, "indexing...");
515 as.xor_(AsmJitUser::ud_to_jit_reg(o.base), (1 << bit));
517 /* If the target is not a register, we need to move it to one
518 * for XORing. For that purpose we use EAX unless it is the second
519 * operand, in which case we use EBX.
521 AsmJit::GPReg scratch;
523 case UD_R_AL: case UD_R_AH: case UD_R_AX: case UD_R_EAX:
524 scratch = AsmJit::ebx;
525 default: scratch = AsmJit::eax; break;
528 unsigned value = o.lval.udword;
529 as.push(scratch); // push scratch to stack
530 as.mov(scratch, value); // mov to scratch register
532 // XXX: shouldn't this be: XOR scratch register AND then move the
533 // result back to original memory address???
534 as.xor_(AsmJitUser::ud_to_jit_reg(p.base), (1 << bit));
535 as.pop(scratch); // pop previously stored scratch register
538 commit_asm(as, _am, _vcpu);
541 * Step 3: We only generated code to flip the bit. We still need to execute
542 * the original instruction. If we decrement the _flip_eip here by
543 * the original instruction length, then a future restart will continue
544 * at the original EIP:
551 * Prepare output flip by determining the target register.
555 _check(_ud.operand[0].scale != UD_NONE, "scaling in target reg?");
556 _target = _ud.operand[0];
562 * Emulate SEU in the register allocation table which maps physical
565 * When hitting an instruction, we determine which operand (if more than 1) is
566 * going to be affected:
569 * - store current value
570 * - overwrite with randomized value
572 * - restore current value
575 * - store current value
576 * - overwrite with randomized value
578 * - write to randomized target
579 * - restore current value
581 * Randomization works as follows: We simulate a physical register file that is
582 * 10x as large as the needed register file (this is the case on Intel's
583 * Sandybridge architecture, which has 160 integer registers for 16 GPRs).
584 * Thus, with a chance of 10% we pick a random other register from the available
585 * GPRs, and with a chance of 90% we use a random input and ignore the output
588 class RATFlipEmulator : public Flipper
590 ud_type which; // which operand is flipped
591 unsigned scratch; // temp storage
592 unsigned target; // which target reg to use
594 l4_umword_t randomize()
596 unsigned x = random() % 10;
597 if (x == 1) { // 10% chose real register
598 x = random() % 8 + 1;
600 case 1: target = UD_R_EAX; break;
601 case 2: target = UD_R_EBX; break;
602 case 3: target = UD_R_ECX; break;
603 case 4: target = UD_R_EDX; break;
604 case 5: target = UD_R_ESP; break;
605 case 6: target = UD_R_EBP; break;
606 case 7: target = UD_R_ESI; break;
607 case 8: target = UD_R_EDI; break;
609 return register_to_value((ud_type)target);
617 RATFlipEmulator(L4vcpu::Vcpu *vcpu,
618 Romain::App_model *am,
619 Romain::App_instance *inst)
620 : Flipper(vcpu, am, inst),
621 which(UD_NONE), scratch(0), target(0)
623 unsigned opcount = 0;
624 unsigned operands[4] = { ~0U, ~0U, ~0U, ~0U };
626 RAT_IDX_MASK = 0x0FF,
627 RAT_IDX_OFFSET = 0x100
630 for (unsigned i = 0; i < UD_MAX_OPERANDS; ++i) {
632 * Case 1: operand is a register
634 if (_ud.operand[i].type == UD_OP_REG) {
635 operands[opcount++] = i;
636 } else if (_ud.operand[i].type == UD_OP_MEM) {
638 * Case 2: operand is memory op.
640 * In this case, we may have 2 registers involved for the
641 * index-scale address calculation.
644 MSG() << "mem " << _ud.operand[i].base << " "
645 << _ud.operand[i].index;
647 if (_ud.operand[i].base != 0) // 0 if hard-wired mem operand
648 operands[opcount++] = i;
649 if (_ud.operand[i].index != 0)
650 operands[opcount++] = i + RAT_IDX_OFFSET;
654 unsigned rnd = opcount ? random() % opcount : 0;
656 if (operands[rnd] > RAT_IDX_OFFSET) {
657 which = _ud.operand[operands[rnd] - RAT_IDX_OFFSET].index;
659 which = _ud.operand[operands[rnd]].base;
661 MSG() << "operands " << opcount << " which " << which;
668 * In every case we need to get the original register value
669 * and restore it later on, because we emulate the real
670 * register not being touched at all.
672 scratch = register_to_value(which);
673 value_to_register(randomize(), which);
674 MSG() << "RAT: target " << std::hex << which
675 << " val " << scratch << " -> "
676 << register_to_value(which);
681 virtual void revert()
683 MSG() << "RAT: revert " << which << " flip target " << target;
684 if (which == _ud.operand[0].base) { // we work on an output reg
686 case 0: // output goes to nirvana, ignore
688 default: // output goes to another reg
689 // -> read from orig operand and write to target
691 l4_umword_t val = register_to_value(which);
692 value_to_register(val, (ud_type)target);
697 value_to_register(scratch, which);
701 class MemFlipEmulator : public Flipper,
705 MemFlipEmulator(L4vcpu::Vcpu *vcpu,
706 Romain::App_model *am,
707 Romain::App_instance *inst)
708 : Flipper(vcpu, am, inst)
714 for ( ; i < UD_MAX_OPERANDS; ++i) {
715 if (_ud.operand[i].type == UD_OP_MEM)
719 MSG() << "Mem operand is #" << i;
720 MSG() << " base " << std::setw(2) << _ud.operand[i].base;
721 MSG() << " index " << std::setw(2) << _ud.operand[i].index;
722 MSG() << " scale " << std::setw(2) << (int)_ud.operand[i].scale;
723 MSG() << " offs " << std::setw(2) << (int)_ud.operand[i].offset;
724 MSG() << " lval " << std::setw(8) << std::hex << (int)_ud.operand[i].lval.sdword;
726 l4_umword_t addr = 0;
727 if (_ud.operand[i].base != 0)
728 addr += register_to_value(_ud.operand[i].base);
729 if (_ud.operand[i].index != 0)
730 addr += (register_to_value(_ud.operand[i].index) << (unsigned)_ud.operand[i].scale);
731 switch(_ud.operand[i].offset) {
733 case 8: addr += _ud.operand[i].lval.sbyte; break;
734 case 16: addr += _ud.operand[i].lval.sword; break;
735 case 32: addr += _ud.operand[i].lval.sdword; break;
736 default: enter_kdebug("offset"); break;
739 MSG() << "target addr: " << std::hex << addr << " ("
740 << *(unsigned*)_translator->translate(addr) << ")";
742 AsmJit::Assembler as;
743 ud_operand_t &udreg = _ud.operand[0];
744 MSG() << "target op: " << udreg.base << "(" << register_to_value(udreg.base) << ")";
745 AsmJit::GPReg tmp = udreg.base == UD_R_EAX ? AsmJit::ebx : AsmJit::eax;
748 if (i == 0) { // output operand
749 ud_operand_t ®2 = _ud.operand[1];
753 MSG() << "const: " << std::hex << _ud.operand[1].lval.sdword;
754 as.push(AsmJit::ecx);
755 as.mov(AsmJit::ecx, AsmJit::imm(reg2.lval.sdword));
756 as.mov(tmp, AsmJit::imm(addr));
758 flip_bit_in_register(as, tmp);
760 as.mov(AsmJit::dword_ptr(tmp), AsmJit::ecx);
764 MSG() << _ud.operand[1].type;
768 } else { // input operand
769 _check(_ud.operand[0].index != 0, "indexing...");
770 _check(_ud.operand[0].scale != 0, "scaling...");
771 _check(_ud.operand[0].offset != 0, "offsetting...");
773 as.mov(tmp, AsmJit::imm(addr));
775 flip_bit_in_register(as, tmp);
777 as.mov(AsmJitUser::ud_to_jit_reg(udreg.base), AsmJit::dword_ptr(tmp));
781 commit_asm(as, _am, _vcpu);
783 //enter_kdebug("mem::flip");
787 virtual void revert()
791 class SWIFIPriv : public Romain::SWIFIObserver
795 GPR, // register bit flip
796 INSTR, // instruction decoding bit flip
797 ALU, // ALU fault injection
798 RAT, // RAT fault injection
799 MEM, // flip in mem address
802 Breakpoint * _breakpoint;
807 unsigned _alu_mode; // 0 - flip instr, 1 - flip input, 2 - flip output
809 void flipper_trap1(Romain::App_thread* t);
810 void flipper_trap3(Romain::App_thread* t, bool want_stepping = true);
812 void remove_breakpoint(Romain::App_thread *t)
814 MSG() << "bp: " << std::hex << _breakpoint;
818 t->vcpu()->r()->ip--;
822 unsigned decode_insn(l4_addr_t local, l4_addr_t remote, ud_t *ud);
826 DECLARE_OBSERVER("swifi");