Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 2defec03 authored by Siarhei Vishniakou's avatar Siarhei Vishniakou
Browse files

Fuzz InputDispatcher

Initial version of a fuzzer for InputDispatcher.

The goal is to reproduce crashes that are triggered by the fatal logs,
mainly around:
1) mismatching downtime / eventTimes
2) unexpected hover events

Currently, the fuzzer runs without hitting those targets.
However, it hits an ODR due to rect so currently it has to run without
checking for ODRs.

It also currently hits an out of memory issue after a short run, finding
a problem in
AStatsManager_setPullAtomCallback packages/modules/StatsD/lib/libstatspull/stats_pull_atom_callback.cpp:397:46

Bug: 281806933
Test: FUZZER=inputflinger_input_dispatcher_fuzzer; m $FUZZER && ASAN_OPTIONS=detect_odr_violation=0 $ANDROID_HOST_OUT/fuzz/x86_64/$FUZZER/$FUZZER
Test: atest inputflinger_benchmarks
Change-Id: I465ea11520fc9cc21886646c0ecf20dc529b2698
parent 0659550f
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -1138,6 +1138,24 @@ private:
    std::queue<std::unique_ptr<TouchModeEvent>> mTouchModeEventPool;
};

/**
 * An input event factory implementation that simply creates the input events on the heap, when
 * needed. The caller is responsible for destroying the returned references.
 * It is recommended that the caller wrap these return values into std::unique_ptr.
 */
class DynamicInputEventFactory : public InputEventFactoryInterface {
public:
    explicit DynamicInputEventFactory(){};
    ~DynamicInputEventFactory(){};

    KeyEvent* createKeyEvent() override { return new KeyEvent(); };
    MotionEvent* createMotionEvent() override { return new MotionEvent(); };
    FocusEvent* createFocusEvent() override { return new FocusEvent(); };
    CaptureEvent* createCaptureEvent() override { return new CaptureEvent(); };
    DragEvent* createDragEvent() override { return new DragEvent(); };
    TouchModeEvent* createTouchModeEvent() override { return new TouchModeEvent(); };
};

/*
 * Describes a unique request to enable or disable Pointer Capture.
 */
