|
Here's the call stack from the Visual Studio debugger when the bad_alloc is thrown in the 64-bit debug Windows version:
>Debug.ListCallStack
|
Index Function
|
--------------------------------------------------------------------------------
|
1 KernelBase.dll!RaiseException()
|
2 mongod.exe!_CxxThrowException(void * pExceptionObject=0x00000000078ab748, const _s__ThrowInfo * pThrowInfo=0x0000000141f193d8)
|
*3 mongod.exe!operator new(unsigned __int64 size=0xfeeefeeefeeefef0)
|
4 mongod.exe!std::_Allocate<char>(unsigned __int64 _Count=0xfeeefeeefeeefef0, char * __formal=0x0000000000000000)
|
5 mongod.exe!std::allocator<char>::allocate(unsigned __int64 _Count=0xfeeefeeefeeefef0)
|
6 mongod.exe!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Copy(unsigned __int64 _Newsize=0xfeeefeeefeeefeee, unsigned __int64 _Oldlen=0x0000000000000000)
|
7 mongod.exe!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::_Grow(unsigned __int64 _Newsize=0xfeeefeeefeeefeee, bool _Trim=false)
|
8 mongod.exe!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::assign(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & _Right=<Bad Ptr>, unsigned __int64 _Roff=0x0000000000000000, unsigned __int64 _Count=0xffffffffffffffff)
|
9 mongod.exe!std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> >(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & _Right=<Bad Ptr>)
|
10 mongod.exe!mongo::ClientCursor::ns()
|
11 mongod.exe!mongo::handleCursorCommand(__int64 id=0x0000000000000000, mongo::BSONObj & cmdObj={...}, mongo::BSONObjBuilder & result={...})
|
12 mongod.exe!mongo::PipelineCommand::run(const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & db="test", mongo::BSONObj & cmdObj={...}, int options=0x00000000, std::basic_string<char,std::char_traits<char>,std::allocator<char> > & errmsg="", mongo::BSONObjBuilder & result={...}, bool fromRepl=false)
|
13 mongod.exe!mongo::_execCommand(mongo::Command * c=0x0000000141f58498, const std::basic_string<char,std::char_traits<char>,std::allocator<char> > & dbname="test", mongo::BSONObj & cmdObj={...}, int queryOptions=0x00000000, std::basic_string<char,std::char_traits<char>,std::allocator<char> > & errmsg="", mongo::BSONObjBuilder & result={...}, bool fromRepl=false)
|
14 mongod.exe!mongo::Command::execCommand(mongo::Command * c=0x0000000141f58498, mongo::Client & client={...}, int queryOptions=0x00000000, const char * cmdns=0x0000000002006404, mongo::BSONObj & cmdObj={...}, mongo::BSONObjBuilder & result={...}, bool fromRepl=false)
|
15 mongod.exe!mongo::_runCommands(const char * ns=0x0000000002006404, mongo::BSONObj & _cmdobj={...}, mongo::_BufBuilder<mongo::TrivialAllocator> & b={...}, mongo::BSONObjBuilder & anObjBuilder={...}, bool fromRepl=false, int queryOptions=0x00000000)
|
16 mongod.exe!mongo::runCommands(const char * ns=0x0000000002006404, mongo::BSONObj & jsobj={...}, mongo::CurOp & curop={...}, mongo::_BufBuilder<mongo::TrivialAllocator> & b={...}, mongo::BSONObjBuilder & anObjBuilder={...}, bool fromRepl=false, int queryOptions=0x00000000)
|
17 mongod.exe!mongo::runQuery(mongo::Message & m={...}, mongo::QueryMessage & q={...}, mongo::CurOp & curop={...}, mongo::Message & result={...})
|
18 mongod.exe!mongo::receivedQuery(mongo::Client & c={...}, mongo::DbResponse & dbresponse={...}, mongo::Message & m={...})
|
19 mongod.exe!mongo::assembleResponse(mongo::Message & m={...}, mongo::DbResponse & dbresponse={...}, const mongo::HostAndPort & remote={...})
|
20 mongod.exe!mongo::MyMessageHandler::process(mongo::Message & m={...}, mongo::AbstractMessagingPort * port=0x0000000001fc3190, mongo::LastError * le=0x0000000001ebf150)
|
21 mongod.exe!mongo::PortMessageServer::handleIncomingMsg(void * arg=0x0000000001fc3650)
|
22 mongod.exe!boost::_bi::list1<boost::_bi::value<mongo::PortMessageServer::HandleIncomingMsgParam * __ptr64> >::operator()<void * __ptr64,void * __ptr64 (__cdecl*)(void * __ptr64),boost::_bi::list0>(boost::_bi::type<void *> __formal={...}, void * (void *)* & f=0x000000013fbe378a, boost::_bi::list0 & a={...}, boost::_bi::type<void *> __formal={...})
|
23 mongod.exe!boost::_bi::bind_t<void * __ptr64,void * __ptr64 (__cdecl*)(void * __ptr64),boost::_bi::list1<boost::_bi::value<mongo::PortMessageServer::HandleIncomingMsgParam * __ptr64> > >::operator()()
|
24 mongod.exe!boost::detail::thread_data<boost::_bi::bind_t<void * __ptr64,void * __ptr64 (__cdecl*)(void * __ptr64),boost::_bi::list1<boost::_bi::value<mongo::PortMessageServer::HandleIncomingMsgParam * __ptr64> > > >::run()
|
25 mongod.exe!boost::`anonymous namespace'::thread_start_function(void * param=0x00000000004097d0)
|
26 mongod.exe!_callthreadstartex()
|
27 mongod.exe!_threadstartex(void * ptd=0x0000000001ebee20)
|
28 kernel32.dll!BaseThreadInitThunk()
|
29 ntdll.dll!RtlUserThreadStart()
|
|
>
|
Note the _Newsize=0xfeeefeeefeeefeee in the call to Grow() in line 7. The value 0xfeee is written to freed blocks in the debug heap, so it looks like we're trying to use freed memory.
In this code (src/mongo/db/commands/pipeline_command.cpp lines 92 to 105)
// The initial ok() on a cursor may be very expensive so we don't do it when batchSize
|
// is 0 since that indicates a desire for a fast return.
|
if (batchSize != 0 && !cursor->ok()) {
|
// There is no more data. Kill the cursor.
|
pin.release();
|
ClientCursor::erase(id);
|
id = 0;
|
}
|
|
BSONObjBuilder cursorObj(result.subobjStart("cursor"));
|
cursorObj.append("id", id);
|
cursorObj.append("ns", cursor->ns());
|
cursorObj.append("firstBatch", resultsArray.arr());
|
cursorObj.done();
|
}
|
in the call to cursor->ns() [line 103 in the file, line 12 above], cursor has been deleted, causing cursor._ns to have a "size" of 0xfeeefeeefeeefeee.
I verified that making a local copy of the cursor's namespace before the cursor is deleted and using it instead of the call to cursor->ns() avoids the bad_alloc and makes the test pass.
In case there are other issues revealed in looking at this code, I'll leave it to Mathias to decide how to fix it.
|