mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-23 16:48:06 +02:00
#3309: optimize parsing from stream (no copying of entire JSON to memory); limit maximum depth to avoid stack overflow with malicious documents (fuzzing - #3285); code cleanup
This commit is contained in:
@@ -138,10 +138,7 @@ void ParseHandler::setValue(const Var& value)
|
||||
_key.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
throw JSONException("Attempt to set value on an empty stack");
|
||||
}
|
||||
else throw JSONException("Attempt to set value on an empty stack");
|
||||
}
|
||||
|
||||
|
||||
|
@@ -29,8 +29,8 @@ namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
Parser::Parser(const Handler::Ptr& pHandler, std::size_t bufSize):
|
||||
ParserImpl(pHandler, bufSize)
|
||||
Parser::Parser(const Handler::Ptr& pHandler):
|
||||
ParserImpl(pHandler)
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -34,10 +34,26 @@ namespace Poco {
|
||||
namespace JSON {
|
||||
|
||||
|
||||
ParserImpl::ParserImpl(const Handler::Ptr& pHandler, std::size_t bufSize):
|
||||
extern "C"
|
||||
{
|
||||
static int istream_get(void* ptr)
|
||||
{
|
||||
std::istream* pIstr = reinterpret_cast<std::istream*>(ptr);
|
||||
return pIstr->get();
|
||||
}
|
||||
|
||||
static int istream_peek(void* ptr)
|
||||
{
|
||||
std::istream* pIstr = reinterpret_cast<std::istream*>(ptr);
|
||||
return pIstr->peek();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ParserImpl::ParserImpl(const Handler::Ptr& pHandler):
|
||||
_pJSON(new json_stream),
|
||||
_pHandler(pHandler),
|
||||
_depth(JSON_UNLIMITED_DEPTH),
|
||||
_depth(JSON_DEFAULT_DEPTH),
|
||||
_decimalPoint('.'),
|
||||
_allowNullByte(true),
|
||||
_allowComments(false)
|
||||
@@ -68,12 +84,34 @@ void ParserImpl::handle(const std::string& json)
|
||||
// json_open*() call, which calls internal init()
|
||||
json_set_streaming(_pJSON, false);
|
||||
/////////////////////////////////
|
||||
handle(); checkError();
|
||||
handle();
|
||||
checkError();
|
||||
if (JSON_DONE != json_next(_pJSON))
|
||||
throw JSONException("Excess characters found after JSON end.");
|
||||
json_close(_pJSON);
|
||||
}
|
||||
catch (std::exception&)
|
||||
catch (...)
|
||||
{
|
||||
json_close(_pJSON);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ParserImpl::handle(std::istream& json)
|
||||
{
|
||||
try
|
||||
{
|
||||
json_open_user(_pJSON, istream_get, istream_peek, &json);
|
||||
checkError();
|
||||
json_set_streaming(_pJSON, false);
|
||||
handle();
|
||||
checkError();
|
||||
if (JSON_DONE != json_next(_pJSON))
|
||||
throw JSONException("Excess characters found after JSON end.");
|
||||
json_close(_pJSON);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
json_close(_pJSON);
|
||||
throw;
|
||||
@@ -89,17 +127,33 @@ Dynamic::Var ParserImpl::parseImpl(const std::string& json)
|
||||
stripComments(str);
|
||||
handle(str);
|
||||
}
|
||||
else handle(json);
|
||||
else
|
||||
{
|
||||
handle(json);
|
||||
}
|
||||
|
||||
return asVarImpl();
|
||||
}
|
||||
|
||||
|
||||
Dynamic::Var ParserImpl::parseImpl(std::istream& in)
|
||||
Dynamic::Var ParserImpl::parseImpl(std::istream& json)
|
||||
{
|
||||
std::ostringstream os;
|
||||
StreamCopier::copyStream(in, os);
|
||||
return parseImpl(os.str());
|
||||
if (_allowComments || !_allowNullByte)
|
||||
{
|
||||
std::string str;
|
||||
Poco::StreamCopier::copyToString(json, str);
|
||||
if (_allowComments)
|
||||
{
|
||||
stripComments(str);
|
||||
}
|
||||
handle(str);
|
||||
}
|
||||
else
|
||||
{
|
||||
handle(json);
|
||||
}
|
||||
|
||||
return asVarImpl();
|
||||
}
|
||||
|
||||
|
||||
@@ -146,7 +200,10 @@ void ParserImpl::handleArray()
|
||||
tok = json_peek(_pJSON);
|
||||
}
|
||||
|
||||
if (tok == JSON_ARRAY_END) handle();
|
||||
if (tok == JSON_ARRAY_END)
|
||||
{
|
||||
handle();
|
||||
}
|
||||
else throw JSONException("JSON array end not found");
|
||||
}
|
||||
|
||||
@@ -162,70 +219,74 @@ void ParserImpl::handleObject()
|
||||
tok = json_peek(_pJSON);
|
||||
}
|
||||
|
||||
if (tok == JSON_OBJECT_END) handle();
|
||||
if (tok == JSON_OBJECT_END)
|
||||
{
|
||||
handle();
|
||||
}
|
||||
else throw JSONException("JSON object end not found");
|
||||
}
|
||||
|
||||
|
||||
void ParserImpl::handle()
|
||||
{
|
||||
if (json_get_depth(_pJSON) > _depth)
|
||||
throw JSONException("Maximum depth exceeded");
|
||||
|
||||
enum json_type type = json_next(_pJSON);
|
||||
switch (type)
|
||||
{
|
||||
case JSON_DONE:
|
||||
return;
|
||||
case JSON_NULL:
|
||||
_pHandler->null();
|
||||
break;
|
||||
case JSON_TRUE:
|
||||
if (_pHandler) _pHandler->value(true);
|
||||
break;
|
||||
case JSON_FALSE:
|
||||
if (_pHandler) _pHandler->value(false);
|
||||
break;
|
||||
case JSON_NUMBER:
|
||||
case JSON_DONE:
|
||||
return;
|
||||
case JSON_NULL:
|
||||
_pHandler->null();
|
||||
break;
|
||||
case JSON_TRUE:
|
||||
if (_pHandler) _pHandler->value(true);
|
||||
break;
|
||||
case JSON_FALSE:
|
||||
if (_pHandler) _pHandler->value(false);
|
||||
break;
|
||||
case JSON_NUMBER:
|
||||
if (_pHandler)
|
||||
{
|
||||
if (_pHandler)
|
||||
std::string str(json_get_string(_pJSON, NULL));
|
||||
if (str.find(_decimalPoint) != str.npos || str.find('e') != str.npos || str.find('E') != str.npos)
|
||||
{
|
||||
std::string str(json_get_string(_pJSON, NULL));
|
||||
if (str.find(_decimalPoint) != str.npos || str.find('e') != str.npos || str.find('E') != str.npos)
|
||||
{
|
||||
_pHandler->value(NumberParser::parseFloat(str));
|
||||
}
|
||||
_pHandler->value(NumberParser::parseFloat(str));
|
||||
}
|
||||
else
|
||||
{
|
||||
Poco::Int64 val;
|
||||
if (NumberParser::tryParse64(str, val))
|
||||
_pHandler->value(val);
|
||||
else
|
||||
{
|
||||
Poco::Int64 val;
|
||||
if (NumberParser::tryParse64(str, val))
|
||||
_pHandler->value(val);
|
||||
else
|
||||
_pHandler->value(NumberParser::parseUnsigned64(str));
|
||||
}
|
||||
_pHandler->value(NumberParser::parseUnsigned64(str));
|
||||
}
|
||||
break;
|
||||
}
|
||||
case JSON_STRING:
|
||||
if (_pHandler)
|
||||
{
|
||||
std::size_t length = 0;
|
||||
const char* val = json_get_string(_pJSON, &length);
|
||||
_pHandler->value(std::string(val, length == 0 ? 0 : length - 1)); // Decrease the length by 1 because it also contains the terminating null character
|
||||
}
|
||||
break;
|
||||
case JSON_OBJECT:
|
||||
if (_pHandler) _pHandler->startObject();
|
||||
handleObject();
|
||||
break;
|
||||
case JSON_OBJECT_END:
|
||||
if (_pHandler) _pHandler->endObject();
|
||||
return;
|
||||
case JSON_ARRAY:
|
||||
if (_pHandler) _pHandler->startArray();
|
||||
handleArray();
|
||||
break;
|
||||
case JSON_ARRAY_END:
|
||||
if (_pHandler) _pHandler->endArray();
|
||||
return;
|
||||
case JSON_ERROR:
|
||||
break;
|
||||
case JSON_STRING:
|
||||
if (_pHandler)
|
||||
{
|
||||
std::size_t length = 0;
|
||||
const char* val = json_get_string(_pJSON, &length);
|
||||
_pHandler->value(std::string(val, length == 0 ? 0 : length - 1)); // Decrease the length by 1 because it also contains the terminating null character
|
||||
}
|
||||
break;
|
||||
case JSON_OBJECT:
|
||||
if (_pHandler) _pHandler->startObject();
|
||||
handleObject();
|
||||
break;
|
||||
case JSON_OBJECT_END:
|
||||
if (_pHandler) _pHandler->endObject();
|
||||
return;
|
||||
case JSON_ARRAY:
|
||||
if (_pHandler) _pHandler->startArray();
|
||||
handleArray();
|
||||
break;
|
||||
case JSON_ARRAY_END:
|
||||
if (_pHandler) _pHandler->endArray();
|
||||
return;
|
||||
case JSON_ERROR:
|
||||
{
|
||||
const char* pErr = json_get_error(_pJSON);
|
||||
std::string err(pErr ? pErr : "JSON parser error.");
|
||||
|
Reference in New Issue
Block a user