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

Commit 34293296 authored by Prabir Pradhan's avatar Prabir Pradhan Committed by Automerger Merge Worker
Browse files

Layer: Guard against Region offset overflows am: 33da9468 am: f293ef19 am: 94c84455

parents cd7d27d4 94c84455
Loading
Loading
Loading
Loading
+49 −0
Original line number Original line Diff line number Diff line
@@ -566,6 +566,55 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
    bgSurface->expectTap(12, 24);
    bgSurface->expectTap(12, 24);
}
}


TEST_F(InputSurfacesTest, touchable_region) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);

    surface->mInputInfo.touchableRegion.set(Rect{19, 29, 21, 31});

    surface->showAt(11, 22);

    // A tap within the surface but outside the touchable region should not be sent to the surface.
    injectTap(20, 30);
    EXPECT_EQ(surface->consumeEvent(200 /*timeoutMs*/), nullptr);

    injectTap(31, 52);
    surface->expectTap(20, 30);
}

TEST_F(InputSurfacesTest, input_respects_touchable_region_offset_overflow) {
    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
    bgSurface->showAt(100, 100);

    // Set the touchable region to the values at the limit of its corresponding type.
    // Since the surface is offset from the origin, the touchable region will be transformed into
    // display space, which would trigger an overflow or an underflow. Ensure that we are protected
    // against such a situation.
    fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});

    fgSurface->showAt(100, 100);

    // Expect no crash for overflow. The overflowed touchable region is ignored, so the background
    // surface receives touch.
    injectTap(112, 124);
    bgSurface->expectTap(12, 24);
}

TEST_F(InputSurfacesTest, input_respects_scaled_touchable_region_overflow) {
    std::unique_ptr<InputSurface> bgSurface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> fgSurface = makeSurface(100, 100);
    bgSurface->showAt(0, 0);

    fgSurface->mInputInfo.touchableRegion.orSelf(Rect{INT32_MIN, INT32_MIN, INT32_MAX, INT32_MAX});
    fgSurface->showAt(0, 0);

    fgSurface->doTransaction([&](auto &t, auto &sc) { t.setMatrix(sc, 2.0, 0, 0, 2.0); });

    // Expect no crash for overflow.
    injectTap(12, 24);
    fgSurface->expectTap(6, 12);
}

// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
// Ensure we ignore transparent region when getting screen bounds when positioning input frame.
TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+33 −1
Original line number Original line Diff line number Diff line
@@ -2194,6 +2194,37 @@ Rect Layer::getInputBounds() const {
    return getCroppedBufferSize(getDrawingState());
    return getCroppedBufferSize(getDrawingState());
}
}


// Applies the given transform to the region, while protecting against overflows caused by any
// offsets. If applying the offset in the transform to any of the Rects in the region would result
// in an overflow, they are not added to the output Region.
static Region transformTouchableRegionSafely(const ui::Transform& t, const Region& r,
                                             const std::string& debugWindowName) {
    // Round the translation using the same rounding strategy used by ui::Transform.
    const auto tx = static_cast<int32_t>(t.tx() + 0.5);
    const auto ty = static_cast<int32_t>(t.ty() + 0.5);

    ui::Transform transformWithoutOffset = t;
    transformWithoutOffset.set(0.f, 0.f);

    const Region transformed = transformWithoutOffset.transform(r);

    // Apply the translation to each of the Rects in the region while discarding any that overflow.
    Region ret;
    for (const auto& rect : transformed) {
        Rect newRect;
        if (__builtin_add_overflow(rect.left, tx, &newRect.left) ||
            __builtin_add_overflow(rect.top, ty, &newRect.top) ||
            __builtin_add_overflow(rect.right, tx, &newRect.right) ||
            __builtin_add_overflow(rect.bottom, ty, &newRect.bottom)) {
            ALOGE("Applying transform to touchable region of window '%s' resulted in an overflow.",
                  debugWindowName.c_str());
            continue;
        }
        ret.orSelf(newRect);
    }
    return ret;
}

void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) {
void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDisplay) {
    Rect tmpBounds = getInputBounds();
    Rect tmpBounds = getInputBounds();
    if (!tmpBounds.isValid()) {
    if (!tmpBounds.isValid()) {
@@ -2256,7 +2287,8 @@ void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDi
    info.transform = inputToDisplay.inverse();
    info.transform = inputToDisplay.inverse();


    // The touchable region is specified in the input coordinate space. Change it to display space.
    // The touchable region is specified in the input coordinate space. Change it to display space.
    info.touchableRegion = inputToDisplay.transform(info.touchableRegion);
    info.touchableRegion =
            transformTouchableRegionSafely(inputToDisplay, info.touchableRegion, mName);
}
}


void Layer::fillTouchOcclusionMode(WindowInfo& info) {
void Layer::fillTouchOcclusionMode(WindowInfo& info) {