blob: d0ec4c5e5481859e84195c1bb0eeeeffbecbe1ad [file] [log] [blame]
// Copyright 2024 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/sched/run-queue.h>
#include <lib/sched/thread-base.h>
#include <zircon/time.h>
#include <memory>
#include <vector>
#include <fuzzer/FuzzedDataProvider.h>
#include "test-thread.h"
//
// This file defines a simple fuzzer of sched::RunQueue, simulating the ongoing
// scheduling of threads.
//
namespace {
using Duration = sched::Duration;
using FlexibleWeight = sched::FlexibleWeight;
using Time = sched::Time;
Time ConsumeTime(FuzzedDataProvider& provider) {
return Time{provider.ConsumeIntegral<zx_time_t>()};
}
FlexibleWeight ConsumeFlexibleWeight(FuzzedDataProvider& provider, FlexibleWeight min) {
return FlexibleWeight{
provider.ConsumeIntegralInRange<int64_t>(min.raw_value(), FlexibleWeight::Max().raw_value())};
}
Duration ConsumeDuration(FuzzedDataProvider& provider, Duration min, Duration max) {
ZX_ASSERT(min >= 0);
ZX_ASSERT(max >= min);
return Duration{provider.ConsumeIntegralInRange<zx_duration_t>(min.raw_value(), max.raw_value())};
}
TestThread* AllocateNewThread(FuzzedDataProvider& provider,
std::vector<std::unique_ptr<TestThread>>& threads) {
Time start = ConsumeTime(provider);
Duration period = ConsumeDuration(provider, Duration{1}, (Time::Max() - start) + Time{1});
Duration firm_capacity = ConsumeDuration(provider, Duration{0}, period);
FlexibleWeight flexible_weight =
ConsumeFlexibleWeight(provider, FlexibleWeight{firm_capacity == 0 ? 1 : 0});
threads.emplace_back(std::make_unique<TestThread>(
sched::BandwidthParameters{
.period = period,
.firm_capacity = firm_capacity,
.flexible_weight = flexible_weight,
},
start));
return threads.back().get();
}
} // namespace
extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
FuzzedDataProvider provider(data, size);
std::vector<std::unique_ptr<TestThread>> threads;
Time now = ConsumeTime(provider);
TestThread* current = nullptr;
sched::RunQueue<TestThread> queue;
while (now < Time::Max() && provider.remaining_bytes() > 0) {
if (provider.ConsumeBool()) {
queue.Queue(*AllocateNewThread(provider, threads), now);
}
ZX_ASSERT(current == queue.current_thread());
if (current) {
ZX_ASSERT(!current->IsQueued());
ZX_ASSERT(current->start() <= now);
}
auto [next, preemption] = queue.SelectNextThread(now);
ZX_ASSERT(current == next || !current || current->IsQueued());
if (next) {
ZX_ASSERT(!next->IsQueued());
ZX_ASSERT(next->start() <= now);
ZX_ASSERT(now < preemption);
ZX_ASSERT(preemption <= next->finish());
next->Tick(preemption - now);
}
now = preemption;
current = next;
}
return 0;
}