Exemplo n.º 1
0
    def post(self):
        """Handle a post request."""
        if not auth.get_current_user():
            raise helpers.AccessDeniedException()

        project = request.get('project')
        fuzz_target = request.get('fuzz_target')
        stacktrace = request.get('stacktrace')

        state = stack_analyzer.get_crash_data(stacktrace,
                                              symbolize_flag=False,
                                              fuzz_target=fuzz_target,
                                              already_symbolized=True,
                                              detect_ooms_and_hangs=True)
        security_flag = crash_analyzer.is_security_issue(
            state.crash_stacktrace, state.crash_type, state.crash_address)

        if data_handler.find_testcase(project, state.crash_type,
                                      state.crash_state, security_flag):
            new_or_duplicate = 'duplicate'
        else:
            new_or_duplicate = 'new'

        return self.render_json({
            'result': new_or_duplicate,
            'state': state.crash_state,
            'type': state.crash_type,
            'security': security_flag,
        })
Exemplo n.º 2
0
  def process_bad_units(self, bad_units_path, quarantine_corpus_path, crashes):
    """Process bad units found during merge."""
    # TODO(ochang): A lot of this function is similar to parts of fuzz_task.
    # Ideally fuzz_task can be refactored in a way that lets us share the common
    # code.

    environment.reset_current_memory_tool_options(redzone_size=DEFAULT_REDZONE)
    self.runner.process_sanitizer_options()

    logs.log('Processing bad units.')
    corpus_file_paths = _get_corpus_file_paths(bad_units_path)
    num_bad_units = 0

    # Run each corpus item individually.
    for i, unit_path in enumerate(corpus_file_paths, 1):
      if i % 100 == 0:
        logs.log('Up to %d' % i)

      unit_name = os.path.basename(unit_path)
      if unit_name.startswith('timeout-') or unit_name.startswith('oom-'):
        # Don't waste time re-running timeout or oom testcases.
        unit_path = self._quarantine_unit(unit_path, quarantine_corpus_path)
        num_bad_units += 1
        continue

      result = self._run_single_unit(unit_path)

      if (not result.timed_out and
          not crash_analyzer.is_memory_tool_crash(result.output)):
        # Didn't crash or time out.
        continue

      if result.timed_out:
        # Slow unit. Quarantine it.
        unit_path = self._quarantine_unit(unit_path, quarantine_corpus_path)
        num_bad_units += 1
        continue

      # Get memory tool crash information.
      state = stack_analyzer.get_crash_data(result.output, symbolize_flag=True)

      # Crashed or caused a leak. Quarantine it.
      unit_path = self._quarantine_unit(unit_path, quarantine_corpus_path)
      num_bad_units += 1

      if crash_analyzer.ignore_stacktrace(state.crash_stacktrace):
        continue

      # Local de-duplication.
      if state.crash_state not in crashes:
        security_flag = crash_analyzer.is_security_issue(
            state.crash_stacktrace, state.crash_type, state.crash_address)
        crashes[state.crash_state] = CorpusCrash(
            state.crash_state, state.crash_type, state.crash_address,
            state.crash_stacktrace, unit_path, security_flag)

    logs.log('Found %d bad units, %d unique crashes.' % (num_bad_units,
                                                         len(crashes)))
Exemplo n.º 3
0
def handle_update(testcase, revision, stacktrace, error):
    """Handle update."""
    logs.log('Got external update for testcase.',
             testcase_id=testcase.key.id())
    if error:
        _mark_errored(testcase, revision, error)
        return

    last_tested_revision = (testcase.get_metadata('last_tested_revision')
                            or testcase.crash_revision)

    if revision < last_tested_revision:
        logs.log_warn(f'Revision {revision} less than previously tested '
                      f'revision {last_tested_revision}.')
        return

    fuzz_target = testcase.get_fuzz_target()
    if fuzz_target:
        fuzz_target_name = fuzz_target.binary
    else:
        fuzz_target_name = None

    # Record use of fuzz target to avoid garbage collection (since fuzz_task does
    # not run).
    data_handler.record_fuzz_target(fuzz_target.engine, fuzz_target.binary,
                                    testcase.job_type)

    state = stack_analyzer.get_crash_data(stacktrace,
                                          fuzz_target=fuzz_target_name,
                                          symbolize_flag=False,
                                          already_symbolized=True,
                                          detect_ooms_and_hangs=True)
    crash_comparer = CrashComparer(state.crash_state, testcase.crash_state)
    if not crash_comparer.is_similar():
        logs.log(f'State no longer similar ('
                 f'testcase_id={testcase.key.id()}, '
                 f'old_state={testcase.crash_state}, '
                 f'new_state={state.crash_state})')
        _mark_as_fixed(testcase, revision)
        return

    is_security = crash_analyzer.is_security_issue(state.crash_stacktrace,
                                                   state.crash_type,
                                                   state.crash_address)
    if is_security != testcase.security_flag:
        logs.log(f'Security flag for {testcase.key.id()} no longer matches.')
        _mark_as_fixed(testcase, revision)
        return

    logs.log(f'{testcase.key.id()} still crashes.')
    testcase.last_tested_crash_stacktrace = stacktrace
    data_handler.update_progression_completion_metadata(testcase,
                                                        revision,
                                                        is_crash=True)
