Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7fe06a89 authored by Zach Johnson's avatar Zach Johnson
Browse files

Add fake_timerfd so time is deterministic in fuzz tests

Has same interface as timerfd with 2 additional functions:

fake_timerfd_reset(), to reset everything after a test.
fake_timerfd_advance(ms), to move time forward a number of ms.

Test: fuzz/run --host bluetooth_gd_hci_fuzz_test
Change-Id: Ifedada770797b014d08e439cb1915f4e733697e6
parent 7e22ea2d
Loading
Loading
Loading
Loading
+3 −0
Original line number Diff line number Diff line
@@ -166,6 +166,9 @@ cc_library {
    defaults: [
        "libbluetooth_gd_defaults",
    ],
    srcs: [
        ":BluetoothOsSources_fuzz",
    ],
    cflags: [
        "-DFUZZ_TARGET",
    ],
+8 −1
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include "hci/fuzz/dev_null_hci.h"
#include "hci/hci_layer.h"
#include "module.h"
#include "os/fuzz/fake_timerfd.h"
#include "os/log.h"

#include <fuzzer/FuzzedDataProvider.h>
@@ -28,6 +29,8 @@ using bluetooth::TestModuleRegistry;
using bluetooth::hal::HciHal;
using bluetooth::hal::fuzz::FuzzHciHal;
using bluetooth::hci::fuzz::DevNullHci;
using bluetooth::os::fuzz::fake_timerfd_advance;
using bluetooth::os::fuzz::fake_timerfd_reset;

extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
  FuzzedDataProvider dataProvider(data, size);
@@ -42,9 +45,12 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
    const uint8_t action = dataProvider.ConsumeIntegralInRange(0, 2);
    switch (action) {
      case 1:
        fuzzHal->injectAcl(dataProvider.ConsumeBytes<uint8_t>(dataProvider.ConsumeIntegral<size_t>()));
        fake_timerfd_advance(dataProvider.ConsumeIntegral<uint64_t>());
        break;
      case 2:
        fuzzHal->injectAcl(dataProvider.ConsumeBytes<uint8_t>(dataProvider.ConsumeIntegral<size_t>()));
        break;
      case 3:
        fuzzHal->injectHciEvent(dataProvider.ConsumeBytes<uint8_t>(dataProvider.ConsumeIntegral<size_t>()));
        break;
    }
@@ -54,5 +60,6 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
    LOG_ERROR("idle timed out");
  }
  moduleRegistry.StopAll();
  fake_timerfd_reset();
  return 0;
}
+11 −4
Original line number Diff line number Diff line
@@ -30,3 +30,10 @@ filegroup {
         "queue_benchmark.cc",
    ],
}

filegroup {
    name: "BluetoothOsSources_fuzz",
    srcs: [
        "fuzz/fake_timerfd.cc",
    ],
}
+128 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 The Android Open Source Project
 *
 * 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
 *
 *      http://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.
 */

#include "os/fuzz/fake_timerfd.h"
#include <sys/eventfd.h>
#include <unistd.h>
#include <map>

namespace bluetooth {
namespace os {
namespace fuzz {

class FakeTimerFd {
 public:
  int fd;
  bool active;
  uint64_t trigger_ms;
  uint64_t period_ms;
};

static std::map<int, FakeTimerFd*> fake_timers;
static uint64_t clock = 0;

static uint64_t timespec_to_ms(const timespec* t) {
  return t->tv_sec * 1000 + t->tv_nsec / 1000000;
}

int fake_timerfd_create(int clockid, int flags) {
  int fd = eventfd(0, 0);
  if (fd == -1) {
    return fd;
  }

  FakeTimerFd* entry = new FakeTimerFd();
  fake_timers[fd] = entry;
  entry->fd = fd;
  return fd;
}

int fake_timerfd_settime(int fd, int flags, const struct itimerspec* new_value, struct itimerspec* old_value) {
  if (fake_timers.find(fd) == fake_timers.end()) {
    return -1;
  }

  FakeTimerFd* entry = fake_timers[fd];

  uint64_t trigger_delta_ms = timespec_to_ms(&new_value->it_value);
  entry->active = trigger_delta_ms != 0;
  if (!entry->active) {
    return 0;
  }

  uint64_t period_ms = timespec_to_ms(&new_value->it_value);
  entry->trigger_ms = clock + trigger_delta_ms;
  entry->period_ms = period_ms;
  return 0;
}

int fake_timerfd_close(int fd) {
  auto timer_iterator = fake_timers.find(fd);
  if (timer_iterator != fake_timers.end()) {
    delete timer_iterator->second;
    fake_timers.erase(timer_iterator);
  }
  return close(fd);
}

void fake_timerfd_reset() {
  clock = 0;
  // if there are entries still here, it is a failure of our users to clean up
  // so let them leak and trigger errors
  fake_timers.clear();
}

static bool fire_next_event(uint64_t new_clock) {
  uint64_t earliest_time = new_clock;
  FakeTimerFd* to_fire = nullptr;
  for (auto it = fake_timers.begin(); it != fake_timers.end(); it++) {
    FakeTimerFd* entry = it->second;
    if (!entry->active) {
      continue;
    }

    if (entry->trigger_ms > clock && entry->trigger_ms <= new_clock) {
      if (to_fire == nullptr || entry->trigger_ms < earliest_time) {
        to_fire = entry;
        earliest_time = entry->trigger_ms;
      }
    }
  }

  if (to_fire == nullptr) {
    return false;
  }

  bool is_periodic = to_fire->period_ms != 0;
  if (is_periodic) {
    to_fire->trigger_ms += to_fire->period_ms;
  }
  to_fire->active = is_periodic;
  uint64_t value = 1;
  write(to_fire->fd, &value, sizeof(uint64_t));
  return true;
}

void fake_timerfd_advance(uint64_t ms) {
  uint64_t new_clock = clock + ms;
  while (fire_next_event(new_clock)) {
  }
  clock = new_clock;
}

}  // namespace fuzz
}  // namespace os
}  // namespace bluetooth
+38 −0
Original line number Diff line number Diff line
/*
 * Copyright 2020 The Android Open Source Project
 *
 * 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
 *
 *      http://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.
 */

#pragma once

#include <sys/timerfd.h>
#include <cstdint>

namespace bluetooth {
namespace os {
namespace fuzz {

int fake_timerfd_create(int clockid, int flags);

int fake_timerfd_settime(int fd, int flags, const struct itimerspec* new_value, struct itimerspec* old_value);

int fake_timerfd_close(int fd);

void fake_timerfd_reset();

void fake_timerfd_advance(uint64_t ms);

}  // namespace fuzz
}  // namespace os
}  // namespace bluetooth
Loading