diff --git a/bin/bochs-experiment-runner.py b/bin/bochs-experiment-runner.py new file mode 100755 index 0000000..17190a5 --- /dev/null +++ b/bin/bochs-experiment-runner.py @@ -0,0 +1,147 @@ +#!/usr/bin/python3 + +import os, sys +from optparse import OptionParser +from subprocess import * +from tempfile import mkstemp, mkdtemp +import shutil +from distutils.spawn import find_executable +import os.path + +def parseArgs(): + parser = OptionParser() + parser.add_option("-e", "--elf-file", dest="elf_file", + help="elf file to be executed", metavar="ELF") + parser.add_option("-i", "--iso-file", dest="iso_file", + help="iso file to be executed", metavar="ISO") + parser.add_option("-f", "--fail-client", dest="fail_client", + help="fail-client to be executed", metavar="ISO") + parser.add_option("-m", "--memory", dest="memory", default="16", + help="memory for the bochs VM", metavar="SIZE") + + parser.add_option("-b", "--bios", dest="bios", default="/fs/proj/i4ezs/tools/fail-ws21/buildartifacts/BIOS-bochs-latest", + help="bios image for bochs", metavar="BIOS") + + parser.add_option("-V", "--vgabios", dest="vgabios", default="/fs/proj/i4ezs/tools/fail-ws21/buildartifacts/vgabios.bin", + help="vgabios image for bochs", metavar="VGABIOS") + + parser.add_option("-F", "--freq", dest="freq", default="5", + help="frquency in MHZ", metavar="MHZ") + + parser.add_option("-1", "--once", + action="store_false", dest="forever", default=True, + help="fail-client to be executed") + + parser.add_option("-j", "--jobs", + dest="jobs", default="1", + help="parallel execution") + + + (options, args) = parser.parse_args() + + if not (options.elf_file and options.iso_file and options.fail_client): + parser.error("elf, iso and fail-client are required") + + return options, args + +def execute(options, args, bochsrc, statedir): + failcmd = options.fail_client + command = "FAIL_ELF_PATH=%s FAIL_STATEDIR=%s %s -q -f %s %s" % \ + (options.elf_file, statedir, failcmd, bochsrc, " ".join(args)) + print("executing: " + command) + p = Popen(command, shell=True, stdout=PIPE, stderr=STDOUT) + reconnect = 0 + while p.poll() is None: + line = p.stdout.readline() + if line is None: + break + if "Connection refused" in line.decode("utf-8", "ignore"): + reconnect += 1 + print(line), + if reconnect > 10: + return 1 + p.wait() + + if reconnect > 0: + return 123 + return p.returncode + +def main(options, args): + bochsrc_args = { + "memory": options.memory, + "bios": options.bios, + "vgabios": options.vgabios, + "iso": options.iso_file, + "ips": int(options.freq) * 1000000, + } + + bochsrc_text = """ +config_interface: textconfig +display_library: nogui +romimage: file="{bios}" +cpu: count=1, ips={ips}, reset_on_triple_fault=1, ignore_bad_msrs=1, msrs="msrs.def" +cpuid: mmx=1, sep=1, sse=sse4_2, xapic=1, aes=1, movbe=1, xsave=1, cpuid_limit_winnt=0 +memory: guest={memory}, host={memory} +vgaromimage: file="{vgabios}" +vga: extension=vbe +ata0: enabled=1, ioaddr1=0x1f0, ioaddr2=0x3f0, irq=14 +ata1: enabled=0, ioaddr1=0x170, ioaddr2=0x370, irq=15 +ata2: enabled=0, ioaddr1=0x1e8, ioaddr2=0x3e0, irq=11 +ata3: enabled=0, ioaddr1=0x168, ioaddr2=0x360, irq=9 +ata0-slave: type=cdrom, path="{iso}", status=inserted +port_e9_hack: enabled=1 +com1: enabled=1, mode=file, dev=serial.out +boot: cdrom +clock: sync=none, time0=946681200 +floppy_bootsig_check: disabled=0 +panic: action=fatal +error: action=fatal +info: action=ignore +debug: action=ignore +pass: action=ignore +debugger_log: - +parport1: enabled=0 +vga_update_interval: 300000 +keyboard_serial_delay: 250 +keyboard_paste_delay: 100000 +private_colormap: enabled=0 +i440fxsupport: enabled=0, slot1=pcivga +""".format(**bochsrc_args) + + bochsrc = mkstemp() + fd = os.fdopen(bochsrc[0], "w") + fd.write(bochsrc_text) + fd.close() + bochsrc = bochsrc[1] + + statedir = mkdtemp() + + if options.forever: + while True: + res = execute(options, args, bochsrc, statedir) + if res != 0: + break + + ret = 0 + else: + ret = execute(options, args, bochsrc, statedir) + + os.unlink(bochsrc) + shutil.rmtree(statedir) + sys.exit(ret) + +if __name__ == "__main__": + (options, args) = parseArgs() + + import threading + i = 1 + # n-1 jobs in threads + while i < int(options.jobs): + fred = threading.Thread(target = main, args = (options, args)) + fred.start() + print("starting: "+str(i)) + i = i + 1 + + main(options, args) + + diff --git a/bin/fail-x86-tracing b/bin/fail-x86-tracing new file mode 100755 index 0000000..5d2a5d4 Binary files /dev/null and b/bin/fail-x86-tracing differ diff --git a/bin/generic-experiment-client b/bin/generic-experiment-client new file mode 100755 index 0000000..37fdbce Binary files /dev/null and b/bin/generic-experiment-client differ diff --git a/bin/generic-experiment-server b/bin/generic-experiment-server new file mode 100755 index 0000000..0ac7d7e Binary files /dev/null and b/bin/generic-experiment-server differ diff --git a/bin/import-trace b/bin/import-trace new file mode 100755 index 0000000..427fe20 Binary files /dev/null and b/bin/import-trace differ diff --git a/bin/prune-trace b/bin/prune-trace new file mode 100755 index 0000000..259bedf Binary files /dev/null and b/bin/prune-trace differ diff --git a/bin/resultbrowser.py b/bin/resultbrowser.py new file mode 120000 index 0000000..3dbc8a6 --- /dev/null +++ b/bin/resultbrowser.py @@ -0,0 +1 @@ +./resultbrowser/run.py \ No newline at end of file diff --git a/bin/resultbrowser/README b/bin/resultbrowser/README new file mode 100644 index 0000000..f955e93 --- /dev/null +++ b/bin/resultbrowser/README @@ -0,0 +1,19 @@ +FAIL* Result Browser + +Requirements: + * Python + * Flask (sudo pip install Flask) + * MySQLDB (sudo aptitude install python-mysqldb) + * YAML (sudo aptitude install python-yaml) + +Based on Flask web microframework (Werkzeug, Jinja 2) +and old school MySQL bindings. + +Connects to a FAIL* result database given by a mysql config file. + +Usage: + ./run.py + Defaults to mysql config file ~/.my.cnf, or + ./run.py -c + +YAML based configuration for table and variant details. diff --git a/bin/resultbrowser/app/__init__.py b/bin/resultbrowser/app/__init__.py new file mode 100644 index 0000000..a6c9e33 --- /dev/null +++ b/bin/resultbrowser/app/__init__.py @@ -0,0 +1,4 @@ +from flask import Flask + +app = Flask(__name__) +from app import views diff --git a/bin/resultbrowser/app/data.py b/bin/resultbrowser/app/data.py new file mode 100644 index 0000000..1552831 --- /dev/null +++ b/bin/resultbrowser/app/data.py @@ -0,0 +1,143 @@ +from pprint import pprint +from . import details +from . import model + +def scrub(table_name): + return ''.join( chr for chr in table_name if chr.isalnum() or chr == '_' ) + +class Resulttype: + def __init__(self, name, count): + self.name = name + self.count = count + + def getName(self): + return self.name + + def getCount(self): + return self.count + +class Variant: + def __init__(self, id, name, table, benchmark, detail): + self.id = id + self.dbname = name + self.parenttable = table # TableDetails + self.details = detail # VariantDetails + self.benchmark = benchmark # BenchmarkDetails + self.results = {} + self.totalresults = 0 + + + def getMapper(self): + mapper = self.benchmark.getMapper() + if not mapper: #try benchmark mapper + mapper = self.details.getMapper() + if not mapper: # of not there, try parent tables mapper + mapper = self.parenttable.getMapper() + if not mapper: # no mapper found at all, try default mapper + mapper = model.detaildealer.getDefaultMapper() + return mapper + + + def addResulttype(self, name, count): + mapper = self.getMapper() + label = mapper.getLabel(name) + oldcount = self.results.setdefault(label, 0) + self.results[label] = oldcount + count + self.totalresults += count + + def getResultLabels(self): + return self.results.keys() + + def getDBName(self): + return str(self.name) + + def getId(self): + return self.id + + def getResults(self): + return self.results + + def getTableDetails(self): + return self.parenttable + + def getBenchmarkDetails(self): + return self.benchmark + + def getDetails(self): + return self.details + + def getTotals(self): + return self.totalresults + + def __str__(self): + ret = "Variant: " + self.getDetails().getTitle() + " - " + self.getBenchmarkDetails().getTitle() +" (id: " + str( self.id )+ ")" + " " + ret += "Total Results: " + str( self.totalresults ) + "\n" + for v in self.results: + ret += "\t" + v.name + ": " + str( v.count ) + "\n" + return ret + + __repr__ = __str__ + +'''A ResultTable contains n Variants''' +class ResultTable: + + def __init__(self, name, cfg): + self.name = scrub(name) + self.details = cfg.getTable(name) + self.variants = {} + + def addVariant(self, var): + if var.getId() in self.variants: + return + self.variants[var.getId()] = var # Add if not existing yet + + def getVariant(self, id): + if id in self.variants: + return self.variants[id] + return None + + def getVariantById(self, varid): + for k,v in self.variants.items(): + if int(v.getId()) == int(varid): + return v + return None + + def getDetails(self): + return self.details + + def getVariants(self): + return self.variants + + def __str__(self): + ret = "Result: " + self.getDetails().getTitle() + "\n" + for k,v in self.variants.items(): + ret += "\t" + str(v) + "\n" + return ret + __repr__ = __str__ + +'''Overview has n ResultTables''' +class Overview: + def __init__(self): + self.tables = {} + + def add(self, table): + self.tables[table.getDetails().getDBName()] = table + + def getTables(self): + return self.tables + + def getTable(self, dbname): + return self.tables.get(dbname, None) + + def getVariantById(self, variant_id): + for key,table in self.tables.items(): + variant = table.getVariantById(variant_id) + if variant: + return variant + print("Variant not found.") + return None + + def length(self): + return len(self.tables) + + diff --git a/bin/resultbrowser/app/details.py b/bin/resultbrowser/app/details.py new file mode 100644 index 0000000..b288424 --- /dev/null +++ b/bin/resultbrowser/app/details.py @@ -0,0 +1,216 @@ + +class BasicDetails(object): + + def __init__(self,name): + self.dbname = name + self.title = name + self.details = '' + self.mapper = None + + def getDBName(self): + return self.dbname + + def getDetails(self): + return self.details + + def setDetails(self,det): + self.details = det + + def getTitle(self): + return self.title + + def addMapper(self, mapper): + self.mapper = mapper + + def getMapper(self): + return self.mapper + + def extractDetails(self, dictionary): + self.details = dictionary.pop(('details'), '') + self.title = dictionary.pop(('title'), self.dbname) + custommapping = dictionary.pop(('mapping'), None) + if custommapping: + self.mapper = ResulttypeMapper() + self.mapper.add(custommapping) + else: + self.mapper = None + + + def __repr__(self): + return self.getTitle() + ": " + self.getDetails() + + + __str__ = __repr__ + + + +class BenchmarkDetails(BasicDetails): + + def __init__(self, dbname): + BasicDetails.__init__(self, dbname) + + def __repr__(self): + return "Benchmark: " + BasicDetails.__repr__(self) + + __str__ = __repr__ + + + +class VariantDetails(BasicDetails): + + def __init__(self, dbname): + BasicDetails.__init__(self, dbname) + self.benchmarks = {} + + def addBenchmark(self, bm): + self.benchmarks[bm.getDBName()] = bm + + def getBenchmark(self, dbbm): + return self.benchmarks.get(dbbm, BenchmarkDetails(dbbm)) + + def __repr__(self): + ret = "Variant: " + BasicDetails.__repr__(self) + for v in self.benchmarks.values(): + ret += "\n\t\t" + str(v) + return ret + + + __str__ = __repr__ + +class TableDetails(BasicDetails): + + def __init__(self, tbl): + BasicDetails.__init__(self, tbl) + self.variants = {} + + def addVariant(self, var): + self.variants[var.getDBName()] = var + + def getVariant(self, varname): + return self.variants.get(varname, VariantDetails(varname)) + + def __repr__(self): + ret = "Table: " + BasicDetails.__repr__(self) + "(" + self.getDBName() + ")" + for v in self.variants.values(): + ret += "\n\t" + str(v) + return ret + + __str__ = __repr__ + +from pprint import pprint + + +class ResulttypeMapper(object): + + def __init__(self): + self.mappings = {} + + def add(self, mapping): + for label, dbnamelist in mapping.items(): + self.mappings[label] = dbnamelist + + def getLabel(self, dbname): + for label, dbnamelist in self.mappings.items(): + if dbname in dbnamelist: + return label + return dbname + + def getLabelList(self): + return self.mappings.keys() + + def getDBNames(self, label): + return self.mappings.get(label, None) + + def __repr__(self): + ret = "Resulttype Mapper:" + for label,dbnames in self.mappings.items(): + ret += "\n\t" + label + for db in dbnames: + ret += "\n\t\t" + db + return ret + + __str__ = __repr__ + +import yaml +class DetailDealer: + + def __init__(self, configfile=None): + self.tables = {} + self.defaultMapper = ResulttypeMapper() + + if not configfile: + return + self.reload(configfile) + + if not self.tables: + print("DetailDealer: no details found for " + configfile) + + def reload(self, configfile): + self.tables = {} + self.defaultMapper = ResulttypeMapper() + if not configfile: + return # no details. + f = open(configfile) + # use safe_load instead load + cfg = yaml.safe_load(f) + f.close() + # Read out default mapping, if existent + self.extractDefaults(cfg) + tables = cfg.pop('tables', None) + if tables: + for tablename,details in tables.items(): + tab = TableDetails(tablename) + # pop: return and remove when key present, else return 'notfound' + tab.extractDetails(details) + variants = details.pop('variants') + for variantname, vdetails in variants.items(): + var = VariantDetails(variantname) + var.extractDetails(vdetails) + benchmarks = vdetails.pop('benchmarks') + for benchmark, bdetails in benchmarks.items(): + bm = BenchmarkDetails(benchmark) + bm.extractDetails(bdetails) + var.addBenchmark(bm) + tab.addVariant(var) + self.tables[tab.getDBName()] = (tab) + + + def extractDefaults(self, cfg): + defs = cfg.pop('defaults', None) + if defs: + defmap = defs.pop('mapping', None) + if defmap: + self.defaultMapper.add(defmap) + + def getDefaultMapper(self): + return self.defaultMapper + + def getTable(self, tablename): + tab = self.tables.get(tablename, None) + if tab: + return tab + return TableDetails(tablename) + + def getVariant(self, tablename, variantname): + tab = self.getTable(tablename) + if tab: + return tab.getVariant(variantname) + return VariantDetails(variantname) # Default + + def getBenchmark(self, table, variant, bechmark): + tab = self.getTable(table) + if tab: + var = tab.getVariant(variant) + if var: + return var.getBenchmark(bechmark) + return BenchmarkDetails(benchmark) # Default + + def __repr__(self): + ret = str(self.defaultMapper) + '\n' + for tabledetails in self.tables.values(): + ret += str(tabledetails) + '\n' + return ret + +if __name__ == "__main__": + dd = DetailDealer('./test.yml') + pprint(dd) diff --git a/bin/resultbrowser/app/model.py b/bin/resultbrowser/app/model.py new file mode 100755 index 0000000..d9529f2 --- /dev/null +++ b/bin/resultbrowser/app/model.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python +import MySQLdb +import MySQLdb.cursors +import yaml + +import sys +import os.path + +from pprint import pprint +from . import data +from . import details + +"""Get command line options""" +from optparse import OptionParser +parser = OptionParser() +parser.add_option("-c", "--conf", type="string", help="MySQL config file", dest="config", default= os.path.join(os.path.expanduser("~"),".my.cnf")) +parser.add_option("-s", "--host", type="string", help="Webserver hostname", dest="host", default="localhost") +parser.add_option("-d", "--details", type="string", help="Detailed information (YAML configuration file)", dest="details", default=None) +parser.add_option("-p", "--port", type="string", help="Webserver port", dest="port", default="5000") +opts, args = parser.parse_args() + +"""Check if configuration files exist""" +def checkConfigFile(msg, fname): + if not os.path.isfile(fname): + sys.exit("Error: '" + fname + "' not found") + else: + print(msg, "->", fname) + +# Check sql config +sqlconfig = opts.config +checkConfigFile("MySQL config", sqlconfig) + +# Check details file +if opts.details: + checkConfigFile("Details", opts.details) + +# Instantiate global detail dealer, will be initialized in reloadOverview +detaildealer = details.DetailDealer() + + +"""Remove all characters from string except alphanuermics and _""" +def scrub(table_name): + return ''.join( chr for chr in table_name if chr.isalnum() or chr == '_' ) + +"""Global mysql handles""" +db = None +cur = None +def loadSession(dbconf): + global db + if db: + db.close() + db = MySQLdb.connect(read_default_file=dbconf, cursorclass=MySQLdb.cursors.DictCursor) + return db.cursor() + + +def closeSession(): + if cur: cur.close() + global db + db.close() + db = None + + +'''Populate variant results for overview data''' +def getVariants(cur, table): + restbl = table.getDetails().getDBName() + cur.execute("""SELECT sum((t.time2 - t.time1 + 1) * width) AS total, resulttype,variant, v.id as variant_id, benchmark, details FROM variant v JOIN trace t ON v.id = t.variant_id JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_address = t.data_address JOIN %s r ON r.pilot_id = g.pilot_id JOIN fsppilot p ON r.pilot_id = p.id GROUP BY v.id, resulttype, details""" % (restbl)) # % is used here, as a tablename must not be quoted + res = cur.fetchall() + rdic = {} + # Build dict with variant id as key + for r in res: + # if variant entry already exists: + variant = table.getVariant(int(r['variant_id'])) + if not variant: # if variant did not exist yet, create it: + variant_details = detaildealer.getVariant(restbl, r['variant']) + benchmark_details = detaildealer.getBenchmark(restbl, r['variant'], r['benchmark']) + table_details = detaildealer.getTable(restbl) + variant = data.Variant(int(r['variant_id']), r['variant'], table_details, benchmark_details, variant_details) + variant.addResulttype(r['resulttype'], r['total']) + table.addVariant(variant) + +'''Get overview data for index page''' +def reloadOverview(): + overview = data.Overview() + detaildealer.reload(opts.details) + cur = loadSession(sqlconfig) + cur.execute("show tables like 'result_%'") + result_tables = cur.fetchall() + results = {} + for rdic in result_tables: + # r is the tablename, -> result_FOOBAR + for key, tablename in rdic.items(): + table = data.ResultTable(tablename,detaildealer) + getVariants(cur, table) + overview.add(table) + # Check if objdump table exists + cur.execute("SHOW TABLES like 'objdump'") + objdump_exists = (len(cur.fetchall()) == 1) + closeSession() + return overview, objdump_exists + +"""Load overview data at server startup""" +print("Loading overview data from database. This may take a while ...") +overview_data, objdump_exists = reloadOverview() +print("done.") +## Get overview data for views.index() +def getOverview(): + return overview_data + +def objdumpExists(): + return objdump_exists + + +"""Get Results for one variant id""" +def getVariantResult(table, variantid): + cur = loadSession(sqlconfig) + restbl = scrub(table) + + stmt = "SELECT resulttype, count(*) as total from %s r join fsppilot on r.pilot_id=fsppilot.id join variant on fsppilot.variant_id=variant.id" % (restbl) + where = " WHERE variant.id = %s group by resulttype ORDER BY resulttype " + stmt = stmt + where + cur.execute(stmt, variantid) + res = cur.fetchall() + closeSession() + return res + +'''Show objdump together with according injection result types.''' +def getCode(result_table, variant_id, resultlabel=None): + result_table = scrub(result_table) + filt = '' + if not variant_id or not result_table: + return None + variant = overview_data.getVariantById(variant_id) + mapper = variant.getMapper() + if resultlabel: + dbnames = mapper.getDBNames(resultlabel) + if dbnames: + filt = " and ( " + for dbn in dbnames[:-1]: + filt += "resulttype = '" + dbn + "' OR " + filt += "resulttype = '" + dbnames[-1] +"' ) " + else: + filt = " and resulttype = '" + resultlabel + "' " + + # I especially like this one: + select = "SELECT instr_address, opcode, disassemble, comment, sum(t.time2 - t.time1 + 1) as totals, GROUP_CONCAT(DISTINCT resulttype SEPARATOR ', ') as results FROM variant v " + join = " JOIN trace t ON v.id = t.variant_id JOIN fspgroup g ON g.variant_id = t.variant_id AND g.instr2 = t.instr2 AND g.data_address = t.data_address JOIN %s r ON r.pilot_id = g.pilot_id JOIN fsppilot p ON r.pilot_id = p.id JOIN objdump ON objdump.variant_id = v.id AND objdump.instr_address = injection_instr_absolute " %(scrub(result_table)) + where = "WHERE v.id = %s " + group = "GROUP BY injection_instr_absolute ORDER BY totals DESC " + + cur = loadSession(sqlconfig) + stmt = select + join + where + filt + group + cur.execute(stmt, (variant_id)) + dump = cur.fetchall() + + closeSession() + resulttypes = variant.getResultLabels() + return dump, resulttypes + +def getCodeExcerpt(variant_id, instr_addr): + code = {} + limit = 8 + cur = loadSession(sqlconfig) + cur.execute( """(SELECT instr_address, opcode, disassemble, comment FROM objdump \ + WHERE instr_address < %s AND variant_id = %s \ + ORDER BY instr_address DESC LIMIT %s) \ + ORDER BY instr_address ASC""" , (instr_addr, variant_id, limit)) + below = cur.fetchall() + code['below'] = below + cur.execute("""SELECT instr_address, opcode, disassemble, comment FROM objdump \ + WHERE instr_address >= %s AND variant_id = %s \ + ORDER BY instr_address ASC LIMIT %s""", (instr_addr, variant_id, limit+1)) + upper = cur.fetchall() + code['upper'] = upper + closeSession() + return code + +def getResultsbyInstruction(result_table, variant_id, instr_addr, resultlabel=None): + restypefilter = None + if resultlabel: + variant = overview_data.getVariantById(variant_id) + mapper = variant.getMapper() + if resultlabel: + dbnames = mapper.getDBNames(resultlabel) + if dbnames: + restypefilter = " and ( " + for dbn in dbnames[:-1]: + restypefilter += "resulttype = '" + dbn + "' OR " + restypefilter += "resulttype = '" + dbnames[-1] +"' ) " + + select = "SELECT bitoffset as 'Bit Offset', hex(injection_instr_absolute) as 'Instruction Address', hex(original_value) as 'Original Value', hex(data_address) as 'Data Address', resulttype as 'Result Type', details as 'Details' from %s " % scrub(result_table) + join = "JOIN fsppilot ON pilot_id = fsppilot.id " + where = "WHERE variant_id = %s and injection_instr_absolute = %s " + order = "ORDER BY data_address, bitoffset" + + cur = loadSession(sqlconfig) + if not restypefilter: + stmt = select + join + where + order + cur.execute(stmt, (variant_id, instr_addr)) + else: + stmt = select + join + where + restypefilter + order + cur.execute(stmt, (variant_id, instr_addr)) + + res = cur.fetchall() + closeSession() + return res + +def showDBstatus(): + res = "TODO" + return res + + diff --git a/bin/resultbrowser/app/static/css/barchart.css b/bin/resultbrowser/app/static/css/barchart.css new file mode 100644 index 0000000..b68912c --- /dev/null +++ b/bin/resultbrowser/app/static/css/barchart.css @@ -0,0 +1,5 @@ +dl.horizontal {font-size:12px; width:850px;} +dl.horizontal dt {float:left; width:300px; clear:both; margin:0 0 5px 0; padding:3px;} +dl.horizontal dd {float:left; width:500px; border:1px solid #aaaaaa; margin:0 0 5px 0; padding:2px; -moz-box-shadow: 1px 1px 3px #aaaaaa;} +dl.horizontal dd span {background:#91b4e6; display:block; color:black; text-indent:4px;} + diff --git a/bin/resultbrowser/app/static/css/main.css b/bin/resultbrowser/app/static/css/main.css new file mode 100644 index 0000000..a6ec20e --- /dev/null +++ b/bin/resultbrowser/app/static/css/main.css @@ -0,0 +1,214 @@ + +body { + margin: 0; + padding: 0; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + color: #444; +} + +a:visited { + color : #444; +} + +a:link { + color : #444; + text-decoration: none; +} +/* + * Create dark grey header with a white logo + */ + +header { + background-color: #2B2B2B; + height: 30px; + width: 100%; + opacity: .9; + margin-bottom: 10px; +} + +header h1.logo { + margin: 0; + font-size: 1.5em; + color: #fff; + text-transform: uppercase; + float: left; +} + +header h1.logo:hover { + color: #fff; + text-decoration: none; +} + + +/* + * Center the body content + */ + +.container { + width: 95%; + margin: 0 auto; +} + +div.jumbo { + padding: 10px 0 30px 0; + background-color: #eeeeee; + -webkit-border-radius: 6px; + -moz-border-radius: 6px; + border-radius: 6px; +} + +h2 { + font-size: 1.2em; + margin-top: 40px; + text-align: center; + letter-spacing: -1px; +} + +h3 { + font-size: 1.0em; + font-weight: 100; + margin-top: 30px; + text-align: center; + /*letter-spacing: -1px;*/ + color: #999; +} + + +#footer { + text-align: center; + color: #444; + font-size:10pt; +} + +/* + * Table styles + */ + +.codetable, .resulttable, .overviewtable{ + font-family: monospace; + margin: auto; +} + +.overviewtable { + border: 1px gray solid; + margin: auto; +} +/* +.overviewtable tr:nth-child(4n), .overviewtable tr:nth-child(4n-1) { + background: #fff; +} +.overviewtable tr:nth-child(4n-2), .overviewtable tr:nth-child(4n-3) { + background: #e8ffb3; +} +*/ + +.resulttable, .codetable, .overviewtable{ + margin: auto; + border: solid #ccc 1px; + -moz-border-radius: 6px; + -webkit-border-radius: 6px; + border-radius: 6px; + -webkit-box-shadow: 0 1px 1px #ccc; + -moz-box-shadow: 0 1px 1px #ccc; + box-shadow: 0 1px 1px #ccc; +} + +.codetable tr:hover, .resulttable tr:hover { + background: #fbf8e9; +/* -o-transition: all 0.1s ease-in-out; + -webkit-transition: all 0.1s ease-in-out; + -moz-transition: all 0.1s ease-in-out; + -ms-transition: all 0.1s ease-in-out; + transition: all 0.1s ease-in-out; +*/ +} + +.resulttable td, .resulttable th, .codetable td, .codetable th { + border-left: none; + border-top: none; + padding: 2px; + text-align: left; + display: table-cell; +} + +.codetable td a { + text-decoration: none; + display: block; + padding: 0px; + height: 100%; +} + +.resulttable th, .codetable th, .overviewtable th { + background-color: #dce9f9; + background-image: -webkit-gradient(linear, left top, left bottom, from(#efe), to(#91b4e6)); + background-image: -webkit-linear-gradient(top, #efe, #91b4e6); + background-image: -moz-linear-gradient(top, #efe, #91b4e6); + background-image: -ms-linear-gradient(top, #efe, #91b4e6); + background-image: -o-linear-gradient(top, #efe, #91b4e6); + background-image: linear-gradient(top, #efe, #91b4e6); + -webkit-box-shadow: 0 1px 0 rgba(255,255,255,.8) inset; + -moz-box-shadow:0 1px 0 rgba(255,255,255,.8) inset; + box-shadow: 0 1px 0 rgba(255,255,255,.8) inset; + border-top: none; + text-shadow: 0 1px 0 rgba(255,255,255,.5); +} + +.resulttable td:first-child, .resulttable th:first-child, .codetable td:first-child, .codetable th:first-child { + border-left: none; +} + +.resulttable th:first-child, .codetable th:first-child { + -moz-border-radius: 6px 0 0 0; + -webkit-border-radius: 6px 0 0 0; + border-radius: 6px 0 0 0; +} + +.resulttable th:last-child, .codetable th:last-child { + -moz-border-radius: 0 6px 0 0; + -webkit-border-radius: 0 6px 0 0; + border-radius: 0 6px 0 0; +} + +.resulttable th:only-child, .codetable th:only-child{ + -moz-border-radius: 6px 6px 0 0; + -webkit-border-radius: 6px 6px 0 0; + border-radius: 6px 6px 0 0; +} + +.resulttable tr:last-child td:first-child, .codetable tr:last-child td:first-child { + -moz-border-radius: 0 0 0 6px; + -webkit-border-radius: 0 0 0 6px; + border-radius: 0 0 0 6px; +} + +.resulttable tr:last-child td:last-child, .codetable tr:last-child td:last-child { + -moz-border-radius: 0 0 6px 0; + -webkit-border-radius: 0 0 6px 0; + border-radius: 0 0 6px 0; +} + +.resulttypemenu { + text-align: center; + font-size: 12px +} +/* + * Display navigation links inline + */ + +.menu { + float: right; + margin-top: 8px; +} + +.menu li { + display: inline; +} + +.menu li + li { + margin-left: 35px; +} + +.menu li a { + color: #999; + text-decoration: none; +} diff --git a/bin/resultbrowser/app/static/favicon.ico b/bin/resultbrowser/app/static/favicon.ico new file mode 100644 index 0000000..cf9bfb1 Binary files /dev/null and b/bin/resultbrowser/app/static/favicon.ico differ diff --git a/bin/resultbrowser/app/templates/about.html b/bin/resultbrowser/app/templates/about.html new file mode 100644 index 0000000..22d0562 --- /dev/null +++ b/bin/resultbrowser/app/templates/about.html @@ -0,0 +1,11 @@ +{% extends "layout.html" %} + +{% block content %} + +{% if status %} +

