From b8ebcf1c35ad99322c7eea5061f232fb14235560 Mon Sep 17 00:00:00 2001 From: Luboš Luňák Date: Tue, 20 Apr 2021 17:20:31 +0200 Subject: prevent cairo from breaking because of a large matrix (tdf#125949) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some presentation animations temporarily cause extensive zoom matrix and Cairo doesn't take that well. Change-Id: I1eb6d63fc2dcde6553bc8cc7ab967532d085a579 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114344 Tested-by: Jenkins Reviewed-by: Luboš Luňák (cherry picked from commit 3a4bfe3e45be2d5b591ab5cae3694c9492ca9e1b) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114419 Reviewed-by: Caolán McNamara --- canvas/source/cairo/cairo_spritehelper.cxx | 32 ++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'canvas') diff --git a/canvas/source/cairo/cairo_spritehelper.cxx b/canvas/source/cairo/cairo_spritehelper.cxx index 1fe9eb9152e0..b0eb7e9b7e44 100644 --- a/canvas/source/cairo/cairo_spritehelper.cxx +++ b/canvas/source/cairo/cairo_spritehelper.cxx @@ -28,6 +28,7 @@ #include #include +#include #include "cairo_spritehelper.hxx" @@ -140,6 +141,37 @@ namespace cairocanvas cairo_clip( pCairo.get() ); cairo_set_matrix( pCairo.get(), &aOrigMatrix ); + cairo_matrix_t aInverseMatrix = aOrigMatrix; + bool matrixProblem = false; + // tdf#125949: Cairo internally uses the pixman library, and _cairo_matrix_to_pixman_matrix() + // checks all matrix components to fix PIXMAN_MAX_INT, which is about 32k. Which means that + // if our transformation is large, such as an initial step of some zooming animations, + // the conversion will fail. To make things worse, once something in cairo fails, it's treated + // as a fatal error, the error status of that cairo_t gets set, and there's no way to reset it + // besides recreating the whole cairo_t + // (https://lists.cairographics.org/archives/cairo/2006-September/007892.html). + // So copy&paste PIXMAN_MAX_INT here, and if our matrix could fail, bail out. +#define PIXMAN_MAX_INT ((pixman_fixed_1 >> 1) - pixman_fixed_e) /* need to ensure deltas also fit */ + if(cairo_matrix_invert(&aInverseMatrix) == CAIRO_STATUS_SUCCESS) + { + if(abs(aOrigMatrix.xx) > PIXMAN_MAX_INT || abs(aOrigMatrix.xx) > PIXMAN_MAX_INT + || abs(aOrigMatrix.xy) > PIXMAN_MAX_INT || abs(aOrigMatrix.yx) > PIXMAN_MAX_INT + || abs(aOrigMatrix.x0) > PIXMAN_MAX_INT || abs(aOrigMatrix.y0) > PIXMAN_MAX_INT + || abs(aInverseMatrix.xx) > PIXMAN_MAX_INT || abs(aInverseMatrix.xx) > PIXMAN_MAX_INT + || abs(aInverseMatrix.xy) > PIXMAN_MAX_INT || abs(aInverseMatrix.yx) > PIXMAN_MAX_INT + || abs(aInverseMatrix.x0) > PIXMAN_MAX_INT || abs(aInverseMatrix.y0) > PIXMAN_MAX_INT) + matrixProblem = true; +#undef PIXMAN_MAX_INT + } + else + matrixProblem = true; + if(matrixProblem) + { + SAL_WARN( "canvas.cairo", "matrix would overflow PIXMAN_MAX_INT, avoiding drawing" ); + cairo_restore( pCairo.get() ); + return; + } + if( isContentFullyOpaque() ) cairo_set_operator( pCairo.get(), CAIRO_OPERATOR_SOURCE ); cairo_set_source_surface( pCairo.get(), -- cgit