yamerl_parser: Fix an infinite loop

... with document/directive end markers not terminated by a whitespace
or a newline.

Thanks to Barry Allard for the bug report and testcases.

Fixes #18.
This commit is contained in:
Jean-Sébastien Pédron 2017-06-06 00:11:15 +02:00
parent a12e146d8c
commit 4bc4fa0278
No known key found for this signature in database
GPG Key ID: 39E99761A5FD94CC
4 changed files with 173 additions and 0 deletions

View File

@ -723,6 +723,9 @@ determine_token_type([$-, $-, $-, C | _] = Chars, Line, 1 = Col, Delta,
when ?IS_NEWLINE(C) orelse ?IS_SPACE(C) orelse
(Version == {1,1} andalso ?IS_NEWLINE_11(C)) ->
parse_document_sep(Chars, Line, Col, Delta, Parser, directives_end);
determine_token_type([$-, $-, $-] = Chars, Line, 1 = Col, Delta,
#yamerl_parser{raw_eos = true} = Parser) ->
parse_document_sep(Chars, Line, Col, Delta, Parser, directives_end);
%% Document end indicator.
determine_token_type([$., $., $., C | _] = Chars, Line, 1 = Col, Delta,
@ -730,6 +733,9 @@ determine_token_type([$., $., $., C | _] = Chars, Line, 1 = Col, Delta,
when ?IS_NEWLINE(C) orelse ?IS_SPACE(C) orelse
(Version == {1,1} andalso ?IS_NEWLINE_11(C)) ->
parse_document_sep(Chars, Line, Col, Delta, Parser, document_end);
determine_token_type([$., $., $.] = Chars, Line, 1 = Col, Delta,
#yamerl_parser{raw_eos = true} = Parser) ->
parse_document_sep(Chars, Line, Col, Delta, Parser, document_end);
%% Directive indicator.
determine_token_type([$% | _] = Chars, Line, 1 = Col, Delta,

View File

@ -0,0 +1,62 @@
-module(document_delimiters).
-include_lib("eunit/include/eunit.hrl").
%% This file contains testcases provided by Barry Allard when he
%% reported #18. Before the bugfix, they would trigger an infinite loop
%% in the parser.
%%
%% FIXME: It's still unclear if the returned values are correct for the
%% given documents.
setup() ->
application:start(yamerl).
parse_blank_string_test_() ->
{setup,
fun setup/0,
[
?_assertEqual([], yamerl_constr:string(""))
]
}.
parse_empty_json_map_test_() ->
{setup,
fun setup/0,
[
?_assertEqual([[]], yamerl_constr:string("{}"))
]
}.
parse_dos_vuln_string_test_() ->
%% TODO: Consider fuzzing using QuickCheck Mini, PropEr, etc.
{setup,
fun setup/0,
[
?_assertEqual([null], yamerl_constr:string("---")),
?_assertEqual([], yamerl_constr:string("...")),
?_assertEqual([null], yamerl_constr:string("\n---")),
?_assertEqual([null], yamerl_constr:string("\r---")),
?_assertEqual([null], yamerl_constr:string("\r\n---")),
?_assertEqual([null, null], yamerl_constr:string("\n---\n---")),
?_assertEqual([null, null], yamerl_constr:string(" \n---\n---")),
?_assertEqual([null, null], yamerl_constr:string("\n---\n---\n...")),
?_assertEqual(["anything", null], yamerl_constr:string("anything\n---"))
]
}.
parse_dos_vuln_bad_string_1_test_() ->
{setup,
fun setup/0,
[
?_assertEqual(["--"], yamerl_constr:string("--"))
]
}.
parse_dos_vuln_bad_string_2_test_() ->
{setup,
fun setup/0,
[
?_assertEqual(["----"], yamerl_constr:string("----"))
]
}.

View File

@ -0,0 +1,55 @@
-module('directives_end_alone').
-include_lib("eunit/include/eunit.hrl").
single_test_() ->
?_assertMatch(
{yamerl_parser,
string,
[],
<<>>,
3,
true,
[],
0,
4,
1,
4,
false,
1,
4,
utf8,
false,
undefined,
_,
_,
[],
{bcoll,root,0,-1,1,1,-1,1,1},
false,
false,
false,
[{impl_key,false,false,undefined,undefined,1,1}],
false,
false,
_,
[],
0,
6,
5,
undefined,
undefined,
_,
false,
[],
[
{yamerl_stream_end,1,4},
{yamerl_doc_end,1,4},
{yamerl_scalar,1,4,
{yamerl_tag,1,4,{non_specific,"?"}},
flow,plain,""},
{yamerl_doc_start,1,1,{1,2},_},
{yamerl_stream_start,1,1,utf8}
]
},
yamerl_parser:string("---")
).

View File

@ -0,0 +1,50 @@
-module('document_end_alone').
-include_lib("eunit/include/eunit.hrl").
single_test_() ->
?_assertMatch(
{yamerl_parser,
string,
[],
<<>>,
3,
true,
[],
0,
4,
1,
4,
false,
1,
4,
utf8,
false,
undefined,
_,
_,
[],
{bcoll,root,0,-1,1,1,-1,1,1},
false,
false,
false,
[{impl_key,false,false,undefined,undefined,1,1}],
false,
false,
_,
[],
0,
3,
2,
undefined,
undefined,
_,
false,
[],
[
{yamerl_stream_end,1,4},
{yamerl_stream_start,1,1,utf8}
]
},
yamerl_parser:string("...")
).