bgo#738169 - Avoid cycles while resolving paint server fallbacks

If a chain of paint servers, defined through the xlink:href attribute, has a cycle,
then we would loop infinitely while resolving the base paint server.  We now
use a tortoise-and-hare to detect cycles and stop appropriately.

Fixes https://bugzilla.gnome.org/show_bug.cgi?id=738169Signed-off-by: Federico Mena Quintero's avatarFederico Mena Quintero <federico@gnome.org>
parent 40af93e6
......@@ -489,14 +489,97 @@ hasstop (GPtrArray * lookin)
return 0;
}
void
rsvg_linear_gradient_fix_fallback (RsvgLinearGradient * grad)
typedef gpointer (* GetFallbackFn) (gpointer data);
typedef void (* ApplyFallbackFn) (gpointer data, gpointer fallback_data);
/* Some SVG paint servers can reference a "parent" or "fallback" paint server
* through the xlink:href attribute (for example,
* http://www.w3.org/TR/SVG11/pservers.html#LinearGradientElementHrefAttribute )
* This is used to define a chain of properties to be resolved from each
* fallback. However, a malicious SVG may have a cycle in the chain of
* fallbacks, which could lead to an infinite loop when resolving the chain.
*
* This is a generic function to apply a chain of fallbacks without cycling; it
* uses a tortoise-and-hare to detect cycles. The hare moves twice as fast as
* the tortoise; if they catch up, then there is a cycle and we stop.
*
* The parameters are:
*
* @data: the paint server to resolve
* @get_fallback: a function which, given a paint server, will return its fallback (or NULL)
* @apply_fallback: a function which, given a paint server and a fallback one, will apply
* the fallback to the paint server as appropriate
*
* We use plain gpointers because this is called from different places with different
* structure types.
*/
static void
resolve_fallbacks (gpointer data,
GetFallbackFn get_fallback,
ApplyFallbackFn apply_fallback)
{
RsvgNode *ufallback;
ufallback = grad->fallback;
while (ufallback != NULL) {
if (RSVG_NODE_TYPE (ufallback) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
RsvgLinearGradient *fallback = (RsvgLinearGradient *) ufallback;
gpointer tortoise, hare;
g_assert (data != NULL);
tortoise = data;
hare = data;
while (hare) {
gpointer fallback;
fallback = get_fallback (hare);
if (fallback)
apply_fallback (data, fallback);
hare = fallback;
if (hare) {
fallback = get_fallback (hare);
if (fallback)
apply_fallback (data, fallback);
hare = fallback;
}
tortoise = get_fallback (tortoise);
if (tortoise == hare)
break;
}
}
static gpointer
gradient_get_fallback (gpointer data)
{
RsvgNode *node;
node = data;
if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
RsvgLinearGradient *g = (RsvgLinearGradient *) node;
return g->fallback;
} else if (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
RsvgRadialGradient *g = (RsvgRadialGradient *) node;
return g->fallback;
} else
return NULL;
}
static void
linear_gradient_apply_fallback (gpointer data, gpointer fallback_data)
{
RsvgNode *node;
RsvgLinearGradient *grad;
RsvgNode *fallback_node;
node = data;
g_assert (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_LINEAR_GRADIENT);
grad = (RsvgLinearGradient *) node;
fallback_node = fallback_data;
if (RSVG_NODE_TYPE (fallback_node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
RsvgLinearGradient *fallback = (RsvgLinearGradient *) fallback_node;
if (!grad->hasx1 && fallback->hasx1) {
grad->hasx1 = TRUE;
grad->x1 = fallback->x1;
......@@ -528,9 +611,9 @@ rsvg_linear_gradient_fix_fallback (RsvgLinearGradient * grad)
if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
grad->super.children = fallback->super.children;
}
ufallback = fallback->fallback;
} else if (RSVG_NODE_TYPE (ufallback) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
RsvgRadialGradient *fallback = (RsvgRadialGradient *) ufallback;
} else if (RSVG_NODE_TYPE (fallback_node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
RsvgRadialGradient *fallback = (RsvgRadialGradient *) fallback_node;
if (!grad->hastransform && fallback->hastransform) {
grad->hastransform = TRUE;
grad->affine = fallback->affine;
......@@ -546,19 +629,33 @@ rsvg_linear_gradient_fix_fallback (RsvgLinearGradient * grad)
if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
grad->super.children = fallback->super.children;
}
ufallback = fallback->fallback;
}
}
}
void
rsvg_radial_gradient_fix_fallback (RsvgRadialGradient * grad)
rsvg_linear_gradient_fix_fallback (RsvgLinearGradient * grad)
{
resolve_fallbacks (grad,
gradient_get_fallback,
linear_gradient_apply_fallback);
}
static void
radial_gradient_apply_fallback (gpointer data, gpointer fallback_data)
{
RsvgNode *ufallback;
ufallback = grad->fallback;
while (ufallback != NULL) {
if (RSVG_NODE_TYPE (ufallback) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
RsvgRadialGradient *fallback = (RsvgRadialGradient *) ufallback;
RsvgNode *node;
RsvgRadialGradient *grad;
RsvgNode *fallback_node;
node = data;
g_assert (RSVG_NODE_TYPE (node) == RSVG_NODE_TYPE_RADIAL_GRADIENT);
grad = (RsvgRadialGradient *) node;
fallback_node = fallback_data;
if (RSVG_NODE_TYPE (fallback_node) == RSVG_NODE_TYPE_RADIAL_GRADIENT) {
RsvgRadialGradient *fallback = (RsvgRadialGradient *) fallback_node;
if (!grad->hascx && fallback->hascx) {
grad->hascx = TRUE;
grad->cx = fallback->cx;
......@@ -594,9 +691,9 @@ rsvg_radial_gradient_fix_fallback (RsvgRadialGradient * grad)
if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
grad->super.children = fallback->super.children;
}
ufallback = fallback->fallback;
} else if (RSVG_NODE_TYPE (ufallback) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
RsvgLinearGradient *fallback = (RsvgLinearGradient *) ufallback;
} else if (RSVG_NODE_TYPE (fallback_node) == RSVG_NODE_TYPE_LINEAR_GRADIENT) {
RsvgLinearGradient *fallback = (RsvgLinearGradient *) fallback_node;
if (!grad->hastransform && fallback->hastransform) {
grad->hastransform = TRUE;
grad->affine = fallback->affine;
......@@ -612,17 +709,34 @@ rsvg_radial_gradient_fix_fallback (RsvgRadialGradient * grad)
if (!hasstop (grad->super.children) && hasstop (fallback->super.children)) {
grad->super.children = fallback->super.children;
}
ufallback = fallback->fallback;
}
}
}
void
rsvg_pattern_fix_fallback (RsvgPattern * pattern)
rsvg_radial_gradient_fix_fallback (RsvgRadialGradient * grad)
{
resolve_fallbacks (grad,
gradient_get_fallback,
radial_gradient_apply_fallback);
}
static gpointer
pattern_get_fallback (gpointer data)
{
RsvgPattern *pattern = data;
return pattern->fallback;
}
static void
pattern_apply_fallback (gpointer data, gpointer fallback_data)
{
RsvgPattern *pattern;
RsvgPattern *fallback;
for (fallback = pattern->fallback; fallback != NULL; fallback = fallback->fallback) {
pattern = data;
fallback = fallback_data;
if (!pattern->hasx && fallback->hasx) {
pattern->hasx = TRUE;
pattern->x = fallback->x;
......@@ -661,5 +775,12 @@ rsvg_pattern_fix_fallback (RsvgPattern * pattern)
if (!pattern->super.children->len && fallback->super.children->len) {
pattern->super.children = fallback->super.children;
}
}
}
void
rsvg_pattern_fix_fallback (RsvgPattern * pattern)
{
resolve_fallbacks (pattern,
pattern_get_fallback,
pattern_apply_fallback);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment