blob: 7335fb75009e5fa102f09df87134cfd6bc890448 [file] [log] [blame]
/*
* 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