| /* |
| * 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, |
| int32_t 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<int32_t, std::vector<sp<FakeWindowHandle>>>& windowsPerDisplay, |
| FuzzedDataProvider& fdp, std::unique_ptr<InputDispatcher>& dispatcher) { |
| const int32_t 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<int32_t, 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 |