diff options
author | thebusytypist <ounanding@gmail.com> | 2014-07-15 10:16:06 +0400 |
---|---|---|
committer | thebusytypist <ounanding@gmail.com> | 2014-07-15 10:16:06 +0400 |
commit | 1f53c6c041eee6321eb18b5dc7f685f80cd62599 (patch) | |
tree | 6dfa781f9b8c29888da964297c0ea78c835108b2 /include | |
parent | 46e89dad0d973e6dbda335b7f18b599042f6ba5b (diff) |
Implement stack size limitation for iterative parsing.
Diffstat (limited to 'include')
-rw-r--r-- | include/rapidjson/document.h | 45 | ||||
-rw-r--r-- | include/rapidjson/error/en.h | 1 | ||||
-rw-r--r-- | include/rapidjson/error/error.h | 3 | ||||
-rw-r--r-- | include/rapidjson/reader.h | 34 |
4 files changed, 60 insertions, 23 deletions
diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 4448600b..d94cd623 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -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);
}
//!@}
diff --git a/include/rapidjson/error/en.h b/include/rapidjson/error/en.h index e9120c5b..e9a5d1d3 100644 --- a/include/rapidjson/error/en.h +++ b/include/rapidjson/error/en.h @@ -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."); diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index e5c2b1b4..a47dfaaf 100644 --- a/include/rapidjson/error/error.h +++ b/include/rapidjson/error/error.h @@ -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) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index bdfd826a..95a29961 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -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_;
}
- static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string.
+ 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
|