About

+{{ status }} +{% else %} +

Sorry, no status information.

+{% endif %} +{% endblock %} diff --git a/bin/resultbrowser/app/templates/code.html b/bin/resultbrowser/app/templates/code.html new file mode 100644 index 0000000..920b1da --- /dev/null +++ b/bin/resultbrowser/app/templates/code.html @@ -0,0 +1,50 @@ +{% extends "layout.html" %} + +{% block content %} + +

Variant Results

+ +{% if resulttypes %} + + + + + + + +
Result Table{{ variant_details.getTableDetails().getTitle() }}
Variant {{ variant_details.getDetails().getTitle() }}
Details {{ variant_details.getDetails().getDetails() }}
Benchmark {{ variant_details.getBenchmarkDetails().getTitle() }}
Details {{ variant_details.getBenchmarkDetails().getDetails() }}
Result Count {{ results|length }}
+
+

| All Results | + {% for restype in resulttypes %} + {{ restype}} | + {% endfor %} +

+
+ + + + + + + + + + {% for d in results %} + {% set link = url_for('instr_details', table=request.args.get('table'), variant_id=request.args.get('variant_id'), benchmark=request.args.get('benchmark'), variant=request.args.get('variant'), resulttype=request.args.get('resulttype'), instr_address=d['instr_address'] ) %} + + + + + + + + + {% endfor %} +
AddressOpcodeDisassemblyCommentResults# Results
{{ "0x%x"|format(d['instr_address']) }}{{ d['opcode'] }}{{ d['disassemble'] }}{{ d['comment'] }}{{ d['results'] }}{{ d['totals'] }}
+{% else %} +

