<commit_before>// Copyright 2022 The Centipede Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Fuzz target runner (engine) for Centipede.
// Reads the input files and feeds their contents to
// the fuzz target (LLVMFuzzerTestOneInput), then dumps the coverage data.
// If the input path is "/path/to/foo",
// the coverage features are dumped to "/path/to/foo-features"
//
// WARNING: please avoid any C++ libraries here, such as Absl and (most of) STL,
// in order to avoid creating new coverage edges in the binary.
#include "./runner.h"

#include <elf.h>
#include <limits.h>
#include <link.h>     // dl_iterate_phdr
#include <pthread.h>  // NOLINT: use pthread to avoid extra dependencies.
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

#include <vector>

#include "./byte_array_mutator.h"
#include "./defs.h"
#include "./execution_request.h"
#include "./execution_result.h"
#include "./feature.h"
#include "./runner_interface.h"
#include "./shared_memory_blob_sequence.h"

namespace centipede {

GlobalRunnerState state;
thread_local ThreadLocalRunnerState tls;

// Tries to write `description` to `state.failure_description_path`.
static void WriteFailureDescription(const char *description) {
  if (!state.failure_description_path) return;
  FILE *f = fopen(state.failure_description_path, "w");
  if (!f) return;
  fwrite(description, 1, strlen(description), f);
  fclose(f);
}

void ThreadLocalRunnerState::OnThreadStart() {
  LockGuard lock(state.tls_list_mu);
  // Add myself to state.tls_list.
  auto *old_list = state.tls_list;
  tls.next = old_list;
  state.tls_list = &tls;
  if (old_list) old_list->prev = &tls;
}

void ThreadLocalRunnerState::OnThreadStop() {
  LockGuard lock(state.tls_list_mu);
  // Remove myself from state.tls_list. The list never
  // becomes empty because the main thread does not call OnThreadStop().
  if (&tls == state.tls_list) {
    state.tls_list = tls.next;
    tls.prev = nullptr;
  } else {
    auto *prev_tls = tls.prev;
    auto *next_tls = tls.next;
    prev_tls->next = next_tls;
    if (next_tls) next_tls->prev = prev_tls;
  }
}

static size_t GetPeakRSSMb() {
  struct rusage usage = {};
  if (getrusage(RUSAGE_SELF, &usage)) return 0;
  // On Linux, ru_maxrss is in KiB
  return usage.ru_maxrss >> 10;
}

// Returns the current time in microseconds.
static uint64_t TimeInUsec() {
  struct timeval tv = {};
  constexpr size_t kUsecInSec = 1000000;
  gettimeofday(&tv, nullptr);
  return tv.tv_sec * kUsecInSec + tv.tv_usec;
}

static void CheckOOM() {
  if (state.run_time_flags.rss_limit_mb > 0) {
    size_t current_rss_mb = GetPeakRSSMb();
    if (current_rss_mb > state.run_time_flags.rss_limit_mb) {
      fprintf(stderr,
              "========= OOM, RSS limit of %zdMb exceeded (%zdMb); exiting\n",
              state.run_time_flags.rss_limit_mb, current_rss_mb);
      WriteFailureDescription("out-of-memory");
      _exit(EXIT_FAILURE);
    }
  }
}

static void CheckTimeout() {
  time_t start_time = state.timer;
  time_t curr_time = time(nullptr);
  if (state.run_time_flags.timeout_in_seconds != 0) {
    if (curr_time - start_time >
        static_cast<time_t>(state.run_time_flags.timeout_in_seconds)) {
      fprintf(stderr, "========= Timeout of %zd seconds exceeded; exiting\n",
              state.run_time_flags.timeout_in_seconds);
      WriteFailureDescription("timeout-exceeded");
      _exit(EXIT_FAILURE);
    }
  }
}

// Timer thread. Periodically checks if it's time to abort due to a timeout/OOM.
[[noreturn]] static void *TimerThread(void *unused) {
  while (true) {
    sleep(1);
    if (state.timer == 0) continue;  // No calls to ResetTimer() yet.

    CheckTimeout();
    CheckOOM();
  }
}

void GlobalRunnerState::StartTimerThread() {
  if (state.run_time_flags.timeout_in_seconds == 0 &&
      state.run_time_flags.rss_limit_mb == 0) {
    return;
  }
  fprintf(stderr, "timeout_in_seconds: %zd rss_limit_mb: %zd\n",
          state.run_time_flags.timeout_in_seconds,
          state.run_time_flags.rss_limit_mb);
  pthread_t timer_thread;
  pthread_create(&timer_thread, nullptr, TimerThread, nullptr);
  pthread_detach(timer_thread);
}

void GlobalRunnerState::ResetTimer() { timer = time(nullptr); }

// Byte array mutation fallback for a custom mutator, as defined here:
// https://github.com/google/fuzzing/blob/master/docs/structure-aware-fuzzing.md
extern "C" size_t LLVMFuzzerMutate(uint8_t *data, size_t size,
                                   size_t max_size) {
  // TODO(kcc): [as-needed] fix the interface mismatch.
  // LLVMFuzzerMutate is an array-based interface (for compatibility reasons)
  // while centipede::ByteArray has a vector-based interface.
  // This incompatibility causes us to do extra allocate/copy per mutation.
  // It may not cause big problems in practice though.
  centipede::ByteArray array(data, data + size);
  state.byte_array_mutator->Mutate(array);
  if (array.size() > max_size) {
    array.resize(max_size);
  }
  memcpy(data, array.data(), array.size());
  return array.size();
}

// TODO(ussuri): Move all these globals into GlobalRunnerState.

// An arbitrary large size for input data.
static const size_t kMaxDataSize = 1 << 20;
// Input data.
// Every input is stored in `g_input_data` and then passed to the target.
// We allocate `g_input_data` from heap at startup to avoid malloc in a loop.
// We don't make it a global because it will make data flow instrumentation
// treat every input byte touched as a separate feature, which will cause
// arbitrary growth of input size.
static uint8_t *g_input_data = (uint8_t *)malloc(kMaxDataSize);

// An arbitrary large size.
static const size_t kMaxFeatures = 1 << 20;
// FeatureArray used to accumulate features from all sources.
static centipede::FeatureArray<kMaxFeatures> g_features;

static void PrintErrorAndExitIf(bool condition, const char *error) {
  if (!condition) return;
  fprintf(stderr, "error: %s\n", error);
  exit(1);<commit_msg>#Centipede: make sure LLVMFuzzerMutate does not pass empty inputs to ByteArrayMutator.<commit_after>// Copyright 2022 The Centipede Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Fuzz target runner (engine) for Centipede.
// Reads the input files and feeds their contents to
// the fuzz target (LLVMFuzzerTestOneInput), then dumps the coverage data.
// If the input path is "/path/to/foo",
// the coverage features are dumped to "/path/to/foo-features"
//
// WARNING: please avoid any C++ libraries here, such as Absl and (most of) STL,
// in order to avoid creating new coverage edges in the binary.
#include "./runner.h"

#include <elf.h>
#include <limits.h>
#include <link.h>     // dl_iterate_phdr
#include <pthread.h>  // NOLINT: use pthread to avoid extra dependencies.
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/auxv.h>
#include <sys/resource.h>
#include <sys/time.h>
#include <time.h>
#include <unistd.h>

#include <vector>

#include "./byte_array_mutator.h"
#include "./defs.h"
#include "./execution_request.h"
#include "./execution_result.h"
#include "./feature.h"
#include "./runner_interface.h"
#include "./shared_memory_blob_sequence.h"

namespace centipede {

GlobalRunnerState state;
thread_local ThreadLocalRunnerState tls;

// Tries to write `description` to `state.failure_description_path`.
static void WriteFailureDescription(const char *description) {
  if (!state.failure_description_path) return;
  FILE *f = fopen(state.failure_description_path, "w");
  if (!f) return;
  fwrite(description, 1, strlen(description), f);
  fclose(f);
}

void ThreadLocalRunnerState::OnThreadStart() {
  LockGuard lock(state.tls_list_mu);
  // Add myself to state.tls_list.
  auto *old_list = state.tls_list;
  tls.next = old_list;
  state.tls_list = &tls;
  if (old_list) old_list->prev = &tls;
}

void ThreadLocalRunnerState::OnThreadStop() {
  LockGuard lock(state.tls_list_mu);
  // Remove myself from state.tls_list. The list never
  // becomes empty because the main thread does not call OnThreadStop().
  if (&tls == state.tls_list) {
    state.tls_list = tls.next;
    tls.prev = nullptr;
  } else {
    auto *prev_tls = tls.prev;
    auto *next_tls = tls.next;
    prev_tls->next = next_tls;
    if (next_tls) next_tls->prev = prev_tls;
  }
}

static size_t GetPeakRSSMb() {
  struct rusage usage = {};
  if (getrusage(RUSAGE_SELF, &usage)) return 0;
  // On Linux, ru_maxrss is in KiB
  return usage.ru_maxrss >> 10;
}

// Returns the current time in microseconds.
static uint64_t TimeInUsec() {
  struct timeval tv = {};
  constexpr size_t kUsecInSec = 1000000;
  gettimeofday(&tv, nullptr);
  return tv.tv_sec * kUsecInSec + tv.tv_usec;
}

static void CheckOOM() {
  if (state.run_time_flags.rss_limit_mb > 0) {
    size_t current_rss_mb = GetPeakRSSMb();
    if (current_rss_mb > state.run_time_flags.rss_limit_mb) {
      fprintf(stderr,
              "========= OOM, RSS limit of %zdMb exceeded (%zdMb); exiting\n",
              state.run_time_flags.rss_limit_mb, current_rss_mb);
      WriteFailureDescription("out-of-memory");
      _exit(EXIT_FAILURE);
    }
  }
}

static void CheckTimeout() {
  time_t start_time = state.timer;
  time_t curr_time = time(nullptr);
  if (state.run_time_flags.timeout_in_seconds != 0) {
    if (curr_time - start_time >
        static_cast<time_t>(state.run_time_flags.timeout_in_seconds)) {
      fprintf(stderr, "========= Timeout of %zd seconds exceeded; exiting\n",
              state.run_time_flags.timeout_in_seconds);
      WriteFailureDescription("timeout-exceeded");
      _exit(EXIT_FAILURE);
    }
  }
}

// Timer thread. Periodically checks if it's time to abort due to a timeout/OOM.
[[noreturn]] static void *TimerThread(void *unused) {
  while (true) {
    sleep(1);
    if (state.timer == 0) continue;  // No calls to ResetTimer() yet.

    CheckTimeout();
    CheckOOM();
  }
}

void GlobalRunnerState::StartTimerThread() {
  if (state.run_time_flags.timeout_in_seconds == 0 &&
      state.run_time_flags.rss_limit_mb == 0) {
    return;
  }
  fprintf(stderr, "timeout_in_seconds: %zd rss_limit_mb: %zd\n",
          state.run_time_flags.timeout_in_seconds,
          state.run_time_flags.rss_limit_mb);
  pthread_t timer_thread;
  pthread_create(&timer_thread, nullptr, TimerThread, nullptr);
  pthread_detach(timer_thread);
}

void GlobalRunnerState::ResetTimer() { timer = time(nullptr); }

// Byte array mutation fallback for a custom mutator, as defined here:
// https://github.com/google/fuzzing/blob/master/docs/structure-aware-fuzzing.md
extern "C" size_t LLVMFuzzerMutate(uint8_t *data, size_t size,
                                   size_t max_size) {
  // TODO(kcc): [as-needed] fix the interface mismatch.
  // LLVMFuzzerMutate is an array-based interface (for compatibility reasons)
  // while centipede::ByteArray has a vector-based interface.
  // This incompatibility causes us to do extra allocate/copy per mutation.
  // It may not cause big problems in practice though.
  if (max_size == 0) return 0;  // just in case, not expected to happen.
  if (size == 0) {
    // Don't mutate empty data, just return a 1-byte result.
    data[0] = 0;
    return 1;
  }

  centipede::ByteArray array(data, data + size);
  state.byte_array_mutator->Mutate(array);
  if (array.size() > max_size) {
    array.resize(max_size);
  }
  memcpy(data, array.data(), array.size());
  return array.size();
}

// TODO(ussuri): Move all these globals into GlobalRunnerState.

// An arbitrary large size for input data.
static const size_t kMaxDataSize = 1 << 20;
// Input data.
// Every input is stored in `g_input_data` and then passed to the target.
// We allocate `g_input_data` from heap at startup to avoid malloc in a loop.
// We don't make it a global because it will make data flow instrumentation
// treat every input byte touched as a separate feature, which will cause
// arbitrary growth of input size.
static uint8_t *g_input_data = (uint8_t *)malloc(kMaxDataSize);

// An arbitrary large size.
static const size_t kMaxFeatures = 1 << 20;
// FeatureArray used to accumulate features from all sources.
static centipede::FeatureArray<kMaxFeatures> g_features;

static void PrintErrorAndExitIf(bool condition, const char *error) {
  if (!condition) return;
  fprintf(stderr, "error: %s\n", error);
  exit(1);
{'fraction_non_alphanumeric': 0.1177038467250854, 'fraction_numerical': 0.00601514926481509, 'mean_word_length': 3.7721474131821404, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 55, 'https://': 4, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6999825', 'n_tokens_mistral': 4452, 'n_tokens_neox': 4343, 'n_words': 1305}