[SERVER-31516] NamespaceString's no-arguments constructor results in an improperly initialized object, can later cause segfault Created: 11/Oct/17  Updated: 30/Oct/23  Resolved: 11/Oct/17

Status: Closed
Project: Core Server
Component/s: Internal Code, Querying
Affects Version/s: None
Fix Version/s: 3.2.18, 3.4.11, 3.6.0-rc0

Type: Bug Priority: Critical - P2
Reporter: David Storch Assignee: David Storch
Resolution: Fixed Votes: 0
Labels: None
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Backports
Depends
Backwards Compatibility: Fully Compatible
Operating System: ALL
Backport Requested:
v3.4, v3.2, v3.0
Sprint: Query 2017-10-23
Participants:
Linked BF Score: 0

 Description   

The NamespaceString class represents a fully-qualified collection name, such as "myDb.myCollection". Underneath the hood, it consists of a std::string (which owns the string itself), and a size_t giving the index of the '.' character:

https://github.com/mongodb/mongo/blob/b1d1b244c73a726c0c6708cf1cb196079f570414/src/mongo/db/namespace_string.h#L395-L396

It also supports a no-arguments constructor, which can be used to initialize a NamespaceString in an empty state. The intended design is that in the empty state the _ns member is the empty string, and the _dotIndex member is equal to std::string::npos. However, the constructor improperly initializes the _dotIndex member to zero:

https://github.com/mongodb/mongo/blob/b1d1b244c73a726c0c6708cf1cb196079f570414/src/mongo/db/namespace_string-inl.h#L121

This is wrong, because the NamespaceString::db() and NamespaceString::coll() methods check for std::string::npos in order to make sure that they correctly handle the empty state:

https://github.com/mongodb/mongo/blob/b1d1b244c73a726c0c6708cf1cb196079f570414/src/mongo/db/namespace_string-inl.h#L34-L41

If the coll() method is called on an empty NamespaceString, it will end up returning a corrupt StringData. Specifically, it looks like we end up computing the length of the string as -1, then assigning this value to a size_t, resulting in a large positive length value for the string (despite the memory not having been allocated to hold such a long string).

There are few code paths which actually call NamespaceString::coll() on an empty namespace string. At the very least, this has been observed to cause a crash when a globally-owned cursor is killed on a system that is configured to use auditing.



 Comments   
Comment by Githook User [ 27/Oct/17 ]

Author:

{'email': 'david.storch@10gen.com', 'name': 'David Storch', 'username': 'dstorch'}

Message: SERVER-31516 Fix improper initialization of empty NamespaceString.

(cherry picked from commit e7390933e152d8e6f00c90ac341f691780c261fd)

Conflicts:
src/mongo/db/namespace_string_test.cpp
Branch: v3.2
https://github.com/mongodb/mongo/commit/44c83a821606db1c8b34b9c2a1ee2c485d3fff39

Comment by Githook User [ 27/Oct/17 ]

Author:

{'email': 'david.storch@10gen.com', 'name': 'David Storch', 'username': 'dstorch'}

Message: SERVER-31516 Fix improper initialization of empty NamespaceString.

(cherry picked from commit e7390933e152d8e6f00c90ac341f691780c261fd)
Branch: v3.4
https://github.com/mongodb/mongo/commit/34cdaa485cd98bfb3b1916d8a3d8815f91a17359

Comment by Githook User [ 11/Oct/17 ]

Author:

{'email': 'david.storch@10gen.com', 'name': 'David Storch', 'username': 'dstorch'}

Message: SERVER-31516 Add test for auditing killCursors command.

The test exercises a code path which could crash due to the
NamespaceString bug described in this ticket.
Branch: master
https://github.com/10gen/mongo-enterprise-modules/commit/541071b307c4af3b207bec076bbf07bdc3b44c9b

Comment by Githook User [ 11/Oct/17 ]

Author:

{'email': 'david.storch@10gen.com', 'name': 'David Storch', 'username': 'dstorch'}

