Implement stack size limitation for iterative parsing.
This commit is contained in:
parent
46e89dad0d
commit
1f53c6c041
@ -1221,12 +1221,13 @@ public:
|
||||
\tparam SourceEncoding Encoding of input stream
|
||||
\tparam InputStream Type of input stream, implementing Stream concept
|
||||
\param is Input stream to be parsed.
|
||||
\param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
\return The document itself for fluent API.
|
||||
*/
|
||||
template <unsigned parseFlags, typename SourceEncoding, typename InputStream>
|
||||
GenericDocument& ParseStream(InputStream& is) {
|
||||
GenericDocument& ParseStream(InputStream& is, size_t limit = 0) {
|
||||
ValueType::SetNull(); // Remove existing root if exist
|
||||
GenericReader<SourceEncoding, Encoding, Allocator> reader(&GetAllocator());
|
||||
GenericReader<SourceEncoding, Encoding, Allocator> reader(limit, &GetAllocator());
|
||||
ClearStackOnExit scope(*this);
|
||||
parseResult_ = reader.template Parse<parseFlags>(is, *this);
|
||||
if (parseResult_) {
|
||||
@ -1240,21 +1241,23 @@ public:
|
||||
/*! \tparam parseFlags Combination of \ref ParseFlag.
|
||||
\tparam InputStream Type of input stream, implementing Stream concept
|
||||
\param is Input stream to be parsed.
|
||||
\param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
\return The document itself for fluent API.
|
||||
*/
|
||||
template <unsigned parseFlags, typename InputStream>
|
||||
GenericDocument& ParseStream(InputStream& is) {
|
||||
return ParseStream<parseFlags,Encoding,InputStream>(is);
|
||||
GenericDocument& ParseStream(InputStream& is, size_t limit = 0) {
|
||||
return ParseStream<parseFlags,Encoding,InputStream>(is, limit);
|
||||
}
|
||||
|
||||
//! Parse JSON text from an input stream (with \ref kParseDefaultFlags)
|
||||
/*! \tparam InputStream Type of input stream, implementing Stream concept
|
||||
\param is Input stream to be parsed.
|
||||
\param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
\return The document itself for fluent API.
|
||||
*/
|
||||
template <typename InputStream>
|
||||
GenericDocument& ParseStream(InputStream& is) {
|
||||
return ParseStream<kParseDefaultFlags, Encoding, InputStream>(is);
|
||||
GenericDocument& ParseStream(InputStream& is, size_t limit = 0) {
|
||||
return ParseStream<kParseDefaultFlags, Encoding, InputStream>(is, limit);
|
||||
}
|
||||
//!@}
|
||||
|
||||
@ -1265,30 +1268,33 @@ public:
|
||||
/*! \tparam parseFlags Combination of \ref ParseFlag.
|
||||
\tparam SourceEncoding Transcoding from input Encoding
|
||||
\param str Mutable zero-terminated string to be parsed.
|
||||
\param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
\return The document itself for fluent API.
|
||||
*/
|
||||
template <unsigned parseFlags, typename SourceEncoding>
|
||||
GenericDocument& ParseInsitu(Ch* str) {
|
||||
GenericDocument& ParseInsitu(Ch* str, size_t limit = 0) {
|
||||
GenericInsituStringStream<Encoding> s(str);
|
||||
return ParseStream<parseFlags | kParseInsituFlag, SourceEncoding>(s);
|
||||
return ParseStream<parseFlags | kParseInsituFlag, SourceEncoding>(s, limit);
|
||||
}
|
||||
|
||||
//! Parse JSON text from a mutable string
|
||||
/*! \tparam parseFlags Combination of \ref ParseFlag.
|
||||
\param str Mutable zero-terminated string to be parsed.
|
||||
\param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
\return The document itself for fluent API.
|
||||
*/
|
||||
template <unsigned parseFlags>
|
||||
GenericDocument& ParseInsitu(Ch* str) {
|
||||
return ParseInsitu<parseFlags, Encoding>(str);
|
||||
GenericDocument& ParseInsitu(Ch* str, size_t limit = 0) {
|
||||
return ParseInsitu<parseFlags, Encoding>(str, limit);
|
||||
}
|
||||
|
||||
//! Parse JSON text from a mutable string (with \ref kParseDefaultFlags)
|
||||
/*! \param str Mutable zero-terminated string to be parsed.
|
||||
\param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
\return The document itself for fluent API.
|
||||
*/
|
||||
GenericDocument& ParseInsitu(Ch* str) {
|
||||
return ParseInsitu<kParseDefaultFlags, Encoding>(str);
|
||||
GenericDocument& ParseInsitu(Ch* str, size_t limit = 0) {
|
||||
return ParseInsitu<kParseDefaultFlags, Encoding>(str, limit);
|
||||
}
|
||||
//!@}
|
||||
|
||||
@ -1299,28 +1305,31 @@ public:
|
||||
/*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
|
||||
\tparam SourceEncoding Transcoding from input Encoding
|
||||
\param str Read-only zero-terminated string to be parsed.
|
||||
\param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
*/
|
||||
template <unsigned parseFlags, typename SourceEncoding>
|
||||
GenericDocument& Parse(const Ch* str) {
|
||||
GenericDocument& Parse(const Ch* str, size_t limit = 0) {
|
||||
RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag));
|
||||
GenericStringStream<SourceEncoding> s(str);
|
||||
return ParseStream<parseFlags, SourceEncoding>(s);
|
||||
return ParseStream<parseFlags, SourceEncoding>(s, limit);
|
||||
}
|
||||
|
||||
//! Parse JSON text from a read-only string
|
||||
/*! \tparam parseFlags Combination of \ref ParseFlag (must not contain \ref kParseInsituFlag).
|
||||
\param str Read-only zero-terminated string to be parsed.
|
||||
\param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
*/
|
||||
template <unsigned parseFlags>
|
||||
GenericDocument& Parse(const Ch* str) {
|
||||
return Parse<parseFlags, Encoding>(str);
|
||||
GenericDocument& Parse(const Ch* str, size_t limit = 0) {
|
||||
return Parse<parseFlags, Encoding>(str, limit);
|
||||
}
|
||||
|
||||
//! Parse JSON text from a read-only string (with \ref kParseDefaultFlags)
|
||||
/*! \param str Read-only zero-terminated string to be parsed.
|
||||
\param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
*/
|
||||
GenericDocument& Parse(const Ch* str) {
|
||||
return Parse<kParseDefaultFlags>(str);
|
||||
GenericDocument& Parse(const Ch* str, size_t limit = 0) {
|
||||
return Parse<kParseDefaultFlags>(str, limit);
|
||||
}
|
||||
//!@}
|
||||
|
||||
|
@ -40,6 +40,7 @@ inline const RAPIDJSON_ERROR_CHARTYPE* GetParseError_En(ParseErrorCode parseErro
|
||||
|
||||
case kParseErrorTermination: return RAPIDJSON_ERROR_STRING("Terminate parsing due to Handler error.");
|
||||
case kParseErrorUnspecificSyntaxError: return RAPIDJSON_ERROR_STRING("Unspecific syntax error.");
|
||||
case kParseErrorStackSizeLimitExceeded: return RAPIDJSON_ERROR_STRING("Parsing stack size limit is exceeded.");
|
||||
|
||||
default:
|
||||
return RAPIDJSON_ERROR_STRING("Unknown error.");
|
||||
|
@ -59,7 +59,8 @@ enum ParseErrorCode {
|
||||
kParseErrorNumberMissExponent, //!< Miss exponent in number.
|
||||
|
||||
kParseErrorTermination, //!< Parsing was terminated.
|
||||
kParseErrorUnspecificSyntaxError //!< Unspecific syntax error.
|
||||
kParseErrorUnspecificSyntaxError, //!< Unspecific syntax error.
|
||||
kParseErrorStackSizeLimitExceeded //!< Parsing stack size limit is exceeded.
|
||||
};
|
||||
|
||||
//! Result of parsing (wraps ParseErrorCode)
|
||||
|
@ -272,10 +272,11 @@ public:
|
||||
typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type
|
||||
|
||||
//! Constructor.
|
||||
/*! \param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
|
||||
/*! \param limit Parsing stack size limit(in bytes). Pass 0 means no limit.
|
||||
\param allocator Optional allocator for allocating stack memory. (Only use for non-destructive parsing)
|
||||
\param stackCapacity stack capacity in bytes for storing a single decoded string. (Only use for non-destructive parsing)
|
||||
*/
|
||||
GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseResult_() {}
|
||||
GenericReader(size_t limit = 0, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), kStackSizeLimit(limit), parseResult_() {}
|
||||
|
||||
//! Parse JSON text.
|
||||
/*! \tparam parseFlags Combination of \ref ParseFlag.
|
||||
@ -569,8 +570,14 @@ private:
|
||||
if (c == '\\') { // Escape
|
||||
is.Take();
|
||||
Ch e = is.Take();
|
||||
if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e])
|
||||
if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) {
|
||||
if (!(parseFlags & kParseInsituFlag)) {
|
||||
if (!IsStackSpaceSufficient<Ch>(1)) {
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorStackSizeLimitExceeded, is.Tell() - 1);
|
||||
}
|
||||
}
|
||||
os.Put(escape[(unsigned char)e]);
|
||||
}
|
||||
else if (e == 'u') { // Unicode
|
||||
unsigned codepoint = ParseHex4(is);
|
||||
if (codepoint >= 0xD800 && codepoint <= 0xDBFF) {
|
||||
@ -589,6 +596,11 @@ private:
|
||||
}
|
||||
else if (c == '"') { // Closing double quote
|
||||
is.Take();
|
||||
if (!(parseFlags & kParseInsituFlag)) {
|
||||
if (!IsStackSpaceSufficient<Ch>(1)) {
|
||||
RAPIDJSON_PARSE_ERROR(kParseErrorStackSizeLimitExceeded, is.Tell() - 1);
|
||||
}
|
||||
}
|
||||
os.Put('\0'); // null-terminate the string
|
||||
return;
|
||||
}
|
||||
@ -1038,8 +1050,16 @@ private:
|
||||
else if (src == IterativeParsingKeyValueDelimiterState)
|
||||
n = IterativeParsingMemberValueState;
|
||||
// Push current state.
|
||||
if (!IsStackSpaceSufficient<IterativeParsingState>(1)) {
|
||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStackSizeLimitExceeded, is.Tell());
|
||||
return IterativeParsingErrorState;
|
||||
}
|
||||
*stack_.template Push<IterativeParsingState>(1) = n;
|
||||
// Initialize and push the member/element count.
|
||||
if (!IsStackSpaceSufficient<int>(1)) {
|
||||
RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStackSizeLimitExceeded, is.Tell());
|
||||
return IterativeParsingErrorState;
|
||||
}
|
||||
*stack_.template Push<int>(1) = 0;
|
||||
// Call handler
|
||||
if (dst == IterativeParsingObjectInitialState)
|
||||
@ -1206,7 +1226,13 @@ private:
|
||||
return parseResult_;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool IsStackSpaceSufficient(size_t count) const {
|
||||
return kStackSizeLimit == 0 || (stack_.GetSize() + sizeof(T) * count <= kStackSizeLimit);
|
||||
}
|
||||
|
||||
static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
|
||||
const size_t kStackSizeLimit; //!< Stack size limit(in bytes). A value of 0 means no limit.
|
||||
internal::Stack<Allocator> stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing.
|
||||
ParseResult parseResult_;
|
||||
}; // class GenericReader
|
||||
|
@ -1288,6 +1288,18 @@ TEST(Reader, IterativeParsing_ShortCircuit) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST(Reader, IterativeParsing_LimitStackSize) {
|
||||
BaseReaderHandler<> handler;
|
||||
Reader reader(20);
|
||||
StringStream is("[[[]]]");
|
||||
|
||||
ParseResult r = reader.Parse<kParseIterativeFlag>(is, handler);
|
||||
|
||||
EXPECT_TRUE(reader.HasParseError());
|
||||
EXPECT_EQ(kParseErrorStackSizeLimitExceeded, r.Code());
|
||||
EXPECT_EQ(2, r.Offset());
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
RAPIDJSON_DIAG_POP
|
||||
#endif
|
||||
|
Loading…
x
Reference in New Issue
Block a user