Sorry, no dump found.

+{% endif %} + +{%endblock %} + + diff --git a/bin/resultbrowser/app/templates/index.html b/bin/resultbrowser/app/templates/index.html new file mode 100644 index 0000000..31abf68 --- /dev/null +++ b/bin/resultbrowser/app/templates/index.html @@ -0,0 +1,55 @@ +{% extends "layout.html" %} + +{% block content %} +{%if overview %} + {% for tablekey, resulttable in overview.getTables().items() %} +

Result table: {{ resulttable.getDetails().getTitle() }}

+ -> Reload data <- +
+
Details: +
+ {{ resulttable.getDetails().getDetails() }} +
+
+ {% if not objdump_there %} + No objdump found + {% endif %} + + {% for varid, variant in resulttable.getVariants().items() %} + + + {% endif %} + + {% endfor %} +
+ {% set variant_title=variant.getDetails().getTitle() ~ " - " ~ variant.getBenchmarkDetails().getTitle() ~ " id: " ~ variant.getId() %} + {% if objdump_there %} + {{ variant_title }} + {% else %} + {{ variant_title }} + {% endif %} + (Total: {{ variant.getTotals() }}) +
+ {% if variant.getDetails().getDetails() %} + Variant Details: {{ variant.getDetails().getDetails() }} + {% endif %} + {% if variant.getBenchmarkDetails().getDetails() %} +
Benchmark Details: {{ variant.getBenchmarkDetails().getDetails() }}
+
+ {% for reslabel,count in variant.getResults().items() %} +
+ {% if objdump_there %} + {{ reslabel }} + {% else %} + {{ reslabel }} + {% endif %} +
+
{{count}}
+ {% endfor %} +
+ {% endfor %} +{% else %} +