+1 −0
Original line number Diff line number Diff line
@@ -256,6 +256,7 @@ phony {
        "inputflinger_input_reader_fuzzer",
        "inputflinger_blocking_queue_fuzzer",
        "inputflinger_input_classifier_fuzzer",
        "inputflinger_input_dispatcher_fuzzer",

        // Java/Kotlin targets
        "CtsWindowManagerDeviceWindow",
+13 −165
Original line number Diff line number Diff line
@@ -20,10 +20,12 @@
#include <binder/Binder.h>
#include <gui/constants.h>
#include "../dispatcher/InputDispatcher.h"
#include "../tests/FakeApplicationHandle.h"
#include "../tests/FakeInputDispatcherPolicy.h"
#include "../tests/FakeWindowHandle.h"

using android::base::Result;
using android::gui::WindowInfo;
using android::gui::WindowInfoHandle;
using android::os::IInputConstants;
using android::os::InputEventInjectionResult;
using android::os::InputEventInjectionSync;
@@ -33,171 +35,17 @@ namespace android::inputdispatcher {
namespace {

// An arbitrary device id.
constexpr int32_t DEVICE_ID = 1;
constexpr DeviceId DEVICE_ID = 1;

// The default pid and uid for windows created by the test.
constexpr gui::Pid WINDOW_PID{999};
constexpr gui::Uid WINDOW_UID{1001};
// An arbitrary display id
constexpr int32_t DISPLAY_ID = ADISPLAY_ID_DEFAULT;

static constexpr std::chrono::duration INJECT_EVENT_TIMEOUT = 5s;
static constexpr std::chrono::nanoseconds DISPATCHING_TIMEOUT = 100ms;

static nsecs_t now() {
    return systemTime(SYSTEM_TIME_MONOTONIC);
}

// --- FakeInputDispatcherPolicy ---

class FakeInputDispatcherPolicy : public InputDispatcherPolicyInterface {
public:
    FakeInputDispatcherPolicy() = default;
    virtual ~FakeInputDispatcherPolicy() = default;

private:
    void notifyConfigurationChanged(nsecs_t) override {}

    void notifyNoFocusedWindowAnr(
            const std::shared_ptr<InputApplicationHandle>& applicationHandle) override {
        ALOGE("There is no focused window for %s", applicationHandle->getName().c_str());
    }

    void notifyWindowUnresponsive(const sp<IBinder>& connectionToken, std::optional<gui::Pid> pid,
                                  const std::string& reason) override {
        ALOGE("Window is not responding: %s", reason.c_str());
    }

    void notifyWindowResponsive(const sp<IBinder>& connectionToken,
                                std::optional<gui::Pid> pid) override {}

    void notifyInputChannelBroken(const sp<IBinder>&) override {}

    void notifyFocusChanged(const sp<IBinder>&, const sp<IBinder>&) override {}

    void notifySensorEvent(int32_t deviceId, InputDeviceSensorType sensorType,
                           InputDeviceSensorAccuracy accuracy, nsecs_t timestamp,
                           const std::vector<float>& values) override {}

    void notifySensorAccuracy(int32_t deviceId, InputDeviceSensorType sensorType,
                              InputDeviceSensorAccuracy accuracy) override {}

    void notifyVibratorState(int32_t deviceId, bool isOn) override {}

    bool filterInputEvent(const InputEvent& inputEvent, uint32_t policyFlags) override {
        return true; // dispatch event normally
    }

    void interceptKeyBeforeQueueing(const KeyEvent&, uint32_t&) override {}

    void interceptMotionBeforeQueueing(int32_t, nsecs_t, uint32_t&) override {}

    nsecs_t interceptKeyBeforeDispatching(const sp<IBinder>&, const KeyEvent&, uint32_t) override {
        return 0;
    }

    std::optional<KeyEvent> dispatchUnhandledKey(const sp<IBinder>&, const KeyEvent&,
                                                 uint32_t) override {
        return {};
    }

    void notifySwitch(nsecs_t, uint32_t, uint32_t, uint32_t) override {}

    void pokeUserActivity(nsecs_t, int32_t, int32_t) override {}

    void onPointerDownOutsideFocus(const sp<IBinder>& newToken) override {}

    void setPointerCapture(const PointerCaptureRequest&) override {}

    void notifyDropWindow(const sp<IBinder>&, float x, float y) override {}

    void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
                                 const std::set<gui::Uid>& uids) override {}

    InputDispatcherConfiguration mConfig;
};

class FakeApplicationHandle : public InputApplicationHandle {
public:
    FakeApplicationHandle() {}
    virtual ~FakeApplicationHandle() {}

    virtual bool updateInfo() {
        mInfo.dispatchingTimeoutMillis =
                std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
        return true;
    }
};

class FakeInputReceiver {
public:
    void consumeEvent() {
        uint32_t consumeSeq = 0;
        InputEvent* event;

        std::chrono::time_point start = std::chrono::steady_clock::now();
        status_t result = WOULD_BLOCK;
        while (result == WOULD_BLOCK) {
            std::chrono::duration elapsed = std::chrono::steady_clock::now() - start;
            if (elapsed > 10ms) {
                ALOGE("Waited too long for consumer to produce an event, giving up");
                break;
            }
            result = mConsumer->consume(&mEventFactory, /*consumeBatches=*/true, -1, &consumeSeq,
                                        &event);
        }
        if (result != OK) {
            ALOGE("Received result = %d from consume()", result);
        }
        result = mConsumer->sendFinishedSignal(consumeSeq, true);
        if (result != OK) {
            ALOGE("Received result = %d from sendFinishedSignal", result);
        }
    }

protected:
    explicit FakeInputReceiver(InputDispatcher& dispatcher, const std::string name) {
        Result<std::unique_ptr<InputChannel>> channelResult = dispatcher.createInputChannel(name);
        LOG_ALWAYS_FATAL_IF(!channelResult.ok());
        mClientChannel = std::move(*channelResult);
        mConsumer = std::make_unique<InputConsumer>(mClientChannel);
    }

    virtual ~FakeInputReceiver() {}

    std::shared_ptr<InputChannel> mClientChannel;
    std::unique_ptr<InputConsumer> mConsumer;
    PreallocatedInputEventFactory mEventFactory;
};

class FakeWindowHandle : public WindowInfoHandle, public FakeInputReceiver {
public:
    static const int32_t WIDTH = 200;
    static const int32_t HEIGHT = 200;

    FakeWindowHandle(const std::shared_ptr<InputApplicationHandle>& inputApplicationHandle,
                     InputDispatcher& dispatcher, const std::string name)
          : FakeInputReceiver(dispatcher, name), mFrame(Rect(0, 0, WIDTH, HEIGHT)) {
        inputApplicationHandle->updateInfo();
        updateInfo();
        mInfo.applicationInfo = *inputApplicationHandle->getInfo();
    }

    void updateInfo() {
        mInfo.token = mClientChannel->getConnectionToken();
        mInfo.name = "FakeWindowHandle";
        mInfo.dispatchingTimeout = DISPATCHING_TIMEOUT;
        mInfo.frame = mFrame;
        mInfo.globalScaleFactor = 1.0;
        mInfo.touchableRegion.clear();
        mInfo.addTouchableRegion(mFrame);
        mInfo.ownerPid = WINDOW_PID;
        mInfo.ownerUid = WINDOW_UID;
        mInfo.displayId = ADISPLAY_ID_DEFAULT;
    }

protected:
    Rect mFrame;
};

static MotionEvent generateMotionEvent() {
    PointerProperties pointerProperties[1];
    PointerCoords pointerCoords[1];
@@ -263,7 +111,7 @@ static void benchmarkNotifyMotion(benchmark::State& state) {
    // Create a window that will receive motion events
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
            sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);

    dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});

@@ -281,8 +129,8 @@ static void benchmarkNotifyMotion(benchmark::State& state) {
        motionArgs.eventTime = now();
        dispatcher.notifyMotion(motionArgs);

        window->consumeEvent();
        window->consumeEvent();
        window->consumeMotion();
        window->consumeMotion();
    }

    dispatcher.stop();
@@ -298,7 +146,7 @@ static void benchmarkInjectMotion(benchmark::State& state) {
    // Create a window that will receive motion events
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
            sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);

    dispatcher.onWindowInfosChanged({{*window->getInfo()}, {}, 0, 0});

@@ -315,8 +163,8 @@ static void benchmarkInjectMotion(benchmark::State& state) {
                                    INJECT_EVENT_TIMEOUT,
                                    POLICY_FLAG_FILTERED | POLICY_FLAG_PASS_TO_USER);

        window->consumeEvent();
        window->consumeEvent();
        window->consumeMotion();
        window->consumeMotion();
    }

    dispatcher.stop();
