OLD | NEW |
1 #!/usr/bin/python | 1 #!/usr/bin/python |
2 # Copyright (c) 2012 The Native Client Authors. All rights reserved. | 2 # Copyright (c) 2012 The Native Client Authors. All rights reserved. |
3 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
4 # found in the LICENSE file. | 4 # found in the LICENSE file. |
5 | 5 |
6 | 6 |
7 """Simple testing harness for running commands and checking expected output. | 7 """Simple testing harness for running commands and checking expected output. |
8 | 8 |
9 This harness is used instead of shell scripts to ensure windows compatibility | 9 This harness is used instead of shell scripts to ensure windows compatibility |
10 | 10 |
(...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
201 MungeWindowsErrorExit(STATUS_INTEGER_DIVIDE_BY_ZERO), | 201 MungeWindowsErrorExit(STATUS_INTEGER_DIVIDE_BY_ZERO), |
202 ] | 202 ] |
203 | 203 |
204 # We patch Windows' KiUserExceptionDispatcher on x86-64 to terminate | 204 # We patch Windows' KiUserExceptionDispatcher on x86-64 to terminate |
205 # the process safely when untrusted code crashes. We get the exit | 205 # the process safely when untrusted code crashes. We get the exit |
206 # code associated with the HLT instruction. | 206 # code associated with the HLT instruction. |
207 win64_exit_via_ntdll_patch = [ | 207 win64_exit_via_ntdll_patch = [ |
208 MungeWindowsErrorExit(STATUS_PRIVILEGED_INSTRUCTION)] | 208 MungeWindowsErrorExit(STATUS_PRIVILEGED_INSTRUCTION)] |
209 | 209 |
210 | 210 |
| 211 EXC_BAD_ACCESS = 1 |
| 212 EXC_BAD_INSTRUCTION = 2 |
| 213 |
| 214 |
211 # 32-bit processes on Mac OS X return SIGBUS in most of the cases where Linux | 215 # 32-bit processes on Mac OS X return SIGBUS in most of the cases where Linux |
212 # returns SIGSEGV, except for actual x86 segmentation violations. 64-bit | 216 # returns SIGSEGV, except for actual x86 segmentation violations. 64-bit |
213 # processes on Mac OS X behave differently. | 217 # processes on Mac OS X behave differently. |
214 status_map = { | 218 status_map = { |
215 'sigtrap' : { | 219 'sigtrap' : { |
216 'linux2': [-5], # SIGTRAP | 220 'linux2': [-5], # SIGTRAP |
217 'darwin': [-5], # SIGTRAP | 221 'darwin': [-5], # SIGTRAP |
218 }, | 222 }, |
219 'trusted_sigabrt' : { | 223 'trusted_sigabrt' : { |
220 'linux2': [-6], # SIGABRT | 224 'linux2': [-6], # SIGABRT |
(...skipping 15 matching lines...) Expand all Loading... |
236 # This is not used on Windows because Windows does not have an | 240 # This is not used on Windows because Windows does not have an |
237 # equivalent of SIGPIPE. | 241 # equivalent of SIGPIPE. |
238 'linux2': [-13], # SIGPIPE | 242 'linux2': [-13], # SIGPIPE |
239 'mac32': [-13], # SIGPIPE | 243 'mac32': [-13], # SIGPIPE |
240 'mac64': [-13], # SIGPIPE | 244 'mac64': [-13], # SIGPIPE |
241 }, | 245 }, |
242 'untrusted_sigsegv': { | 246 'untrusted_sigsegv': { |
243 'linux2': [-11], # SIGSEGV | 247 'linux2': [-11], # SIGSEGV |
244 'mac32': [-11], # SIGSEGV | 248 'mac32': [-11], # SIGSEGV |
245 'mac64': [-11], # SIGSEGV | 249 'mac64': [-11], # SIGSEGV |
| 250 'mach_exception': EXC_BAD_ACCESS, |
246 'win32': win32_untrusted_crash_exit, | 251 'win32': win32_untrusted_crash_exit, |
247 'win64': win64_exit_via_ntdll_patch, | 252 'win64': win64_exit_via_ntdll_patch, |
248 }, | 253 }, |
249 'untrusted_sigill' : { | 254 'untrusted_sigill' : { |
250 'linux2': [-4], # SIGILL | 255 'linux2': [-4], # SIGILL |
251 'mac32': [-4], # SIGILL | 256 'mac32': [-4], # SIGILL |
252 'mac64': [-4], # SIGILL | 257 'mac64': [-4], # SIGILL |
253 'win32': win32_untrusted_crash_exit, | 258 'win32': win32_untrusted_crash_exit, |
254 'win64': win64_exit_via_ntdll_patch, | 259 'win64': win64_exit_via_ntdll_patch, |
255 }, | 260 }, |
256 'untrusted_sigfpe' : { | 261 'untrusted_sigfpe' : { |
257 'linux2': [-8], # SIGFPE | 262 'linux2': [-8], # SIGFPE |
258 'mac32': [-8], # SIGFPE | 263 'mac32': [-8], # SIGFPE |
259 'mac64': [-8], # SIGFPE | 264 'mac64': [-8], # SIGFPE |
260 'win32': win32_sigfpe, | 265 'win32': win32_sigfpe, |
261 'win64': win64_exit_via_ntdll_patch, | 266 'win64': win64_exit_via_ntdll_patch, |
262 }, | 267 }, |
263 'untrusted_segfault': { | 268 'untrusted_segfault': { |
264 'linux2': [-11], # SIGSEGV | 269 'linux2': [-11], # SIGSEGV |
265 'mac32': [-10], # SIGBUS | 270 'mac32': [-10], # SIGBUS |
266 'mac64': [-10], # SIGBUS | 271 'mac64': [-10], # SIGBUS |
| 272 'mach_exception': EXC_BAD_ACCESS, |
267 'win32': win32_untrusted_crash_exit, | 273 'win32': win32_untrusted_crash_exit, |
268 'win64': win64_exit_via_ntdll_patch, | 274 'win64': win64_exit_via_ntdll_patch, |
269 }, | 275 }, |
270 'untrusted_sigsegv_or_equivalent': { | 276 'untrusted_sigsegv_or_equivalent': { |
271 'linux2': [-11], # SIGSEGV | 277 'linux2': [-11], # SIGSEGV |
272 'mac32': [-11], # SIGSEGV | 278 'mac32': [-11], # SIGSEGV |
273 'mac64': [-10], # SIGBUS | 279 'mac64': [-10], # SIGBUS |
| 280 'mach_exception': EXC_BAD_ACCESS, |
274 'win32': win32_untrusted_crash_exit, | 281 'win32': win32_untrusted_crash_exit, |
275 'win64': win64_exit_via_ntdll_patch, | 282 'win64': win64_exit_via_ntdll_patch, |
276 }, | 283 }, |
277 'trusted_segfault': { | 284 'trusted_segfault': { |
278 'linux2': [-11], # SIGSEGV | 285 'linux2': [-11], # SIGSEGV |
279 'mac32': [-10], # SIGBUS | 286 'mac32': [-10], # SIGBUS |
280 'mac64': [-11], # SIGSEGV | 287 'mac64': [-11], # SIGSEGV |
| 288 'mach_exception': EXC_BAD_ACCESS, |
281 'win32': [MungeWindowsErrorExit(STATUS_ACCESS_VIOLATION)], | 289 'win32': [MungeWindowsErrorExit(STATUS_ACCESS_VIOLATION)], |
282 'win64': [MungeWindowsErrorExit(STATUS_ACCESS_VIOLATION)], | 290 'win64': [MungeWindowsErrorExit(STATUS_ACCESS_VIOLATION)], |
283 }, | 291 }, |
284 'trusted_sigsegv_or_equivalent': { | 292 'trusted_sigsegv_or_equivalent': { |
285 'linux2': [-11], # SIGSEGV | 293 'linux2': [-11], # SIGSEGV |
286 'mac32': [-11], # SIGSEGV | 294 'mac32': [-11], # SIGSEGV |
287 'mac64': [-11], # SIGSEGV | 295 'mac64': [-11], # SIGSEGV |
| 296 'mach_exception': EXC_BAD_ACCESS, |
288 'win32': [], | 297 'win32': [], |
289 'win64': [], | 298 'win64': [], |
290 }, | 299 }, |
291 # This is like 'untrusted_segfault', but without the 'untrusted_' | 300 # This is like 'untrusted_segfault', but without the 'untrusted_' |
292 # prefix which marks the status type as expecting a | 301 # prefix which marks the status type as expecting a |
293 # gracefully-printed exit message from nacl_signal_common.c. This | 302 # gracefully-printed exit message from nacl_signal_common.c. This |
294 # is a special case because we use different methods for writing | 303 # is a special case because we use different methods for writing |
295 # the exception stack frame on different platforms. On Mac and | 304 # the exception stack frame on different platforms. On Mac and |
296 # Windows, NaCl uses a system call which will detect unwritable | 305 # Windows, NaCl uses a system call which will detect unwritable |
297 # pages, so the exit status appears as an unhandled fault from | 306 # pages, so the exit status appears as an unhandled fault from |
298 # untrusted code. On Linux, NaCl's signal handler writes the | 307 # untrusted code. On Linux, NaCl's signal handler writes the |
299 # frame directly, so the exit status comes from getting a SIGSEGV | 308 # frame directly, so the exit status comes from getting a SIGSEGV |
300 # inside the SIGSEGV handler. | 309 # inside the SIGSEGV handler. |
301 'unwritable_exception_stack': { | 310 'unwritable_exception_stack': { |
302 'linux2': [-11], # SIGSEGV | 311 'linux2': [-11], # SIGSEGV |
303 'mac32': [-10], # SIGBUS | 312 'mac32': [-10], # SIGBUS |
304 'mac64': [-10], # SIGBUS | 313 'mac64': [-10], # SIGBUS |
| 314 'mach_exception': 123, |
305 'win32': win32_untrusted_crash_exit, | 315 'win32': win32_untrusted_crash_exit, |
306 'win64': win64_exit_via_ntdll_patch, | 316 'win64': win64_exit_via_ntdll_patch, |
307 }, | 317 }, |
308 } | 318 } |
309 | 319 |
310 | 320 |
311 def ProcessOptions(argv): | 321 def ProcessOptions(argv): |
312 global GlobalPlatform | 322 global GlobalPlatform |
313 | 323 |
314 """Process command line options and return the unprocessed left overs.""" | 324 """Process command line options and return the unprocessed left overs.""" |
(...skipping 28 matching lines...) Expand all Loading... |
343 | 353 |
344 | 354 |
345 # Parse output for signal type and number | 355 # Parse output for signal type and number |
346 # | 356 # |
347 # The '** Signal' output is from the nacl signal handler code. | 357 # The '** Signal' output is from the nacl signal handler code. |
348 # | 358 # |
349 # Since it is possible for there to be an output race with another | 359 # Since it is possible for there to be an output race with another |
350 # thread, or additional output due to atexit functions, we scan the | 360 # thread, or additional output due to atexit functions, we scan the |
351 # output in reverse order for the signal signature. | 361 # output in reverse order for the signal signature. |
352 def GetNaClSignalInfoFromStderr(stderr): | 362 def GetNaClSignalInfoFromStderr(stderr): |
353 sigNum = None | |
354 sigType = 'normal' | |
355 | |
356 lines = stderr.splitlines() | 363 lines = stderr.splitlines() |
357 | |
358 # Scan for signal msg in reverse order | 364 # Scan for signal msg in reverse order |
359 for curline in reversed(lines): | 365 for curline in reversed(lines): |
360 words = curline.split() | 366 match = re.match('\*\* (Signal|Mach exception) (\d+) from ' |
361 if len(words) > 4 and words[0] == '**' and words[1] == 'Signal': | 367 '(trusted|untrusted) code', curline) |
362 sigNum = int(words[2]) | 368 if match is not None: |
363 sigType = words[4] | 369 return match.group(0) |
364 break | 370 return None |
365 return sigNum, sigType | 371 |
366 | 372 |
367 def GetQemuSignalFromStderr(stderr, default): | 373 def GetQemuSignalFromStderr(stderr, default): |
368 for line in reversed(stderr.splitlines()): | 374 for line in reversed(stderr.splitlines()): |
369 # Look for 'qemu: uncaught target signal XXX'. | 375 # Look for 'qemu: uncaught target signal XXX'. |
370 words = line.split() | 376 words = line.split() |
371 if (len(words) > 4 and | 377 if (len(words) > 4 and |
372 words[0] == 'qemu:' and words[1] == 'uncaught' and | 378 words[0] == 'qemu:' and words[1] == 'uncaught' and |
373 words[2] == 'target' and words[3] == 'signal'): | 379 words[2] == 'target' and words[3] == 'signal'): |
374 return -int(words[4]) | 380 return -int(words[4]) |
375 return default | 381 return default |
(...skipping 29 matching lines...) Expand all Loading... |
405 % FormatExitStatus(exit_status)) | 411 % FormatExitStatus(exit_status)) |
406 return False | 412 return False |
407 elif len(intended_statuses) != 1: | 413 elif len(intended_statuses) != 1: |
408 Print('\nERROR: Command returned exit status %s but produced ' | 414 Print('\nERROR: Command returned exit status %s but produced ' |
409 'multiple intended_exit_status lines (%s)' | 415 'multiple intended_exit_status lines (%s)' |
410 % (FormatExitStatus(exit_status), ', '.join(intended_statuses))) | 416 % (FormatExitStatus(exit_status), ', '.join(intended_statuses))) |
411 return False | 417 return False |
412 else: | 418 else: |
413 req_status = intended_statuses[0] | 419 req_status = intended_statuses[0] |
414 | 420 |
| 421 if req_status == 'unwritable_exception_stack' and sys.platform != 'linux2': |
| 422 req_status = 'untrusted_segfault' |
| 423 |
| 424 expected_printed_status = None |
415 expected_sigtype = 'normal' | 425 expected_sigtype = 'normal' |
416 if req_status in status_map: | 426 if req_status in status_map: |
417 expected_statuses = status_map[req_status][GlobalPlatform] | 427 expected_statuses = status_map[req_status][GlobalPlatform] |
418 if using_nacl_signal_handler: | 428 if using_nacl_signal_handler: |
419 if req_status.startswith('trusted_'): | 429 if req_status.startswith('trusted_'): |
420 expected_sigtype = 'trusted' | 430 expected_sigtype = 'trusted' |
421 elif req_status.startswith('untrusted_'): | 431 elif req_status.startswith('untrusted_'): |
422 expected_sigtype = 'untrusted' | 432 expected_sigtype = 'untrusted' |
| 433 |
423 else: | 434 else: |
424 expected_statuses = [int(req_status)] | 435 expected_statuses = [int(req_status)] |
425 | 436 |
426 # On 32-bit Windows, we cannot catch a signal that occurs in x86-32 | 437 if expected_sigtype != 'normal': |
427 # untrusted code, so it always appears as a 'normal' exit, which | 438 if sys.platform == 'darwin': |
428 # means that the signal handler does not print a message. | 439 # Mac OS X |
429 if GlobalPlatform == 'win32' and expected_sigtype == 'untrusted': | 440 expected_printed_status = '** Mach exception %d from %s code' % ( |
430 expected_sigtype = 'normal' | 441 status_map[req_status]['mach_exception'], |
431 | 442 expected_sigtype) |
432 if expected_sigtype == 'normal': | 443 else: |
433 expected_printed_signum = None | 444 # Linux |
434 else: | 445 assert sys.platform != 'win32' |
435 assert sys.platform != 'win32' | 446 assert len(expected_statuses) == 1 |
436 assert len(expected_statuses) == 1 | 447 assert expected_statuses[0] < 0 |
437 assert expected_statuses[0] < 0 | 448 expected_printed_signum = -expected_statuses[0] |
438 expected_printed_signum = -expected_statuses[0] | 449 expected_printed_status = '** Signal %d from %s code' % ( |
439 expected_statuses = [IndirectSignal(expected_printed_signum)] | 450 expected_printed_signum, |
| 451 expected_sigtype) |
| 452 expected_statuses = [IndirectSignal(expected_printed_signum)] |
440 | 453 |
441 # If an uncaught signal occurs under QEMU (on ARM), the exit status | 454 # If an uncaught signal occurs under QEMU (on ARM), the exit status |
442 # contains the signal number, mangled as per IndirectSignal(). We | 455 # contains the signal number, mangled as per IndirectSignal(). We |
443 # extract the unadulterated signal number from QEMU's log message in | 456 # extract the unadulterated signal number from QEMU's log message in |
444 # stderr instead. If we are not using QEMU, or no signal is raised | 457 # stderr instead. If we are not using QEMU, or no signal is raised |
445 # under QEMU, this is a no-op. | 458 # under QEMU, this is a no-op. |
446 if stderr is not None: | 459 if stderr is not None: |
447 exit_status = GetQemuSignalFromStderr(stderr, exit_status) | 460 exit_status = GetQemuSignalFromStderr(stderr, exit_status) |
448 | 461 |
449 msg = '\nERROR: Command returned exit status %s but we expected %s' % ( | 462 msg = '\nERROR: Command returned exit status %s but we expected %s' % ( |
450 FormatExitStatus(exit_status), | 463 FormatExitStatus(exit_status), |
451 ' or '.join(FormatExitStatus(value) for value in expected_statuses)) | 464 ' or '.join(FormatExitStatus(value) for value in expected_statuses)) |
452 if exit_status not in expected_statuses: | 465 if exit_status not in expected_statuses: |
453 Print(msg) | 466 Print(msg) |
454 failed = True | 467 failed = True |
455 if using_nacl_signal_handler and stderr is not None: | 468 if using_nacl_signal_handler and stderr is not None: |
456 expected_printed = (expected_printed_signum, expected_sigtype) | 469 actual_printed_status = GetNaClSignalInfoFromStderr(stderr) |
457 actual_printed = GetNaClSignalInfoFromStderr(stderr) | 470 msg = ('\nERROR: Command printed the signal info %r to stderr ' |
458 msg = ('\nERROR: Command printed the signal info %s to stderr ' | 471 'but we expected %r' % |
459 'but we expected %s' % | 472 (actual_printed_status, expected_printed_status)) |
460 (actual_printed, expected_printed)) | 473 if actual_printed_status != expected_printed_status: |
461 if actual_printed != expected_printed: | |
462 Print(msg) | 474 Print(msg) |
463 failed = True | 475 failed = True |
464 | 476 |
465 return not failed | 477 return not failed |
466 | 478 |
467 def CheckTimeBounds(total_time): | 479 def CheckTimeBounds(total_time): |
468 if GlobalSettings['time_error']: | 480 if GlobalSettings['time_error']: |
469 if total_time > GlobalSettings['time_error']: | 481 if total_time > GlobalSettings['time_error']: |
470 Print('ERROR: should have taken less than %f secs' % | 482 Print('ERROR: should have taken less than %f secs' % |
471 (GlobalSettings['time_error'])) | 483 (GlobalSettings['time_error'])) |
(...skipping 206 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
678 # Bogus time, since only ProcessLogOutputCombined failed. | 690 # Bogus time, since only ProcessLogOutputCombined failed. |
679 Print(FailureMessage(0.0) + ' ProcessLogOutputCombined failed!') | 691 Print(FailureMessage(0.0) + ' ProcessLogOutputCombined failed!') |
680 return 1 | 692 return 1 |
681 return 0 | 693 return 0 |
682 | 694 |
683 if __name__ == '__main__': | 695 if __name__ == '__main__': |
684 retval = Main(sys.argv[1:]) | 696 retval = Main(sys.argv[1:]) |
685 # Add some whitepsace to make the logs easier to read. | 697 # Add some whitepsace to make the logs easier to read. |
686 sys.stdout.write('\n\n') | 698 sys.stdout.write('\n\n') |
687 sys.exit(retval) | 699 sys.exit(retval) |
OLD | NEW |