Exemplo n.º 4
0
    def post(self):
        """Handle a post request."""
        if not auth.get_current_user():
            raise helpers.AccessDeniedException()

        project = request.get('project')
        fuzz_target = request.get('fuzz_target')
        stacktrace = request.get('stacktrace')

        state = stack_analyzer.get_crash_data(stacktrace,
                                              symbolize_flag=False,
                                              fuzz_target=fuzz_target,
                                              already_symbolized=True,
                                              detect_ooms_and_hangs=True)
        security_flag = crash_analyzer.is_security_issue(
            state.crash_stacktrace, state.crash_type, state.crash_address)

        result = {
            'state': state.crash_state,
            'type': state.crash_type,
            'security': security_flag,
        }

        duplicate_testcase = data_handler.find_testcase(
            project, state.crash_type, state.crash_state, security_flag)
        if duplicate_testcase:
            result['result'] = 'duplicate'
            result['duplicate_id'] = duplicate_testcase.key.id()

            bug_id = (duplicate_testcase.bug_information
                      or duplicate_testcase.group_bug_information)
            if bug_id:
                result['bug_id'] = str(bug_id)
        else:
            result['result'] = 'new'

        return self.render_json(result)
Exemplo n.º 5
0
def execute_task(testcase_id, job_type):
  """Run analyze task."""
  # Reset redzones.
  environment.reset_current_memory_tool_options(redzone_size=128)

  # Unset window location size and position properties so as to use default.
  environment.set_value('WINDOW_ARG', '')

  # Locate the testcase associated with the id.
  testcase = data_handler.get_testcase_by_id(testcase_id)
  if not testcase:
    return

  data_handler.update_testcase_comment(testcase, data_types.TaskState.STARTED)

  metadata = data_types.TestcaseUploadMetadata.query(
      data_types.TestcaseUploadMetadata.testcase_id == int(testcase_id)).get()
  if not metadata:
    logs.log_error(
        'Testcase %s has no associated upload metadata.' % testcase_id)
    testcase.key.delete()
    return

  is_lsan_enabled = environment.get_value('LSAN')
  if is_lsan_enabled:
    # Creates empty local blacklist so all leaks will be visible to uploader.
    leak_blacklist.create_empty_local_blacklist()

  # Store the bot name and timestamp in upload metadata.
  bot_name = environment.get_value('BOT_NAME')
  metadata.bot_name = bot_name
  metadata.timestamp = datetime.datetime.utcnow()
  metadata.put()

  # Adjust the test timeout, if user has provided one.
  if metadata.timeout:
    environment.set_value('TEST_TIMEOUT', metadata.timeout)

  # Adjust the number of retries, if user has provided one.
  if metadata.retries is not None:
    environment.set_value('CRASH_RETRIES', metadata.retries)

  # Setup testcase and get absolute testcase path.
  file_list, _, testcase_file_path = setup.setup_testcase(testcase)
  if not file_list:
    return

  # Set up a custom or regular build based on revision.
  build_manager.setup_build(testcase.crash_revision)

  # Check if we have an application path. If not, our build failed
  # to setup correctly.
  app_path = environment.get_value('APP_PATH')
  if not app_path:
    data_handler.update_testcase_comment(testcase, data_types.TaskState.ERROR,
                                         'Build setup failed')

    if data_handler.is_first_retry_for_task(testcase):
      build_fail_wait = environment.get_value('FAIL_WAIT')
      tasks.add_task(
          'analyze', testcase_id, job_type, wait_time=build_fail_wait)
    else:
      close_invalid_testcase_and_update_status(testcase, metadata,
                                               'Build setup failed')
    return

  # Update initial testcase information.
  testcase.absolute_path = testcase_file_path
  testcase.job_type = job_type
  testcase.binary_flag = utils.is_binary_file(testcase_file_path)
  testcase.queue = tasks.default_queue()
  testcase.crash_state = ''

  # Set initial testcase metadata fields (e.g. build url, etc).
  data_handler.set_initial_testcase_metadata(testcase)

  # Update minimized arguments and use ones provided during user upload.
  if not testcase.minimized_arguments:
    minimized_arguments = environment.get_value('APP_ARGS') or ''
    additional_command_line_flags = testcase.get_metadata(
        'uploaded_additional_args')
    if additional_command_line_flags:
      minimized_arguments += ' %s' % additional_command_line_flags
    environment.set_value('APP_ARGS', minimized_arguments)
    testcase.minimized_arguments = minimized_arguments

  # Update other fields not set at upload time.
  testcase.crash_revision = environment.get_value('APP_REVISION')
  data_handler.set_initial_testcase_metadata(testcase)
  testcase.put()

  # Initialize some variables.
  gestures = testcase.gestures
  http_flag = testcase.http_flag
  test_timeout = environment.get_value('TEST_TIMEOUT')

  # Get the crash output.
  result = testcase_manager.test_for_crash_with_retries(
      testcase,
      testcase_file_path,
      test_timeout,
      http_flag=http_flag,
      compare_crash=False)

  # If we don't get a crash, try enabling http to see if we can get a crash.
  # Skip engine fuzzer jobs (e.g. libFuzzer, AFL) for which http testcase paths
  # are not applicable.
  if (not result.is_crash() and not http_flag and
      not environment.is_engine_fuzzer_job()):
    result_with_http = testcase_manager.test_for_crash_with_retries(
        testcase,
        testcase_file_path,
        test_timeout,
        http_flag=True,
        compare_crash=False)
    if result_with_http.is_crash():
      logs.log('Testcase needs http flag for crash.')
      http_flag = True
      result = result_with_http

  # Refresh our object.
  testcase = data_handler.get_testcase_by_id(testcase_id)
  if not testcase:
    return

  # Set application command line with the correct http flag.
  application_command_line = (
      testcase_manager.get_command_line_for_application(
          testcase_file_path, needs_http=http_flag))

  # Get the crash data.
  crashed = result.is_crash()
  crash_time = result.get_crash_time()
  state = result.get_symbolized_data()
  unsymbolized_crash_stacktrace = result.get_stacktrace(symbolized=False)

  # Get crash info object with minidump info. Also, re-generate unsymbolized
  # stacktrace if needed.
  crash_info, _ = (
      crash_uploader.get_crash_info_and_stacktrace(
          application_command_line, state.crash_stacktrace, gestures))
  if crash_info:
    testcase.minidump_keys = crash_info.store_minidump()

  if not crashed:
    # Could not reproduce the crash.
    log_message = (
        'Testcase didn\'t crash in %d seconds (with retries)' % test_timeout)
    data_handler.update_testcase_comment(
        testcase, data_types.TaskState.FINISHED, log_message)

    # For an unreproducible testcase, retry once on another bot to confirm
    # our results and in case this bot is in a bad state which we didn't catch
    # through our usual means.
    if data_handler.is_first_retry_for_task(testcase):
      testcase.status = 'Unreproducible, retrying'
      testcase.put()

      tasks.add_task('analyze', testcase_id, job_type)
      return

    # In the general case, we will not attempt to symbolize if we do not detect
    # a crash. For user uploads, we should symbolize anyway to provide more
    # information about what might be happening.
    crash_stacktrace_output = utils.get_crash_stacktrace_output(
        application_command_line, state.crash_stacktrace,
        unsymbolized_crash_stacktrace)
    testcase.crash_stacktrace = data_handler.filter_stacktrace(
        crash_stacktrace_output)
    close_invalid_testcase_and_update_status(testcase, metadata,
                                             'Unreproducible')

    # A non-reproducing testcase might still impact production branches.
    # Add the impact task to get that information.
    task_creation.create_impact_task_if_needed(testcase)
    return

  # Update http flag and re-run testcase to store dependencies (for bundled
  # archives only).
  testcase.http_flag = http_flag
  if not store_testcase_dependencies_from_bundled_testcase_archive(
      metadata, testcase, testcase_file_path):
    return

  # Update testcase crash parameters.
  testcase.crash_type = state.crash_type
  testcase.crash_address = state.crash_address
  testcase.crash_state = state.crash_state

  # Try to guess if the bug is security or not.
  security_flag = crash_analyzer.is_security_issue(
      state.crash_stacktrace, state.crash_type, state.crash_address)
  testcase.security_flag = security_flag

  # If it is, guess the severity.
  if security_flag:
    testcase.security_severity = severity_analyzer.get_security_severity(
        state.crash_type, state.crash_stacktrace, job_type, bool(gestures))

  log_message = ('Testcase crashed in %d seconds (r%d)' %
                 (crash_time, testcase.crash_revision))
  data_handler.update_testcase_comment(testcase, data_types.TaskState.FINISHED,
                                       log_message)

  # See if we have to ignore this crash.
  if crash_analyzer.ignore_stacktrace(state.crash_stacktrace):
    close_invalid_testcase_and_update_status(testcase, metadata, 'Irrelavant')
    return

  # Test for reproducibility.
  one_time_crasher_flag = not testcase_manager.test_for_reproducibility(
      testcase_file_path, state.crash_state, security_flag, test_timeout,
      http_flag, gestures)
  testcase.one_time_crasher_flag = one_time_crasher_flag

  # Check to see if this is a duplicate.
  project_name = data_handler.get_project_name(job_type)
  existing_testcase = data_handler.find_testcase(
      project_name, state.crash_type, state.crash_state, security_flag)
  if existing_testcase:
    # If the existing test case is unreproducible and we are, replace the
    # existing test case with this one.
    if existing_testcase.one_time_crasher_flag and not one_time_crasher_flag:
      duplicate_testcase = existing_testcase
      original_testcase = testcase
    else:
      duplicate_testcase = testcase
      original_testcase = existing_testcase
      metadata.status = 'Duplicate'
      metadata.duplicate_of = existing_testcase.key.id()

    duplicate_testcase.status = 'Duplicate'
    duplicate_testcase.duplicate_of = original_testcase.key.id()
    duplicate_testcase.put()

  # Set testcase and metadata status if not set already.
  if testcase.status != 'Duplicate':
    testcase.status = 'Processed'
    metadata.status = 'Confirmed'

    # Add new leaks to global blacklist to avoid detecting duplicates.
    # Only add if testcase has a direct leak crash and if it's reproducible.
    if is_lsan_enabled:
      leak_blacklist.add_crash_to_global_blacklist_if_needed(testcase)

  # Add application specific information in the trace.
  crash_stacktrace_output = utils.get_crash_stacktrace_output(
      application_command_line, state.crash_stacktrace,
      unsymbolized_crash_stacktrace)
  testcase.crash_stacktrace = data_handler.filter_stacktrace(
      crash_stacktrace_output)

  # Update the testcase values.
  testcase.put()

  # Update the upload metadata.
  metadata.security_flag = security_flag
  metadata.put()

  # Create tasks to
  # 1. Minimize testcase (minimize).
  # 2. Find regression range (regression).
  # 3. Find testcase impact on production branches (impact).
  # 4. Check whether testcase is fixed (progression).
  # 5. Get second stacktrace from another job in case of
  #    one-time crashers (stack).
  task_creation.create_tasks(testcase)
