diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2021-04-20 17:20:31 +0200 |
---|---|---|
committer | Caolán McNamara <caolanm@redhat.com> | 2021-04-21 20:51:15 +0200 |
commit | 064f3641240b380a12a88e2bd1ef8f88add2ed23 (patch) | |
tree | e7f2025bc37d36b48d4c449e6990cecf5ea3439b | |
parent | 1401037556607ef0b7977f48dc203c328e757528 (diff) |
prevent cairo from breaking because of a large matrix (tdf#125949)
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 <l.lunak@collabora.com>
(cherry picked from commit 3a4bfe3e45be2d5b591ab5cae3694c9492ca9e1b)
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114419
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
-rw-r--r-- | canvas/source/cairo/cairo_spritehelper.cxx | 32 |
1 files changed, 32 insertions, 0 deletions
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 <tools/diagnose_ex.h> #include <cairo.h> +#include <pixman.h> #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(), |