[librsvg] (#398): Detect circular references in gradients



commit 575a809111b78eea8ee59753360ccecb254da786
Author: Federico Mena Quintero <federico gnome org>
Date:   Wed Jan 2 12:05:22 2019 -0600

    (#398): Detect circular references in gradients
    
    Similar to pattern.rs, we need to detect cycles in gradient
    references.
    
    Fixes https://gitlab.gnome.org/GNOME/librsvg/issues/398

 rsvg_internals/src/gradient.rs                     | 69 ++++++++++++----------
 .../infinite-loop/398-recursive-gradient.svg       |  5 ++
 2 files changed, 42 insertions(+), 32 deletions(-)
---
diff --git a/rsvg_internals/src/gradient.rs b/rsvg_internals/src/gradient.rs
index fb27d524..a045a523 100644
--- a/rsvg_internals/src/gradient.rs
+++ b/rsvg_internals/src/gradient.rs
@@ -7,7 +7,7 @@ use attributes::Attribute;
 use bbox::*;
 use coord_units::CoordUnits;
 use defs::Fragment;
-use drawing_ctx::{AcquiredNode, DrawingCtx};
+use drawing_ctx::{AcquiredNode, DrawingCtx, NodeStack};
 use error::*;
 use handle::RsvgHandle;
 use length::*;
@@ -444,18 +444,20 @@ impl Gradient {
     }
 }
 
-fn acquire_gradient<'a>(draw_ctx: &'a mut DrawingCtx, name: &Fragment) -> Option<AcquiredNode> {
-    if let Some(acquired) = draw_ctx.get_acquired_node(name) {
-        let node_type = acquired.get().get_type();
+fn acquire_gradient<'a>(
+    draw_ctx: &'a mut DrawingCtx,
+    name: Option<&Fragment>,
+) -> Option<AcquiredNode> {
+    name.and_then(move |fragment| draw_ctx.get_acquired_node(fragment))
+        .and_then(|acquired| {
+            let node_type = acquired.get().get_type();
 
-        if node_type == NodeType::LinearGradient || node_type == NodeType::RadialGradient {
-            return Some(acquired);
-        }
-    }
-
-    rsvg_log!("element \"{}\" does not exist or is not a gradient", name);
-
-    None
+            if node_type == NodeType::LinearGradient || node_type == NodeType::RadialGradient {
+                Some(acquired)
+            } else {
+                None
+            }
+        })
 }
 
 fn set_common_on_pattern<P: cairo::PatternTrait + cairo::Gradient>(
@@ -538,30 +540,33 @@ impl PaintSource for NodeGradient {
         draw_ctx: &mut DrawingCtx,
         bbox: &BoundingBox,
     ) -> Result<Option<Self::Source>, RenderingError> {
-        let gradient =
-            node.with_impl(|i: &NodeGradient| i.get_gradient_with_color_stops_from_node(node));
+        let node_gradient = node.get_impl::<NodeGradient>().unwrap();
+        let gradient = node_gradient.get_gradient_with_color_stops_from_node(node);
         let mut result = gradient.clone();
+        let mut stack = NodeStack::new();
 
         while !result.is_resolved() {
-            result
-                .common
-                .fallback
-                .as_ref()
-                .and_then(|fallback_name| acquire_gradient(draw_ctx, fallback_name))
-                .and_then(|acquired| {
-                    let fallback_node = acquired.get();
-
-                    fallback_node.with_impl(|i: &NodeGradient| {
-                        let fallback_grad =
-                            i.get_gradient_with_color_stops_from_node(&fallback_node);
-                        result.resolve_from_fallback(&fallback_grad)
-                    });
-                    Some(())
-                })
-                .or_else(|| {
-                    result.resolve_from_defaults();
-                    Some(())
+            if let Some(acquired) = acquire_gradient(draw_ctx, result.common.fallback.as_ref()) {
+                let a_node = acquired.get();
+
+                if stack.contains(a_node) {
+                    rsvg_log!(
+                        "circular reference in gradient {}",
+                        node.get_human_readable_name()
+                    );
+                    return Err(RenderingError::CircularReference);
+                }
+
+                a_node.with_impl(|i: &NodeGradient| {
+                    let fallback_grad = i.get_gradient_with_color_stops_from_node(&a_node);
+                    result.resolve_from_fallback(&fallback_grad)
                 });
+
+                stack.push(a_node);
+                continue;
+            }
+
+            result.resolve_from_defaults();
         }
 
         if result.bounds_are_valid(bbox) {
diff --git a/tests/fixtures/infinite-loop/398-recursive-gradient.svg 
b/tests/fixtures/infinite-loop/398-recursive-gradient.svg
new file mode 100644
index 00000000..dbe2e33d
--- /dev/null
+++ b/tests/fixtures/infinite-loop/398-recursive-gradient.svg
@@ -0,0 +1,5 @@
+<svg>
+  <linearGradient id="g1" xlink:href="#g1"/>
+  <linearGradient id="g2" xlink:href="#g1"/>
+  <rect fill="url(#g2)" width="100" height="100"/>
+</svg>


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]