blob: 214649cf415d1295744e270b57ac190e5263983e [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 "../FakeWindowHandle.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), mVerifier("Fuzz verifier") {}
std::optional<NotifyMotionArgs> nextMotion() {
NotifyMotionArgs args = generateFuzzedMotionArgs(mIdGenerator, mFdp, MAX_RANDOM_DISPLAYS);
const Result<void> result =
mVerifier.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;
InputVerifier mVerifier;
};
} // namespace
sp<FakeWindowHandle> generateFuzzedWindow(FuzzedDataProvider& fdp, 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, "Fake", displayId);
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->setTrustedOverlay(fdp.ConsumeBool());
return window;
}
void randomizeWindows(
std::unordered_map<int32_t, std::vector<sp<FakeWindowHandle>>>& windowsPerDisplay,
FuzzedDataProvider& fdp, 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);
}
},
// Could also clone a window, change flags, reposition, etc...
})();
}
extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
FuzzedDataProvider fdp(data, size);
NotifyStreamProvider streamProvider(fdp);
FakeInputDispatcherPolicy fakePolicy;
InputDispatcher dispatcher(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