Skip to content

Zero the document in yaml_document_initialize on failure - #353

Closed
afonsojanu wants to merge 1 commit into
yaml:masterfrom
afonsojanu:fix-document-initialize-zero-on-failure
Closed

afonsojanu wants to merge 1 commit into
yaml:masterfrom
afonsojanu:fix-document-initialize-zero-on-failure

Conversation

@afonsojanu

Copy link
Copy Markdown

yaml_parser_initialize() and yaml_emitter_initialize() both memset their object to zero before doing any fallible work, which is what lets a caller safely pass the object to the matching _delete() function even after a failed _initialize() call. yaml_document_initialize() was missing this: when it fails partway through (for example when a supplied tag directive isn't valid UTF-8), it returns 0 without ever writing to *document.

A caller that follows the same pattern used elsewhere in this library and around the codebase, initialize then unconditionally delete regardless of the return value, ends up calling yaml_document_delete() on a document that still holds whatever was previously on the stack. That's exactly what the OSS-Fuzz dumper harness for this project does, and it's how #312 reproduces: yaml_document_delete() dereferences the leftover nodes.start/nodes.top from an earlier stack frame and crashes trying to free it.

I reproduced this with ASan/UBSan using the exact commit and POC attached to #312, and it still crashes the same way on current master:

AddressSanitizer: SEGV on unknown address 0x...
    #0 ... in __asan::Allocator::Deallocate
    #1 ... in free
    #2 ... in yaml_free
    #3 ... in yaml_document_delete api.c:1131
    #4 ... in LLVMFuzzerTestOneInput libyaml_dumper_fuzzer.c:308

With tracing added to the harness, the crash happens right after copy_document() fails on the very first document (yaml_document_initialize() rejects a tag directive with invalid UTF-8), and the fuzzer's cleanup loop then deletes that never-initialized document. Adding the same memset() the other two _initialize() functions already use fixes it: after the change the exact same input runs clean under ASan/UBSan.

Added tests/test-document-initialize.c, which builds a tag directive with an invalid UTF-8 prefix to force the failure path, then asserts the document comes back zeroed and that deleting it doesn't crash. It's wired into both tests/Makefile.am and tests/CMakeLists.txt alongside the existing tests. I ran it both ways: it fails (assertion on the memcmp) without the fix and passes with it, and the full CMake test suite (version, reader, nesting, document-initialize) passes.

Fixes #312.

yaml_parser_initialize() and yaml_emitter_initialize() both memset
their object to zero before doing any fallible work, so a caller can
always safely pass the object to the matching _delete() function even
after a failed _initialize() call. yaml_document_initialize() was
missing this: if it failed partway through, for example because a
supplied tag directive was not valid UTF-8, it returned 0 without ever
touching *document.

A caller that follows the same defensive pattern used everywhere else
in this library, initialize then unconditionally delete regardless of
the return value, ends up calling yaml_document_delete() on a document
that still holds whatever was previously on the stack. That is exactly
what the OSS-Fuzz dumper harness for this project does, and it is how
issue #312 (SEGV in yaml_free at api.c:53) reproduces: yaml_document_delete()
dereferences the garbage nodes.start/nodes.top left over from an
earlier stack frame and crashes trying to free it.

I confirmed this with ASan/UBSan against the exact commit and input
attached to #312, and the crash still reproduces on current master.
Adding the same memset() used by the other two _initialize() functions
makes a failed document safely deletable, matching the guarantee the
rest of the API already provides.

Added tests/test-document-initialize.c, which builds a tag directive
with an invalid UTF-8 prefix to force the failure path, then checks
the document is left zeroed and that deleting it doesn't crash.
@afonsojanu

Copy link
Copy Markdown
Author

Closing this one too, same reason: starting fresh and clearing out everything I had open.

@afonsojanu afonsojanu closed this Oct 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] A SEGV in yaml_free at src/api.c:53:14

1 participant