Merge pull request #166 from cdunn2001/stackLimit

Fixes #88 and #56.
This commit is contained in:
Christopher Dunn 2015-02-11 10:35:16 -06:00
commit acbf4eb2ef
3 changed files with 56 additions and 2 deletions

View File

@ -310,6 +310,9 @@ public:
- true if dropped null placeholders are allowed. (See StreamWriterBuilder.) - true if dropped null placeholders are allowed. (See StreamWriterBuilder.)
- "allowNumericKeys": false or true - "allowNumericKeys": false or true
- true if numeric object keys are allowed. - true if numeric object keys are allowed.
- "stackLimit": integer
- This is a security issue (seg-faults caused by deeply nested JSON),
so the default is low.
You can examine 'settings_` yourself You can examine 'settings_` yourself
to see the defaults. You can also write and read them just like any to see the defaults. You can also write and read them just like any

View File

@ -28,6 +28,9 @@
#pragma warning(disable : 4996) #pragma warning(disable : 4996)
#endif #endif
static int const stackLimit_g = 1000;
static int stackDepth_g = 0; // see readValue()
namespace Json { namespace Json {
#if __cplusplus >= 201103L #if __cplusplus >= 201103L
@ -118,6 +121,7 @@ bool Reader::parse(const char* beginDoc,
nodes_.pop(); nodes_.pop();
nodes_.push(&root); nodes_.push(&root);
stackDepth_g = 0; // Yes, this is bad coding, but options are limited.
bool successful = readValue(); bool successful = readValue();
Token token; Token token;
skipCommentTokens(token); skipCommentTokens(token);
@ -140,6 +144,13 @@ bool Reader::parse(const char* beginDoc,
} }
bool Reader::readValue() { bool Reader::readValue() {
// This is a non-reentrant way to support a stackLimit. Terrible!
// But this deprecated class has a security problem: Bad input can
// cause a seg-fault. This seems like a fair, binary-compatible way
// to prevent the problem.
if (stackDepth_g >= stackLimit_g) throw std::runtime_error("Exceeded stackLimit in readValue().");
++stackDepth_g;
Token token; Token token;
skipCommentTokens(token); skipCommentTokens(token);
bool successful = true; bool successful = true;
@ -211,6 +222,7 @@ bool Reader::readValue() {
lastValue_ = &currentValue(); lastValue_ = &currentValue();
} }
--stackDepth_g;
return successful; return successful;
} }
@ -902,7 +914,8 @@ public:
bool strictRoot_; bool strictRoot_;
bool allowDroppedNullPlaceholders_; bool allowDroppedNullPlaceholders_;
bool allowNumericKeys_; bool allowNumericKeys_;
}; // OldFeatures int stackLimit_;
}; // OurFeatures
// exact copy of Implementation of class Features // exact copy of Implementation of class Features
// //////////////////////////////// // ////////////////////////////////
@ -1033,7 +1046,9 @@ private:
Location lastValueEnd_; Location lastValueEnd_;
Value* lastValue_; Value* lastValue_;
std::string commentsBefore_; std::string commentsBefore_;
OurFeatures features_; int stackDepth_;
OurFeatures const features_;
bool collectComments_; bool collectComments_;
}; // OurReader }; // OurReader
@ -1064,6 +1079,7 @@ bool OurReader::parse(const char* beginDoc,
nodes_.pop(); nodes_.pop();
nodes_.push(&root); nodes_.push(&root);
stackDepth_ = 0;
bool successful = readValue(); bool successful = readValue();
Token token; Token token;
skipCommentTokens(token); skipCommentTokens(token);
@ -1086,6 +1102,8 @@ bool OurReader::parse(const char* beginDoc,
} }
bool OurReader::readValue() { bool OurReader::readValue() {
if (stackDepth_ >= features_.stackLimit_) throw std::runtime_error("Exceeded stackLimit in readValue().");
++stackDepth_;
Token token; Token token;
skipCommentTokens(token); skipCommentTokens(token);
bool successful = true; bool successful = true;
@ -1157,6 +1175,7 @@ bool OurReader::readValue() {
lastValue_ = &currentValue(); lastValue_ = &currentValue();
} }
--stackDepth_;
return successful; return successful;
} }
@ -1853,6 +1872,7 @@ CharReader* CharReaderBuilder::newCharReader() const
features.strictRoot_ = settings_["strictRoot"].asBool(); features.strictRoot_ = settings_["strictRoot"].asBool();
features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool(); features.allowDroppedNullPlaceholders_ = settings_["allowDroppedNullPlaceholders"].asBool();
features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool(); features.allowNumericKeys_ = settings_["allowNumericKeys"].asBool();
features.stackLimit_ = settings_["stackLimit"].asInt();
return new OurCharReader(collectComments, features); return new OurCharReader(collectComments, features);
} }
static void getValidReaderKeys(std::set<std::string>* valid_keys) static void getValidReaderKeys(std::set<std::string>* valid_keys)
@ -1863,6 +1883,7 @@ static void getValidReaderKeys(std::set<std::string>* valid_keys)
valid_keys->insert("strictRoot"); valid_keys->insert("strictRoot");
valid_keys->insert("allowDroppedNullPlaceholders"); valid_keys->insert("allowDroppedNullPlaceholders");
valid_keys->insert("allowNumericKeys"); valid_keys->insert("allowNumericKeys");
valid_keys->insert("stackLimit");
} }
bool CharReaderBuilder::validate(Json::Value* invalid) const bool CharReaderBuilder::validate(Json::Value* invalid) const
{ {
@ -1901,6 +1922,7 @@ void CharReaderBuilder::setDefaults(Json::Value* settings)
(*settings)["strictRoot"] = false; (*settings)["strictRoot"] = false;
(*settings)["allowDroppedNullPlaceholders"] = false; (*settings)["allowDroppedNullPlaceholders"] = false;
(*settings)["allowNumericKeys"] = false; (*settings)["allowNumericKeys"] = false;
(*settings)["stackLimit"] = 1000;
//! [CharReaderBuilderDefaults] //! [CharReaderBuilderDefaults]
} }

View File

@ -1713,6 +1713,34 @@ JSONTEST_FIXTURE(CharReaderTest, parseWithDetailError) {
delete reader; delete reader;
} }
JSONTEST_FIXTURE(CharReaderTest, parseWithStackLimit) {
Json::CharReaderBuilder b;
Json::Value root;
char const doc[] =
"{ \"property\" : \"value\" }";
{
b.settings_["stackLimit"] = 2;
Json::CharReader* reader(b.newCharReader());
std::string errs;
bool ok = reader->parse(
doc, doc + std::strlen(doc),
&root, &errs);
JSONTEST_ASSERT(ok);
JSONTEST_ASSERT(errs == "");
JSONTEST_ASSERT_EQUAL("value", root["property"]);
delete reader;
}
{
b.settings_["stackLimit"] = 1;
Json::CharReader* reader(b.newCharReader());
std::string errs;
JSONTEST_ASSERT_THROWS(reader->parse(
doc, doc + std::strlen(doc),
&root, &errs));
delete reader;
}
}
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
JsonTest::Runner runner; JsonTest::Runner runner;
JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr); JSONTEST_REGISTER_FIXTURE(runner, ValueTest, checkNormalizeFloatingPointStr);
@ -1749,6 +1777,7 @@ int main(int argc, const char* argv[]) {
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithOneError); JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithOneError);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseChineseWithOneError); JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseChineseWithOneError);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithDetailError); JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithDetailError);
JSONTEST_REGISTER_FIXTURE(runner, CharReaderTest, parseWithStackLimit);
JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders); JSONTEST_REGISTER_FIXTURE(runner, WriterTest, dropNullPlaceholders);
JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders); JSONTEST_REGISTER_FIXTURE(runner, StreamWriterTest, dropNullPlaceholders);