Bug 2721: StackOverflowError for leafref FIX 98/22698/2
authorMartin Ciglan <mciglan@cisco.com>
Tue, 16 Jun 2015 14:12:51 +0000 (16:12 +0200)
committerGerrit Code Review <gerrit@opendaylight.org>
Wed, 17 Jun 2015 12:44:23 +0000 (12:44 +0000)
Leafref XPath needs to be checked whether it references itself
in order to detect incoming StackOverflowError
and throw appropriate exception if necessary.

Change-Id: Iacfa1a9f9d85f947e590b1d6412c9c482b417dbf
Signed-off-by: Martin Ciglan <mciglan@cisco.com>
code-generator/binding-type-provider/src/main/java/org/opendaylight/yangtools/sal/binding/yang/types/TypeProviderImpl.java
code-generator/binding-type-provider/src/test/java/org/opendaylight/yangtools/sal/binding/yang/types/TypeProviderImplTest.java
code-generator/binding-type-provider/src/test/resources/leafref/leafref-absolute-invalid.yang [new file with mode: 0644]
code-generator/binding-type-provider/src/test/resources/leafref/leafref-relative-invalid.yang [new file with mode: 0644]
code-generator/binding-type-provider/src/test/resources/leafref/leafref-valid.yang [new file with mode: 0644]

index 274ba77e0713f2b393730984ded27a8c03813350..501b6c324ad0fe55ac31f9a8d4b45b6f60e3697d 100644 (file)
@@ -11,6 +11,7 @@ import static org.opendaylight.yangtools.binding.generator.util.BindingGenerator
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNode;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findDataSchemaNodeForRelativeXPath;
 import static org.opendaylight.yangtools.yang.model.util.SchemaContextUtil.findParentModule;
+
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Sets;
 import com.google.common.io.BaseEncoding;
@@ -53,6 +54,8 @@ import org.opendaylight.yangtools.sal.binding.model.api.type.builder.GeneratedTy
 import org.opendaylight.yangtools.sal.binding.model.api.type.builder.MethodSignatureBuilder;
 import org.opendaylight.yangtools.yang.binding.BindingMapping;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataNodeContainer;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafListSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
@@ -82,12 +85,15 @@ import org.opendaylight.yangtools.yang.model.util.Int16;
 import org.opendaylight.yangtools.yang.model.util.Int32;
 import org.opendaylight.yangtools.yang.model.util.Int64;
 import org.opendaylight.yangtools.yang.model.util.Int8;
+import org.opendaylight.yangtools.yang.model.util.RevisionAwareXPathImpl;
+import org.opendaylight.yangtools.yang.model.util.SchemaContextUtil;
 import org.opendaylight.yangtools.yang.model.util.StringType;
 import org.opendaylight.yangtools.yang.model.util.Uint16;
 import org.opendaylight.yangtools.yang.model.util.Uint32;
 import org.opendaylight.yangtools.yang.model.util.Uint64;
 import org.opendaylight.yangtools.yang.model.util.Uint8;
 import org.opendaylight.yangtools.yang.model.util.UnionType;
