Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 # Copyright (c) 2012 The Native Client Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 import collections | |
| 6 import os | |
| 7 import re | |
| 8 import subprocess | |
| 9 | |
| 10 import utils | |
| 11 | |
| 12 | |
| 13 VALIDATORS = ['nc', 'dfa'] | |
| 14 | |
| 15 | |
| 16 scons_out = '../../../../../../scons-out' | |
| 17 ncval32 = os.path.join(scons_out, 'opt-linux-x86-32/staging/ncval') | |
|
Mark Seaborn
2012/09/10 19:38:06
You shouldn't hard-code Scons output filenames lik
Vlad Shcherbina
2012/09/11 14:26:00
I was using it to run locally (on bots paths are a
| |
| 18 ncval64 = os.path.join(scons_out, 'opt-linux-x86-64/staging/ncval') | |
| 19 rdfaval = os.path.join(scons_out, 'opt-linux-x86-32/staging/validator_test') | |
| 20 | |
| 21 | |
| 22 def AddValidatorsOptions(option_parser): | |
| 23 option_parser.add_option('--ncval32', dest='ncval32', type='string') | |
| 24 option_parser.add_option('--ncval64', dest='ncval64', type='string') | |
| 25 option_parser.add_option('--rdfaval', dest='rdfaval', type='string') | |
| 26 | |
| 27 | |
| 28 def ProcessValidatorsOptions(options): | |
| 29 global ncval32 | |
|
Mark Seaborn
2012/09/10 19:38:06
It would be cleaner if you didn't modify global va
Vlad Shcherbina
2012/09/11 14:26:00
Well, I don't like it because it would mean passin
| |
| 30 global ncval64 | |
| 31 global rdfaval | |
| 32 | |
| 33 if options.ncval32 is not None: | |
| 34 ncval32 = options.ncval32 | |
| 35 | |
| 36 if options.ncval64 is not None: | |
| 37 ncval64 = options.ncval64 | |
| 38 | |
| 39 if options.rdfaval is not None: | |
| 40 rdfaval = options.rdfaval | |
| 41 | |
| 42 | |
| 43 def SplitToLines(content): | |
| 44 return [line.rstrip() for line in content.rstrip().split('\n')] | |
| 45 | |
| 46 | |
| 47 def RunNcVal32(hex_name): | |
| 48 args = [ | |
| 49 '--hex_text=-', | |
| 50 '--max_errors=-1', | |
| 51 '--detailed=false', | |
| 52 '--cpuid-all' | |
| 53 ] | |
| 54 | |
| 55 with open(hex_name) as input: | |
| 56 result = utils.CheckOutput([ncval32] + args, stdin=input) | |
| 57 | |
| 58 return SplitToLines(result) | |
| 59 | |
| 60 | |
| 61 def RunNcVal64(hex_name): | |
| 62 args = [ | |
| 63 '--hex_text=-', | |
| 64 '--max_errors=-1', | |
| 65 '--readwrite_sfi', | |
| 66 '--annotate=false', | |
| 67 '--cpuid-all', | |
| 68 '--detailed=false', | |
| 69 ] | |
| 70 | |
| 71 with open(hex_name) as input: | |
| 72 result = utils.CheckOutput([ncval64] + args, stdin=input) | |
| 73 | |
| 74 return SplitToLines(result) | |
| 75 | |
| 76 | |
| 77 def ParseNcValVerdict(last_line): | |
| 78 m = re.match(r'\*\*\* <input> (is safe|IS UNSAFE) \*\*\*$', last_line) | |
| 79 assert m is not None, 'unexpected ncval output "%s"' % last_line | |
| 80 return m.group(1) == 'is safe' | |
| 81 | |
| 82 | |
| 83 def ParseNcVal32(lines): | |
| 84 assert len(lines) > 0, 'ncval output is empty' | |
| 85 | |
| 86 errors = [] | |
| 87 for line in lines[:-1]: | |
| 88 line = line.strip() | |
| 89 if line == '': | |
| 90 continue | |
| 91 if re.match(r'.+ > .+ \(read overflow of .+ bytes\)', line): | |
|
Mark Seaborn
2012/09/10 19:38:06
Why are you dropping this line of output?
Vlad Shcherbina
2012/09/11 14:26:00
Right, this should not happen anyway. It's somethi
| |
| 92 continue | |
| 93 if line == 'ErrorSegmentation': | |
|
Mark Seaborn
2012/09/10 19:38:06
Ditto: why drop this?
Vlad Shcherbina
2012/09/11 14:26:00
Same.
| |
| 94 continue | |
| 95 # Parse error message of the form | |
| 96 # VALIDATOR: 4: Bad prefix usage | |
| 97 m = re.match(r'VALIDATOR: ([0-9a-f]+): (.*)$', line, re.IGNORECASE) | |
| 98 offset = int(m.group(1), 16) | |
| 99 message = m.group(2) | |
| 100 errors.append((offset, message)) | |
| 101 safe = ParseNcValVerdict(lines[-1]) | |
| 102 return safe, errors | |
| 103 | |
| 104 | |
| 105 def ParseNcVal64(lines): | |
| 106 assert len(lines) > 0, 'ncval output is empty' | |
| 107 | |
| 108 errors = [] | |
| 109 for i, line in enumerate(lines[:-1]): | |
| 110 #print line | |
|
Mark Seaborn
2012/09/10 19:38:06
Please remove commented-out code
Vlad Shcherbina
2012/09/11 14:26:00
Done.
| |
| 111 #if 'Bad basic' in line: | |
| 112 # print 'z'*10 | |
| 113 # print line | |
| 114 # raw_input() | |
| 115 | |
| 116 if line.startswith('VALIDATOR: Checking jump targets:'): | |
| 117 continue | |
| 118 if line.startswith('VALIDATOR: Checking that basic blocks are aligned'): | |
| 119 continue | |
| 120 | |
| 121 # Skip disassembler output of the form | |
| 122 # VALIDATOR: 0000000000000003: 49 89 14 07 mov [%r15+%rax*1], %rdx | |
| 123 m = re.match(r'VALIDATOR: ([0-9a-f]+):', line, re.IGNORECASE) | |
| 124 if m is not None: | |
| 125 continue | |
| 126 | |
| 127 # Parse error message of the form | |
| 128 # VALIDATOR: ERROR: 20: Bad basic block alignment. | |
| 129 m = re.match(r'VALIDATOR: ERROR: ([0-9a-f]+): (.*)', line, re.IGNORECASE) | |
| 130 if m is not None: | |
| 131 offset = int(m.group(1), 16) | |
| 132 errors.append((offset, m.group(2))) | |
| 133 continue | |
| 134 | |
| 135 # Parse two-line error messages of the form | |
| 136 # VALIDATOR: 0000000000000003: 49 89 14 07 mov [%r15+%rax*1], %rdx | |
| 137 # VALIDATOR: ERROR: Invalid index register in memory offset | |
| 138 m = re.match(r'VALIDATOR: ((?:ERROR|WARNING): .*)$', line, re.IGNORECASE) | |
| 139 if m is not None: | |
| 140 m2 = re.match(r'VALIDATOR: ([0-9a-f]+):', lines[i-1], re.IGNORECASE) | |
| 141 assert m2 is not None, "can't parse line '%s' preceding line '%s'" % ( | |
| 142 lines[i-1], | |
| 143 line | |
| 144 ) | |
| 145 offset = int(m2.group(1), 16) | |
| 146 errors.append((offset, m.group(1))) | |
| 147 continue | |
| 148 | |
| 149 assert False, "can't parse line '%s'" % line | |
|
Mark Seaborn
2012/09/10 19:38:06
Better style would be:
raise AssertionError("can
Vlad Shcherbina
2012/09/11 14:26:00
Done.
| |
| 150 | |
| 151 safe = ParseNcValVerdict(lines[-1]) | |
| 152 return safe, errors | |
| 153 | |
| 154 | |
| 155 def MakeElf(bits, data, elf_filename): | |
| 156 assert bits in [32, 64] | |
| 157 | |
| 158 with utils.TempFile(mode='w') as asm_file: | |
| 159 for c in data: | |
| 160 asm_file.write('.byte %d\n' % ord(c)) | |
| 161 asm_file.flush() | |
| 162 | |
| 163 subprocess.check_call([ | |
| 164 'as', '--%s' % bits, asm_file.name, '-o', elf_filename]) | |
| 165 | |
| 166 | |
| 167 def RunDfa(bits, data): | |
| 168 with utils.TempFile(mode='w') as elf_file: | |
| 169 with utils.TempFile(mode='w+') as out_file: | |
| 170 MakeElf(bits, data, elf_file.name) | |
| 171 | |
| 172 result = subprocess.call([rdfaval, elf_file.name], stdout=out_file) | |
| 173 out_file.seek(0) | |
| 174 lines = SplitToLines(out_file.read()) | |
| 175 | |
| 176 return result, lines | |
| 177 | |
| 178 | |
| 179 def ParseDfa(lines): | |
|
Mark Seaborn
2012/09/10 19:38:06
Please can you take out all of the DFA-validator r
Vlad Shcherbina
2012/09/11 14:26:00
Done.
| |
| 180 errors = [] | |
| 181 for line in lines: | |
| 182 if re.match(r"file '.*' can not be fully validated$", line): | |
| 183 continue | |
| 184 m = re.match(r'bad jump to around 0x([0-9a-f]+)$', line, re.IGNORECASE) | |
| 185 if m is not None: | |
| 186 offset = int(m.group(1), 16) | |
| 187 errors.append((offset, 'bad jump to around')) | |
| 188 continue | |
| 189 | |
| 190 m = re.match(r'offset 0x([0-9a-f]+): (.*)$', line, re.IGNORECASE) | |
| 191 offset = int(m.group(1), 16) | |
| 192 message = m.group(2) | |
| 193 errors.append((offset, message)) | |
| 194 | |
| 195 return errors | |
| 196 | |
| 197 | |
| 198 ValidationResults = collections.namedtuple('ValidationResults', 'safe, errors') | |
| 199 | |
| 200 | |
| 201 def RunValidator(validator, bits, data): | |
| 202 assert validator in VALIDATORS | |
| 203 assert bits in [32, 64] | |
| 204 assert len(data) % 32 == 0 | |
| 205 | |
| 206 if validator == 'nc': | |
| 207 with utils.TempFile(mode='w') as hex_file: | |
| 208 hex_file.write('%s\n' % utils.DataToReadableHex(data)) | |
| 209 hex_file.flush() | |
| 210 | |
| 211 if bits == 32: | |
| 212 safe, errors = ParseNcVal32(RunNcVal32(hex_file.name)) | |
| 213 elif bits == 64: | |
| 214 safe, errors = ParseNcVal64(RunNcVal64(hex_file.name)) | |
| 215 | |
| 216 elif validator == 'dfa': | |
| 217 code, output = RunDfa(bits, data) | |
| 218 safe = (code == 0) | |
| 219 errors = ParseDfa(output) | |
| 220 | |
| 221 return ValidationResults(safe=safe, errors=errors) | |
| OLD | NEW |