/* * Copyright 2021 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 #include #include #include "Scheduler/DispSyncSource.h" #include "Scheduler/OneShotTimer.h" #include "Scheduler/VSyncDispatchTimerQueue.h" #include "Scheduler/VSyncPredictor.h" #include "Scheduler/VSyncReactor.h" #include "surfaceflinger_fuzzers_utils.h" #include "surfaceflinger_scheduler_fuzzer.h" namespace android::fuzz { using hardware::graphics::composer::hal::PowerMode; constexpr nsecs_t kVsyncPeriods[] = {(30_Hz).getPeriodNsecs(), (60_Hz).getPeriodNsecs(), (72_Hz).getPeriodNsecs(), (90_Hz).getPeriodNsecs(), (120_Hz).getPeriodNsecs()}; constexpr auto kLayerVoteTypes = ftl::enum_range(); constexpr PowerMode kPowerModes[] = {PowerMode::ON, PowerMode::DOZE, PowerMode::OFF, PowerMode::DOZE_SUSPEND, PowerMode::ON_SUSPEND}; constexpr uint16_t kRandomStringLength = 256; constexpr std::chrono::duration kSyncPeriod(16ms); template void dump(T* component, FuzzedDataProvider* fdp) { std::string res = fdp->ConsumeRandomLengthString(kRandomStringLength); component->dump(res); } class SchedulerFuzzer : private VSyncSource::Callback { public: SchedulerFuzzer(const uint8_t* data, size_t size) : mFdp(data, size){}; void process(); private: void fuzzRefreshRateSelection(); void fuzzRefreshRateConfigs(); void fuzzVSyncModulator(); void fuzzVSyncPredictor(); void fuzzVSyncReactor(); void fuzzLayerHistory(); void fuzzDispSyncSource(); void fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch); void fuzzVSyncDispatchTimerQueue(); void fuzzOneShotTimer(); void fuzzEventThread(); PhysicalDisplayId getPhysicalDisplayId(); FuzzedDataProvider mFdp; protected: void onVSyncEvent(nsecs_t /* when */, VSyncSource::VSyncData) {} }; PhysicalDisplayId SchedulerFuzzer::getPhysicalDisplayId() { PhysicalDisplayId internalDispId = PhysicalDisplayId::fromPort(111u); PhysicalDisplayId externalDispId = PhysicalDisplayId::fromPort(222u); PhysicalDisplayId randomDispId = PhysicalDisplayId::fromPort(mFdp.ConsumeIntegral()); PhysicalDisplayId dispId64Bit = PhysicalDisplayId::fromEdid(0xffu, 0xffffu, 0xffff'ffffu); PhysicalDisplayId displayId = mFdp.PickValueInArray( {internalDispId, externalDispId, dispId64Bit, randomDispId}); return displayId; } void SchedulerFuzzer::fuzzEventThread() { const auto getVsyncPeriod = [](uid_t /* uid */) { return kSyncPeriod.count(); }; std::unique_ptr thread = std::make_unique< android::impl::EventThread>(std::move(std::make_unique()), nullptr, nullptr, nullptr, getVsyncPeriod); thread->onHotplugReceived(getPhysicalDisplayId(), mFdp.ConsumeBool()); sp connection = new EventThreadConnection(thread.get(), mFdp.ConsumeIntegral(), nullptr, {} /*eventRegistration*/); thread->requestNextVsync(connection); thread->setVsyncRate(mFdp.ConsumeIntegral() /*rate*/, connection); thread->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral(), (std::chrono::nanoseconds)mFdp.ConsumeIntegral()); thread->registerDisplayEventConnection(connection); thread->onScreenAcquired(); thread->onScreenReleased(); dump(thread.get(), &mFdp); } void SchedulerFuzzer::fuzzDispSyncSource() { std::unique_ptr vSyncDispatch = std::make_unique(); std::unique_ptr vSyncTracker = std::make_unique(); std::unique_ptr dispSyncSource = std::make_unique< scheduler::DispSyncSource>(*vSyncDispatch, *vSyncTracker, (std::chrono::nanoseconds) mFdp.ConsumeIntegral() /*workDuration*/, (std::chrono::nanoseconds)mFdp.ConsumeIntegral() /*readyDuration*/, mFdp.ConsumeBool(), mFdp.ConsumeRandomLengthString(kRandomStringLength).c_str()); dispSyncSource->setVSyncEnabled(true); dispSyncSource->setCallback(this); dispSyncSource->setDuration((std::chrono::nanoseconds)mFdp.ConsumeIntegral(), 0ns); dump(dispSyncSource.get(), &mFdp); } void SchedulerFuzzer::fuzzCallbackToken(scheduler::VSyncDispatchTimerQueue* dispatch) { scheduler::VSyncDispatch::CallbackToken tmp = dispatch->registerCallback( [&](auto, auto, auto) { dispatch->schedule(tmp, {.workDuration = mFdp.ConsumeIntegral(), .readyDuration = mFdp.ConsumeIntegral(), .earliestVsync = mFdp.ConsumeIntegral()}); }, "o.o"); dispatch->schedule(tmp, {.workDuration = mFdp.ConsumeIntegral(), .readyDuration = mFdp.ConsumeIntegral(), .earliestVsync = mFdp.ConsumeIntegral()}); dispatch->unregisterCallback(tmp); dispatch->cancel(tmp); } void SchedulerFuzzer::fuzzVSyncDispatchTimerQueue() { FuzzImplVSyncTracker stubTracker{mFdp.ConsumeIntegral()}; scheduler::VSyncDispatchTimerQueue mDispatch{std::make_unique(), stubTracker, mFdp.ConsumeIntegral() /*dispatchGroupThreshold*/, mFdp.ConsumeIntegral() /*vSyncMoveThreshold*/}; fuzzCallbackToken(&mDispatch); dump(&mDispatch, &mFdp); scheduler::VSyncDispatchTimerQueueEntry entry( "fuzz", [](auto, auto, auto) {}, mFdp.ConsumeIntegral() /*vSyncMoveThreshold*/); entry.update(stubTracker, 0); entry.schedule({.workDuration = mFdp.ConsumeIntegral(), .readyDuration = mFdp.ConsumeIntegral(), .earliestVsync = mFdp.ConsumeIntegral()}, stubTracker, 0); entry.disarm(); entry.ensureNotRunning(); entry.schedule({.workDuration = mFdp.ConsumeIntegral(), .readyDuration = mFdp.ConsumeIntegral(), .earliestVsync = mFdp.ConsumeIntegral()}, stubTracker, 0); auto const wakeup = entry.wakeupTime(); auto const ready = entry.readyTime(); entry.callback(entry.executing(), *wakeup, *ready); entry.addPendingWorkloadUpdate({.workDuration = mFdp.ConsumeIntegral(), .readyDuration = mFdp.ConsumeIntegral(), .earliestVsync = mFdp.ConsumeIntegral()}); dump(&entry, &mFdp); } void SchedulerFuzzer::fuzzVSyncPredictor() { uint16_t now = mFdp.ConsumeIntegral(); uint16_t historySize = mFdp.ConsumeIntegralInRange(1, UINT16_MAX); uint16_t minimumSamplesForPrediction = mFdp.ConsumeIntegralInRange(1, UINT16_MAX); scheduler::VSyncPredictor tracker{mFdp.ConsumeIntegral() /*period*/, historySize, minimumSamplesForPrediction, mFdp.ConsumeIntegral() /*outlierTolerancePercent*/}; uint16_t period = mFdp.ConsumeIntegral(); tracker.setPeriod(period); for (uint16_t i = 0; i < minimumSamplesForPrediction; ++i) { if (!tracker.needsMoreSamples()) { break; } tracker.addVsyncTimestamp(now += period); } tracker.nextAnticipatedVSyncTimeFrom(now); tracker.resetModel(); } void SchedulerFuzzer::fuzzOneShotTimer() { FakeClock* clock = new FakeClock(); std::unique_ptr idleTimer = std::make_unique( mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/, (std::chrono::milliseconds)mFdp.ConsumeIntegral() /*val*/, [] {} /*resetCallback*/, [] {} /*timeoutCallback*/, std::unique_ptr(clock)); idleTimer->start(); idleTimer->reset(); idleTimer->stop(); } void SchedulerFuzzer::fuzzLayerHistory() { TestableSurfaceFlinger flinger; flinger.setupScheduler(std::make_unique(), std::make_unique(), std::make_unique(), std::make_unique()); flinger.setupTimeStats(std::make_unique()); std::unique_ptr renderEngine = std::make_unique(); flinger.setupRenderEngine(std::move(renderEngine)); flinger.setupComposer(std::make_unique()); scheduler::TestableScheduler* scheduler = flinger.scheduler(); scheduler::LayerHistory& historyV1 = scheduler->mutableLayerHistory(); nsecs_t time1 = systemTime(); nsecs_t time2 = time1; uint8_t historySize = mFdp.ConsumeIntegral(); sp layer1 = new FuzzImplLayer(flinger.flinger()); sp layer2 = new FuzzImplLayer(flinger.flinger()); for (int i = 0; i < historySize; ++i) { historyV1.record(layer1.get(), time1, time1, scheduler::LayerHistory::LayerUpdateType::Buffer); historyV1.record(layer2.get(), time2, time2, scheduler::LayerHistory::LayerUpdateType::Buffer); time1 += mFdp.PickValueInArray(kVsyncPeriods); time2 += mFdp.PickValueInArray(kVsyncPeriods); } historyV1.summarize(*scheduler->refreshRateConfigs(), time1); historyV1.summarize(*scheduler->refreshRateConfigs(), time2); scheduler->createConnection(std::make_unique()); scheduler::ConnectionHandle handle; scheduler->createDisplayEventConnection(handle); scheduler->setDuration(handle, (std::chrono::nanoseconds)mFdp.ConsumeIntegral(), (std::chrono::nanoseconds)mFdp.ConsumeIntegral()); dump(scheduler, &mFdp); } void SchedulerFuzzer::fuzzVSyncReactor() { std::shared_ptr vSyncTracker = std::make_shared(); scheduler::VSyncReactor reactor(std::make_unique( std::make_shared()), *vSyncTracker, mFdp.ConsumeIntegral() /*pendingLimit*/, false); reactor.startPeriodTransition(mFdp.ConsumeIntegral()); bool periodFlushed = mFdp.ConsumeBool(); reactor.addHwVsyncTimestamp(0, std::nullopt, &periodFlushed); reactor.addHwVsyncTimestamp(mFdp.ConsumeIntegral() /*newPeriod*/, std::nullopt, &periodFlushed); sp fence = new Fence(memfd_create("fd", MFD_ALLOW_SEALING)); std::shared_ptr ft = std::make_shared(fence); vSyncTracker->addVsyncTimestamp(mFdp.ConsumeIntegral()); FenceTime::Snapshot snap(mFdp.ConsumeIntegral()); ft->applyTrustedSnapshot(snap); reactor.setIgnorePresentFences(mFdp.ConsumeBool()); reactor.addPresentFence(ft); dump(&reactor, &mFdp); } void SchedulerFuzzer::fuzzVSyncModulator() { enum { SF_OFFSET_LATE, APP_OFFSET_LATE, SF_DURATION_LATE, APP_DURATION_LATE, SF_OFFSET_EARLY, APP_OFFSET_EARLY, SF_DURATION_EARLY, APP_DURATION_EARLY, SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU, SF_DURATION_EARLY_GPU, APP_DURATION_EARLY_GPU, HWC_MIN_WORK_DURATION, }; using Schedule = scheduler::TransactionSchedule; using nanos = std::chrono::nanoseconds; using VsyncModulator = scheduler::VsyncModulator; using FuzzImplVsyncModulator = scheduler::FuzzImplVsyncModulator; const VsyncModulator::VsyncConfig early{SF_OFFSET_EARLY, APP_OFFSET_EARLY, nanos(SF_DURATION_LATE), nanos(APP_DURATION_LATE)}; const VsyncModulator::VsyncConfig earlyGpu{SF_OFFSET_EARLY_GPU, APP_OFFSET_EARLY_GPU, nanos(SF_DURATION_EARLY), nanos(APP_DURATION_EARLY)}; const VsyncModulator::VsyncConfig late{SF_OFFSET_LATE, APP_OFFSET_LATE, nanos(SF_DURATION_EARLY_GPU), nanos(APP_DURATION_EARLY_GPU)}; const VsyncModulator::VsyncConfigSet offsets = {early, earlyGpu, late, nanos(HWC_MIN_WORK_DURATION)}; sp vSyncModulator = sp::make(offsets, scheduler::Now); (void)vSyncModulator->setVsyncConfigSet(offsets); (void)vSyncModulator->setTransactionSchedule(Schedule::Late); const auto token = sp::make(); (void)vSyncModulator->setTransactionSchedule(Schedule::EarlyStart, token); vSyncModulator->binderDied(token); } void SchedulerFuzzer::fuzzRefreshRateSelection() { TestableSurfaceFlinger flinger; flinger.setupScheduler(std::make_unique(), std::make_unique(), std::make_unique(), std::make_unique()); sp client; LayerCreationArgs args(flinger.flinger(), client, mFdp.ConsumeRandomLengthString(kRandomStringLength) /*name*/, mFdp.ConsumeIntegral() /*layerFlags*/, LayerMetadata()); sp layer = new BufferQueueLayer(args); layer->setFrameRateSelectionPriority(mFdp.ConsumeIntegral()); } void SchedulerFuzzer::fuzzRefreshRateConfigs() { using RefreshRateConfigs = scheduler::RefreshRateConfigs; using LayerRequirement = RefreshRateConfigs::LayerRequirement; using RefreshRateStats = scheduler::RefreshRateStats; const uint16_t minRefreshRate = mFdp.ConsumeIntegralInRange(1, UINT16_MAX >> 1); const uint16_t maxRefreshRate = mFdp.ConsumeIntegralInRange(minRefreshRate + 1, UINT16_MAX); const DisplayModeId modeId{mFdp.ConsumeIntegralInRange(0, 10)}; DisplayModes displayModes; for (uint16_t fps = minRefreshRate; fps < maxRefreshRate; ++fps) { displayModes.try_emplace(modeId, mock::createDisplayMode(modeId, Fps::fromValue(static_cast(fps)))); } RefreshRateConfigs refreshRateConfigs(displayModes, modeId); const RefreshRateConfigs::GlobalSignals globalSignals = {.touch = false, .idle = false}; std::vector layers = {{.weight = mFdp.ConsumeFloatingPoint()}}; refreshRateConfigs.getBestRefreshRate(layers, globalSignals); layers[0].name = mFdp.ConsumeRandomLengthString(kRandomStringLength); layers[0].ownerUid = mFdp.ConsumeIntegral(); layers[0].desiredRefreshRate = Fps::fromValue(mFdp.ConsumeFloatingPoint()); layers[0].vote = mFdp.PickValueInArray(kLayerVoteTypes.values); auto frameRateOverrides = refreshRateConfigs.getFrameRateOverrides(layers, Fps::fromValue( mFdp.ConsumeFloatingPoint()), globalSignals); refreshRateConfigs.setDisplayManagerPolicy( {modeId, {Fps::fromValue(mFdp.ConsumeFloatingPoint()), Fps::fromValue(mFdp.ConsumeFloatingPoint())}}); refreshRateConfigs.setActiveModeId(modeId); RefreshRateConfigs::isFractionalPairOrMultiple(Fps::fromValue( mFdp.ConsumeFloatingPoint()), Fps::fromValue( mFdp.ConsumeFloatingPoint())); RefreshRateConfigs::getFrameRateDivisor(Fps::fromValue(mFdp.ConsumeFloatingPoint()), Fps::fromValue(mFdp.ConsumeFloatingPoint())); android::mock::TimeStats timeStats; RefreshRateStats refreshRateStats(timeStats, Fps::fromValue(mFdp.ConsumeFloatingPoint()), PowerMode::OFF); const auto fpsOpt = displayModes.get(modeId, [](const auto& mode) { return mode->getFps(); }); refreshRateStats.setRefreshRate(*fpsOpt); refreshRateStats.setPowerMode(mFdp.PickValueInArray(kPowerModes)); } void SchedulerFuzzer::process() { fuzzRefreshRateSelection(); fuzzRefreshRateConfigs(); fuzzVSyncModulator(); fuzzVSyncPredictor(); fuzzVSyncReactor(); fuzzLayerHistory(); fuzzDispSyncSource(); fuzzEventThread(); fuzzVSyncDispatchTimerQueue(); fuzzOneShotTimer(); } extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { SchedulerFuzzer schedulerFuzzer(data, size); schedulerFuzzer.process(); return 0; } } // namespace android::fuzz