Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/json/reader.h
Original file line number Diff line number Diff line change
Expand Up @@ -339,8 +339,8 @@ class JSON_API CharReaderBuilder : public CharReader::Factory {
* - `"allowSingleQuotes": false or true`
* - true if '' are allowed for strings (both keys and values)
* - `"stackLimit": integer`
* - Exceeding stackLimit (recursive depth of `readValue()`) will cause an
* exception.
* - Exceeding stackLimit (recursive depth of `readValue()`) makes the
* parse fail with an error message; no exception is thrown.
* - This is a security issue (seg-faults caused by deeply nested JSON), so
* the default is low.
* - `"failIfExtra": false or true`
Expand Down
9 changes: 4 additions & 5 deletions src/lib_json/json_reader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1050,16 +1050,15 @@ bool OurReader::parse(const char* beginDoc, const char* endDoc, Value& root,
bool OurReader::readValue() {
Token token;
if (nodes_.size() > features_.stackLimit_) {
#if JSON_USE_EXCEPTION
throwRuntimeError("Exceeded stackLimit in readValue().");
#else
// throwRuntimeError aborts. Don't abort here.
// Exceeding the nesting limit is a parse failure, not a crash: report it
// through the error collection instead of throwing, so that parsing
// overly deep input returns false with a message rather than aborting
// the process when the exception is not caught.
token.start_ = current_;
token.end_ = current_;
token.type_ = tokenError;
return addError(
"Exceeded stackLimit for nested object and/or array values.", token);
#endif
}
readTokenSkippingComments(token);
bool successful = true;
Expand Down
52 changes: 37 additions & 15 deletions src/test_lib_json/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3554,7 +3554,6 @@ JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithStackLimit) {
Json::CharReaderBuilder b;
Json::Value root;

#if JSON_USE_EXCEPTION
char const doc[] = R"({ "property" : "value" })";
{
b.settings_["stackLimit"] = 2;
Expand All @@ -3569,20 +3568,12 @@ JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithStackLimit) {
b.settings_["stackLimit"] = 1;
CharReaderPtr reader(b.newCharReader());
Json::String errs;
JSONTEST_ASSERT_THROWS(
reader->parse(doc, doc + std::strlen(doc), &root, &errs));
}
// Default stack limit should reject deeply nested input (regression test for
// stack exhaustion from fuzz input like [[[[...]]]])
{
Json::CharReaderBuilder defaultBuilder;
Json::String nested(300, '[');
CharReaderPtr reader(defaultBuilder.newCharReader());
Json::String errs;
JSONTEST_ASSERT_THROWS(reader->parse(
nested.data(), nested.data() + nested.size(), &root, &errs));
JSONTEST_ASSERT(!reader->parse(doc, doc + std::strlen(doc), &root, &errs));
JSONTEST_ASSERT(
errs ==
"* Line 1, Column 15\n"
" Exceeded stackLimit for nested object and/or array values.\n");
}
#else
b.settings_["stackLimit"] = 10;
CharReaderPtr reader(b.newCharReader());
{
Expand Down Expand Up @@ -3612,7 +3603,38 @@ JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseWithStackLimit) {
JSONTEST_ASSERT(reader->parse(
onLimit.data(), onLimit.data() + onLimit.size(), &root, &errs));
}
#endif // JSON_USE_EXCEPTION
// Default stack limit should reject deeply nested input (regression test for
// stack exhaustion from fuzz input like [[[[...]]]])
{
Json::CharReaderBuilder defaultBuilder;
Json::String nested(300, '[');
CharReaderPtr defaultReader(defaultBuilder.newCharReader());
Json::String errs;
JSONTEST_ASSERT(!defaultReader->parse(
nested.data(), nested.data() + nested.size(), &root, &errs));
JSONTEST_ASSERT(
errs ==
"* Line 1, Column 257\n"
" Exceeded stackLimit for nested object and/or array values.\n");
}
}

// A nesting depth beyond the configured stackLimit must be reported as a
// regular parse failure through CharReader::parse, not as an escaping
// Json::RuntimeError that terminates the process when uncaught.
JSONTEST_FIXTURE_LOCAL(CharReaderTest, parseDeeplyNestedArrayFailsCleanly) {
Json::CharReaderBuilder b;
Json::Value root;
Json::String nested(1100, '[');
CharReaderPtr reader(b.newCharReader());
Json::String errs;
bool ok =
reader->parse(nested.data(), nested.data() + nested.size(), &root, &errs);
JSONTEST_ASSERT(!ok);
JSONTEST_ASSERT(errs ==
"* Line 1, Column 257\n"
" Exceeded stackLimit for nested object and/or array "
"values.\n");
}

JSONTEST_FIXTURE_LOCAL(CharReaderTest, testOperator) {
Expand Down