Skip to content

Commit 6451d97

Browse files
ryanberryhillMongoDB Bot
authored andcommitted
SERVER-100395 Return a parse error when objects are nested too deeply in JParse (#32258)
GitOrigin-RevId: a0afabe538ab2cdffd4ef37ac0a7960816dbcc8e
1 parent b0fd2da commit 6451d97

File tree

5 files changed

+341
-63
lines changed

5 files changed

+341
-63
lines changed

src/mongo/bson/SConscript

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ env.CppUnitTest(
2323
'bson_validate_test.cpp',
2424
'bsonelement_test.cpp',
2525
'bsonobjbuilder_test.cpp',
26+
'json_test.cpp',
2627
'oid_test.cpp',
2728
'simple_bsonobj_comparator_test.cpp',
2829
],

src/mongo/bson/json.cpp

Lines changed: 27 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,15 +172,15 @@ Status JParse::parseError(StringData msg) {
172172
return Status(ErrorCodes::FailedToParse, ossmsg.str());
173173
}
174174

175-
Status JParse::value(StringData fieldName, BSONObjBuilder& builder) {
175+
Status JParse::value(StringData fieldName, BSONObjBuilder& builder, int depth) {
176176
MONGO_JSON_DEBUG("fieldName: " << fieldName);
177177
if (peekToken(LBRACE)) {
178-
Status ret = object(fieldName, builder);
178+
Status ret = object(fieldName, builder, true, depth + 1);
179179
if (ret != Status::OK()) {
180180
return ret;
181181
}
182182
} else if (peekToken(LBRACKET)) {
183-
Status ret = array(fieldName, builder);
183+
Status ret = array(fieldName, builder, true, depth + 1);
184184
if (ret != Status::OK()) {
185185
return ret;
186186
}
@@ -220,7 +220,7 @@ Status JParse::value(StringData fieldName, BSONObjBuilder& builder) {
220220
return ret;
221221
}
222222
} else if (readToken("Dbref") || readToken("DBRef")) {
223-
Status ret = dbRef(fieldName, builder);
223+
Status ret = dbRef(fieldName, builder, depth + 1);
224224
if (ret != Status::OK()) {
225225
return ret;
226226
}
@@ -268,11 +268,14 @@ Status JParse::value(StringData fieldName, BSONObjBuilder& builder) {
268268
}
269269

270270
Status JParse::parse(BSONObjBuilder& builder) {
271-
return isArray() ? array("UNUSED", builder, false) : object("UNUSED", builder, false);
271+
return isArray() ? array("UNUSED", builder, false, 0) : object("UNUSED", builder, false, 0);
272272
}
273273

274-
Status JParse::object(StringData fieldName, BSONObjBuilder& builder, bool subObject) {
274+
Status JParse::object(StringData fieldName, BSONObjBuilder& builder, bool subObject, int depth) {
275275
MONGO_JSON_DEBUG("fieldName: " << fieldName);
276+
if (depth > kMaxDepth) {
277+
return parseError("Reached nested object limit");
278+
}
276279
if (!readToken(LBRACE)) {
277280
return parseError("Expecting '{'");
278281
}
@@ -354,7 +357,7 @@ Status JParse::object(StringData fieldName, BSONObjBuilder& builder, bool subObj
354357
if (!subObject) {
355358
return parseError("Reserved field name in base object: $ref");
356359
}
357-
Status ret = dbRefObject(fieldName, builder);
360+
Status ret = dbRefObject(fieldName, builder, depth + 1);
358361
if (ret != Status::OK()) {
359362
return ret;
360363
}
@@ -429,7 +432,7 @@ Status JParse::object(StringData fieldName, BSONObjBuilder& builder, bool subObj
429432
if (!readToken(COLON)) {
430433
return parseError("Expecting ':'");
431434
}
432-
Status valueRet = value(firstField, *objBuilder);
435+
Status valueRet = value(firstField, *objBuilder, depth);
433436
if (valueRet != Status::OK()) {
434437
return valueRet;
435438
}
@@ -443,7 +446,7 @@ Status JParse::object(StringData fieldName, BSONObjBuilder& builder, bool subObj
443446
if (!readToken(COLON)) {
444447
return parseError("Expecting ':'");
445448
}
446-
Status nextFieldValueRet = value(nextFieldName, *objBuilder);
449+
Status nextFieldValueRet = value(nextFieldName, *objBuilder, depth);
447450
if (nextFieldValueRet != Status::OK()) {
448451
return nextFieldValueRet;
449452
}
@@ -773,7 +776,10 @@ Status JParse::regexObjectCanonical(StringData fieldName, BSONObjBuilder& builde
773776
return Status::OK();
774777
}
775778

776-
Status JParse::dbRefObject(StringData fieldName, BSONObjBuilder& builder) {
779+
Status JParse::dbRefObject(StringData fieldName, BSONObjBuilder& builder, int depth) {
780+
if (depth > kMaxDepth) {
781+
return parseError("Reached nested object limit");
782+
}
777783
BSONObjBuilder subBuilder(builder.subobjStart(fieldName));
778784

779785
if (!readToken(COLON)) {
@@ -797,7 +803,7 @@ Status JParse::dbRefObject(StringData fieldName, BSONObjBuilder& builder) {
797803
if (!readToken(COLON)) {
798804
return parseError("DBRef: Expecting ':'");
799805
}
800-
Status valueRet = value("$id", subBuilder);
806+
Status valueRet = value("$id", subBuilder, depth);
801807
if (valueRet != Status::OK()) {
802808
return valueRet;
803809
}
@@ -945,8 +951,11 @@ Status JParse::maxKeyObject(StringData fieldName, BSONObjBuilder& builder) {
945951
return Status::OK();
946952
}
947953

948-
Status JParse::array(StringData fieldName, BSONObjBuilder& builder, bool subObject) {
954+
Status JParse::array(StringData fieldName, BSONObjBuilder& builder, bool subObject, int depth) {
949955
MONGO_JSON_DEBUG("fieldName: " << fieldName);
956+
if (depth > kMaxDepth) {
957+
return parseError("Reached nested object limit");
958+
}
950959
if (!readToken(LBRACKET)) {
951960
return parseError("Expecting '['");
952961
}
@@ -961,7 +970,7 @@ Status JParse::array(StringData fieldName, BSONObjBuilder& builder, bool subObje
961970
if (!peekToken(RBRACKET)) {
962971
DecimalCounter<uint32_t> index;
963972
do {
964-
Status ret = value(StringData{index}, *arrayBuilder);
973+
Status ret = value(StringData{index}, *arrayBuilder, depth);
965974
if (!ret.isOK()) {
966975
return ret;
967976
}
@@ -1158,7 +1167,10 @@ Status JParse::numberInt(StringData fieldName, BSONObjBuilder& builder) {
11581167
return Status::OK();
11591168
}
11601169

1161-
Status JParse::dbRef(StringData fieldName, BSONObjBuilder& builder) {
1170+
Status JParse::dbRef(StringData fieldName, BSONObjBuilder& builder, int depth) {
1171+
if (depth > kMaxDepth) {
1172+
return parseError("Reached nested object limit");
1173+
}
11621174
BSONObjBuilder subBuilder(builder.subobjStart(fieldName));
11631175

11641176
if (!readToken(LPAREN)) {
@@ -1176,7 +1188,7 @@ Status JParse::dbRef(StringData fieldName, BSONObjBuilder& builder) {
11761188
return parseError("Expecting ','");
11771189
}
11781190

1179-
Status valueRet = value("$id", subBuilder);
1191+
Status valueRet = value("$id", subBuilder, depth);
11801192
if (valueRet != Status::OK()) {
11811193
return valueRet;
11821194
}

src/mongo/bson/json.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333

3434
#include "mongo/base/status.h"
3535
#include "mongo/base/status_with.h"
36+
#include "mongo/base/string_data.h"
37+
#include "mongo/bson/bson_depth.h"
3638
#include "mongo/bson/bsonobj.h"
3739
#include "mongo/util/time_support.h"
3840

@@ -101,6 +103,7 @@ std::string tojson(const BSONObj& obj,
101103
*/
102104
class JParse {
103105
public:
106+
constexpr static int kMaxDepth = BSONDepth::kDefaultMaxAllowableDepth;
104107
explicit JParse(StringData str);
105108

106109
/*
@@ -136,7 +139,7 @@ class JParse {
136139
* | new CONSTRUCTOR
137140
*/
138141
private:
139-
Status value(StringData fieldName, BSONObjBuilder&);
142+
Status value(StringData fieldName, BSONObjBuilder&, int depth);
140143

141144
/*
142145
* OBJECT :
@@ -166,7 +169,7 @@ class JParse {
166169
*
167170
*/
168171
public:
169-
Status object(StringData fieldName, BSONObjBuilder&, bool subObj = true);
172+
Status object(StringData fieldName, BSONObjBuilder&, bool subObj = true, int depth = 0);
170173
Status parse(BSONObjBuilder& builder);
171174
bool isArray();
172175

@@ -235,7 +238,7 @@ class JParse {
235238
* | { FIELD("$ref") : std::string , FIELD("$id") : OBJECTID }
236239
* | { FIELD("$ref") : std::string , FIELD("$id") : OIDOBJECT }
237240
*/
238-
Status dbRefObject(StringData fieldName, BSONObjBuilder&);
241+
Status dbRefObject(StringData fieldName, BSONObjBuilder&, int depth);
239242

240243
/*
241244
* UNDEFINEDOBJECT :
@@ -288,7 +291,7 @@ class JParse {
288291
* VALUE
289292
* | VALUE , ELEMENTS
290293
*/
291-
Status array(StringData fieldName, BSONObjBuilder&, bool subObj = true);
294+
Status array(StringData fieldName, BSONObjBuilder&, bool subObj, int depth);
292295

293296
/*
294297
* NOTE: Currently only Date can be preceded by the "new" keyword
@@ -346,7 +349,7 @@ class JParse {
346349
* DBREF :
347350
* Dbref( <namespace std::string> , <24 character hex std::string> )
348351
*/
349-
Status dbRef(StringData fieldName, BSONObjBuilder&);
352+
Status dbRef(StringData fieldName, BSONObjBuilder&, int depth);
350353

351354
/*
352355
* REGEX :

0 commit comments

Comments
 (0)