diff options
author | Luboš Luňák <l.lunak@collabora.com> | 2021-04-20 17:20:31 +0200 |
---|---|---|
committer | Luboš Luňák <l.lunak@collabora.com> | 2021-04-21 18:29:49 +0200 |
commit | 3a4bfe3e45be2d5b591ab5cae3694c9492ca9e1b (patch) | |
tree | fcffc7ab48fb535e42d6de2885296fe31f417880 /canvas | |
parent | fc9919bcfcf32f2ecefa4fce18a49545b93d3499 (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>
Diffstat (limited to 'canvas')
-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 929ecdf3b384..264c720e995e 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" @@ -139,6 +140,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(), |