diff options
Diffstat (limited to 'src/mongo/rpc')
-rw-r--r-- | src/mongo/rpc/object_check.h | 23 | ||||
-rw-r--r-- | src/mongo/rpc/object_check_test.cpp | 44 |
2 files changed, 61 insertions, 6 deletions
diff --git a/src/mongo/rpc/object_check.h b/src/mongo/rpc/object_check.h index 26bc175539e..ac9ddb899d8 100644 --- a/src/mongo/rpc/object_check.h +++ b/src/mongo/rpc/object_check.h @@ -29,10 +29,14 @@ #pragma once +#include <algorithm> + #include "mongo/base/data_type_validated.h" #include "mongo/bson/bson_validate.h" #include "mongo/bson/bsontypes.h" #include "mongo/db/server_options.h" +#include "mongo/logv2/redaction.h" +#include "mongo/util/hex.h" // We do not use the rpc namespace here so we can specialize Validator. namespace mongo { @@ -47,7 +51,24 @@ template <> struct Validator<BSONObj> { inline static Status validateLoad(const char* ptr, size_t length) { - return serverGlobalParams.objcheck ? validateBSON(ptr, length) : Status::OK(); + if (!serverGlobalParams.objcheck) { + return Status::OK(); + } + + auto status = validateBSON(ptr, length); + if (serverGlobalParams.crashOnInvalidBSONError && !status.isOK()) { + std::string msg = "Invalid BSON was received: " + status.toString() + + // Using std::min with length so we do not max anything out in case the corruption + // is in the size of the object. The hex dump will be longer if needed. + ", beginning 5000 characters: " + std::string(ptr, std::min(length, (size_t)5000)) + + ", length: " + std::to_string(length) + + // Using std::min with hex dump length, too, to ensure we do not throw in hexdump() + // because of exceeded length and miss out on the core dump of the fassert below. + ", hex dump: " + hexdump(ptr, std::min(length, (size_t)(1000000 - 1))); + Status builtStatus(ErrorCodes::InvalidBSON, redact(msg)); + fassertFailedWithStatus(50761, builtStatus); + } + return status; } static Status validateStore(const BSONObj& toStore); diff --git a/src/mongo/rpc/object_check_test.cpp b/src/mongo/rpc/object_check_test.cpp index 52010604f53..7ecb0d0ed93 100644 --- a/src/mongo/rpc/object_check_test.cpp +++ b/src/mongo/rpc/object_check_test.cpp @@ -35,23 +35,22 @@ #include "mongo/db/jsobj.h" #include "mongo/db/server_options.h" #include "mongo/rpc/object_check.h" +#include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" #include "mongo/util/scopeguard.h" namespace { using namespace mongo; +using std::begin; +using std::end; +using std::swap; TEST(DataTypeValidated, BSONValidationEnabled) { - using std::swap; - bool wasEnabled = serverGlobalParams.objcheck; const auto setValidation = [&](bool enabled) { serverGlobalParams.objcheck = enabled; }; ON_BLOCK_EXIT([=] { setValidation(wasEnabled); }); - using std::begin; - using std::end; - BSONObj valid = BSON("baz" << "bar" << "garply" @@ -88,4 +87,39 @@ TEST(DataTypeValidated, BSONValidationEnabled) { ASSERT_OK(cdrc.readAndAdvanceNoThrow(&v)); } } + +DEATH_TEST(ObjectCheck, BSONValidationEnabledWithCrashOnError, "50761") { + bool objcheckValue = serverGlobalParams.objcheck; + serverGlobalParams.objcheck = true; + ON_BLOCK_EXIT([=] { serverGlobalParams.objcheck = objcheckValue; }); + + bool crashOnErrorValue = serverGlobalParams.crashOnInvalidBSONError; + serverGlobalParams.crashOnInvalidBSONError = true; + ON_BLOCK_EXIT([&] { serverGlobalParams.crashOnInvalidBSONError = crashOnErrorValue; }); + + BSONObj valid = BSON("baz" + << "bar" + << "garply" + << BSON("foo" + << "bar")); + char buf[1024] = {0}; + std::copy(valid.objdata(), valid.objdata() + valid.objsize(), begin(buf)); + + { + // mess up the data + DataRangeCursor drc(begin(buf), end(buf)); + // skip past size so we don't trip any sanity checks. + drc.advanceNoThrow(4).transitional_ignore(); // skip size + while (drc.writeAndAdvanceNoThrow(0xFF).isOK()) + ; + } + + { + Validated<BSONObj> v; + ConstDataRangeCursor cdrc(begin(buf), end(buf)); + // Crashes because of invalid BSON + cdrc.readAndAdvanceNoThrow(&v).ignore(); + } +} + } // namespace |