From e3e8fea0f311f646768438168a9bcfdd812bfff3 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 26 Jul 2014 21:40:11 +0800 Subject: [PATCH] Remove stack size limit feature It is not very useful for iterative parsing as the worst case of heap size is O(n) where n is number of character in JSON, for the worst synthetic cases. This is reasonable and should not create stack overflow security problem as in recursive parsing. --- include/rapidjson/document.h | 45 +++++++++++++-------------------- include/rapidjson/error/error.h | 1 - include/rapidjson/reader.h | 25 ++---------------- test/unittest/readertest.cpp | 12 --------- 4 files changed, 20 insertions(+), 63 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index d94cd62..4448600 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1221,13 +1221,12 @@ 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 - GenericDocument& ParseStream(InputStream& is, size_t limit = 0) { + GenericDocument& ParseStream(InputStream& is) { ValueType::SetNull(); // Remove existing root if exist - GenericReader reader(limit, &GetAllocator()); + GenericReader reader(&GetAllocator()); ClearStackOnExit scope(*this); parseResult_ = reader.template Parse(is, *this); if (parseResult_) { @@ -1241,23 +1240,21 @@ 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 - GenericDocument& ParseStream(InputStream& is, size_t limit = 0) { - return ParseStream(is, limit); + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); } //! 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 - GenericDocument& ParseStream(InputStream& is, size_t limit = 0) { - return ParseStream(is, limit); + GenericDocument& ParseStream(InputStream& is) { + return ParseStream(is); } //!@} @@ -1268,33 +1265,30 @@ 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 - GenericDocument& ParseInsitu(Ch* str, size_t limit = 0) { + GenericDocument& ParseInsitu(Ch* str) { GenericInsituStringStream s(str); - return ParseStream(s, limit); + return ParseStream(s); } //! 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 - GenericDocument& ParseInsitu(Ch* str, size_t limit = 0) { - return ParseInsitu(str, limit); + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); } //! 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, size_t limit = 0) { - return ParseInsitu(str, limit); + GenericDocument& ParseInsitu(Ch* str) { + return ParseInsitu(str); } //!@} @@ -1305,31 +1299,28 @@ 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 - GenericDocument& Parse(const Ch* str, size_t limit = 0) { + GenericDocument& Parse(const Ch* str) { RAPIDJSON_ASSERT(!(parseFlags & kParseInsituFlag)); GenericStringStream s(str); - return ParseStream(s, limit); + return ParseStream(s); } //! 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 - GenericDocument& Parse(const Ch* str, size_t limit = 0) { - return Parse(str, limit); + GenericDocument& Parse(const Ch* str) { + return Parse(str); } //! 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, size_t limit = 0) { - return Parse(str, limit); + GenericDocument& Parse(const Ch* str) { + return Parse(str); } //!@} diff --git a/include/rapidjson/error/error.h b/include/rapidjson/error/error.h index a47dfaa..981cbd9 100644 --- a/include/rapidjson/error/error.h +++ b/include/rapidjson/error/error.h @@ -60,7 +60,6 @@ enum ParseErrorCode { kParseErrorTermination, //!< Parsing was terminated. 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 d00c92b..64094f6 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -273,11 +273,10 @@ public: typedef typename SourceEncoding::Ch Ch; //!< SourceEncoding character type //! Constructor. - /*! \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 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(size_t limit = 0, Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), kStackSizeLimit_(limit), parseResult_() {} + GenericReader(Allocator* allocator = 0, size_t stackCapacity = kDefaultStackCapacity) : stack_(allocator, stackCapacity), parseResult_() {} //! Parse JSON text. /*! \tparam parseFlags Combination of \ref ParseFlag. @@ -572,11 +571,6 @@ private: is.Take(); Ch e = is.Take(); if ((sizeof(Ch) == 1 || unsigned(e) < 256) && escape[(unsigned char)e]) { - if (!(parseFlags & kParseInsituFlag)) { - if (!CheckStackSpaceQuota(sizeof(Ch))) { - RAPIDJSON_PARSE_ERROR(kParseErrorStackSizeLimitExceeded, is.Tell() - 1); - } - } os.Put(escape[(unsigned char)e]); } else if (e == 'u') { // Unicode @@ -597,11 +591,6 @@ private: } else if (c == '"') { // Closing double quote is.Take(); - if (!(parseFlags & kParseInsituFlag)) { - if (!CheckStackSpaceQuota(sizeof(Ch))) { - RAPIDJSON_PARSE_ERROR(kParseErrorStackSizeLimitExceeded, is.Tell() - 1); - } - } os.Put('\0'); // null-terminate the string return; } @@ -1059,11 +1048,6 @@ private: n = IterativeParsingElementState; else if (src == IterativeParsingKeyValueDelimiterState) n = IterativeParsingMemberValueState; - // Check stack space limit. - if (!CheckStackSpaceQuota(sizeof(IterativeParsingState) + sizeof(int))) { - RAPIDJSON_PARSE_ERROR_NORETURN(kParseErrorStackSizeLimitExceeded, is.Tell()); - return IterativeParsingErrorState; - } // Push current state. *stack_.template Push(1) = n; // Initialize and push the member/element count. @@ -1235,13 +1219,8 @@ private: return parseResult_; } - bool CheckStackSpaceQuota(size_t size) const { - return kStackSizeLimit_ == 0 || (stack_.GetSize() + size <= kStackSizeLimit_); - } - static const size_t kDefaultStackCapacity = 256; //!< Default stack capacity in bytes for storing a single decoded string. internal::Stack stack_; //!< A stack for storing decoded string temporarily during non-destructive parsing. - const size_t kStackSizeLimit_; //!< Stack size limit(in bytes). A value of 0 means no limit. ParseResult parseResult_; }; // class GenericReader diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 1919e94..d6dd4c4 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -932,18 +932,6 @@ TEST(Reader, IterativeParsing_ShortCircuit) { } } -TEST(Reader, IterativeParsing_LimitStackSize) { - BaseReaderHandler<> handler; - Reader reader(20); - StringStream is("[[[]]]"); - - ParseResult r = reader.Parse(is, handler); - - EXPECT_TRUE(reader.HasParseError()); - EXPECT_EQ(kParseErrorStackSizeLimitExceeded, r.Code()); - EXPECT_EQ(2u, r.Offset()); -} - #ifdef __GNUC__ RAPIDJSON_DIAG_POP #endif