Skip to content
Merged
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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
/libtool
CMakeCache.txt
CMakeFiles/
DartConfiguration.tcl
Comment thread
ingydotnet marked this conversation as resolved.
GNUmakefile
Makefile
Makefile.in
Expand All @@ -40,6 +41,7 @@ stamp-h1
/tests/example-reformatter
/tests/example-reformatter-alt
/tests/run-test-suite
/tests/test-nesting
/tests/test-reader
/tests/test-version
/dist/
29 changes: 0 additions & 29 deletions src/parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -82,10 +82,6 @@ yaml_parser_set_parser_error_context(yaml_parser_t *parser,
const char *context, yaml_mark_t context_mark,
const char *problem, yaml_mark_t problem_mark);

static int
yaml_maximum_level_reached(yaml_parser_t *parser,
yaml_mark_t context_mark, yaml_mark_t problem_mark);

/*
* State functions.
*/
Expand Down Expand Up @@ -229,15 +225,6 @@ yaml_parser_set_parser_error_context(yaml_parser_t *parser,
return 0;
}

static int
yaml_maximum_level_reached(yaml_parser_t *parser,
yaml_mark_t context_mark, yaml_mark_t problem_mark)
{
yaml_parser_set_parser_error_context(parser,
"while parsing", context_mark, "Maximum nesting level reached, set with yaml_set_max_nest_level())", problem_mark);
return 0;
}

