Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion src/FSharp.Data.Xml.Core/XsdInference.fs
Original file line number Diff line number Diff line change
Expand Up @@ -104,9 +104,12 @@ module XsdParsing =
let elements =
System.Collections.Generic.Dictionary<XmlSchemaElement, XsdModel.XsdElement>()

let visitingGroups = System.Collections.Generic.HashSet<XmlQualifiedName>()

member x.GetElement name = getElm name
member x.GetSubstitutions elm = getSubst elm
member x.Elements = elements
member x.VisitingGroups = visitingGroups


open XsdModel
Expand Down Expand Up @@ -183,7 +186,17 @@ module XsdParsing =
match par with
| :? XmlSchemaAny -> Any occurs
| :? XmlSchemaGroupBase as grp -> parseParticles grp
| :? XmlSchemaGroupRef as grpRef -> parseParticle ctx grpRef.Particle
| :? XmlSchemaGroupRef as grpRef ->
// Guard against circular group references (e.g. group A β†’ group B β†’ group A).
// XSD allows groups to reference each other transitively; without a cycle guard
// this causes unbounded recursion and a StackOverflowException.
if ctx.VisitingGroups.Contains grpRef.RefName then
Empty // break the cycle
else
ctx.VisitingGroups.Add grpRef.RefName |> ignore
let result = parseParticle ctx grpRef.Particle
ctx.VisitingGroups.Remove grpRef.RefName |> ignore
result
| :? XmlSchemaElement as elm ->
let e =
if elm.RefName.IsEmpty then
Expand Down
32 changes: 32 additions & 0 deletions tests/FSharp.Data.DesignTime.Tests/InferenceTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -934,3 +934,35 @@ let ``abstract elements can be recursive``() =
getInferedTypes formulaXsd [| sample1; sample2 |]
|> ignore

[<Test>]
let ``circular group references do not cause a stack overflow``() =

// Schema with a group that (indirectly) creates a recursive content model:
// RecursiveGroup contains element "children" whose anonymous type references RecursiveGroup.
// Without a GroupRef cycle guard, this can cause unbounded recursion in parseParticle
// on .NET runtimes where the compiled XmlSchema object graph contains shared group particle references.
let xsd =
"""
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
elementFormDefault="qualified">
<xs:group name="RecursiveGroup">
<xs:sequence>
<xs:element name="value" type="xs:string" minOccurs="0"/>
<xs:element name="children" minOccurs="0">
<xs:complexType>
<xs:group ref="RecursiveGroup" minOccurs="0" maxOccurs="unbounded"/>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:group>
<xs:element name="Root">
<xs:complexType>
<xs:group ref="RecursiveGroup"/>
</xs:complexType>
</xs:element>
</xs:schema>
"""

// Must complete without StackOverflowException
getInferedTypeFromSchema xsd |> ignore

Loading