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

Commit ccb75e89 authored by Harry Cutts's avatar Harry Cutts
Browse files

inputflinger: Add touchpad stack fuzzer

Heavily inspired by MultiTouchInputFuzzer, this one works similarly but
has additional code to fuzz touchpad setting values and device-specific
configuration. The evdev axis info is also fuzzed, since unspecified
resolution values have caused bugs in the Gestures library before.
(Other fuzzers only fuzz the status_t return value from
getAbsoluteAxisInfo, not the axis info struct itself.)

Bug: 264582512
Test: build with SANITIZE_TARGET=hwaddress, run for a while on a device
Change-Id: Ic22ac0f29d433fdf3c17331df620a39937ebd7eb
parent 3985065a
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -243,6 +243,7 @@ phony {
        "inputflinger_keyboard_input_fuzzer",
        "inputflinger_multitouch_input_fuzzer",
        "inputflinger_switch_input_fuzzer",
        "inputflinger_touchpad_input_fuzzer",
        "inputflinger_input_reader_fuzzer",
        "inputflinger_blocking_queue_fuzzer",
        "inputflinger_input_classifier_fuzzer",
+16 −0
Original line number Diff line number Diff line
@@ -99,6 +99,22 @@ cc_fuzz {
    ],
}

cc_fuzz {
    name: "inputflinger_touchpad_input_fuzzer",
    defaults: [
        "inputflinger_fuzz_defaults",
    ],
    srcs: [
        "TouchpadInputFuzzer.cpp",
    ],
    static_libs: [
        "libchrome-gestures",
    ],
    header_libs: [
        "libchrome-gestures_headers",
    ],
}

