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

Commit 33da9468 authored by Prabir Pradhan's avatar Prabir Pradhan
Browse files

Layer: Guard against Region offset overflows

When applying a transform to a Region, if the Region is translated to
bounds that overlow the int32_t type, the process crashes.

For now, we guard against process crashes by manually applying the
offset to the Region and checking for overflows. If we detect an
overflow in one of the Rects in a Region, we remove the Rect from the
resulting Region.

Bug: 234247210
Test: atest libgui_test
Change-Id: Icd47539accae2e59a7dbd9c9621201bd040fc402
parent 8f4e41d6
Loading
Loading
Loading
Loading
+49 −0
Original line number Diff line number Diff line
@@ -566,6 +566,55 @@ TEST_F(InputSurfacesTest, input_respects_scaled_surface_insets_overflow) {
    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.
TEST_F(InputSurfacesTest, input_ignores_transparent_region) {
    std::unique_ptr<InputSurface> surface = makeSurface(100, 100);
+33 −1
Original line number Diff line number Diff line
@@ -2194,6 +2194,37 @@ Rect Layer::getInputBounds() const {
    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) {
    Rect tmpBounds = getInputBounds();
    if (!tmpBounds.isValid()) {
@@ -2256,7 +2287,8 @@ void Layer::fillInputFrameInfo(WindowInfo& info, const ui::Transform& screenToDi
    info.transform = inputToDisplay.inverse();

    // 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) {