| 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 |