From 1e6094bc26da56c7cbb3dc16c67a11f79fc5ce87 Mon Sep 17 00:00:00 2001 From: Ruoxin Xu Date: Tue, 21 Oct 2025 04:34:47 +0100 Subject: [PATCH] =?UTF-8?q?SERVER-103960=20Impose=20a=20proper=20check=20o?= =?UTF-8?q?n=20the=20FieldRef=20to=20ensure=20the=20num=E2=80=A6=20(#41077?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GitOrigin-RevId: ec710de2238c59017c7f4875813f15c1ff588017 --- etc/backports_required_for_multiversion_tests.yml | 4 ++++ jstests/core/write/update/updatea.js | 8 ++++++++ src/mongo/db/field_ref.cpp | 6 +++++- src/mongo/db/field_ref_test.cpp | 5 +++++ 4 files changed, 22 insertions(+), 1 deletion(-) diff --git a/etc/backports_required_for_multiversion_tests.yml b/etc/backports_required_for_multiversion_tests.yml index b12726ea243..40c051bceed 100644 --- a/etc/backports_required_for_multiversion_tests.yml +++ b/etc/backports_required_for_multiversion_tests.yml @@ -625,6 +625,8 @@ last-continuous: ticket: SERVER-102477 - test_file: jstests/sharding/query/union_with_doubly_nested_lookup.js ticket: SERVER-108341 + - test_file: jstests/core/query/update/update_set_unset.js + ticket: SERVER-103960 - test_file: jstests/core/txns/multi_statement_transaction_abort.js ticket: SERVER-84081 - test_file: jstests/change_streams/ddl_create_drop_index_events.js @@ -1308,6 +1310,8 @@ last-lts: ticket: SERVER-102477 - test_file: jstests/sharding/query/union_with_doubly_nested_lookup.js ticket: SERVER-108341 + - test_file: jstests/core/query/update/update_set_unset.js + ticket: SERVER-103960 - test_file: jstests/core/txns/multi_statement_transaction_abort.js ticket: SERVER-84081 - test_file: jstests/change_streams/ddl_create_drop_index_events.js diff --git a/jstests/core/write/update/updatea.js b/jstests/core/write/update/updatea.js index 99938c433fa..a148e0a8218 100644 --- a/jstests/core/write/update/updatea.js +++ b/jstests/core/write/update/updatea.js @@ -75,3 +75,11 @@ res = t.update({"c": 2}, {'$inc': {'a.c000': 1}}); assert.commandWorked(res); assert.eq({"c00": 1, "c000": 1}, t.findOne().a, "D1"); + +// SERVER-103960: Field paths cannot contain more than 255 dots. +assert(t.drop()); + +const longPath = '.'.repeat(256); + +assert.commandWorked(t.insertMany([{a: 1}, {b: 1}])); +assert.commandFailedWithCode(t.update({a: 1}, {$set: {[longPath]: "y"}}), 10396001); diff --git a/src/mongo/db/field_ref.cpp b/src/mongo/db/field_ref.cpp index d897b6605af..529a5bc134d 100644 --- a/src/mongo/db/field_ref.cpp +++ b/src/mongo/db/field_ref.cpp @@ -108,6 +108,7 @@ void FieldRef::appendPart(StringData part) { _replacements.push_back(part.toString()); _parts.push_back(boost::none); + uassert(10396002, "Field paths cannot contain more than 255 '.'", _parts.size() <= 255); } void FieldRef::removeLastPart() { @@ -135,6 +136,7 @@ void FieldRef::removeFirstPart() { size_t FieldRef::appendParsedPart(FieldRef::StringView part) { _parts.push_back(part); _cachedSize++; + uassert(10396001, "Field paths cannot contain more than 255 '.'", _parts.size() <= 255); return _parts.size(); } @@ -172,7 +174,9 @@ void FieldRef::reserialize() const { // There is one case where we expect to see the "where" iterator to be at "end" here: we // are at the last part of the FieldRef and that part is the empty string. In that case, we // need to make sure we do not dereference the "where" iterator. - invariant(where != end || (size == 0 && i == parts - 1)); + tassert(10396003, + "FieldRef was incorrectly dereferenced", + where != end || (size == 0 && i == parts - 1)); if (!size) { part = StringView{}; } else { diff --git a/src/mongo/db/field_ref_test.cpp b/src/mongo/db/field_ref_test.cpp index f631b6bdb4e..ebb5f67a84d 100644 --- a/src/mongo/db/field_ref_test.cpp +++ b/src/mongo/db/field_ref_test.cpp @@ -444,6 +444,11 @@ TEST(AppendShort, SetEmptyPartThenAppend) { ASSERT_EQUALS("", path.getPart(1)); } +TEST(FieldRefTest, ContainsTooManyDots) { + std::string path(255, '.'); + ASSERT_THROWS_CODE(FieldRef(path), AssertionException, 10396001); +} + // The "medium" append tests feature an append operation that spills out of reserve space (i.e., // we append to a path that has _size == kReserveAhead). TEST(AppendMedium, Simple) {