Exemplo n.º 6
0
 def is_security_issue(self):
     """Return True if this crash is a security issue."""
     state = self.get_unsymbolized_data()
     return crash_analyzer.is_security_issue(state.crash_stacktrace,
                                             state.crash_type,
                                             state.crash_address)
Exemplo n.º 7
0
        at com.example.foo.CrashyClass.crash(CrashyClass.java:6)
        at com.bugsnag.android.example.ExampleActivity.crashUnhandled(ExampleActivity.kt:55)
        at com.bugsnag.android.example.ExampleActivity$onCreate$1.invoke(ExampleActivity.kt:33)
        at com.bugsnag.android.example.ExampleActivity$onCreate$1.invoke(ExampleActivity.kt:14)
        at com.bugsnag.android.example.ExampleActivity$sam$android_view_View_OnClickListener$0.onClick(ExampleActivity.kt)
        at android.view.View.performClick(View.java:5637)
        at android.view.View$PerformClick.run(View.java:22429)
        at android.os.Handler.handleCallback(Handler.java:751)
        at android.os.Handler.dispatchMessage(Handler.java:95)
        at android.os.Looper.loop(Looper.java:154)
        at android.app.ActivityThread.main(ActivityThread.java:6119)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)"""
application_command_line = ""
state = stack_analyzer.get_crash_data(unsymbolized_crash_stacktrace,
                                      symbolize_flag=False,
                                      already_symbolized=True)
security_flag = crash_analyzer.is_security_issue(unsymbolized_crash_stacktrace,
                                                 state.crash_type,
                                                 state.crash_address)
key = '%s,%s,%s' % (state.crash_type, state.crash_state, security_flag)
should_be_ignored = crash_analyzer.ignore_stacktrace(state.crash_stacktrace)
print("key:", key)

# sample output:
#   key: Fatal Exception,com.example.foo.CrashyClass.sendMessage
#   com.example.foo.CrashyClass.crash
#   com.bugsnag.android.example.ExampleActivity.crashUnhandled
#   ,False