Sorry, no results found.

+{% endif %} + +{% endblock %} diff --git a/bin/resultbrowser/app/templates/instr_details.html b/bin/resultbrowser/app/templates/instr_details.html new file mode 100644 index 0000000..232c379 --- /dev/null +++ b/bin/resultbrowser/app/templates/instr_details.html @@ -0,0 +1,65 @@ +{% extends "layout.html" %} + +{% block content %} +{% if code %} +

Result by Instruction

+ + + + + + + +
Result Table{{ variant_details.getTableDetails().getTitle() }}
Variant {{ variant_details.getDetails().getTitle() }}
Benchmark {{ variant_details.getBenchmarkDetails().getTitle() }}
Details {{ variant_details.getDetails().getDetails() }}
Instruction Address {{ "0x%x (Dec: %d)"|format(request.args.get('instr_address')|int, request.args.get('instr_address')|int) }}
Total Results{{ result|length }}
+
+

Code

+ + + + + + + + {% for d in code['below'] %} + + + + + + + {% endfor %} + + + + + + + {% for d in code['upper'][1:] %} + + + + + + + {% endfor %}
AddressOpcodeDisassemblyComment
{{ "0x%x"|format(d['instr_address']) }}{{ d['opcode'] }}{{ d['disassemble'] }}{{ d['comment'] }}
{{ "0x%x"|format(code['upper'][0]['instr_address']) }}{{ code['upper'][0]['opcode'] }}{{ code['upper'][0]['disassemble'] }}{{ code['upper'][0]['comment'] }}
{{ "0x%x"|format(d['instr_address']) }}{{ d['opcode'] }}{{ d['disassemble'] }}{{ d['comment'] }}
+
+

