Skip to content

Commit

Permalink
Merge pull request #65 from esl/fix-null
Browse files Browse the repository at this point in the history
Throw exception on null characters

Any further data passed via exml_stream:parse/2 to the parser won't be processed as well because rapidxml works on null-terminated strings and treats the whole input as empty. The similar issue happens when null character is in the middle of input (other exception is thrown but it has eof flag as well).

I implemented the fix that checks (in advance) whether the buffer contains null character. If so, it throws the error immediately. This way we can make sure that input received from Erlang will be valid in term of processing.
  • Loading branch information
NelsonVides committed Nov 14, 2022
2 parents b74dfc2 + 794f085 commit 7921d7e
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 11 deletions.
8 changes: 8 additions & 0 deletions c_src/exml.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

#include <algorithm>
#include <chrono>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
Expand Down Expand Up @@ -551,6 +552,13 @@ static ERL_NIF_TERM parse_next(ErlNifEnv *env, int argc,
enif_make_string(env, result.error_message.c_str(), ERL_NIF_LATIN1));
}

// Raise an exception when null character is found.
std::size_t rest_size = &Parser::buffer.back() - result.rest;
if (std::strlen(reinterpret_cast<const char*>(result.rest)) != rest_size)
return enif_make_tuple2(
env, atom_error,
enif_make_string(env, "null character found in buffer", ERL_NIF_LATIN1));

return enif_make_tuple3(
env, atom_ok, element,
enif_make_uint64(env, result.rest - Parser::buffer.data()));
Expand Down
28 changes: 17 additions & 11 deletions test/exml_stream_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -191,18 +191,24 @@ stream_max_child_size_test() ->

infinite_stream_partial_chunk_test() ->
{ok, Parser0} = exml_stream:new_parser([{infinite_stream, true}, {autoreset, true}]),
{ok, Parser1, Open} = exml_stream:parse(Parser0, <<"<open xmlns='urn:ietf:params:xml:ns:xmpp-framing' to='i.am.banana.com' version='1.0'/>">>),
{ok, Parser1, Open} = exml_stream:parse(Parser0, <<"<open xmlns='urn:ietf:params:xml:ns:xmpp-framing' to='i.am.banana.com' version='1.0'/>">>),
?assertEqual(
[#xmlel{name = <<"open">>,
attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>},
{<<"to">>, <<"i.am.banana.com">>},
{<<"version">>, <<"1.0">>}]}],
attrs = [{<<"xmlns">>, <<"urn:ietf:params:xml:ns:xmpp-framing">>},
{<<"to">>, <<"i.am.banana.com">>},
{<<"version">>, <<"1.0">>}]}],
Open),
{ok, Parser2, A} = exml_stream:parse(Parser1, <<"<a></a>">>),
?assertEqual([#xmlel{name = <<"a">>, attrs = []}], A),
{ok, Parser3, Empty0} = exml_stream:parse(Parser2, <<" ">>),
?assertEqual([], Empty0),
{ok, Parser4, Empty1} = exml_stream:parse(Parser3, <<"<b></b">>),
?assertEqual([], Empty1),
{ok, _Parser5, B} = exml_stream:parse(Parser4, <<">">>),
?assertEqual([#xmlel{name = <<"b">>, attrs = []}], B).
?assertEqual([#xmlel{name = <<"a">>, attrs = []}], A),
{ok, Parser3, Empty0} = exml_stream:parse(Parser2, <<" ">>),
?assertEqual([], Empty0),
{ok, Parser4, Empty1} = exml_stream:parse(Parser3, <<"<b></b">>),
?assertEqual([], Empty1),
{ok, _Parser5, B} = exml_stream:parse(Parser4, <<">">>),
?assertEqual([#xmlel{name = <<"b">>, attrs = []}], B).

null_character_test() ->
{ok, P1} = exml_stream:new_parser(),
?assertMatch({error, _}, exml_stream:parse(P1, <<"\0<stream>">>)),
{ok, P2} = exml_stream:new_parser(),
?assertMatch({error, _}, exml_stream:parse(P2, <<"<stream>\0">>)).

0 comments on commit 7921d7e

Please sign in to comment.