+import org.opendaylight.yangtools.yang.parser.util.YangValidationException;
 
 public final class TypeProviderImpl implements TypeProvider {
     private static final Pattern NUMBERS_PATTERN = Pattern.compile("[0-9]+\\z");
@@ -236,6 +242,49 @@ public final class TypeProviderImpl implements TypeProvider {
         return gtob.toInstance();
     }
 
+    private boolean isLeafRefSelfReference(final LeafrefTypeDefinition leafref, final SchemaNode parentNode) {
+        final SchemaNode leafRefValueNode;
+        final RevisionAwareXPath leafRefXPath = leafref.getPathStatement();
+        final RevisionAwareXPath leafRefStrippedXPath = new RevisionAwareXPathImpl(leafRefXPath.toString()
+                .replaceAll("\\[(.*?)\\]", ""), leafRefXPath.isAbsolute());
+
+        ///// skip leafrefs in augments - they're checked once augments are resolved
+        final Iterator<QName> iterator = parentNode.getPath().getPathFromRoot().iterator();
+        boolean isAugmenting = false;
+        DataNodeContainer current = null;
+        DataSchemaNode dataChildByName;
+
+        while (iterator.hasNext() && !isAugmenting) {
+            final QName next = iterator.next();
+            if (current == null) {
+                dataChildByName = schemaContext.getDataChildByName(next);
+            } else {
+                dataChildByName = current.getDataChildByName(next);
+            }
+            if (dataChildByName != null) {
+                isAugmenting = dataChildByName.isAugmenting();
+            } else {
+                return false;
+            }
+            if (dataChildByName instanceof DataNodeContainer) {
+                current = (DataNodeContainer) dataChildByName;
+            }
+        }
+        if (isAugmenting) {
+            return false;
+        }
+        /////
+
+        Module parentModule = getParentModule(parentNode);
+        if (!leafRefStrippedXPath.isAbsolute()) {
+            leafRefValueNode = SchemaContextUtil.findDataSchemaNodeForRelativeXPath(schemaContext, parentModule,
+                    parentNode, leafRefStrippedXPath);
+        } else {
+            leafRefValueNode = SchemaContextUtil.findDataSchemaNode(schemaContext, parentModule, leafRefStrippedXPath);
+        }
+        return (leafRefValueNode != null) ? leafRefValueNode.equals(parentNode) : false;
+    }
+
     /**
      * Returns JAVA <code>Type</code> for instances of the type
      * <code>LeafrefTypeDefinition</code> or
@@ -248,6 +297,10 @@ public final class TypeProviderImpl implements TypeProvider {
     private Type javaTypeForLeafrefOrIdentityRef(final TypeDefinition<?> typeDefinition, final SchemaNode parentNode) {
         if (typeDefinition instanceof LeafrefTypeDefinition) {
             final LeafrefTypeDefinition leafref = (LeafrefTypeDefinition) typeDefinition;
+            if (isLeafRefSelfReference(leafref, parentNode)) {
+                throw new YangValidationException("Leafref " + leafref.toString() + " is referencing itself, incoming" +
+                        " StackOverFlowError detected.");
+            }
             return provideTypeForLeafref(leafref, parentNode);
         } else if (typeDefinition instanceof IdentityrefTypeDefinition) {
             final IdentityrefTypeDefinition idref = (IdentityrefTypeDefinition) typeDefinition;
index f1ddfbcabe1639d372b9e63550901ce6532ec233..1742adcceea3d61801974eeffd8426d5d775cc64 100644 (file)
@@ -14,6 +14,7 @@ import static org.junit.Assert.assertTrue;
 
 import com.google.common.base.Optional;
 import java.io.File;
+import java.net.URI;
 import java.net.URISyntaxException;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -22,11 +23,16 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 import org.opendaylight.yangtools.binding.generator.util.generated.type.builder.GeneratedTypeBuilderImpl;
+import org.opendaylight.yangtools.sal.binding.model.api.Type;
 import org.opendaylight.yangtools.yang.common.QName;
+import org.opendaylight.yangtools.yang.model.api.DataSchemaNode;
 import org.opendaylight.yangtools.yang.model.api.IdentitySchemaNode;
 import org.opendaylight.yangtools.yang.model.api.LeafSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.ListSchemaNode;
+import org.opendaylight.yangtools.yang.model.api.Module;
 import org.opendaylight.yangtools.yang.model.api.SchemaContext;
 import org.opendaylight.yangtools.yang.model.api.SchemaPath;
+import org.opendaylight.yangtools.yang.model.api.TypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition;
 import org.opendaylight.yangtools.yang.model.api.type.EnumTypeDefinition.EnumPair;
 import org.opendaylight.yangtools.yang.model.util.BinaryType;
@@ -40,12 +46,67 @@ import org.opendaylight.yangtools.yang.parser.builder.impl.IdentitySchemaNodeBui
 import org.opendaylight.yangtools.yang.parser.builder.impl.LeafSchemaNodeBuilder;
 import org.opendaylight.yangtools.yang.parser.builder.impl.ModuleBuilder;
 import org.opendaylight.yangtools.yang.parser.impl.YangParserImpl;
+import org.opendaylight.yangtools.yang.parser.util.YangValidationException;
 
 public class TypeProviderImplTest {
 
     @Rule
     public ExpectedException expException = ExpectedException.none();
 
+    @Test (expected = YangValidationException.class)
+    public void testLeafRefRelativeSelfReference() throws Exception {
+        File relative = new File(getClass().getResource("/leafref/leafref-relative-invalid.yang").toURI());
+
+        final YangParserImpl yangParser = new YangParserImpl();
+        final SchemaContext schemaContext = yangParser.parseFiles(Arrays.asList(relative));
+        final Module moduleRelative = schemaContext.findModuleByNamespace(new URI("urn:xml:ns:yang:lrr")).iterator().next();
+        final TypeProviderImpl typeProvider = new TypeProviderImpl(schemaContext);
+
+        DataSchemaNode leafref = ((ListSchemaNode) moduleRelative.getDataChildByName("neighbor")).getDataChildByName("neighbor-id");
+        LeafSchemaNode leaf = (LeafSchemaNode) leafref;
+        TypeDefinition<?> leafType = leaf.getType();
+        Type leafrefResolvedType = typeProvider.javaTypeForSchemaDefinitionType(leafType, leaf);
+    }
+
+    @Test (expected = YangValidationException.class)
+    public void testLeafRefAbsoluteSelfReference() throws Exception {
+        File relative = new File(getClass().getResource("/leafref/leafref-absolute-invalid.yang").toURI());
+
+        final YangParserImpl yangParser = new YangParserImpl();
+        final SchemaContext schemaContext = yangParser.parseFiles(Arrays.asList(relative));
+        final Module moduleRelative = schemaContext.findModuleByNamespace(new URI("urn:xml:ns:yang:lra")).iterator().next();
+        final TypeProviderImpl typeProvider = new TypeProviderImpl(schemaContext);
+
+        DataSchemaNode leafref = ((ListSchemaNode) moduleRelative.getDataChildByName("neighbor")).getDataChildByName("neighbor-id");
+        LeafSchemaNode leaf = (LeafSchemaNode) leafref;
+        TypeDefinition<?> leafType = leaf.getType();
+        Type leafrefResolvedType = typeProvider.javaTypeForSchemaDefinitionType(leafType, leaf);
+    }
+
+    @Test
+    public void testLeafRefRelativeAndAbsoluteValidReference() throws URISyntaxException {
+        File valid = new File(getClass().getResource("/leafref/leafref-valid.yang").toURI());
+
+        final YangParserImpl yangParser = new YangParserImpl();
+        final SchemaContext schemaContext = yangParser.parseFiles(Arrays.asList(valid));
+        final Module moduleValid = schemaContext.findModuleByNamespace(new URI("urn:xml:ns:yang:lrv")).iterator().next();
+        final TypeProviderImpl typeProvider = new TypeProviderImpl(schemaContext);
+
+        DataSchemaNode leafrefRel = ((ListSchemaNode) moduleValid.getDataChildByName("neighbor")).getDataChildByName
+                ("neighbor-id");
+        LeafSchemaNode leafRel = (LeafSchemaNode) leafrefRel;
+        TypeDefinition<?> leafTypeRel = leafRel.getType();
+        Type leafrefRelResolvedType = typeProvider.javaTypeForSchemaDefinitionType(leafTypeRel, leafRel);
+        assertNotNull(leafrefRelResolvedType);
+
+        DataSchemaNode leafrefAbs = ((ListSchemaNode) moduleValid.getDataChildByName("neighbor")).getDataChildByName
+                ("neighbor2-id");
+        LeafSchemaNode leafAbs = (LeafSchemaNode) leafrefAbs;
+        TypeDefinition<?> leafTypeAbs = leafAbs.getType();
+        Type leafrefAbsResolvedType = typeProvider.javaTypeForSchemaDefinitionType(leafTypeAbs, leafAbs);
+        assertNotNull(leafrefAbsResolvedType);
+    }
+
     @Test
     public void testMethodsOfTypeProviderImpl() throws URISyntaxException {
         final YangParserImpl yangParser = new YangParserImpl();
diff --git a/code-generator/binding-type-provider/src/test/resources/leafref/leafref-absolute-invalid.yang b/code-generator/binding-type-provider/src/test/resources/leafref/leafref-absolute-invalid.yang
new file mode 100644 (file)
index 0000000..97d1503
--- /dev/null
@@ -0,0 +1,16 @@
+module leafref-absolute-invalid {
+    namespace "urn:xml:ns:yang:lra";
+    prefix lra;
+    revision "2015-02-25";
+
+    list neighbor {
+        description
+           "List of neighbors.";
+        leaf neighbor-id {
+            type leafref {
+                path "/lra:neighbor/lra:neighbor-id";
+            }
+            description "Neighbor.";
+        }
+    }
+}
\ No newline at end of file
diff --git a/code-generator/binding-type-provider/src/test/resources/leafref/leafref-relative-invalid.yang b/code-generator/binding-type-provider/src/test/resources/leafref/leafref-relative-invalid.yang
new file mode 100644 (file)
index 0000000..a933ae4
--- /dev/null
@@ -0,0 +1,16 @@
+module leafref-relative-invalid {
+    namespace "urn:xml:ns:yang:lrr";
+    prefix lrr;
+    revision "2015-02-25";
+
+    list neighbor {
+         description
+           "List of neighbors.";
+         leaf neighbor-id {
+             type leafref {
+                 path "../../neighbor/neighbor-id";
+             }
+             description "Neighbor.";
+         }
+    }
+}
\ No newline at end of file
diff --git a/code-generator/binding-type-provider/src/test/resources/leafref/leafref-valid.yang b/code-generator/binding-type-provider/src/test/resources/leafref/leafref-valid.yang
new file mode 100644 (file)
index 0000000..fdfa25c
--- /dev/null
@@ -0,0 +1,26 @@
+module leafref-valid {
+     namespace "urn:xml:ns:yang:lrv";
+     prefix lrv;
+     revision "2015-02-25";
+
+    list neighbor {
+        description
+           "List of neighbors.";
+        leaf neighbor-id {
+            type leafref {
+                path "../../neighbor/mystring";
+            }
+            description "Neighbor.";
+        }
+
+        leaf neighbor2-id {
+            type leafref {
+                path "/lrv:neighbor/lrv:mystring";
+            }
+        }
+
+        leaf mystring {
+            type string;
+        }
+    }
+}
\ No newline at end of file