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 |