/* * Copyright 2023 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 <android-base/stringprintf.h> #include <fuzzer/FuzzedDataProvider.h> #include "../FakeApplicationHandle.h" #include "../FakeInputDispatcherPolicy.h" #include "../FakeWindows.h" #include "FuzzedInputStream.h" #include "dispatcher/InputDispatcher.h" #include "input/InputVerifier.h" namespace android { using android::base::Result; using android::gui::WindowInfo; namespace inputdispatcher { namespace { static constexpr int32_t MAX_RANDOM_DISPLAYS = 4; static constexpr int32_t MAX_RANDOM_WINDOWS = 4; /** * Provide a valid motion stream, to make the fuzzer more effective. */ class NotifyStreamProvider { public: NotifyStreamProvider(FuzzedDataProvider& fdp) : mFdp(fdp), mIdGenerator(IdGenerator::Source::OTHER) {} std::optional<NotifyMotionArgs> nextMotion() { NotifyMotionArgs args = generateFuzzedMotionArgs(mIdGenerator, mFdp, MAX_RANDOM_DISPLAYS); auto [it, _] = mVerifiers.emplace(args.displayId, "Fuzz Verifier"); InputVerifier& verifier = it->second; const Result<void> result = verifier.processMovement(args.deviceId, args.source, args.action, args.getPointerCount(), args.pointerProperties.data(), args.pointerCoords.data(), args.flags); if (result.ok()) { return args; } return {}; } private: FuzzedDataProvider& mFdp; IdGenerator mIdGenerator; std::map<int32_t /*displayId*/, InputVerifier> mVerifiers; }; void scrambleWindow(FuzzedDataProvider& fdp, FakeWindowHandle& window) { const int32_t left = fdp.ConsumeIntegralInRange<int32_t>(0, 100); const int32_t top = fdp.ConsumeIntegralInRange<int32_t>(0, 100); const int32_t width = fdp.ConsumeIntegralInRange<int32_t>(0, 100); const int32_t height = fdp.ConsumeIntegralInRange<int32_t>(0, 100); window.setFrame(Rect(left, top, left + width, top + height)); window.setSlippery(fdp.ConsumeBool()); window.setDupTouchToWallpaper(fdp.ConsumeBool()); window.setIsWallpaper(fdp.ConsumeBool()); window.setVisible(fdp.ConsumeBool()); window.setPreventSplitting(fdp.ConsumeBool()); const bool isTrustedOverlay = fdp.ConsumeBool(); window.setTrustedOverlay(isTrustedOverlay); if (isTrustedOverlay) { window.setSpy(fdp.ConsumeBool()); } else { window.setSpy(false); } } } // namespace sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, std::unique_ptr<InputDispatcher>& dispatcher, ui::LogicalDisplayId displayId) { static size_t windowNumber = 0; std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>(); std::string windowName = android::base::StringPrintf("Win") + std::to_string(windowNumber++); sp<FakeWindowHandle> window = sp<FakeWindowHandle>::make(application, dispatcher, windowName, displayId); scrambleWindow(fdp, *window); return window; } void randomizeWindows(std::unordered_map<ui::LogicalDisplayId, std::vector<sp<FakeWindowHandle>>>& windowsPerDisplay, FuzzedDataProvider& fdp, std::unique_ptr<InputDispatcher>& dispatcher) { const ui::LogicalDisplayId displayId{ fdp.ConsumeIntegralInRange<int32_t>(0, MAX_RANDOM_DISPLAYS - 1)}; std::vector<sp<FakeWindowHandle>>& windows = windowsPerDisplay[displayId]; fdp.PickValueInArray<std::function<void()>>({ // Add a new window [&]() -> void { if (windows.size() < MAX_RANDOM_WINDOWS) { windows.push_back(generateFuzzedWindow(fdp, dispatcher, displayId)); } }, // Remove a window [&]() -> void { if (windows.empty()) { return; } const int32_t erasedPosition = fdp.ConsumeIntegralInRange<int32_t>(0, windows.size() - 1); windows.erase(windows.begin() + erasedPosition); if (windows.empty()) { windowsPerDisplay.erase(displayId); } }, // Change flags or move some of the existing windows [&]() -> void { for (auto& window : windows) { if (fdp.ConsumeBool()) { scrambleWindow(fdp, *window); } } }, })(); } extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) { FuzzedDataProvider fdp(data, size); NotifyStreamProvider streamProvider(fdp); FakeInputDispatcherPolicy fakePolicy; auto dispatcher = std::make_unique<InputDispatcher>(fakePolicy); dispatcher->setInputDispatchMode(/*enabled=*/true, /*frozen=*/false); // Start InputDispatcher thread dispatcher->start(); std::unordered_map<ui::LogicalDisplayId, std::vector<sp<FakeWindowHandle>>> windowsPerDisplay; // Randomly invoke InputDispatcher api's until randomness is exhausted. while (fdp.remaining_bytes() > 0) { fdp.PickValueInArray<std::function<void()>>({ [&]() -> void { std::optional<NotifyMotionArgs> motion = streamProvider.nextMotion(); if (motion) { dispatcher->notifyMotion(*motion); } }, [&]() -> void { // Scramble the windows we currently have randomizeWindows(/*byref*/ windowsPerDisplay, fdp, dispatcher); std::vector<WindowInfo> windowInfos; for (const auto& [displayId, windows] : windowsPerDisplay) { for (const sp<FakeWindowHandle>& window : windows) { windowInfos.emplace_back(*window->getInfo()); } } dispatcher->onWindowInfosChanged( {windowInfos, {}, /*vsyncId=*/0, /*timestamp=*/0}); }, // Consume on all the windows [&]() -> void { for (const auto& [_, windows] : windowsPerDisplay) { for (const sp<FakeWindowHandle>& window : windows) { // To speed up the fuzzing, don't wait for consumption. If there's an // event pending, this can be consumed on the next call instead. // We also don't care about whether consumption succeeds here, or what // kind of event is returned. window->consume(0ms); } } }, })(); } dispatcher->stop(); return 0; } } // namespace inputdispatcher } // namespace android