cc_fuzz {
    name: "inputflinger_input_reader_fuzzer",
    defaults: [
+4 −0
Original line number Diff line number Diff line
@@ -73,6 +73,10 @@ public:

    InputReaderConfiguration& getPolicyConfig() { return mPolicyConfig; }

    void setAbsoluteAxisInfo(int axis, const RawAbsoluteAxisInfo& axisInfo) {
        mFuzzEventHub->setAbsoluteAxisInfo(mFuzzDevice->getId(), axis, axisInfo);
    }

    template <class T, typename... Args>
    T& getMapper(Args... args) {
        int32_t eventhubId = mFdp->ConsumeIntegral<int32_t>();
+14 −0
Original line number Diff line number Diff line
@@ -15,6 +15,9 @@
 */
#pragma once

#include <map>

#include <EventHub.h>
#include <InputDevice.h>
#include <InputMapper.h>
#include <InputReader.h>
@@ -92,6 +95,7 @@ class FuzzEventHub : public EventHubInterface {
    InputDeviceIdentifier mIdentifier;
    std::vector<TouchVideoFrame> mVideoFrames;
    PropertyMap mFuzzConfig;
    std::map<int32_t /* deviceId */, std::map<int /* axis */, RawAbsoluteAxisInfo>> mAxes;
    std::shared_ptr<ThreadSafeFuzzedDataProvider> mFdp;

public:
@@ -111,8 +115,18 @@ public:
    std::optional<PropertyMap> getConfiguration(int32_t deviceId) const override {
        return mFuzzConfig;
    }
    void setAbsoluteAxisInfo(int32_t deviceId, int axis, const RawAbsoluteAxisInfo& axisInfo) {
        mAxes[deviceId][axis] = axisInfo;
    }
    status_t getAbsoluteAxisInfo(int32_t deviceId, int axis,
                                 RawAbsoluteAxisInfo* outAxisInfo) const override {
        if (auto deviceAxesIt = mAxes.find(deviceId); deviceAxesIt != mAxes.end()) {
            const std::map<int, RawAbsoluteAxisInfo>& deviceAxes = deviceAxesIt->second;
            if (auto axisInfoIt = deviceAxes.find(axis); axisInfoIt != deviceAxes.end()) {
                *outAxisInfo = axisInfoIt->second;
                return OK;
            }
        }
        return mFdp->ConsumeIntegral<status_t>();
    }
    bool hasRelativeAxis(int32_t deviceId, int axis) const override { return mFdp->ConsumeBool(); }
+176 −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.
 */

#include <limits>
#include <string>
#include <vector>

#include <linux/input-event-codes.h>

#include <FuzzContainer.h>
#include <MapperHelpers.h>
#include <TouchpadInputMapper.h>

namespace android {

namespace {

void setAxisInfo(ThreadSafeFuzzedDataProvider& fdp, FuzzContainer& fuzzer, int axis) {
    if (fdp.ConsumeBool()) {
        fuzzer.setAbsoluteAxisInfo(axis,
                                   RawAbsoluteAxisInfo{
                                           .valid = fdp.ConsumeBool(),
                                           .minValue = fdp.ConsumeIntegral<int32_t>(),
                                           .maxValue = fdp.ConsumeIntegral<int32_t>(),
                                           .flat = fdp.ConsumeIntegral<int32_t>(),
                                           .fuzz = fdp.ConsumeIntegral<int32_t>(),
                                           .resolution = fdp.ConsumeIntegral<int32_t>(),
                                   });
    }
}

void setAxisInfos(ThreadSafeFuzzedDataProvider& fdp, FuzzContainer& fuzzer) {
    setAxisInfo(fdp, fuzzer, ABS_MT_SLOT);
    setAxisInfo(fdp, fuzzer, ABS_MT_POSITION_X);
    setAxisInfo(fdp, fuzzer, ABS_MT_POSITION_Y);
    setAxisInfo(fdp, fuzzer, ABS_MT_PRESSURE);
    setAxisInfo(fdp, fuzzer, ABS_MT_ORIENTATION);
    setAxisInfo(fdp, fuzzer, ABS_MT_TOUCH_MAJOR);
    setAxisInfo(fdp, fuzzer, ABS_MT_TOUCH_MINOR);
    setAxisInfo(fdp, fuzzer, ABS_MT_WIDTH_MAJOR);
    setAxisInfo(fdp, fuzzer, ABS_MT_WIDTH_MINOR);
}

const std::vector<std::string> boolPropertiesToFuzz = {
        "gestureProp.Compute_Surface_Area_from_Pressure",
        "gestureProp.Drumroll_Suppression_Enable",
        "gestureProp.Fling_Buffer_Suppress_Zero_Length_Scrolls",
        "gestureProp.Stationary_Wiggle_Filter_Enabled",
};
const std::vector<std::string> doublePropertiesToFuzz = {
        "gestureProp.Fake_Timestamp_Delta",
        "gestureProp.Finger_Moving_Energy",
        "gestureProp.Finger_Moving_Hysteresis",
        "gestureProp.IIR_a1",
        "gestureProp.IIR_a2",
        "gestureProp.IIR_b0",
        "gestureProp.IIR_b1",
        "gestureProp.IIR_b2",
        "gestureProp.IIR_b3",
        "gestureProp.Max_Allowed_Pressure_Change_Per_Sec",
        "gestureProp.Max_Hysteresis_Pressure_Per_Sec",
        "gestureProp.Max_Stationary_Move_Speed",
        "gestureProp.Max_Stationary_Move_Speed_Hysteresis",
        "gestureProp.Max_Stationary_Move_Suppress_Distance",
        "gestureProp.Multiple_Palm_Width",
        "gestureProp.Palm_Edge_Zone_Width",
        "gestureProp.Palm_Eval_Timeout",
        "gestureProp.Palm_Pressure",
        "gestureProp.Palm_Width",
        "gestureProp.Pressure_Calibration_Offset",
        "gestureProp.Pressure_Calibration_Slope",
        "gestureProp.Tap_Exclusion_Border_Width",
        "gestureProp.Touchpad_Device_Output_Bias_on_X-Axis",
        "gestureProp.Touchpad_Device_Output_Bias_on_Y-Axis",
        "gestureProp.Two_Finger_Vertical_Close_Distance_Thresh",
};

void setDeviceSpecificConfig(ThreadSafeFuzzedDataProvider& fdp, FuzzContainer& fuzzer) {
    // There are a great many gesture properties offered by the Gestures library, all of which could
    // potentially be set in Input Device Configuration files. Maintaining a complete list is
    // impractical, so instead we only fuzz properties which are used in at least one IDC file, or
    // which are likely to be used in future (e.g. ones for controlling palm rejection).

    if (fdp.ConsumeBool()) {
        fuzzer.addProperty("gestureProp.Touchpad_Stack_Version",
                           std::to_string(fdp.ConsumeIntegral<int>()));
    }

    for (auto& propertyName : boolPropertiesToFuzz) {
        if (fdp.ConsumeBool()) {
            fuzzer.addProperty(propertyName, fdp.ConsumeBool() ? "1" : "0");
        }
    }

    for (auto& propertyName : doublePropertiesToFuzz) {
        if (fdp.ConsumeBool()) {
            fuzzer.addProperty(propertyName, std::to_string(fdp.ConsumeFloatingPoint<double>()));
        }
    }

    if (fdp.ConsumeBool()) {
        fuzzer.addProperty("gestureProp." + fdp.ConsumeRandomLengthString(),
                           std::to_string(fdp.ConsumeIntegral<int>()));
    }
}

void setTouchpadSettings(ThreadSafeFuzzedDataProvider& fdp, InputReaderConfiguration& config) {
    config.touchpadPointerSpeed = fdp.ConsumeIntegralInRange(-7, 7);
    config.touchpadNaturalScrollingEnabled = fdp.ConsumeBool();
    config.touchpadTapToClickEnabled = fdp.ConsumeBool();
    config.touchpadRightClickZoneEnabled = fdp.ConsumeBool();
}

} // namespace

extern "C" int LLVMFuzzerTestOneInput(uint8_t* data, size_t size) {
    std::shared_ptr<ThreadSafeFuzzedDataProvider> fdp =
            std::make_shared<ThreadSafeFuzzedDataProvider>(data, size);
    FuzzContainer fuzzer(fdp);
    setAxisInfos(*fdp, fuzzer);
    setDeviceSpecificConfig(*fdp, fuzzer);

    auto policyConfig = fuzzer.getPolicyConfig();
    // Some settings are fuzzed here, as well as in the main loop, to provide randomized data to the
    // TouchpadInputMapper constructor.
    setTouchpadSettings(*fdp, policyConfig);
    policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
    TouchpadInputMapper& mapper = fuzzer.getMapper<TouchpadInputMapper>(policyConfig);

    // Loop through mapper operations until randomness is exhausted.
    while (fdp->remaining_bytes() > 0) {
        fdp->PickValueInArray<std::function<void()>>({
                [&]() -> void {
                    std::string dump;
                    mapper.dump(dump);
                },
                [&]() -> void {
                    InputDeviceInfo info;
                    mapper.populateDeviceInfo(info);
                },
                [&]() -> void { mapper.getSources(); },
                [&]() -> void {
                    setTouchpadSettings(*fdp, policyConfig);
                    policyConfig.pointerCaptureRequest.enable = fdp->ConsumeBool();
                    std::list<NotifyArgs> unused =
                            mapper.reconfigure(fdp->ConsumeIntegral<nsecs_t>(), policyConfig,
                                               InputReaderConfiguration::Change(
                                                       fdp->ConsumeIntegral<uint32_t>()));
                },
                [&]() -> void {
                    std::list<NotifyArgs> unused = mapper.reset(fdp->ConsumeIntegral<nsecs_t>());
                },
                [&]() -> void {
                    RawEvent event = getFuzzedRawEvent(*fdp);
                    std::list<NotifyArgs> unused = mapper.process(&event);
                },
        })();
    }

    return 0;
}

} // namespace android