/*
* State dispatcher.
*/
Expand Down Expand Up @@ -677,43 +664,27 @@ yaml_parser_parse_node(yaml_parser_t *parser, yaml_event_t *event,
return 1;
}
else if (token->type == YAML_FLOW_SEQUENCE_START_TOKEN) {
if (!STACK_LIMIT(parser, parser->indents, MAX_NESTING_LEVEL - parser->flow_level)) {
yaml_maximum_level_reached(parser, start_mark, token->start_mark);
goto error;
}
end_mark = token->end_mark;
parser->state = YAML_PARSE_FLOW_SEQUENCE_FIRST_ENTRY_STATE;
SEQUENCE_START_EVENT_INIT(*event, anchor, tag, implicit,
YAML_FLOW_SEQUENCE_STYLE, start_mark, end_mark);
return 1;
}
else if (token->type == YAML_FLOW_MAPPING_START_TOKEN) {
if (!STACK_LIMIT(parser, parser->indents, MAX_NESTING_LEVEL - parser->flow_level)) {
yaml_maximum_level_reached(parser, start_mark, token->start_mark);
goto error;
}
end_mark = token->end_mark;
parser->state = YAML_PARSE_FLOW_MAPPING_FIRST_KEY_STATE;
MAPPING_START_EVENT_INIT(*event, anchor, tag, implicit,
YAML_FLOW_MAPPING_STYLE, start_mark, end_mark);
return 1;
}
else if (block && token->type == YAML_BLOCK_SEQUENCE_START_TOKEN) {
if (!STACK_LIMIT(parser, parser->indents, MAX_NESTING_LEVEL - parser->flow_level)) {
yaml_maximum_level_reached(parser, start_mark, token->start_mark);
goto error;
}
end_mark = token->end_mark;
parser->state = YAML_PARSE_BLOCK_SEQUENCE_FIRST_ENTRY_STATE;
SEQUENCE_START_EVENT_INIT(*event, anchor, tag, implicit,
YAML_BLOCK_SEQUENCE_STYLE, start_mark, end_mark);
return 1;
}
else if (block && token->type == YAML_BLOCK_MAPPING_START_TOKEN) {
if (!STACK_LIMIT(parser, parser->indents, MAX_NESTING_LEVEL - parser->flow_level)) {
yaml_maximum_level_reached(parser, start_mark, token->start_mark);
goto error;
}
end_mark = token->end_mark;
parser->state = YAML_PARSE_BLOCK_MAPPING_FIRST_KEY_STATE;
MAPPING_START_EVENT_INIT(*event, anchor, tag, implicit,
Expand Down
18 changes: 18 additions & 0 deletions src/scanner.c
Original file line number Diff line number Diff line change
Expand Up @@ -478,6 +478,12 @@

#include "yaml_private.h"

/*
* Maximum nesting level (defined in parser.c).
*/

extern int MAX_NESTING_LEVEL;

/*
* Ensure that the buffer contains the required number of characters.
* Return 1 on success, 0 on failure (reader error or memory error).
Expand Down Expand Up @@ -1175,6 +1181,12 @@ yaml_parser_increase_flow_level(yaml_parser_t *parser)
return 0;
}

if (!STACK_LIMIT(parser, parser->indents, MAX_NESTING_LEVEL - parser->flow_level)) {
return yaml_parser_set_scanner_error(parser,
"while increasing flow level", parser->mark,
"exceeded maximum nesting depth");
}

parser->flow_level++;

return 1;
Expand Down Expand Up @@ -1223,6 +1235,12 @@ yaml_parser_roll_indent(yaml_parser_t *parser, ptrdiff_t column,
if (!PUSH(parser, parser->indents, parser->indent))
return 0;

if (!STACK_LIMIT(parser, parser->indents, MAX_NESTING_LEVEL - parser->flow_level)) {
Comment thread
ingydotnet marked this conversation as resolved.
return yaml_parser_set_scanner_error(parser,
"while increasing block level", parser->mark,
"exceeded maximum nesting depth");
}

if (column > INT_MAX) {
parser->error = YAML_MEMORY_ERROR;
return 0;
Expand Down
2 changes: 2 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ foreach(name IN ITEMS
run-parser
run-parser-test-suite
run-scanner
test-nesting
test-reader
test-version
)
Expand All @@ -24,4 +25,5 @@ endforeach()

add_test(NAME version COMMAND test-version)
add_test(NAME reader COMMAND test-reader)
add_test(NAME nesting COMMAND test-nesting)

4 changes: 2 additions & 2 deletions tests/Makefile.am
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
AM_CPPFLAGS = -I$(top_srcdir)/include -Wall
#AM_CFLAGS = -Wno-pointer-sign
LDADD = $(top_builddir)/src/libyaml.la
TESTS = test-version test-reader
check_PROGRAMS = test-version test-reader
TESTS = test-version test-reader test-nesting
check_PROGRAMS = test-version test-reader test-nesting
noinst_PROGRAMS = run-scanner run-parser run-loader run-emitter run-dumper \
example-reformatter example-reformatter-alt \
example-deconstructor example-deconstructor-alt \
Expand Down
115 changes: 115 additions & 0 deletions tests/test-nesting.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include <yaml.h>

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

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

/*
* Test that the scanner enforces the maximum nesting depth.
*/

static int
scan_string(const char *input, size_t length)
{
yaml_parser_t parser;
yaml_token_t token;
int done = 0;
int error = 0;

assert(yaml_parser_initialize(&parser));
yaml_parser_set_input_string(&parser,
(const unsigned char *)input, length);

while (!done) {
if (!yaml_parser_scan(&parser, &token)) {
error = 1;
break;
}
done = (token.type == YAML_STREAM_END_TOKEN);
yaml_token_delete(&token);
}

yaml_parser_delete(&parser);
return error;
}

int
main(void)
{
char *input;
int i;

/* Test 1: nesting beyond the default limit (1000) must fail. */
{
int depth = 2000;
input = (char *)malloc(depth + 1);
assert(input);
memset(input, '[', depth);
input[depth] = '\0';

printf("Test 1: %d nested '[' (exceeds limit) ... ", depth);
fflush(stdout);
assert(scan_string(input, depth) == 1);
printf("OK (rejected)\n");
free(input);
}

/* Test 2: block nesting beyond the limit must fail. */
{
/*
* Build a YAML string with 2000 levels of block mapping nesting:
* "a:\n b:\n c:\n d:\n..." — each level indented one more space.
*/
int depth = 2000;
int len = 0;
int pos = 0;

/* Each level: indent spaces + "X:\n" */
for (i = 0; i < depth; i++)
len += i + 2 + 1; /* i spaces + "X:" + newline */
input = (char *)malloc(len + 1);
assert(input);

for (i = 0; i < depth; i++) {
int j;
for (j = 0; j < i; j++)
input[pos++] = ' ';
input[pos++] = 'a' + (i % 26);
input[pos++] = ':';
input[pos++] = '\n';
}
input[pos] = '\0';

printf("Test 2: %d levels block nesting (exceeds limit) ... ", depth);
fflush(stdout);
assert(scan_string(input, pos) == 1);
printf("OK (rejected)\n");
free(input);
}

/* Test 3: flow nesting within the limit must succeed. */
{
int depth = 500;
int len = depth * 2;
input = (char *)malloc(len + 1);
assert(input);
for (i = 0; i < depth; i++)
input[i] = '[';
for (i = 0; i < depth; i++)
input[depth + i] = ']';
input[len] = '\0';

printf("Test 2: %d nested '[]' (within limit) ... ", depth);
fflush(stdout);
assert(scan_string(input, len) == 0);
printf("OK (accepted)\n");
free(input);
}

return 0;
}
Loading