Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -491,12 +491,6 @@ yaml_parser_parse_document_end(yaml_parser_t *parser, yaml_event_t *event)
implicit = 0;
}

while (!STACK_EMPTY(parser, parser->tag_directives)) {
yaml_tag_directive_t tag_directive = POP(parser, parser->tag_directives);
yaml_free(tag_directive.handle);
yaml_free(tag_directive.prefix);
}

parser->state = YAML_PARSE_DOCUMENT_START_STATE;
DOCUMENT_END_EVENT_INIT(*event, implicit, start_mark, end_mark);

Expand Down Expand Up @@ -1250,6 +1244,7 @@ yaml_parser_process_directives(yaml_parser_t *parser,
};
yaml_tag_directive_t *default_tag_directive;
yaml_version_directive_t *version_directive = NULL;
int first_directive = 1;
struct {
yaml_tag_directive_t *start;
yaml_tag_directive_t *end;
Expand All @@ -1266,6 +1261,17 @@ yaml_parser_process_directives(yaml_parser_t *parser,
while (token->type == YAML_VERSION_DIRECTIVE_TOKEN ||
token->type == YAML_TAG_DIRECTIVE_TOKEN)
{
if (first_directive) {
/* First directive seen: this document has its own directives,
* so discard any inherited from the previous document. */
while (!STACK_EMPTY(parser, parser->tag_directives)) {
yaml_tag_directive_t tag_directive = POP(parser, parser->tag_directives);
yaml_free(tag_directive.handle);
yaml_free(tag_directive.prefix);
}
first_directive = 0;
}

if (token->type == YAML_VERSION_DIRECTIVE_TOKEN) {
if (version_directive) {
yaml_parser_set_parser_error(parser,
Expand Down
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ foreach(name IN ITEMS
test-nesting
test-reader
test-version
test-directive-inheritance
)
add_yaml_executable(${name})
endforeach()

add_test(NAME directive-inheritance COMMAND test-directive-inheritance)
add_test(NAME version COMMAND test-version)
add_test(NAME reader COMMAND test-reader)
add_test(NAME nesting COMMAND test-nesting)
Expand Down
59 changes: 59 additions & 0 deletions tests/test-directive-inheritance.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#include <yaml.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#ifdef NDEBUG
#undef NDEBUG
#endif
#include <assert.h>

/*
* Test: %TAG directive inheritance across documents in a multi-document stream.
*
* YAML 1.1 spec section 7.4:
* "If the document specifies no directives, it is parsed using the same
* settings as the previous document. If the document does specify any
* directives, all directives of previous documents, if any, are ignored."
*
* libyaml bug: yaml_parser_parse_document_end() unconditionally clears
* parser->tag_directives, so document 2 (which has no directives) cannot
* inherit the %TAG mapping from document 1. When the parser then encounters
* !x!bar in document 2 it cannot resolve the handle and throws a parse
* error. The assert inside the loop will FAIL until the bug is fixed.
*/

static const unsigned char yaml_input[] =
"%YAML 1.1\n"
"%TAG !x! tag:example.com,2026:\n"
"--- !x!food\n"
"x: 0\n"
"--- !x!bar\n"
"x: 1\n";

int main(void)
{
yaml_parser_t parser;
yaml_event_t event;
int done = 0;

assert(yaml_parser_initialize(&parser));
yaml_parser_set_input_string(&parser, yaml_input, sizeof(yaml_input) - 1);

printf("Testing Directive Inheritance ... ");
fflush(stdout);

while (!done) {
/* With the bug, this assert fires when the parser hits !x!bar
* in doc 2 and cannot resolve the handle. A spec-compliant
* parser must succeed for the entire stream. */
assert(yaml_parser_parse(&parser, &event));
done = (event.type == YAML_STREAM_END_EVENT);
yaml_event_delete(&event);
}

yaml_parser_delete(&parser);
printf("PASSED\n");
return 0;
}