Commit 1bc72d3b authored by German Leon's avatar German Leon
Browse files

Faltaba las classes

parent b3520f72
import random
import sys
import gdb
import re
import common_functions as cf # All common functions will be at common_functions module
import common_parameters as cp # All common parameters will be at common_parameters module
# from collections import deque
"""
BitFlip class
to implement the bit flip process.
The methods in this class will execute only inside a CUDA kernel
"""
class BitFlip:
def __init__(self, **kwargs):
# If kernel is not accessible it must return
self.__bits_to_flip = kwargs.get('bits_to_flip')
self.__fault_model = kwargs.get('fault_model')
self.__logging = kwargs.get('logging')
self.__injection_site = kwargs.get('injection_site')
self.fault_injected = False
"""
print exception info
"""
@staticmethod
def __exception_str():
exc_type, exc_obj, exc_tb = sys.exc_info()
return "Exception type {} at line {}".format(exc_type, exc_tb.tb_lineno)
"""
TODO: Describe the method
"""
def single_event(self):
# fault_injected attribute will be set by the methods that perform fault injection
# Focusing the thread. If focus succeed, otherwise not
if self.__thread_focus():
# Do the fault injection magic
# Register File mode
if cp.RF == self.__injection_site:
# Select the register before the injection
self.__select_register()
# RF is the default mode of injection
self.__rf_generic_injector()
# Instruction Output mode
elif cp.INST_OUT == self.__injection_site:
self.__inst_generic_injector()
# Instruction Address mode
elif cp.INST_ADD == self.__injection_site:
self.__logging.exception("INST_ADD NOT IMPLEMENTED YET")
self.__logging.exception(self.__exception_str())
self.fault_injected = False
# Test fault injection result
self.__logging.info("Fault Injection " + ("Successful" if self.fault_injected else "Went Wrong"))
"""
Selects a valid thread for a specific
kernel
return the coordinates for the block
and the thread
"""
def __thread_focus(self):
try:
# Selecting the block, it must be a valid block
blocks = cf.execute_command(gdb=gdb, to_execute="info cuda blocks")
# empty lists are always false
while blocks:
chosen_block = random.choice(blocks)
# remove it from the options
blocks.remove(chosen_block)
m = re.match(r".*\(.*\).*\((\d+),(\d+),(\d+)\).*", chosen_block)
# Try to focus
if m:
change_focus_block_cmd = "cuda block {},{},{}".format(m.group(1), m.group(2), m.group(3))
block_focus = cf.execute_command(gdb=gdb, to_execute=change_focus_block_cmd)
# empty lists are always false
if block_focus:
# Thread focus return information
self.__logging.info("CUDA_BLOCK_FOCUS:{}".format(block_focus))
# No need to continue to seek
break
# Selecting the thread
threads = cf.execute_command(gdb=gdb, to_execute="info cuda threads")
while threads:
chosen_thread = random.choice(threads)
# remove it from the options
threads.remove(chosen_thread)
m = re.match(r".*\(.*\).*\(.*\).*\(.*\).*\((\d+),(\d+),(\d+)\).*", chosen_thread)
# Try to focus
if m:
change_focus_thread_cmd = "cuda thread {},{},{}".format(m.group(1), m.group(2), m.group(3))
thread_focus = cf.execute_command(gdb=gdb, to_execute=change_focus_thread_cmd)
# empty lists are always false
if thread_focus:
# Thread focus return information
self.__logging.info("CUDA_THREAD_FOCUS:{}".format(thread_focus))
# No need to continue to seek
break
except Exception as err:
err_str = str(err)
self.__logging.exception("CUDA_FOCUS_CANNOT_BE_REQUESTED, ERROR:" + err_str)
# No need to continue if no active kernel
if err_str == cp.FOCUS_ERROR_STRING:
return False
# If we are inside the kernel return true
return True
"""
Flip a bit or multiple bits based on a fault model
"""
def __rf_generic_injector(self):
try:
# get register content
reg_cmd = cf.execute_command(gdb, "p/t ${}".format(self.__register))
m = re.match(r'\$(\d+)[ ]*=[ ]*(\S+).*', reg_cmd[0])
reg_content_old = str(m.group(2))
# Make sure that binary value will have max size register
reg_content_full_bits = str(
'0' * (cp.SINGLE_MAX_SIZE_REGISTER - len(reg_content_old))) + reg_content_old
reg_content_new = ''
# Single or double bit flip or Least significant bits
if self.__fault_model in [cp.FLIP_SINGLE_BIT, cp.FLIP_TWO_BITS, cp.LEAST_16_BITS, cp.LEAST_8_BITS]:
# single bit flip or Double bit flip
reg_content_new = reg_content_full_bits
for bit_to_flip in self.__bits_to_flip:
reg_content_new = self.__flip_a_bit(int(bit_to_flip), reg_content_new)
reg_content_new = hex(int(reg_content_new, 2))
# Random value or Zero value
elif self.__fault_model == cp.RANDOM_VALUE or self.__fault_model == cp.ZERO_VALUE:
# random value is stored at bits_to_flip[0]
reg_content_new = self.__bits_to_flip[0]
# send the new value to gdb
flip_command = "set ${} = {}".format(self.__register, reg_content_new)
reg_cmd_flipped = cf.execute_command(gdb, flip_command)
# ['$2 = 100000000111111111111111']
modify_output = cf.execute_command(gdb, "p/t ${}".format(self.__register))[0]
# With string split it is easier to crash
reg_modified = re.match(r"(.*)=(.*)", modify_output).group(2).strip()
# Return the fault confirmation
self.fault_injected = reg_content_old != reg_modified
# Log the register only if the fault was injected, reduce unnecessary file write
if self.fault_injected:
# LOGGING
self.__logging.info("SELECTED_REGISTER:{}".format(self.__register))
# Logging info result extracted from register
self.__logging.info("old_value:{}".format(reg_content_old))
# Also logging the new value
self.__logging.info("new_value:{}".format(reg_modified))
# Log if something was printed
# empty list is always false
if reg_cmd_flipped:
self.__logging.info("flip command return:{}".format(reg_cmd_flipped))
except Exception as err:
self.__logging.exception("fault_injection_python_exception: {}".format(err))
self.__logging.exception(self.__exception_str())
self.fault_injected = False
"""
Flip only a bit in a register content
"""
@staticmethod
def __flip_a_bit(bit_to_flip, reg_content):
return reg_content[:bit_to_flip] + ('0' if reg_content[bit_to_flip] == '1' else '1') + reg_content[
bit_to_flip + 1:]
"""
Runtime select register
"""
def __select_register(self):
# Start on 1 to consider R0
max_num_register = 1
# registers_list.popleft()
registers_list = cf.execute_command(gdb=gdb, to_execute="info registers")
for line in registers_list:
m = re.match(r".*R.*0x([0-9a-fA-F]+).*", line)
if m and m.group(1) != '0':
max_num_register += 1
self.__register = "R{}".format(random.randint(0, max_num_register))
"""
Instruction injector
"""
def __inst_generic_injector(self):
disassemble_array = cf.execute_command(gdb=gdb, to_execute="disassemble")
# Search the line to inject
# -1 will use the next instruction after program counter
for i in range(0, len(disassemble_array) - 1):
next_line = disassemble_array[i + 1]
# => defines where the program counter is
# There is an instruction on this line
# then inject in the output register
if "=>" in disassemble_array[i] and re.match(r".*:\t(\S+) .*", next_line):
# If it gets the PC + 1 then I must inject in the input of the next instruction
# Which is the most right register (-1 index)
self.__register = "R{}".format(re.findall(r"R(\d+)", next_line)[-1])
self.__logging.info("SELECTED_REGISTER:{}".format(self.__register))
self.__logging.info("ASSM_LINE:{}".format(next_line))
# __rf_generic_injector will set fault_injected attribute
self.__rf_generic_injector()
break # avoid execute the else
else:
self.__logging.exception("SEARCH_FOR_PC_INDICATOR_FAILED")
self.fault_injected = False
from inspect import currentframe
import random
import gdb
import re
import common_functions as cf # All common functions will be at common_functions module
import common_parameters as cp # All common parameters will be at common_parameters module
GDB_TYPES_DICT = {
gdb.TYPE_CODE_PTR: "The type is a pointer.",
gdb.TYPE_CODE_ARRAY: "The type is an array.",
gdb.TYPE_CODE_STRUCT: "The type is a structure.",
gdb.TYPE_CODE_UNION: "The type is a union.",
gdb.TYPE_CODE_ENUM: "The type is an enum.",
gdb.TYPE_CODE_FLAGS: "A bit flags type, used for things such as status registers.",
gdb.TYPE_CODE_FUNC: "The type is a function.",
gdb.TYPE_CODE_INT: "The type is an integer type.",
gdb.TYPE_CODE_FLT: "A floating point type.",
gdb.TYPE_CODE_VOID: "The special type void.",
gdb.TYPE_CODE_SET: "A Pascal set type.",
gdb.TYPE_CODE_RANGE: "A range type, that is, an integer type with bounds.",
gdb.TYPE_CODE_STRING: "A string type. Note that this is only used for certain languages with "
"language-defined string types; C strings are not represented this way.",
gdb.TYPE_CODE_BITSTRING: "A string of bits. It is deprecated.",
gdb.TYPE_CODE_ERROR: "An unknown or erroneous type.",
gdb.TYPE_CODE_METHOD: "A method type, as found in C++ or Java.",
gdb.TYPE_CODE_METHODPTR: "A pointer-to-member-function.",
gdb.TYPE_CODE_MEMBERPTR: "A pointer-to-member.",
gdb.TYPE_CODE_REF: "A reference type.",
gdb.TYPE_CODE_CHAR: "A character type.",
gdb.TYPE_CODE_BOOL: "A boolean type.",
gdb.TYPE_CODE_COMPLEX: "A complex float type.",
gdb.TYPE_CODE_TYPEDEF: "A typedef to some other type.",
gdb.TYPE_CODE_NAMESPACE: "A C++ namespace.",
gdb.TYPE_CODE_DECFLOAT: "A decimal floating point type.",
gdb.TYPE_CODE_INTERNAL_FUNCTION: "A function internal to gdb. This "
"is the type used to represent convenience functions."
}
"""
Fault injection breakpoint class, will do a flip of the bit(s) when breakpoint is hit
"""
class FaultInjectionBreakpoint(gdb.Breakpoint):
def __init__(self, *args, **kwargs):
# If kernel is not accessible it must return
self.__kludge = kwargs.pop('kludge') if 'kludge' in kwargs else None
self.__register = kwargs.pop('register') if 'register' in kwargs else None
self.__bits_to_flip = kwargs.pop('bits_to_flip') if 'bits_to_flip' in kwargs else None
self.__fault_model = kwargs.pop('fault_model') if 'fault_model' in kwargs else None
self.__logging = kwargs.pop('logging') if 'logging' in kwargs else None
self.__injection_mode = kwargs.pop('injection_mode') if 'injection_mode' in kwargs else None
super(FaultInjectionBreakpoint, self).__init__(*args, **kwargs)
def stop(self):
if self.__kludge:
return True
# This if avoid the creation of another event connection
# for some reason gdb cannot breakpoint addresses before
# a normal breakpoint is hit
self.__logging.debug("Trying Fault Injection with {} mode".format(self.__injection_mode))
try:
# Focusing the thread
self.__thread_focus()
except Exception as err:
# Even if CUDA focus was not successful we keep going
self.__logging.exception("CUDA_FOCUS_CANNOT_BE_REQUESTED. KEEP GOING, with error {}".format(err))
try:
# Register if fault was injected or not
fault_injected = False
# Do the fault injection magic
# RF is the default mode of injection
if 'RF' in self.__injection_mode or self.__injection_mode is None:
fault_injected = self.__rf_generic_injector()
elif 'VARS' in self.__injection_mode:
fault_injected = self.__var_generic_injector()
elif 'INST' in self.__injection_mode:
fault_injected = self.__inst_generic_injector()
# Test fault injection result
if fault_injected:
self.__logging.info("Fault Injection Successful")
else:
self.__logging.info("Fault Injection Went Wrong")
except Exception as err:
self.__logging.exception("fault_injection_python_exception: {}".format(err))
self.__logging.exception("Fault Injection Went Wrong")
return True
"""
Selects a valid thread for a specific
kernel
return the coordinates for the block
and the thread
"""
def __thread_focus(self):
# Selecting the block
blocks = cf.execute_command(gdb=gdb, to_execute="info cuda blocks")
# it must be a valid block
block = None
block_len = len(blocks)
while not block:
block_index = random.randint(0, block_len)
if 'running' in blocks[block_index] and '*' not in blocks[block_index]:
m = re.match(".*\((\d+),(\d+),(\d+)\).*\((\d+),(\d+),(\d+)\).*", blocks[block_index])
if m:
block = "{},{},{}".format(m.group(1), m.group(2), m.group(3))
change_focus_block_cmd = "cuda block {}".format(block)
block_focus = cf.execute_command(gdb=gdb, to_execute=change_focus_block_cmd)
# Thread focus return information
self.__logging.info(
"CUDA_BLOCK_FOCUS: " + str(block_focus).replace("[", "").replace("]", "").strip())
# Selecting the thread
threads = cf.execute_command(gdb=gdb, to_execute="info cuda threads")
thread = None
thread_len = len(threads)
while not thread:
thread_index = random.randint(0, thread_len)
pattern = ".*\((\d+),(\d+),(\d+)\).*\((\d+),(\d+),(\d+)\).*\((\d+),(\d+),(\d+)\).*\((\d+),(\d+),(\d+)\).*"
m = re.match(pattern, threads[thread_index])
if m:
thread = "{},{},{}".format(m.group(10), m.group(11), m.group(12))
change_focus_thread_cmd = "cuda thread {}".format(thread)
thread_focus = cf.execute_command(gdb=gdb, to_execute=change_focus_thread_cmd)
# Thread focus return information
self.__logging.info(
"CUDA_THREAD_FOCUS: " + str(thread_focus).replace("[", "").replace("]", "").strip())
"""
Flip a bit or multiple bits based on a fault model
"""
def __rf_generic_injector(self):
# get register content
reg_cmd = cf.execute_command(gdb, "p/t ${}".format(self.__register))
m = re.match('\$(\d+)[ ]*=[ ]*(\S+).*', reg_cmd[0])
reg_content_old = str(m.group(2))
# Make sure that binary value will have max size register
reg_content_full_bits = str('0' * (cp.SINGLE_MAX_SIZE_REGISTER - len(reg_content_old))) + reg_content_old
reg_content_new = ''
# Single bit flip or Least significant bits
if self.__fault_model in [0, 1, 4]:
# single bit flip or Double bit flip
reg_content_new = reg_content_full_bits
for bit_to_flip in self.__bits_to_flip:
reg_content_new = self.__flip_a_bit(int(bit_to_flip), reg_content_new)
reg_content_new = hex(int(reg_content_new, 2))
# Random value or Zero value
elif self.__fault_model in [2, 3]:
# random value is stored at bits_to_flip[0]
reg_content_new = self.__bits_to_flip[0]
# send the new value to gdb
flip_command = "set ${} = {}".format(self.__register, reg_content_new)
reg_cmd_flipped = cf.execute_command(gdb, flip_command)
# ['$2 = 100000000111111111111111']
reg_modified = str(cf.execute_command(gdb, "p/t ${}".format(self.__register))[0]).split("=")[1].strip()
# LOGGING
# Logging info result extracted from register
self.__logging.info("old_value:{}".format(reg_content_old))
# Also logging the new value
self.__logging.info("new_value:{}".format(reg_modified))
# Log command return only something was printed
if len(reg_cmd_flipped) > 0:
self.__logging.info("flip command return:{}".format(reg_cmd_flipped))
# Return the fault confirmation
return reg_content_old != reg_modified
"""
Flip only a bit in a register content
"""
@staticmethod
def __flip_a_bit(bit_to_flip, reg_content):
new_bit = '0' if reg_content[bit_to_flip] == '1' else '1'
reg_content = reg_content[:bit_to_flip] + new_bit + reg_content[bit_to_flip + 1:]
return reg_content
"""
generic injector
"""
def __var_generic_injector(self):
self.__logging.debug("VAR GENERIC INJECTOR")
# inferior = gdb.selected_inferior()
for inf in gdb.inferiors():
self.__logging.debug("Inferior PID: " + str(inf.pid))
self.__logging.debug("Inferior is valid: " + str(inf.is_valid()))
self.__logging.debug("Inferior #threads: " + str(len(inf.threads())))
self.__logging.debug("Backtrace BEGIN:")
bt = gdb.execute("bt", to_string=True)
self.__logging.debug(bt)
source_lines = gdb.execute("list", to_string=True)
self.__logging.debug(source_lines)
self.__logging.debug("FOR INFERIOR THREADS")
threads_symbols = self.__get_all_valid_symbols()
if len(threads_symbols) > 0:
self.__logging.debug("TH SYMBOLS APPEND")
self.__logging.debug("Thread name: " + str(self.thread.name))
self.__logging.debug("Thread num: " + str(self.thread.num))
self.__logging.debug("Thread ptid: " + str(self.thread.ptid))
return self.__chose_frame_to_flip(threads_symbols)
"""
Get all the symbols of the stacked frames, returns a list of tuples [frame, symbolsList]
where frame is a GDB Frame object and symbolsList is a list of all symbols of this frame
"""
def __get_all_valid_symbols(self):
frame = gdb.selected_frame()
symbols = self.__get_frame_symbols(frame)
return symbols
"""
Returns a list of all symbols of the frame, frame is a GDB Frame object
"""
def __get_frame_symbols(self, frame):
try:
symbols = list()
block = frame.block()
while block:
for symbol in block:
if self.__is_bit_flip_possible(symbol, frame):
symbols.append(symbol)
block = block.superblock
return symbols
except Exception as err:
self.__logging.exception("GET_FRAME_SYMBOLS_ERROR: {}".format(err))
return None
"""
Returns True if we can bitflip some bit of this symbol, i.e. if this is a variable or
constant and not functions and another symbols
"""
def __is_bit_flip_possible(self, symbol, frame):
if symbol.is_variable or symbol.is_constant or symbol.is_argument:
var = symbol.value(frame)
address = re.sub("<.*>|\".*\"", "", str(var.address))
if var.address is not None and not var.is_optimized_out and hex(int(address, 16)) > hex(
int("0x0", 16)):
self.__logging.debug("BIT FLIP IS POSSIBLE")
return True
return False
def __single_bit_flip_word_address(self, address, byte_sizeof):
buf_fog = "Fault Model: Single bit-flip"
buf_fog += "\n"
buf_fog += "base address to flip value: " + str(address)
self.__logging.debug(buf_fog)
random.seed()
address_offset = random.randint(0, byte_sizeof - 1)
address_f = hex(int(address, 16) + address_offset)
x_mem = "x/1tb " + str(address_f)
bin_data = gdb.execute(x_mem, to_string=True)
bin_data = re.sub(".*:|\s", "", bin_data)
bin_data_l = list(bin_data)
bit_pos = random.randint(0, len(bin_data_l) - 1)
if bin_data_l[bit_pos] == '1':
bin_data_l[bit_pos] = '0'
else:
bin_data_l[bit_pos] = '1'
bin_data = ''.join(bin_data_l)
set_cmd = "set {char}" + address_f + " = " + hex(int(bin_data, 2))
gdb.execute(set_cmd)
@staticmethod
def __show_memory_content(address, byte_sizeof):
x_mem = "x/" + str(byte_sizeof) + "xb " + address
hex_data = gdb.execute(x_mem, to_string=True)
hex_data = re.sub(".*:|\s", "", hex_data)
return hex_data
def __generic_bit_flip(self, value):
address = re.sub("<.*>|\".*\"", "", str(value.address))
byte_sizeof = value.type.strip_typedefs().sizeof
self.__logging.debug("old_value:{}".format(self.__show_memory_content(address, byte_sizeof)))
if self.__fault_model == 0:
self.__single_bit_flip_word_address(address, byte_sizeof)
elif self.__fault_model == 1:
raise NotImplementedError("Fault model not implemented yet")
elif self.__fault_model == 2:
raise NotImplementedError("Fault model not implemented yet")
elif self.__fault_model == 3:
raise NotImplementedError("Fault model not implemented yet")
elif self.__fault_model == 4:
raise NotImplementedError("Fault model not implemented yet")
self.__logging.debug("new_value:{}".format(self.__show_memory_content(address, byte_sizeof)))
def __chose_frame_to_flip(self, frame_symbols):
frames_num = len(frame_symbols)
if frames_num <= 0:
return False
self.__logging.debug("INSIDE CHOOSED FRAME TO FLIP FRAME POS {}".format(frames_num))
random.seed()
symbols = frame_symbols
symbols_num = len(symbols)
symbol_pos = random.randint(0, symbols_num - 1)
symbol = symbols[symbol_pos]
var_gdb = symbol.value(gdb.selected_frame())
# Testing if I can access GDB registers
self.__logging.info("TEST VAR GDB {}".format(var_gdb))
self.__logging.info(
"SYMTAB {}\nis_valid() {}\n".format(symbol.symtab, symbol.symtab.is_valid()))
self.__logging.info(
"Filename {}\nObjfile {}\nproducer {}\nfullname {}\nglobal block {}\nstatic block {}linetable {}".format(
symbol.symtab.filename, symbol.symtab.objfile, symbol.symtab.producer, symbol.symtab.is_valid(),
symbol.symtab.fullname(), symbol.symtab.global_block(), symbol.symtab.static_block(),
symbol.symtab.linetable()))
self.__logging.info("TEST SYMBOL ATTRIBUTES")
self.__logging.info(
"Symbol type {}\nline {}\nname {}\nlinkage_name {}\nprint_name {}\naddr_class {}\nneeds_frame {}\n".format(
symbol.type, symbol.line, symbol.name, symbol.linkage_name, symbol.print_name, symbol.addr_class,
symbol.needs_frame))
self.__var_bit_flip_value(var_gdb)
if var_gdb.type.strip_typedefs().code is gdb.TYPE_CODE_RANGE:
self.__logging.debug("Type range: " + str(var_gdb.type.strip_typedefs().range()))
# If it is an structure it has fields
try:
for field in symbol.type.fields():
self.__logging.debug("Field name: " + str(field.name))
self.__logging.debug("Field Type: " + str(GDB_TYPES_DICT[field.type.strip_typedefs().code]))
self.__logging.debug("Field Type sizeof: " + str(field.type.strip_typedefs().sizeof))
if field.type.strip_typedefs().code is gdb.TYPE_CODE_RANGE:
self.__logging.debug("Field Type range: " + str(field.type.strip_typedefs().range()))
except Exception as err:
self.__logging.debug("ERR {} LINE {}".format(err, currentframe().f_back.f_lineno))
return True
def __var_bit_flip_value(self, value):
self.__logging.debug("{}. VALUE {}".format(GDB_TYPES_DICT[value.type.strip_typedefs().code], value))
if value.type.strip_typedefs().code is gdb.TYPE_CODE_PTR:
random.seed()
pointer_flip = random.randint(0, 1)
pointed_address = re.sub("<.*>|\".*\"", "", str(value.referenced_value().address))
if pointer_flip or hex(int(pointed_address, 16)) <= hex(int("0x0", 16)):
self.__logging.debug("Flipping a bit of the pointer")
self.__generic_bit_flip(value)
else:
self.__logging.debug("Flipping a bit of the value pointed by a pointer")
self.__var_bit_flip_value(value.referenced_value())
elif value.type.strip_typedefs().code is gdb.TYPE_CODE_REF:
random.seed()
ref_flip = random.randint(0, 1)
pointed_address = re.sub("<.*>|\".*\"", "", str(value.referenced_value().address))
if ref_flip or hex(int(pointed_address, 16)) <= hex(int("0x0", 16)):
self.__logging.debug("Flipping a bit of the reference")
self.__generic_bit_flip(value)
else:
self.__logging.debug("Flipping a bit of the value pointed by a reference")
self.__var_bit_flip_value(value.referenced_value())
elif value.type.strip_typedefs().code is gdb.TYPE_CODE_ARRAY:
range_max = value.type.strip_typedefs().range()[1]
random.seed()
array_pos = random.randint(0, range_max)
self.__logging.debug("Flipping array at pos: " + str(array_pos))
self.__var_bit_flip_value(value[array_pos])
elif value.type.strip_typedefs().code is gdb.TYPE_CODE_STRUCT:
fields = value.type.fields()
if not fields:
self.__generic_bit_flip(value)
random.seed()
field_pos = random.randint(0, len(fields) - 1)
new_value = value[fields[field_pos]]
count = 0
new_address = re.sub("<.*>|\".*\"", "", str(new_value.address))
while new_value.address is None or new_value.is_optimized_out or hex(int(new_address, 16)) <= hex(
int("0x0", 16)):
random.seed()
field_pos = random.randint(0, len(fields) - 1)
new_value = value[fields[field_pos]]
new_address = re.sub("<.*>|\".*\"", "", str(new_value.address))
count += 1
if count == 20:
raise Exception("Unable to exit loop in struct fields; Exiting wihtout making a bit flip")
self.__logging.debug("Flipping value of field: " + str(fields[field_pos].name))
self.__var_bit_flip_value(new_value)
elif value.type.strip_typedefs().code is gdb.TYPE_CODE_UNION:
fields = value.type.fields()
random.seed()
field_pos = random.randint(0, len(fields) - 1)
new_value = value[fields[field_pos]]
count = 0
new_address = re.sub("<.*>|\".*\"", "", str(new_value.address))
while new_value.address is None or new_value.is_optimized_out or hex(int(new_address, 16)) <= hex(
int("0x0", 16)):
random.seed()
field_pos = random.randint(0, len(fields) - 1)
new_value = value[fields[field_pos]]
count += 1
if count == 20:
# logMsg(str("Error: unable to exit loop in union fields; Exiting wihtout making bitflip"))
# return
raise Exception("Unable to exit loop in union fields; Exiting wihtout making bitflip")
self.__logging.debug("Flipping value of field name: " + str(fields[field_pos].name))
self.__var_bit_flip_value(new_value)
else:
self.__logging.debug("Generic bit flip")
self.__generic_bit_flip(value)
"""
Instructions generic injector
"""
def __inst_generic_injector(self):
raise NotImplementedError
import time
import datetime
"""
Logging class
"""
class Logging:
log_file = None
debug_var = None
unique_id = None
def __init__(self, log_file, unique_id=''):
self.log_file = log_file
self.unique_id = unique_id
def info(self, msg):
with open(self.log_file, "a") as fp:
d = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')
fp.write("[INFO -- " + d + "]\n" + msg + "\n")
# fp.close()
def exception(self, msg):
with open(self.log_file, "a") as fp:
d = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')
fp.write("[EXCEPTION -- " + d + "]\n" + msg + "\n")
# fp.close()
def error(self, msg):
with open(self.log_file, "a") as fp:
d = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')
fp.write("[ERROR -- " + d + "]\n" + msg + "\n")
# fp.close()
def debug(self, msg):
with open(self.log_file, "a") as fp:
d = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')
fp.write("[DEBUG -- " + d + "]\n" + msg + "\n")
# fp.close()
def summary(self, msg):
with open(self.log_file, "a") as fp:
d = datetime.datetime.fromtimestamp(time.time()).strftime('%Y-%m-%d %H:%M:%S')
fp.write("[SUMMARY -- " + d + "]\nFI-uniqueID=" + str(self.unique_id) + "\n" + msg + "\n")
# fp.close()
def search(self, find):
with open(self.log_file, "r") as fp:
lines = fp.readlines()
# fp.close()
for l in lines:
if find in l:
return l
return None
import traceback
import gdb
import common_functions as cf # All common functions will be at common_functions module
import common_parameters as cp # All common parameters will be at common_parameters module
"""
ProfilerBreakpoint class
will manager the breakpoint event when gdb stops
"""
class ProfilerBreakpoint(gdb.Breakpoint):
def __init__(self, *args, **kwargs):
if cp.DEBUG_PROFILER:
print("IT IS IN __INIT__ METHOD")
self.__kludge = kwargs.pop('kludge') if 'kludge' in kwargs else False
self.__kernel_name = kwargs.pop('kernel_name') if 'kernel_name' in kwargs else None
self.__kernel_line = kwargs.pop('kernel_line') if 'kernel_line' in kwargs else None
self.__kernel_end_line = kwargs.pop('kernel_end_line') if 'kernel_end_line' in kwargs else None
self.__addresses = None
self.__kernel_line = kwargs.get('spec')
super(ProfilerBreakpoint, self).__init__(*args, **kwargs)
def stop(self):
if cp.DEBUG_PROFILER:
print("IT IS IN STOP METHOD")
if self.__kludge:
return True
print("FOUND A KERNEL LINE {}".format(self.__kernel_line))
self.__generate_source_ass_list()
kernel_info = {
'addresses': self.__addresses,
'kernel_name': self.__kernel_name,
'kernel_line': self.__kernel_line,
'kernel_end_line': self.__kernel_end_line
}
cf.append_file(file_path=cp.KERNEL_INFO_DIR, data=kernel_info)
"""
inject faults only on the resources used at that source line
"""
def __generate_source_ass_list(self):
source_list = []
assemble_lines = []
disassemble_gdb = cf.execute_command(gdb, "disassemble /m")
for l in disassemble_gdb:
if '0x' in l:
assemble_lines.append(l)
else:
source_list.append([[l], assemble_lines[:]])
assemble_lines = []
last_not_zero_size = None
self.__addresses = []
for l, ass_line in source_list:
# I have to merge with
if len(ass_line) is not 0:
last_not_zero_size = l
self.__addresses.append([l, ass_line])
else:
last_not_zero_size.append(l)
# from multiprocessing import Process
import os
from threading import Thread
from os import path
from subprocess import Popen, PIPE
from re import search
import common_parameters as cp # All common parameters will be at common_parameters module
from common_functions import printf
"""
Class RunGdb: necessary to run gdb while
the main thread register the time
If RunGdb execution time > max timeout allowed
this thread will be killed
"""
class RunGDB(Thread):
def __init__(self, unique_id, gdb_exec_name, flip_script, carol_fi_base_path, gdb_env_string,
inj_output_path, inj_err_path, gpu_to_execute):
super(RunGDB, self).__init__()
self.__gdb_exe_name = gdb_exec_name
self.__flip_script = flip_script
self.__unique_id = unique_id
self.__base_path = carol_fi_base_path
self.__gdb_env_string = gdb_env_string
self.__inj_output_path = inj_output_path
self.__inj_err_path = inj_err_path
self.__gpu_to_execute = gpu_to_execute
os.environ['OMP_NUM_THREADS'] = '1'
def run(self):
if cp.DEBUG:
printf("GDB Thread run, id: {}".format(self.__unique_id))
start_cmd = "{}/{}".format(self.__base_path, self.__flip_script)
script = 'env CUDA_VISIBLE_DEVICES={} {} -ex \'py arg0 = "{}"\' -n -batch -x {} > {} 2>{} &'
#script = 'env CUDA_VISIBLE_DEVICES=1 {} -ex \'py arg0 = "{}"\' -n -batch -x {} > {} 2>{} &'
#printf(script.format(self.__gpu_to_execute, self.__gdb_exe_name, self.__gdb_env_string,start_cmd, self.__inj_output_path,self.__inj_err_path))
os.system(script.format(self.__gpu_to_execute, self.__gdb_exe_name, self.__gdb_env_string,
start_cmd, self.__inj_output_path,
self.__inj_err_path))
print(script.format(self.__gpu_to_execute, self.__gdb_exe_name, self.__gdb_env_string,
start_cmd, self.__inj_output_path,
self.__inj_err_path))
#os.system(script.format(self.__gdb_exe_name, self.__gdb_env_string,
# start_cmd, self.__inj_output_path,
# self.__inj_err_path))
def kill_subprocess(self):
os.system("killall -9 {} > /dev/null 2>&1".format(os.path.basename(self.__gdb_exe_name)))
"""
Check if the process is still alive
must also check the OS
"""
def is_alive(self):
if super(RunGDB, self).is_alive():
return True
# check both gdb and gdb bin name
for exe in [path.basename(self.__gdb_exe_name), self.__gdb_exe_name]:
check_running = "ps -e | grep -i " + exe
process = Popen(check_running, stdout=PIPE, shell=True)
(out, err) = process.communicate()
# Mathews complains
del process
if search(exe, str(out)):
return True
return False
import time
from classes.Logging import Logging
from threading import Thread
from random import uniform
import common_parameters as cp # All common parameters will bet at common_parameters module
import os
import sys
"""
Signal the app to stop so GDB can execute the script to flip a value
"""
class SignalApp(Thread):
def __init__(self, signal_cmd, max_wait_time, log_path, unique_id, signals_to_send, init_sleep, syncro,waitfinish):
global hang
hang=False
super(SignalApp, self).__init__()
self.__signal_cmd = signal_cmd
os.system("rm -f {}".format(log_path))
self.__log = Logging(log_file=log_path, unique_id=unique_id)
# Most of the benchmarks we cannot wait until the end of the processing
# Considering most of 90% of the time
#self.__init_wait_time = uniform(init_sleep, max_wait_time * cp.MAX_SIGNAL_BEFORE_ENDING)
self.__init_wait_time = uniform(0, max_wait_time * cp.MAX_SIGNAL_BEFORE_ENDING)
self.__signals_to_send = signals_to_send
self.__time_to_sleep = (max_wait_time - self.__init_wait_time) / self.__signals_to_send
# self.__time_to_sleep = (max_wait_time) / self.__signals_to_send
self._syncro=syncro
self._waitfinish=waitfinish
def run(self):
# Send a series of signal to make sure gdb will flip a value in one of the interrupt signals
log_string = "Sending a signal using command: {} after {}s and each {}s.".format(self.__signal_cmd, self.__init_wait_time,self.__time_to_sleep)
if cp.DEBUG:
print(log_string)
# Sleep for a random time
# time.sleep(self.__init_wait_time)
#a=[ uniform.randint(1,1000) for _ in range(self.__signals_to_send))]
#a.sort()
# os.system("{} > /dev/null 2>/dev/null".format(self.__signal_cmd))
self.__log.info(log_string)
try:
(self._syncro).wait()
#except threading.BrokenBarrierError:
except:
(self._syncro).abort()
print("Breakpoint fuera de tiempo")
hang=True
self.__log.info("Timeout syncron of breakpoint\n")
# Time random
#print ("INIT:"+str(self.__init_wait_time)+"sleep"+str())
time.sleep(self.__init_wait_time)
for signals in range(0, self.__signals_to_send):
os.system("{} > /dev/null 2>/dev/null".format(self.__signal_cmd))
self.__log.info("sending signal {}".format(signals))
time.sleep(self.__time_to_sleep)
(self._syncro).reset()
try:
(self._waitfinish).wait()
#except threading.BrokenBarrierError:
except:
(self._waitfinish).abort()
print("Hang timeout execution")
hang=True
self.__log.info("Timeout execution programa")
(self._waitfinish).reset()
def ishang (self):
return hang
def get_int_wait_time(self):
return self.__init_wait_time
import csv
import os
"""
Class SummaryFile: this class will write the information
of each injection in a csv file to make easier data
parsing after fault injection
"""
class SummaryFile:
# Filename
__filename = ""
# csv file
__csv_file = None
# Dict reader
__dict_buff = None
# Fieldnames
__fieldnames = None
# It will only open in a W mode for the first time
def __init__(self, **kwargs):
# Set arguments
self.__filename = kwargs.get("filename")
self.__fieldnames = kwargs.get("fieldnames")
# Open and start csv file
self.__open_csv(mode='w')
self.__dict_buff.writeheader()
self.__close_csv()
"""
Open a file if it exists
mode can be r or w
default is r
"""
def __open_csv(self, mode='a'):
if os.path.isfile(self.__filename):
self.__csv_file = open(self.__filename, mode)
elif mode == 'w':
self.__csv_file = open(self.__filename, mode)
else:
raise IOError(str(self.__filename) + " FILE NOT FOUND")
# If file exists it is append or read
if mode in ['w', 'a']:
self.__dict_buff = csv.DictWriter(self.__csv_file, self.__fieldnames)
elif mode == 'r':
self.__dict_buff = csv.DictReader(self.__csv_file)
"""
To not use __del__ method, close csv file
"""
def __close_csv(self):
if not self.__csv_file.closed:
self.__csv_file.close()
self.__dict_buff = None
"""
Write a csv row, if __mode == w or a
row must be a dict
"""
def write_row(self, row):
# If it is a list must convert first
row_ready = {}
if isinstance(row, list):
for fields, data in zip(self.__fieldnames, row):
row_ready[fields] = data
else:
row_ready = row
# Open file first
self.__open_csv()
self.__dict_buff.writerow(row_ready)
self.__close_csv()
"""
Read rows
return read rows from file in a list
"""
def read_rows(self):
self.__open_csv()
rows = [row for row in self.__dict_buff]
self.__close_csv()
return rows
"""
Write series of rows to csv
"""
def write_rows(self, rows_list):
self.__open_csv()
for row in rows_list:
row_ready = {}
if isinstance(row, list):
for fields, data in zip(self.__fieldnames, row):
row_ready[fields] = data
else:
row_ready = row
self.__dict_buff.writerow(row_ready)
self.__close_csv()
import time
from classes.Logging import Logging
from threading import Thread
from random import uniform
import common_parameters as cp # All common parameters will bet at common_parameters module
import os
import sys
import signal
"""
Signal the app to stop so GDB can execute the script to flip a value
"""
class SignalApp(Thread):
def __init__(self, signal_cmd, max_wait_time, log_path, unique_id, signals_to_send, init_sleep):
super(SignalApp, self).__init__()
self.__signal_cmd = signal_cmd
self.__noterminado = True
os.system("rm -f {}".format(log_path))
self.__log = Logging(log_file=log_path, unique_id=unique_id)
# Most of the benchmarks we cannot wait until the end of the processing
# Considering most of 90% of the time
self.__init_wait_time = uniform(init_sleep, max_wait_time * cp.MAX_SIGNAL_BEFORE_ENDING)
self.__signals_to_send = signals_to_send
self.__time_to_sleep = (max_wait_time - self.__init_wait_time) / self.__signals_to_send
def receiveSignal(signalNumber, frame):
print ("Alcanzado el breakpoint, y recevida la señal",signalNumber);
os.system("{} > /dev/null 2>/dev/null".format(self.__signal_cmd))
self.__log.info(log_string)
for signals in range(0, self.__signals_to_send):
os.system("{} > /dev/null 2>/dev/null".format(self.__signal_cmd))
self.__log.info("sending signal {}".format(signals))
time.sleep(self.__time_to_sleep)
self.__noterminado=False
return
def run(self):
# Send a series of signal to make sure gdb will flip a value in one of the interrupt signals
log_string = "Sending a signal using command: {} after {}s.".format(self.__signal_cmd, self.__init_wait_time)
if cp.DEBUG:
print(log_string)
print ("Esperando breakpoint.....")
signal.signal(signal.SIGINT,self.receiveSignal);
# Sleep for a random time
# time.sleep(self.__init_wait_time)
while self__noterminado:
printf("Waiting....")
time.sleep(3)
printf ("Terminado")
def get_int_wait_time(self):
return self.__init_wait_time
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment