Repository navigation
Fix memory leak of node pairs/items arrays on allocation failure in the loader - #357
Open
bg0d-droid wants to merge 1 commit into
Open
bg0d-droid wants to merge 1 commit into
bg0d-droid wants to merge 1 commit into
Conversation
When PUSH() grows the document node stack and the growth allocation fails, yaml_parser_load_mapping() and yaml_parser_load_sequence() jump to their error paths before the freshly allocated pairs/items array has been attached to the document. The document cleanup only frees attached data, so the array leaks. Free the arrays on those error paths. They are still unattached there, and yaml_free(NULL) covers the earlier failure points. Found with an allocation-failure sweep over yaml_parser_load(): every allocation is failed in turn (771 failure points under ASan/LSan). After the fix the sweep is clean. The new test-load-error reproduces this portably: it compiles the library sources with the allocator renamed, injects one allocation failure per run, and asserts that no blocks remain allocated. The corpus places mapping/sequence nodes on node-stack growth boundaries (nodes 17 and 33), where the leak occurs; without this fix it reports "1 block(s) leaked when allocation 51 failed" for both documents.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
yaml_parser_load_mapping()andyaml_parser_load_sequence()leak their freshly allocated node pairs/items array whenPUSH()fails to grow the document node stack.Sequence of events:
STACK_INIT()allocates the pairs/items array (16 entries, 128/64 bytes).MAPPING_NODE_INIT()/SEQUENCE_NODE_INIT()copies the array pointer into a stack-localyaml_node_t.PUSH(parser, parser->document->nodes, node)attaches the node to the document — but when the node stack is full,PUSH()first callsyaml_stack_extend(), whoserealloc()can fail under memory pressure. The node is then never attached.error:path freestagandanchorbut not the pairs/items array.The document cleanup in
yaml_parser_load()(yaml_document_delete()) only frees data attached to the document, so the array from step 1 is lost. In the fixed paths the array is still unattached aterror:, so freeing it there is safe;yaml_free(NULL)covers the earlier failure points.Impact
A leak on the out-of-memory path in the composing loader. Long-running processes that parse documents from untrusted input (and whose allocations can be exhausted) lose 64–128 bytes per failed composition at the exact moment memory is already tight.
Reproduction
An allocation-failure sweep over
yaml_parser_load(): fail the Nth allocation for every N and run LeakSanitizer. With the test corpus below (mapping/sequence nodes placed on node-stack growth boundaries) the unpatched loader reports:for the mappings document, and the equivalent 64-byte leak for the sequences document.
Fix
src/loader.c: freepairs.start/items.starton the error paths ofyaml_parser_load_mapping()/yaml_parser_load_sequence().Tests
tests/test-load-error(+test-alloc.c/.h, wired intotests/CMakeLists.txt): compiles the library sources withmalloc/realloc/free/strduprenamed to counting, failure-injecting equivalents — portable, no LD_PRELOAD or linker tricks — then loads each corpus document once per allocation failure point and asserts that zero blocks remain allocated.1 block(s) leaked when allocation 51 failedfor both documents; with the fix it passes across 68 failure points per document.ctest4/4 (version, reader, nesting, load-error).