iabudiab / HTMLKit

An Objective-C framework for your everyday HTML needs.
MIT License
239 stars 27 forks source link

Circular references between parent and child cause infinite recursion #33

Closed dsanghan closed 5 years ago

dsanghan commented 6 years ago

Example Stacktrace:

Crashed: com.app.imap.download
0    CoreFoundation                 0x19d7b39f4 __CFStringChangeSizeMultiple + 244
1    CoreFoundation                 0x19d7ae61c __CFStringAppendBytes + 620
2    CoreFoundation                 0x19d79fa78 __CFStringAppendFormatCore + 12648
3    CoreFoundation                 0x19d7b2224 _CFStringAppendFormatAndArgumentsAux2 + 48
4    CoreFoundation                 0x19d6dbcf8 -[__NSCFString appendFormat:] + 100
5    App                            0x1029faec4 -[HTMLElement outerHTML] (HTMLElement.m:158)
6    Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
7    Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
8    App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
9    App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
10   Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
...
2548 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2549 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2550 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2551 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2552 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2553 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2554 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2555 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2556 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2557 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2558 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2559 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2560 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2561 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2562 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2563 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2564 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2565 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2566 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2567 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2568 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2569 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2570 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2571 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2572 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2573 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2574 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2575 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2576 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2577 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2578 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2579 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2580 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2581 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2582 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2583 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2584 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2585 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2586 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2587 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2588 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2589 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2590 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2591 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2592 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2593 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2594 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2595 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2596 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2597 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2598 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2599 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2600 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2601 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2602 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2603 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2604 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2605 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2606 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2607 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2608 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2609 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2610 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2611 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2612 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2613 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2614 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2615 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2616 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2617 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2618 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2619 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2620 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2621 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2622 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2623 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2624 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2625 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2626 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2627 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2628 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2629 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2630 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2631 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2632 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2633 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2634 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2635 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2636 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2637 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2638 Foundation                     0x19e16d7d0 -[NSObject(NSKeyValueCoding) valueForKey:] + 268
2639 Foundation                     0x19e1c9940 -[NSArray(NSKeyValueCoding) valueForKey:] + 392
2640 App                            0x102a00848 -[HTMLNode innerHTML] (HTMLNode.m:727)
2641 App                            0x1029fb16c -[HTMLElement outerHTML] (HTMLElement.m:182)
2642 App                            0x1026c74d8 -[MRLinkParser parseUnsubscribeWithDocument:] (MRLinkParser.m:123)

Maybe put in a check for that? Not sure how to reproduce but seeing this out in the wild.

iabudiab commented 6 years ago

@dsanghan Hey there, could you provide a HTML snippet/document, that causes this?

dsanghan commented 5 years ago

@iabudiab Here's an example:

zoho.zip

dsanghan commented 5 years ago

@iabudiab: The attached html file will work on the main thread, but async thread's have a recursion depth of 1000 and the attached html has 2066 nodes by depth - which is what causes the crash.

dsanghan commented 5 years ago

@iabudiab: To repro:

dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
        NSString *path = [[NSBundle mainBundle] pathForResource:@"example" ofType:@"txt"];
        NSString *html = [[NSString alloc] initWithContentsOfFile:path encoding:0 error:nil];
        HTMLDocument *document = [HTMLDocument documentWithString:html];

        HTMLNode *node = document.body.childNodes[1];
        NSMutableArray *stack = [[NSMutableArray alloc] init];
        while (node) {
            if ([node childNodesCount] > 2) {
                node = [node childNodeAtIndex:2];
                [stack safeAddObject:node];
            } else {
                node = nil;
            }
        }

        NSLog(@"Stack depth: %lu", stack.count);
        html = document.rootElement.outerHTML;
    });
dsanghan commented 5 years ago

@iabudiab Temporary workaround:

diff --git a/Canary/Pods/HTMLKit/Sources/HTMLNode.m b/Canary/Pods/HTMLKit/Sources/HTMLNode.m
index d948ff9fc..c90d2bc76 100644
--- a/Canary/Pods/HTMLKit/Sources/HTMLNode.m
+++ b/Canary/Pods/HTMLKit/Sources/HTMLNode.m
@@ -139,6 +139,10 @@ - (NSUInteger)index
        return [_parentNode indexOfChildNode:self];
 }

+- (NSUInteger)level {
+    return _parentNode.level + 1;
+}
+
 - (NSString *)textContent
 {
        return nil;
@@ -264,6 +268,10 @@ - (HTMLNode *)insertNode:(HTMLNode *)node beforeChildNode:(HTMLNode *)child
 #ifndef HTMLKIT_NO_DOM_CHECKS
        [self ensurePreInsertionValidityOfNode:node beforeChildNode:child];
 #endif
+    
+    if (self.level >= 400) {
+        return nil;
+    }

        [self.ownerDocument adoptNode:node];
iabudiab commented 5 years ago

@dsanghan Hey there, thanks for the sample file and the workarounds 👍 . The fix, that I've already started, should fix the underlying issue fundamentally, i.e. should work for any depth. I hope I'll have a fix and a release sometime next week.

iabudiab commented 5 years ago

@dsanghan Hey there, I've just pushed a new implementation for HTML serialization, which does not use recursion => should work for any DOM depth, even async on iOS.

You can check it out on this commit: 98f7c8304fbf6ed92fa1a2556e6164ba5a5468b5

I'll release a new version after I've written some tests to ensure correct behaviour.

dsanghan commented 5 years ago

@iabudiab Thanks a lot!