Skip to content

Commit 60a935a

Browse files
committed
SR-8560: XMLDocument on Linux crashes with a Segmentation Fault
- When returning an XMLNode, take a reference to the parent XMLDocument to prevent the underlying libxml xml document from being freed. - This prevents the document being freed if a reference is still held to a XMLNode but not the XMLDocument. (cherry picked from commit aabed33)
1 parent 82b955b commit 60a935a

File tree

2 files changed

+25
-0
lines changed

2 files changed

+25
-0
lines changed

Foundation/XMLNode.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ open class XMLNode: NSObject, NSCopying {
9191
}
9292

9393
internal let _xmlNode: _CFXMLNodePtr
94+
internal var _xmlDocument: XMLDocument?
9495

9596
open func copy(with zone: NSZone? = nil) -> Any {
9697
let newNode = _CFXMLCopyNode(_xmlNode, true)
@@ -761,6 +762,8 @@ open class XMLNode: NSObject, NSCopying {
761762
node.detach()
762763
}
763764

765+
_xmlDocument = nil
766+
764767
switch kind {
765768
case .document:
766769
_CFXMLFreeDocument(_CFXMLDocPtr(_xmlNode))
@@ -790,6 +793,11 @@ open class XMLNode: NSObject, NSCopying {
790793
withUnretainedReference {
791794
_CFXMLNodeSetPrivateData(_xmlNode, $0)
792795
}
796+
if let documentPtr = _CFXMLNodeGetDocument(_xmlNode) {
797+
if documentPtr != ptr {
798+
_xmlDocument = XMLDocument._objectNodeForNode(documentPtr)
799+
}
800+
}
793801
}
794802

795803
internal class func _objectNodeForNode(_ node: _CFXMLNodePtr) -> XMLNode {

TestFoundation/TestXMLDocument.swift

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class TestXMLDocument : LoopbackServerTest {
3434
("test_addNamespace", test_addNamespace),
3535
("test_removeNamespace", test_removeNamespace),
3636
("test_optionPreserveAll", test_optionPreserveAll),
37+
("test_rootElementRetainsDocument", test_rootElementRetainsDocument),
3738
]
3839
}
3940

@@ -527,6 +528,22 @@ class TestXMLDocument : LoopbackServerTest {
527528
let expected = xmlString.lowercased() + "\n"
528529
XCTAssertEqual(expected, String(describing: document))
529530
}
531+
532+
func test_rootElementRetainsDocument() {
533+
let str = """
534+
<?xml version="1.0" encoding="UTF-8"?>
535+
<plans></plans>
536+
"""
537+
538+
let data = str.data(using: .utf8)!
539+
540+
func test() throws -> String? {
541+
let doc = try XMLDocument(data: data, options: []).rootElement()
542+
return doc?.name
543+
}
544+
545+
XCTAssertEqual(try? test(), "plans")
546+
}
530547
}
531548

532549
fileprivate extension XMLNode {

0 commit comments

Comments
 (0)