prevent stack overflow when parsing huge json array

Signed-off-by: Seonghyun Kim <sh8281.kim@samsung.com>
This commit is contained in:
Seonghyun Kim 2026-04-28 19:03:27 +09:00 committed by MuHong Byun
commit c8588c323c
2 changed files with 48 additions and 4 deletions

View file

@ -83,6 +83,30 @@ struct JSONStringStream {
const Ch* tail_;
};
template <typename JSONCharType>
class JSONDocument : public rapidjson::GenericDocument<JSONCharType> {
public:
JSONDocument(ExecutionState& state)
: m_state(state)
{
}
bool StartObject()
{
CHECK_STACK_OVERFLOW(m_state);
return rapidjson::GenericDocument<JSONCharType>::StartObject();
}
bool StartArray()
{
CHECK_STACK_OVERFLOW(m_state);
return rapidjson::GenericDocument<JSONCharType>::StartArray();
}
private:
ExecutionState& m_state;
};
template <typename CharType, typename JSONCharType>
static Value parseJSONWorker(ExecutionState& state, const rapidjson::GenericValue<JSONCharType>& value)
{
@ -156,12 +180,12 @@ static Value parseJSONWorker(ExecutionState& state, const rapidjson::GenericValu
}
template <typename CharType, typename JSONCharType>
static Value parseJSON(ExecutionState& state, const CharType* data, size_t length, rapidjson::GenericDocument<JSONCharType>& jsonDocument)
static Value parseJSON(ExecutionState& state, const CharType* data, size_t length, JSONDocument<JSONCharType>& jsonDocument)
{
auto strings = &state.context()->staticStrings();
JSONStringStream<JSONCharType> stringStream(data, length);
jsonDocument.ParseStream(stringStream);
jsonDocument.template ParseStream<rapidjson::kParseDefaultFlags, JSONCharType, JSONStringStream<JSONCharType>, JSONDocument<JSONCharType>>(stringStream);
if (jsonDocument.HasParseError()) {
ErrorObject::throwBuiltinError(state, ErrorCode::SyntaxError, strings->JSON.string(), true, strings->parse.string(), rapidjson::GetParseError_En(jsonDocument.GetParseError()));
}
@ -197,7 +221,7 @@ Value JSON::parse(ExecutionState& state, Value text, Value reviver)
// 1, 2, 3
String* JText = text.toString(state);
rapidjson::GenericDocument<rapidjson::UTF16<char16_t>> parseResult;
JSONDocument<rapidjson::UTF16<char16_t>> parseResult(state);
Value unfiltered;
if (JText->has8BitContent()) {
size_t len = JText->length();

View file

@ -2063,6 +2063,26 @@ public:
//!@name Parse from stream
//!@{
//! Parse JSON text from an input stream (with Encoding conversion)
/*! \tparam parseFlags Combination of \ref ParseFlag.
\tparam SourceEncoding Encoding of input stream
\tparam InputStream Type of input stream, implementing Stream concept
\param is Input stream to be parsed.
\return The document itself for fluent API.
*/
template <unsigned parseFlags, typename SourceEncoding, typename InputStream, typename Handler>
GenericDocument& ParseStream(InputStream& is)
{
ValueType::SetNull(); // Remove existing root if exist
GenericReader<SourceEncoding, Encoding, StackAllocator> reader(&stack_.GetAllocator());
ClearStackOnExit scope(*this);
parseResult_ = reader.template Parse<parseFlags>(is, (Handler&)*this);
if (parseResult_) {
RAPIDJSON_ASSERT(stack_.GetSize() == sizeof(ValueType)); // Got one and only one root object
this->RawAssign(*stack_.template Pop<ValueType>(1)); // Add this-> to prevent issue 13.
}
return *this;
}
//! Parse JSON text from an input stream (with Encoding conversion)
/*! \tparam parseFlags Combination of \ref ParseFlag.
@ -2189,7 +2209,7 @@ public:
//! Get the capacity of stack in bytes.
size_t GetStackCapacity() const { return stack_.GetCapacity(); }
private:
protected:
// clear stack on any exit from ParseStream, e.g. due to exception
struct ClearStackOnExit {
explicit ClearStackOnExit(GenericDocument& d)