Results ({{ result|length }})

+ + + {% for key, value in result[0].items() -%} + + {% endfor -%} + + {% for r in result -%} + + {% for k,v in r.items() -%} + + {% endfor -%} + + {% endfor -%} + +
{{ key }}
{{ v }}
+{% else %} +

Sorry, no details found.

+{% endif %} +{% endblock %} diff --git a/bin/resultbrowser/app/templates/layout.html b/bin/resultbrowser/app/templates/layout.html new file mode 100644 index 0000000..5aa0805 --- /dev/null +++ b/bin/resultbrowser/app/templates/layout.html @@ -0,0 +1,32 @@ + + + + FAIL* Results + + + + + +
+
+

FAIL*

+ +
+
+ +
+ {% block content %} + {% endblock %} +
+ +
+ + + diff --git a/bin/resultbrowser/app/views.py b/bin/resultbrowser/app/views.py new file mode 100644 index 0000000..240802d --- /dev/null +++ b/bin/resultbrowser/app/views.py @@ -0,0 +1,42 @@ +from flask import render_template,request +from app import app + +# import model +# import data + +from . import model +from . import data + +@app.route('/') +@app.route('/index') +def index(): + reload_overview = request.args.get('reload', False) + if reload_overview: + print("Reloading overview...") + model.reloadOverview() + return render_template("index.html", overview=model.getOverview(), objdump_there = model.objdumpExists()) + +@app.route('/code') +def code(): + variant_id = request.args.get('variant_id', None) + resulttype = request.args.get('resulttype', None) + table = request.args.get('table', None) + res,restypes = model.getCode(table, variant_id, resulttype) + var_dets = model.getOverview().getVariantById(variant_id) + return render_template("code.html", results=res, resulttypes=restypes, variant_details=var_dets ) + +@app.route('/instr_details') +def instr_details(): + table = request.args.get('table', None) + variant_id = request.args.get('variant_id', None) + instr_addr = request.args.get('instr_address', None) + resulttype = request.args.get('resulttype', None) + codeexcerpt = model.getCodeExcerpt(variant_id, instr_addr) + var_dets = model.getOverview().getVariantById(variant_id) + results = model.getResultsbyInstruction(table, variant_id, instr_addr, resulttype) + return render_template("instr_details.html", code=codeexcerpt, result=results, variant_details=var_dets) + +@app.route('/about') +def about(): + stat = model.showDBstatus() + return render_template("about.html", status=stat) diff --git a/bin/resultbrowser/conf.yml b/bin/resultbrowser/conf.yml new file mode 100644 index 0000000..14105de --- /dev/null +++ b/bin/resultbrowser/conf.yml @@ -0,0 +1,69 @@ +# YAML-based: http://yaml.org/ +# Online parser for testing: http://yaml-online-parser.appspot.com/ +# Some notes: +# YAML is case-sensitive and structured by indention! +# +# The 'defaults' section describes an *optional* default result type mapping for all tables. +# The 'tables' section describes result tables in more detail. +# A table consists of variants, each variant of benchmarks. +# Each of these configuration items +# title: Table title +# details: Some textual description +# mapping: A distinct mapping, if not set, the parent item's mapping is used + +defaults: + mapping: + Everything OK: + - OK + - OK_DETECTED_ERROR + - OK_WRONG_CONTROL_FLOW + Outside Data Section: + - ERR_OUTSIDE_DATA + Hardware Trap: + - ERR_OUTSIDE_TEXT + - ERR_TRAP + Silent Data Corruption: + - ERR_WRONG_RESULT + +tables: + result_CoredVoterProtoMsg: + title: CoRed Voter Experiment Results + variants: + x86_cored_voter: + title: x86 CoRed Voter Experiment + details: Some interesting details about the experiment. + + benchmarks: + ean-random-4: + title: Random 4 bit injections + details: | + The details can also written this way. + The pipe insert the newlines. Cool, isn't it? + + ean-random-5: + title: Random 5 bit injections + details: Details about 5 bit random injection benchmark. + mapping: + Alright: + - OK + - OK_DETECTED_ERROR + - OK_WRONG_CONTROL_FLOW + Not Alright: + - ERR_OUTSIDE_DATA + - ERR_OUTSIDE_TEXT + - ERR_TRAP + - ERR_WRONG_RESULT + Timeout: + - ERR_TIMEOUT + # Another variant within result_CoredVoterProtoMsg + x86_cored_voter2: + title: variant title + details: variant details + benchmarks: + ean-random-2: + title: benchmarktitle + details: some benchmark details + + ean-random-3: + title: benchmark random 3 + details: some benchmark 3 details diff --git a/bin/resultbrowser/run.py b/bin/resultbrowser/run.py new file mode 100755 index 0000000..1084893 --- /dev/null +++ b/bin/resultbrowser/run.py @@ -0,0 +1,5 @@ +#!/usr/bin/env python +from app import app +from app import model + +app.run(debug=False, port=int(model.opts.port), host=model.opts.host) diff --git a/share/BIOS-bochs-latest b/share/BIOS-bochs-latest new file mode 100755 index 0000000..2d3fea3 Binary files /dev/null and b/share/BIOS-bochs-latest differ diff --git a/share/vgabios.bin b/share/vgabios.bin new file mode 100755 index 0000000..fa9806b Binary files /dev/null and b/share/vgabios.bin differ