SERVER-89143 Make IDL timestamp parsing more restrictive (#40723)

GitOrigin-RevId: 5132e4578d5d7aa53c604c26636c2a05261468fa
This commit is contained in:
Alex Li 2025-09-11 12:35:03 -04:00 committed by MongoDB Bot
parent eaa3f1395b
commit a368103e52
19 changed files with 169 additions and 217 deletions

View File

@ -218,16 +218,6 @@ def _validate_type_properties(ctxt, idl_type, syntax_type):
idl_type, syntax_type, idl_type.name, "deserializer"
)
if idl_type.deserializer is not None and "BSONElement" not in idl_type.deserializer:
ctxt.add_not_custom_scalar_serialization_not_supported_error(
idl_type, syntax_type, idl_type.name, bson_type
)
if idl_type.serializer is not None:
ctxt.add_not_custom_scalar_serialization_not_supported_error(
idl_type, syntax_type, idl_type.name, bson_type
)
if bson_type == "bindata" and isinstance(idl_type, syntax.Type) and idl_type.default:
ctxt.add_bindata_no_default(idl_type, syntax_type, idl_type.name)

View File

@ -582,53 +582,6 @@ class TestBinder(testcase.IDLTestcase):
idl.errors.ERROR_ID_BAD_ANY_TYPE_USE,
)
# Test unsupported serialization
for bson_type in [
"bool",
"date",
"null",
"decimal",
"double",
"int",
"long",
"objectid",
"regex",
"timestamp",
"undefined",
]:
self.assert_bind_fail(
textwrap.dedent(
"""
types:
foofoo:
description: foo
cpp_type: std::string
bson_serialization_type: %s
serializer: foo
deserializer: BSONElement::fake
is_view: false
"""
% (bson_type)
),
idl.errors.ERROR_ID_CUSTOM_SCALAR_SERIALIZATION_NOT_SUPPORTED,
)
self.assert_bind_fail(
textwrap.dedent(
"""
types:
foofoo:
description: foo
cpp_type: std::string
bson_serialization_type: %s
deserializer: foo
is_view: false
"""
% (bson_type)
),
idl.errors.ERROR_ID_CUSTOM_SCALAR_SERIALIZATION_NOT_SUPPORTED,
)
# Test 'any' serialization needs deserializer
self.assert_bind_fail(
textwrap.dedent("""

View File

@ -18,7 +18,10 @@ const clusterParameterInsertSucceeds = (doc) => {
};
clusterParameterInsertSucceeds({"_id": "testIntClusterParameter"});
clusterParameterInsertSucceeds({"_id": "testStrClusterParameter", "clusterParameterTime": "abcd"});
assert.commandFailedWithCode(
conn.getDB("config").clusterParameters.insert({"_id": "testStrClusterParameter", "clusterParameterTime": "abcd"}),
ErrorCodes.TypeMismatch,
);
assert.commandFailedWithCode(conn.getDB("config").clusterParameters.insert({"_id": 12345}), ErrorCodes.OperationFailed);
assert.commandFailedWithCode(conn.getDB("config").clusterParameters.insert({"_id": ""}), ErrorCodes.OperationFailed);

View File

@ -714,7 +714,6 @@ filegroup(
"//src/mongo/db/repl:oplog_entry_serialization.h",
"//src/mongo/db/repl:optime_hdrs",
"//src/mongo/db/repl:read_concern_gen",
"//src/mongo/db/repl:read_concern_idl_hdrs",
"//src/mongo/db/repl:read_concern_level.h",
"//src/mongo/db/repl:repl_client_info.h",
"//src/mongo/db/repl:repl_server_parameters_gen",
@ -763,11 +762,11 @@ filegroup(
"//src/mongo/executor:remote_command_request.h",
"//src/mongo/executor:remote_command_response.h",
"//src/mongo/executor:task_executor.h",
"//src/mongo/idl:basic_types_serialization.h",
"//src/mongo/idl:command_generic_argument.h",
"//src/mongo/idl:generic_argument.h",
"//src/mongo/idl:generic_argument_gen",
"//src/mongo/idl:idl_parser.h",
"//src/mongo/idl:error_status_idl.h",
"//src/mongo/platform:visibility.h",
"//src/mongo/rpc:get_status_from_command_result.h",
"//src/mongo/rpc:get_status_from_command_result_write_util.h",

View File

@ -1734,7 +1734,6 @@ mongo_cc_library(
"generic_argument_util.cpp",
"index_names.cpp",
"keypattern.cpp",
"logical_time.cpp",
"mirror_maestro_feature_flag_gen",
"read_write_concern_provenance.cpp",
"server_options.cpp",
@ -1769,7 +1768,6 @@ mongo_cc_library(
"//src/mongo/db/repl:read_concern_args.cpp",
"//src/mongo/db/repl:read_concern_args_gen",
"//src/mongo/db/repl:read_concern_gen",
"//src/mongo/db/repl:read_concern_idl.cpp",
"//src/mongo/db/sharding_environment:shard_id.cpp",
"//src/mongo/db/sharding_environment:sharding_types_gen",
"//src/mongo/db/vector_clock:vector_clock_gen",
@ -1781,10 +1779,8 @@ mongo_cc_library(
"//src/mongo/db/versioning_protocol:shard_version.cpp",
"//src/mongo/db/versioning_protocol:shard_version_gen",
"//src/mongo/idl:command_generic_argument.cpp",
"//src/mongo/idl:error_status_idl.cpp",
"//src/mongo/idl:generic_argument.cpp",
"//src/mongo/idl:generic_argument_gen",
"//src/mongo/rpc:get_status_from_command_result.cpp",
"//src/mongo/rpc/metadata:audit_metadata_gen",
"//src/mongo/util:database_name_util.cpp",
"//src/mongo/util:fail_point.cpp",
@ -1822,11 +1818,14 @@ mongo_cc_library(
],
deps = [
":cluster_role",
":logical_time",
"//src/mongo:base",
"//src/mongo/bson/util:bson_extract",
"//src/mongo/db/repl:optime",
"//src/mongo/db/session:logical_session_id",
"//src/mongo/idl:basic_types_serialization",
"//src/mongo/idl:idl_parser",
"//src/mongo/rpc:get_status_from_command_result",
"//src/mongo/util:pcre_wrapper",
"//src/mongo/util/net:hostandport_impl",
],
@ -2338,6 +2337,19 @@ mongo_cc_library(
],
)
mongo_cc_library(
name = "logical_time",
srcs = [
"logical_time.cpp",
],
hdrs = [
"logical_time.h",
],
deps = [
"//src/mongo:base",
],
)
mongo_cc_library(
name = "query_expressions",
srcs = [

View File

@ -41,7 +41,7 @@ global:
- "mongo/bson/bson_time_support.h"
- "mongo/util/uuid.h"
- "mongo/idl/command_generic_argument.h"
- "mongo/idl/error_status_idl.h"
- "mongo/idl/basic_types_serialization.h"
types:
string:
@ -290,16 +290,16 @@ types:
logicalTime:
bson_serialization_type: any
description: "A MongoDB LogicalTime."
cpp_type: LogicalTime
serializer: LogicalTime::serializeToBSON
deserializer: LogicalTime::parseFromBSON
cpp_type: "mongo::LogicalTime"
serializer: "mongo::LogicalTime::serializeToBSON"
deserializer: "mongo::idl::deserializeLogicalTime"
is_view: false
timestamp:
bson_serialization_type: timestamp
description: "A BSON TimeStamp"
cpp_type: "mongo::Timestamp"
deserializer: "mongo::BSONElement::timestamp"
deserializer: "mongo::idl::deserializeTimestamp"
is_view: false
namespacestring:

View File

@ -164,7 +164,7 @@ void updateParameter(OperationContext* opCtx,
}
auto cptElem = doc[kCPTField];
if ((cptElem.type() != BSONType::date) && (cptElem.type() != BSONType::timestamp)) {
if ((cptElem.type() != BSONType::timestamp)) {
LOGV2_DEBUG(6226302,
1,
"Update to cluster server parameter has invalid clusterParameterTime",

View File

@ -54,7 +54,7 @@
#include "mongo/db/session/session_catalog_mongod.h"
#include "mongo/db/sharding_environment/config_server_test_fixture.h"
#include "mongo/db/versioning_protocol/chunk_version.h"
#include "mongo/idl/error_status_idl.h"
#include "mongo/idl/basic_types_serialization.h"
#include "mongo/unittest/unittest.h"
#include "mongo/util/assert_util.h"

View File

@ -1186,6 +1186,7 @@ mongo_cc_library(
"//src/mongo/util/version:releases_header",
],
deps = [
"//src/mongo/idl:basic_types_serialization",
"//src/mongo/idl:idl_parser",
],
)

View File

@ -685,6 +685,7 @@ mongo_cc_library(
deps = [
"//src/mongo:base",
"//src/mongo/bson/util:bson_extract",
"//src/mongo/idl:basic_types_serialization",
"//src/mongo/idl:idl_parser",
],
)
@ -1708,12 +1709,6 @@ filegroup(
srcs = [":optime.h"],
)
# TODO(SERVER-96856): Remove cycle created by moving //src/mongo/db/repl:read_concern_idl.h to //src/mongo/db:server_base
filegroup(
name = "read_concern_idl_hdrs",
srcs = [":read_concern_idl.h"],
)
mongo_cc_library(
name = "oplog_entry_test_helpers",
srcs = [

View File

@ -29,7 +29,6 @@ global:
cpp_namespace: "mongo::repl"
cpp_includes:
- "mongo/db/logical_time.h"
- "mongo/db/repl/read_concern_idl.h"
imports:
- "mongo/db/basic_types.idl"
@ -47,18 +46,6 @@ enums:
kAvailableReadConcern: "available"
kSnapshotReadConcern: "snapshot"
types:
# TODO: SERVER-89143 delete this type, use logicalTime from basic_types.idl.
ReadConcernLogicalTime:
bson_serialization_type: any
description:
"A MongoDB LogicalTime. This type has stricter deserialization rules than the
logicalTime defined in basic_types.idl."
cpp_type: ::mongo::LogicalTime
serializer: ::mongo::serializeReadConcernLogicalTime
deserializer: ::mongo::deserializeReadConcernLogicalTime
is_view: false
structs:
ReadConcernIdl:
description:
@ -77,11 +64,11 @@ structs:
optional: true
afterClusterTime:
description: Read data after cluster-wide cluster time.
type: ReadConcernLogicalTime
type: logicalTime
optional: true
atClusterTime:
description: Read data at a particular cluster time.
type: ReadConcernLogicalTime
type: logicalTime
optional: true
provenance:
type: ReadWriteConcernProvenanceSource

View File

@ -1,53 +0,0 @@
/**
* Copyright (C) 2024-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#include "mongo/db/repl/read_concern_idl.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/bsontypes.h"
namespace mongo {
LogicalTime deserializeReadConcernLogicalTime(BSONElement e) {
uassert(ErrorCodes::TypeMismatch,
fmt::format("\"{}\" had the wrong type. Expected {}, found {}",
e.fieldNameStringData(),
BSONType::timestamp,
typeName(e.type())),
e.type() == BSONType::timestamp);
return LogicalTime(Timestamp(e.timestampValue()));
}
void serializeReadConcernLogicalTime(const LogicalTime& w,
StringData fieldName,
BSONObjBuilder* builder) {
builder->append(fieldName, w.asTimestamp());
}
} // namespace mongo

View File

@ -1,43 +0,0 @@
/**
* Copyright (C) 2024-present MongoDB, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the Server Side Public License, version 1,
* as published by MongoDB, Inc.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Server Side Public License for more details.
*
* You should have received a copy of the Server Side Public License
* along with this program. If not, see
* <http://www.mongodb.com/licensing/server-side-public-license>.
*
* As a special exception, the copyright holders give permission to link the
* code of portions of this program with the OpenSSL library under certain
* conditions as described in each individual source file and distribute
* linked combinations including the program with the OpenSSL library. You
* must comply with the Server Side Public License in all respects for
* all of the code used other than as permitted herein. If you modify file(s)
* with this exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do so,
* delete this exception statement from your version. If you delete this
* exception statement from all source files in the program, then also delete
* it in the license file.
*/
#pragma once
#include "mongo/bson/bsonelement.h"
#include "mongo/db/logical_time.h"
#include "mongo/util/modules.h"
namespace MONGO_MOD_PUB mongo {
// Helpers for IDL parsing
LogicalTime deserializeReadConcernLogicalTime(BSONElement e);
void serializeReadConcernLogicalTime(const LogicalTime& w,
StringData fieldName,
BSONObjBuilder* builder);
} // namespace MONGO_MOD_PUB mongo

View File

@ -342,3 +342,20 @@ mongo_cc_benchmark(
"//src/mongo/rpc:message",
],
)
mongo_cc_library(
name = "basic_types_serialization",
srcs = [
"basic_types_serialization.cpp",
],
hdrs = [
"basic_types_serialization.h",
"//src/mongo:core_headers",
],
deps = [
"//src/mongo:base",
"//src/mongo/bson/util:bson_extract",
"//src/mongo/db:logical_time",
"//src/mongo/rpc:get_status_from_command_result",
],
)

View File

@ -27,7 +27,7 @@
* it in the license file.
*/
#include "mongo/idl/error_status_idl.h"
#include "mongo/idl/basic_types_serialization.h"
#include "mongo/bson/util/bson_extract.h"
#include "mongo/rpc/get_status_from_command_result.h"
@ -48,4 +48,25 @@ Status deserializeErrorStatus(const BSONElement& bsonElem) {
return getErrorStatusFromCommandResult(bsonElem.Obj());
}
void assertBSONIsTimestamp(const BSONElement& e) {
uassert(ErrorCodes::TypeMismatch,
fmt::format("\"{}\" had the wrong type. Expected {}, found {}",
e.fieldNameStringData(),
BSONType::timestamp,
typeName(e.type())),
e.type() == BSONType::timestamp);
}
LogicalTime deserializeLogicalTime(const BSONElement& e) {
assertBSONIsTimestamp(e);
return LogicalTime(Timestamp(e.timestampValue()));
}
Timestamp deserializeTimestamp(const BSONElement& e) {
assertBSONIsTimestamp(e);
return Timestamp(e.timestampValue());
}
} // namespace mongo::idl

View File

@ -33,15 +33,27 @@
#include "mongo/base/string_data.h"
#include "mongo/bson/bsonelement.h"
#include "mongo/bson/bsonobjbuilder.h"
#include "mongo/bson/timestamp.h"
#include "mongo/db/logical_time.h"
#include "mongo/util/modules.h"
namespace mongo::idl {
// Serializes an error status. OK statuses will throw.
namespace MONGO_MOD_PUB mongo {
namespace idl {
// Serializers and deserializers for basic_types.idl
// OK statuses will throw.
void serializeErrorStatus(const Status& status, StringData fieldName, BSONObjBuilder* builder);
// Deserializes an error status from the BSONElement which must hold an object.
// OK status codes will throw. The BSON object may include fields "code" and "errmsg", which
// are parsed into the returned Status.
Status deserializeErrorStatus(const BSONElement& bsonElem);
} // namespace mongo::idl
Timestamp deserializeTimestamp(const BSONElement& bsonElem);
// Asserts that the BSONElement has bson type Timestamp.
LogicalTime deserializeLogicalTime(const BSONElement& e);
} // namespace idl
} // namespace MONGO_MOD_PUB mongo

View File

@ -5194,8 +5194,8 @@ TEST(IDLOwnershipTests, NonViewStructParseAssumesOwnership) {
NonViewStruct idlStruct;
BSONObj ownedBSON = BSON("a" << "b");
ASSERT_TRUE(ownedBSON.isOwned());
BSONObj ownedElementBSON = BSON("field34" << "a");
BSONElement ownedElement = ownedElementBSON.getField("field34");
BSONObj ownedElementBSON = BSON("field33" << "a");
BSONElement ownedElement = ownedElementBSON.getField("field33");
ASSERT_TRUE(ownedElementBSON.isOwned());
{
uint8_t testArray[] = {1, 2, 3};
@ -5248,18 +5248,17 @@ TEST(IDLOwnershipTests, NonViewStructParseAssumesOwnership) {
bob.append("field23", 23);
bob.append("field24", 25);
bob.append("field25", 26);
bob.append("field26", testLogicalTimeBSON);
bob.append("field27", testLogicalTimeBSON);
bob.append("field28", testTimestamp);
bob.append("field29", testNamespaceString);
bob.append("field26", testTimestamp);
bob.append("field27", testTimestamp);
bob.append("field28", testNamespaceString);
bob.append("field29", "abcd");
bob.append("field30", "abcd");
bob.append("field31", "abcd");
bob.append("field32", testConnectionString);
bob.append("field33", testFCVstring);
bob.append("field31", testConnectionString);
bob.append("field32", testFCVstring);
bob.append(ownedElement);
bob.append("field35", testOID);
bob.append("field36", testTenantIdStr);
bob.append("field37", testDatabaseNameStr);
bob.append("field34", testOID);
bob.append("field35", testTenantIdStr);
bob.append("field36", testDatabaseNameStr);
auto tmp = bob.obj();
// We want to test that idlStruct is internally a non view type, and that the struct
// inherently owns all its members.
@ -5306,7 +5305,6 @@ TEST(IDLOwnershipTests, NonViewStructParseAssumesOwnership) {
idlStruct.getField34();
idlStruct.getField35();
idlStruct.getField36();
idlStruct.getField37();
ASSERT_BSONOBJ_EQ(idlStruct.getField20(), BSON("a" << "b"));
}
@ -5359,5 +5357,37 @@ TEST(IDLTrie, TestPrefixes) {
ASSERT_TRUE(TestTrieArgs::hasField("swimmed"));
}
template <typename StructType, typename ParseValueType>
void testBasicTypeSerialization(StringData fieldName, ParseValueType value) {
// Positive: parse correct type.
{
auto testDoc = BSON(fieldName << value);
StructType::parse(testDoc);
}
// Negative: parsing date bson type should fail.
{
auto testDoc = BSON(fieldName << Date_t::max());
ASSERT_THROWS_CODE(
StructType::parse(testDoc), AssertionException, ErrorCodes::TypeMismatch);
}
// Negative: parsing other bson type should fail.
{
auto testDoc = BSON(fieldName << "string");
ASSERT_THROWS_CODE(
StructType::parse(testDoc), AssertionException, ErrorCodes::TypeMismatch);
}
}
TEST(IDLBasicTypeSerialization, Timestamp) {
testBasicTypeSerialization<TimestampStruct, Timestamp>("timestamp", Timestamp::max());
}
TEST(IDLBasicTypeSerialization, LogicalTime) {
// The LogicalTime type parses from BSON using the timestamp BSON type.
testBasicTypeSerialization<LogicalTimeStruct, Timestamp>("logicalTime", Timestamp::max());
}
} // namespace
} // namespace mongo

View File

@ -212,6 +212,22 @@ structs:
field1: int
field2: int
##################################################################################################
#
# Structs to test serialization and parsing of basic types
#
##################################################################################################
TimestampStruct:
description: UnitTest for parsing and serialization of timestamp
fields:
timestamp: timestamp
LogicalTimeStruct:
description: UnitTest for parsing and serialization of logicalTime
fields:
logicalTime: logicalTime
##################################################################################################
#
# Struct to test non view basic types
@ -247,21 +263,20 @@ structs:
field24: milliseconds
field25: seconds
field26: logicalTime
field27: logicalTime
field28: timestamp
field29: namespacestring
field30: base64string
field31: base64urlstring
field32: connection_string
field33: fcv_string
field34: IDLAnyTypeOwned
field35:
field27: timestamp
field28: namespacestring
field29: base64string
field30: base64urlstring
field31: connection_string
field32: fcv_string
field33: IDLAnyTypeOwned
field34:
type: tenant_id
optional: true
field36:
field35:
type: tenant_id_hex
optional: true
field37: database_name
field36: database_name
##################################################################################################
#

View File

@ -213,6 +213,19 @@ mongo_cc_library(
],
)
mongo_cc_library(
name = "get_status_from_command_result",
srcs = [
"get_status_from_command_result.cpp",
],
hdrs = [
"get_status_from_command_result.h",
],
deps = [
"//src/mongo:base",
],
)
mongo_cc_integration_test(
name = "rpc_integration_test",
srcs = [