Index: src/trusted/validator/x86/testing/tf/val_runner.py |
diff --git a/src/trusted/validator/x86/testing/tf/val_runner.py b/src/trusted/validator/x86/testing/tf/val_runner.py |
new file mode 100644 |
index 0000000000000000000000000000000000000000..d345bef0be9c9aacb4ffa55d32a3442ab1dfb176 |
--- /dev/null |
+++ b/src/trusted/validator/x86/testing/tf/val_runner.py |
@@ -0,0 +1,174 @@ |
+# Copyright (c) 2012 The Native Client Authors. All rights reserved. |
+# Use of this source code is governed by a BSD-style license that can be |
+# found in the LICENSE file. |
+ |
+import collections |
+import re |
+ |
+import utils |
+ |
+ |
+VALIDATORS = ['nc', 'dfa'] |
+ |
+ |
+ncval32 = None |
+ncval64 = None |
+rdfaval = None |
+ |
+ |
+def AddValidatorsOptions(option_parser): |
+ option_parser.add_option('--ncval32', dest='ncval32', type='string') |
+ option_parser.add_option('--ncval64', dest='ncval64', type='string') |
+ option_parser.add_option('--rdfaval', dest='rdfaval', type='string') |
+ |
+ |
+def ProcessValidatorsOptions(options): |
+ global ncval32 |
+ global ncval64 |
+ global rdfaval |
+ |
+ if options.ncval32 is not None: |
+ ncval32 = options.ncval32 |
+ |
+ if options.ncval64 is not None: |
+ ncval64 = options.ncval64 |
+ |
+ if options.rdfaval is not None: |
+ rdfaval = options.rdfaval |
+ |
+ |
+def SplitToLines(content): |
+ return [line.rstrip() for line in content.rstrip().split('\n')] |
+ |
+ |
+def RunNcVal32(hex_name): |
+ args = [ |
+ '--hex_text=-', |
+ '--max_errors=-1', |
+ '--detailed=false', |
+ '--cpuid-all' |
+ ] |
+ |
+ with open(hex_name) as input: |
+ result = utils.CheckOutput([ncval32] + args, stdin=input) |
+ |
+ return SplitToLines(result) |
+ |
+ |
+def RunNcVal64(hex_name): |
+ args = [ |
+ '--hex_text=-', |
+ '--max_errors=-1', |
+ '--readwrite_sfi', |
+ '--annotate=false', |
+ '--cpuid-all', |
+ '--detailed=false', |
+ ] |
+ |
+ with open(hex_name) as input: |
+ result = utils.CheckOutput([ncval64] + args, stdin=input) |
+ |
+ return SplitToLines(result) |
+ |
+ |
+def ParseNcValVerdict(last_line): |
+ m = re.match(r'\*\*\* <input> (is safe|IS UNSAFE) \*\*\*$', last_line) |
+ assert m is not None, 'unexpected ncval output "%s"' % last_line |
+ return m.group(1) == 'is safe' |
+ |
+ |
+def ParseNcVal32(lines): |
+ assert len(lines) > 0, 'ncval output is empty' |
+ |
+ errors = [] |
+ for line in lines[:-1]: |
+ line = line.strip() |
+ if line == '': |
+ continue |
+ |
+ if re.match(r'.+ > .+ \(read overflow of .+ bytes\)', line): |
+ errors.append((0, line)) |
+ continue |
+ if line == 'ErrorSegmentation': |
+ errors.append((0, line)) |
+ continue |
+ |
+ # Parse error message of the form |
+ # VALIDATOR: 4: Bad prefix usage |
+ m = re.match(r'VALIDATOR: ([0-9a-f]+): (.*)$', line, re.IGNORECASE) |
+ |
+ assert m is not None, "can't parse '%s'" % line |
+ |
+ offset = int(m.group(1), 16) |
+ message = m.group(2) |
+ errors.append((offset, message)) |
+ safe = ParseNcValVerdict(lines[-1]) |
+ return safe, errors |
+ |
+ |
+def ParseNcVal64(lines): |
+ assert len(lines) > 0, 'ncval output is empty' |
+ |
+ errors = [] |
+ for i, line in enumerate(lines[:-1]): |
+ if line.startswith('VALIDATOR: Checking jump targets:'): |
+ continue |
+ if line.startswith('VALIDATOR: Checking that basic blocks are aligned'): |
+ continue |
+ |
+ # Skip disassembler output of the form |
+ # VALIDATOR: 0000000000000003: 49 89 14 07 mov [%r15+%rax*1], %rdx |
+ m = re.match(r'VALIDATOR: ([0-9a-f]+):', line, re.IGNORECASE) |
+ if m is not None: |
+ continue |
+ |
+ # Parse error message of the form |
+ # VALIDATOR: ERROR: 20: Bad basic block alignment. |
+ m = re.match(r'VALIDATOR: ERROR: ([0-9a-f]+): (.*)', line, re.IGNORECASE) |
+ if m is not None: |
+ offset = int(m.group(1), 16) |
+ errors.append((offset, m.group(2))) |
+ continue |
+ |
+ # Parse two-line error messages of the form |
+ # VALIDATOR: 0000000000000003: 49 89 14 07 mov [%r15+%rax*1], %rdx |
+ # VALIDATOR: ERROR: Invalid index register in memory offset |
+ m = re.match(r'VALIDATOR: ((?:ERROR|WARNING): .*)$', line, re.IGNORECASE) |
+ if m is not None: |
+ m2 = re.match(r'VALIDATOR: ([0-9a-f]+):', lines[i-1], re.IGNORECASE) |
+ assert m2 is not None, "can't parse line '%s' preceding line '%s'" % ( |
+ lines[i-1], |
+ line |
+ ) |
+ offset = int(m2.group(1), 16) |
+ errors.append((offset, m.group(1))) |
+ continue |
+ |
+ raise AssertionError("can't parse line '%s'" % line) |
+ |
+ safe = ParseNcValVerdict(lines[-1]) |
+ return safe, errors |
+ |
+ |
+ValidationResults = collections.namedtuple('ValidationResults', 'safe, errors') |
+ |
+ |
+def RunValidator(validator, bits, data): |
+ assert validator in VALIDATORS |
+ assert bits in [32, 64] |
+ assert len(data) % 32 == 0 |
+ |
+ if validator == 'nc': |
+ with utils.TempFile(mode='w') as hex_file: |
+ hex_file.write('%s\n' % utils.DataToReadableHex(data)) |
+ hex_file.flush() |
+ |
+ if bits == 32: |
+ safe, errors = ParseNcVal32(RunNcVal32(hex_file.name)) |
+ elif bits == 64: |
+ safe, errors = ParseNcVal64(RunNcVal64(hex_file.name)) |
+ |
+ elif validator == 'dfa': |
+ raise NotImplementedError('Code for running rdfa is temporarily removed') |
+ |
+ return ValidationResults(safe=safe, errors=errors) |