@@ -332,7 +180,7 @@ static void benchmarkOnWindowInfosChanged(benchmark::State& state) {
    // Create a window
    std::shared_ptr<FakeApplicationHandle> application = std::make_shared<FakeApplicationHandle>();
    sp<FakeWindowHandle> window =
            sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window");
            sp<FakeWindowHandle>::make(application, dispatcher, "Fake Window", DISPLAY_ID);

    std::vector<gui::WindowInfo> windowInfos{*window->getInfo()};
    gui::DisplayInfo info;
+3 −1
Original line number Diff line number Diff line
@@ -21,7 +21,9 @@
#include <android-base/properties.h>
#include <binder/IBinder.h>
#include <gui/InputApplication.h>
#include <gui/PidUid.h>
#include <input/Input.h>
#include <input/InputDevice.h>
#include <utils/RefBase.h>
#include <set>

@@ -146,7 +148,7 @@ public:
    virtual void notifyDropWindow(const sp<IBinder>& token, float x, float y) = 0;

    /* Notifies the policy that there was an input device interaction with apps. */
    virtual void notifyDeviceInteraction(int32_t deviceId, nsecs_t timestamp,
    virtual void notifyDeviceInteraction(DeviceId deviceId, nsecs_t timestamp,
                                         const std::set<gui::Uid>& uids) = 0;
};

+48 −0
Original line number Diff line number Diff line
/*
 * 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.
 */

#pragma once

#include <android-base/properties.h>
#include <android/os/IInputConstants.h>
#include <gui/InputApplication.h>

namespace android {

namespace inputdispatcher {

class FakeApplicationHandle : public InputApplicationHandle {
public:
    FakeApplicationHandle() {
        static const std::chrono::duration DISPATCHING_TIMEOUT = std::chrono::milliseconds(
                android::os::IInputConstants::UNMULTIPLIED_DEFAULT_DISPATCHING_TIMEOUT_MILLIS *
                android::base::HwTimeoutMultiplier());
        mInfo.name = "Fake Application";
        mInfo.token = sp<BBinder>::make();
        mInfo.dispatchingTimeoutMillis =
                std::chrono::duration_cast<std::chrono::milliseconds>(DISPATCHING_TIMEOUT).count();
    }
    virtual ~FakeApplicationHandle() {}

    bool updateInfo() override { return true; }

    void setDispatchingTimeout(std::chrono::milliseconds timeout) {
        mInfo.dispatchingTimeoutMillis = timeout.count();
    }
};

} // namespace inputdispatcher
} // namespace android
Loading