Message: SERVER-31516 Fix improper initialization of empty NamespaceString.
Branch: master
https://github.com/mongodb/mongo/commit/e7390933e152d8e6f00c90ac341f691780c261fd

Comment by David Storch [ 11/Oct/17 ]

This can result in a crash with a stack like the following:

/home/charlie/github/mongo/src/mongo/util/stacktrace_posix.cpp:172:30: mongo::printStackTrace(std::ostream&)
 /home/charlie/github/mongo/src/mongo/util/signal_handlers_synchronous.cpp:180:5: mongo::(anonymous namespace)::printSignalAndBacktrace(int)
 /home/charlie/github/mongo/src/mongo/util/signal_handlers_synchronous.cpp:276:0: mongo::(anonymous namespace)::abruptQuitWithAddrSignal(int, siginfo_t*, void*)
 ??:0:0: ??
 /build/glibc-bfm8X4/glibc-2.23/string/../sysdeps/x86_64/multiarch/memcpy-sse2-unaligned.S:150:0: ??
 /home/charlie/github/mongo/src/mongo/base/string_data.h:266:9: mongo::StringData::copyTo(char*, bool) const
 /home/charlie/github/mongo/src/mongo/bson/util/builder.h:271:0: mongo::_BufBuilder<mongo::SharedBufferAllocator>::appendStr(mongo::StringData, bool)
 /home/charlie/github/mongo/src/mongo/bson/bsonobjbuilder.h:487:0: mongo::BSONObjBuilder::append(mongo::StringData, mongo::StringData)
 /home/charlie/github/mongo/src/mongo/bson/mutable/document.cpp:2280:5: mongo::mutablebson::Document::makeElementString(mongo::StringData, mongo::StringData)
 /home/charlie/github/mongo/src/mongo/bson/mutable/element.cpp:68:21: mongo::mutablebson::Element::appendString(mongo::StringData, mongo::StringData)
 /home/charlie/github/mongo/src/mongo/db/modules/enterprise/src/audit/audit_authz_check.cpp:165:5: mongo::audit::logKillCursorsAuthzCheck(mongo::Client*, mongo::NamespaceString const&, long long, mongo::ErrorCodes::Error)
 /home/charlie/github/mongo/src/mongo/db/cursor_manager.cpp:659:13: mongo::CursorManager::eraseCursor(mongo::OperationContext*, long long, bool)
 /home/charlie/github/mongo/src/mongo/db/commands/killcursors_cmd.cpp:106:16: mongo::KillCursorsCmd::_killCursor(mongo::OperationContext*, mongo::NamespaceString const&, long long)
 /home/charlie/github/mongo/src/mongo/db/commands/killcursors_common.cpp:82:25: mongo::KillCursorsCmdBase::run(mongo::OperationContext*, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, mongo::BSONObj const&, mongo::BSONObjBuilder&)
 /home/charlie/github/mongo/src/mongo/db/commands.cpp:398:12: mongo::BasicCommand::enhancedRun(mongo::OperationContext*, mongo::OpMsgRequest const&, mongo::BSONObjBuilder&)
 /home/charlie/github/mongo/src/mongo/db/commands.cpp:336:16: mongo::Command::publicRun(mongo::OperationContext*, mongo::OpMsgRequest const&, mongo::BSONObjBuilder&)
 /home/charlie/github/mongo/src/mongo/db/service_entry_point_mongod.cpp:448:18: mongo::(anonymous namespace)::runCommandImpl(mongo::OperationContext*, mongo::Command*, mongo::OpMsgRequest const&, mongo::rpc::ReplyBuilderInterface*, mongo::LogicalTime)
 /home/charlie/github/mongo/src/mongo/db/service_entry_point_mongod.cpp:713:0: mongo::(anonymous namespace)::execCommandDatabase(mongo::OperationContext*, mongo::Command*, mongo::OpMsgRequest const&, mongo::rpc::ReplyBuilderInterface*)
 /home/charlie/github/mongo/src/mongo/db/service_entry_point_mongod.cpp:829:13: mongo::(anonymous namespace)::runCommands(mongo::OperationContext*, mongo::Message const&)::$_4::operator()() const
 /home/charlie/github/mongo/src/mongo/db/service_entry_point_mongod.cpp:785:0: mongo::(anonymous namespace)::runCommands(mongo::OperationContext*, mongo::Message const&)
 /home/charlie/github/mongo/src/mongo/db/service_entry_point_mongod.cpp:1090:0: mongo::ServiceEntryPointMongod::handleRequest(mongo::OperationContext*, mongo::Message const&)
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.cpp:307:29: mongo::ServiceStateMachine::_processMessage(mongo::ServiceStateMachine::ThreadGuard&)
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.cpp:401:17: mongo::ServiceStateMachine::_runNextInGuard(mongo::ServiceStateMachine::ThreadGuard&)
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.cpp:365:12: mongo::ServiceStateMachine::runNext()
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.cpp:429:0: mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4::operator()() const
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.h:164:0: void mongo::ServiceStateMachine::_scheduleFunc<mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4>(mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4&&, mongo::transport::ServiceExecutor::ScheduleFlags)::{lambda()#1}::operator()() const
 /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1871:0: std::_Function_handler<void (), void mongo::ServiceStateMachine::_scheduleFunc<mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4>(mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4&&, mongo::transport::ServiceExecutor::ScheduleFlags)::{lambda()#1}>::_M_invoke(std::_Any_data const&)
 /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2267:14: std::function<void ()>::operator()() const
 /home/charlie/github/mongo/src/mongo/transport/service_executor_synchronous.cpp:114:0: mongo::transport::ServiceExecutorSynchronous::schedule(std::function<void ()>, mongo::transport::ServiceExecutor::ScheduleFlags)
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.h:163:25: void mongo::ServiceStateMachine::_scheduleFunc<mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4>(mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4&&, mongo::transport::ServiceExecutor::ScheduleFlags)
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.cpp:429:0: mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.cpp:228:16: mongo::ServiceStateMachine::_sourceCallback(mongo::Status)
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.cpp:388:21: mongo::ServiceStateMachine::_runNextInGuard(mongo::ServiceStateMachine::ThreadGuard&)
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.cpp:365:12: mongo::ServiceStateMachine::runNext()
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.cpp:429:0: mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4::operator()() const
 /home/charlie/github/mongo/src/mongo/transport/service_state_machine.h:164:0: void mongo::ServiceStateMachine::_scheduleFunc<mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4>(mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4&&, mongo::transport::ServiceExecutor::ScheduleFlags)::{lambda()#1}::operator()() const
 /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1871:0: std::_Function_handler<void (), void mongo::ServiceStateMachine::_scheduleFunc<mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4>(mongo::ServiceStateMachine::scheduleNext(mongo::transport::ServiceExecutor::ScheduleFlags)::$_4&&, mongo::transport::ServiceExecutor::ScheduleFlags)::{lambda()#1}>::_M_invoke(std::_Any_data const&)
 /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2267:14: std::function<void ()>::operator()() const
 /home/charlie/github/mongo/src/mongo/transport/service_executor_synchronous.cpp:131:0: mongo::transport::ServiceExecutorSynchronous::schedule(std::function<void ()>, mongo::transport::ServiceExecutor::ScheduleFlags)::$_2::operator()() const
 /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:1871:0: std::_Function_handler<void (), mongo::transport::ServiceExecutorSynchronous::schedule(std::function<void ()>, mongo::transport::ServiceExecutor::ScheduleFlags)::$_2>::_M_invoke(std::_Any_data const&)
 /usr/bin/../lib/gcc/x86_64-linux-gnu/5.4.0/../../../../include/c++/5.4.0/functional:2267:14: std::function<void ()>::operator()() const
 /home/charlie/github/mongo/src/mongo/transport/service_entry_point_utils.cpp:55:0: mongo::(anonymous namespace)::runFunc(void*)
 ??:0:0: start_thread
 /build/glibc-bfm8X4/glibc-2.23/misc/../sysdeps/unix/sysv/linux/x86_64/clone.S:109:0: clone

Generated at Thu Feb 08 04:27:18 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.