/* * 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 #include #include namespace bluetooth { namespace os { namespace fuzz { class FakeTimerFd { public: int fd; bool active; uint64_t trigger_ms; uint64_t period_ms; }; static std::map fake_timers; static uint64_t clock = 0; static uint64_t max_clock = UINT64_MAX; 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; max_clock = UINT64_MAX; // 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; if (new_clock > max_clock) { new_clock = max_clock; } while (fire_next_event(new_clock)) { } clock = new_clock; } void fake_timerfd_cap_at(uint64_t ms) { max_clock = ms; } } // namespace fuzz } // namespace os } // namespace bluetooth