Skip to content

Commit

Permalink
Fix major memory safety issue where MyHTML support class was destroye…
Browse files Browse the repository at this point in the history
…d too quickly
  • Loading branch information
PopFlamingo committed Jan 20, 2019
1 parent dd9a25c commit 607f2c1
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 4 deletions.
6 changes: 5 additions & 1 deletion Sources/MyHTML/MyHTML.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,12 @@ public class MyHTML {

}

init(raw: OpaquePointer) {
self.raw = raw
}

deinit {
myhtml_destroy(raw)
assert(myhtml_destroy(raw) == nil, "Unsuccessful destroy")
}


Expand Down
14 changes: 12 additions & 2 deletions Sources/MyHTML/Tree.swift
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import CMyHTML

public class Tree {
var raw: OpaquePointer

public init(context: MyHTML, html: String) throws {
guard let raw = myhtml_tree_create() else {
throw Error.cannotCreateBaseStructure
}
self.raw = raw
self.context = context
let status = myhtml_tree_init(raw, context.raw)
guard status == MyHTML_STATUS_OK.rawValue else {
throw Error.statusError(rawValue: status)
Expand All @@ -22,9 +23,18 @@ public class Tree {
}

deinit {
myhtml_tree_destroy(raw)
assert(myhtml_tree_destroy(raw) == nil, "Unsuccessful destroy")
}


var raw: OpaquePointer

/// The context is a private variable that keeps MyHTML ref count > 0
/// as long as it is needed, this way, no early call to its C free
/// functions is done. Test `testRightTimeContextDeinit` attempts
/// to catch cases where this wouldn't be done correctly;
private var context: MyHTML

public var htmlNode: Node? {
guard let rawNode = myhtml_tree_get_node_html(raw) else { return nil }
return Node(raw: rawNode)
Expand Down
19 changes: 18 additions & 1 deletion Tests/MyHTMLTests/MyHTMLTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,22 @@ final class MyHTMLTests: XCTestCase {
XCTAssertEqual(tree.getChildNodes(named: "img")[0].attributes[1].value,
nil)
}


func testRightTimeContextDeinit() throws {
func create() throws -> (Tree, OpaquePointer) {
let fooHtml = try MyHTML(options: .single, threadCount: 1)
return try (Tree(context: fooHtml, html: sampleCodeA), fooHtml.raw)
}

let (tree, htmlPointer) = try create()
let htmlPointerThroughC: OpaquePointer? = myhtml_tree_get_myhtml(tree.raw)
XCTAssertNotNil(htmlPointerThroughC)
XCTAssertEqual(htmlPointer, htmlPointerThroughC)
let rawTree = myhtml_tree_create()
myhtml_tree_init(rawTree, htmlPointerThroughC) // Should fail if early free
myhtml_tree_destroy(rawTree)
}

static var allTests = [
("testAttributeContainsValue", testAttributeContainsValue),
Expand All @@ -110,7 +126,8 @@ final class MyHTMLTests: XCTestCase {
("testGetNodeTextContent", testGetNodeTextContent),
("testNodeIdentity", testNodeIdentity),
("testAttributesCount", testAttributesCount),
("testAttributesValues", testAttributesValues)
("testAttributesValues", testAttributesValues),
("testRightTimeContextDeinit", testRightTimeContextDeinit)
]

let sampleCodeA = """
Expand Down

0 comments on commit 607f2c1

Please sign in to comment.