惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
C
Cyber Attacks, Cyber Crime and Cyber Security
L
LINUX DO - 热门话题
G
GRAHAM CLULEY
S
Schneier on Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
S
SegmentFault 最新的问题
IT之家
IT之家
阮一峰的网络日志
阮一峰的网络日志
Recorded Future
Recorded Future
I
Intezer
云风的 BLOG
云风的 BLOG
博客园 - Franky
月光博客
月光博客
大猫的无限游戏
大猫的无限游戏
T
Tenable Blog
The Hacker News
The Hacker News
T
The Blog of Author Tim Ferriss
Attack and Defense Labs
Attack and Defense Labs
D
DataBreaches.Net
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
N
News and Events Feed by Topic
有赞技术团队
有赞技术团队
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
N
News and Events Feed by Topic
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Secure Thoughts
The Register - Security
The Register - Security
B
Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
The Cloudflare Blog
Webroot Blog
Webroot Blog
W
WeLiveSecurity
H
Heimdal Security Blog
博客园 - 三生石上(FineUI控件)
V
Vulnerabilities – Threatpost
G
Google Developers Blog
O
OpenAI News
V
V2EX
罗磊的独立博客
博客园_首页
N
News | PayPal Newsroom
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
TaoSecurity Blog
TaoSecurity Blog
Cloudbric
Cloudbric
H
Hacker News: Front Page
博客园 - 叶小钗
T
Tor Project blog
AI
AI

John Bokma's Hacking and Hiking

Failed to verify signature archive-contents.sig in Emacs Aquamacs 3.6 Hangs When Saving An Encrypted File PAR trouble How To Create a Bootable LibreELEC Installation using Mac OS Running pdflatex Using the Alpine Pandoc LaTeX Docker Image A Docker Image for Sass Timezones in Alpine Docker Containers A Tale of Three Docker Images Debugging a Perl Docker container Perl Time::Piece Unicode Issue Giving Docker Desktop for macOS a Second Chance Flashing another TP-Link TL-WDR4300 with OpenWrt firmware Rehousing two tarantulas Getting started with the Perl version of tumblelog on Ubuntu 18.04 LTS A visit to Avonturia De Vogelkelder Flashing a TP-Link TL-WDR4300 with OpenWrt firmware Mounting a VDI File in a Different VirtualBox Guest Wireless Headless Raspberry Pi - John Bokma A Matter of Time - John Bokma Hand coding an RSS 2.0 feed in Python RFC #822 and RFC #3339 dates in Perl RFC #822 and RFC #3339 dates in Python Hand coding an RSS 2.0 feed in Perl Nav Element with no Heading
Rewriting CommonMark Nodes in Perl "right" this time
John Bokma · 2019-09-17 · via John Bokma's Hacking and Hiking

September 16, 2019

Although I thought I had found a solution to the memory corruption problem I ran into when rewriting CommonMark nodes in Perl testing my code in tumblelog showed this not to be the case. After some more experimentation and looking at the XS Perl module I gave up and contacted the author of the CommonMark interface module, Nick Wellnhofer. I included a minimal version of a Perl program showing this issue; which took some time to get right, as in showing the memory corruption.

Nick was so kind to reply back very soon. He explained:

The problem is that some cmark inline nodes point directly into the text buffer of the parent block. If the parent block is freed, these pointers become invalid. This issue should probably fixed in libcmark.

And gave as a solution to keep the paragraph nodes I unlinked around to prevent this from happening. Today I tried this out in tumblelog with a large Markdown file and the solution given by Nick works!

He also mentioned that he opened this as an issue in cmark, the CommonMark parsing and rendering library in C: Inline nodes can reference text data of parent block #309.

The current code as used in upcoming version 2.0.0 of tumblelog is given below. First, I import some constants as follows:

use CommonMark qw(:opt :node :event);

Next, there is a rewrite_ast function which returns the nodes that are unlinked but should be kept around until after the rendering of the nodes to prevent memory corruption:

sub rewrite_ast {

    # Rewrite an image at the start of a paragraph followed by some text
    # to an image with a figcaption inside a figure element

    my $ast = shift;

    my @nodes;
    my $iter = $ast->iterator;
    while ( my ( $ev_type, $node ) = $iter->next() ) {
        if ( $node->get_type() == NODE_PARAGRAPH && $ev_type == EVENT_EXIT ) {
            my $child = $node->first_child();
            next unless defined $child && $child->get_type() == NODE_IMAGE;
            next if $node->last_child() == $child;

            my $sibling = $child->next();
            if ( $sibling->get_type() == NODE_SOFTBREAK ) {
                # remove this sibling
                $sibling->unlink();
            }

            my $figcaption = CommonMark->create_custom_block(
                on_enter => '<figcaption>',
                on_exit  => '</figcaption>',
            );

            $sibling = $child->next();
            while ( $sibling ) {
                my $next = $sibling->next();
                $figcaption->append_child($sibling);
                $sibling = $next;
            }
            my $figure = CommonMark->create_custom_block(
                on_enter => '<figure>',
                on_exit  => '</figure>',
                children => [$child, $figcaption], # append_child unlinks for us
            );

            $node->replace( $figure );
            push @nodes, $node;
        }
    }

    return \@nodes;
}

The function iterates over all nodes in the abstract syntax tree. If a paragraph node is encountered and it's an exit event, the algorithm is leaving the node, it checks if there is a child of type image and if it's not the only image. Because if that's the case there is no caption.

If there are siblings, though, and the first one is a softbreak, it's removed.

Next, a figcaption node is created and all siblings left are added as children to this node. Note how the pointer to the next sibling is kept before the sibling is added as a child because unlinks the sibling and sets its next pointer to undefined.

Next, a figure node is created and both the image ($child) and the figcaption node are made children of it.

Then the paragraph node is replaced with the figure node; unlinking the paragraph node. We keep this node, though, to prevent memory corruption and finally return all such nodes as a result of the function.

The rewrite_ast function is called by html_for_entry, which keeps a reference to all rewritten nodes around until after the abstract syntax tree has been rendered as HTML and the reference goes out of scope because of the return statement.

The option OPT_UNSAFE is used to allow for inline HTML and HTML blocks in Markdown. As the Markdown is fully under control of the tumblelog blog author this is safe to do.

sub html_for_entry {

    my $ast = CommonMark->parse_document( shift );
    my $nodes = rewrite_ast($ast);

    return qq(<article>\n)
        . $ast->render_html( OPT_UNSAFE )  # we want (inline) HTML to work
        . "</article>\n";
}

Version 2.0.0 of tumblelog will be released soon.

Related