[SERVER-14892] Invalid {$elemMatch: {$where}} query causes memory leak Created: 14/Aug/14  Updated: 11/Jul/16  Resolved: 20/Aug/14

Status: Closed
Project: Core Server
Component/s: Querying
Affects Version/s: 2.6.1, 2.6.4, 2.7.4
Fix Version/s: 2.6.5, 2.7.6

Type: Bug Priority: Major - P3
Reporter: Kamran K. Assignee: J Rassi
Resolution: Done Votes: 0
Labels: 28qa
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified

Issue Links:
Depends
Related
related to SERVER-16889 Query subsystem public API should use... Closed
is related to SERVER-13503 The $where operator should not be all... Closed
Tested
Operating System: ALL
Backport Completed:
Steps To Reproduce:

var t = db.query_leak;
t.drop();
t.insert({});
 
for (var i = 0; i < 1000000; i++) {
    try {
        t.find({a: {$elemMatch: {$where: "1"}}}).itcount();
    } catch(e) {
        // ignore
    }
}

Participants:

 Description   
Issue Status as of Aug 21, 2014

ISSUE SUMMARY
The server will leak memory when receiving a query with a $where expression inside an $elemMatch expression. Note that these queries are not valid and an error message will be correctly returned to the user. For example:

> db.foo.find({a:{$elemMatch:{$where:"1"}}})
error: {
	"$err" : "Can't canonicalize query: BadValue $elemMatch cannot contain $where expression",
	"code" : 17287
}
>

USER IMPACT
Users may see increased memory consumption by mongod processes. In extreme circumstances with many such queries, the memory leak may lead mongod to consume too much memory and either crash or be killed by the operating system.

WORKAROUNDS
Fix the invalid query to remove either the $elemMatch operator or the $where operator.

AFFECTED VERSIONS
MongoDB production releases from 2.6.1 to 2.6.4 (inclusive) are affected by this issue.

FIX VERSION
The fix is included in the 2.6.5 production release.

RESOLUTION DETAILS
Fix memory leak in MatchExpressionParser::_parseElemMatch().

Original description

The above JS test will cause the server to leak memory (and eventually crash). The leak is reproducible in versions 2.6.1+ but not 2.6.0.

SERVER-13503 seems like the obvious culprit.

Valgrind output:

valgrind -q --leak-check=full --show-leak-kinds=definite --soname-synonyms=somalloc=NONE --suppressions=valgrind.suppressions --num-callers=35 ./mongod
 
==29826== 3,250 (800 direct, 2,450 indirect) bytes in 10 blocks are definitely lost in loss record 4,322 of 4,395
==29826==    at 0x4C2B2C0: operator new(unsigned long) (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==29826==    by 0xA84311: mongo::WhereCallbackReal::parseWhere(mongo::BSONElement const&) const (expression_where.cpp:189)
==29826==    by 0xA76739: mongo::MatchExpressionParser::_parse(mongo::BSONObj const&, int) (expression_parser.cpp:336)
==29826==    by 0xA76ED7: mongo::MatchExpressionParser::_parseElemMatch(char const*, mongo::BSONElement const&, int) (expression_parser.cpp:699)
==29826==    by 0xA78908: mongo::MatchExpressionParser::_parseSubField(mongo::BSONObj const&, mongo::AndMatchExpression const*, char const*, mongo::BSONElement const&, int) (expression_parser.cpp:262)
==29826==    by 0xA75659: mongo::MatchExpressionParser::_parseSub(char const*, mongo::BSONObj const&, mongo::AndMatchExpression*, int) (expression_parser.cpp:456)
==29826==    by 0xA76114: mongo::MatchExpressionParser::_parse(mongo::BSONObj const&, int) (expression_parser.cpp:376)
==29826==    by 0xB3AE58: mongo::CanonicalQuery::canonicalize(mongo::QueryMessage const&, mongo::CanonicalQuery**, mongo::MatchExpressionParser::WhereCallback const&) (expression_parser.h:71)
==29826==    by 0xB6564B: mongo::newRunQuery(mongo::OperationContext*, mongo::Message&, mongo::QueryMessage&, mongo::CurOp&, mongo::Message&) (new_find.cpp:499)
==29826==    by 0xA4877D: mongo::assembleResponse(mongo::OperationContext*, mongo::Message&, mongo::DbResponse&, mongo::HostAndPort const&) (instance.cpp:263)
==29826==    by 0x7D5C6B: mongo::MyMessageHandler::process(mongo::Message&, mongo::AbstractMessagingPort*, mongo::LastError*) (db.cpp:198)
==29826==    by 0xE682E0: mongo::PortMessageServer::handleIncomingMsg(void*) (message_server_port.cpp:227)
==29826==    by 0x4E3F181: start_thread (pthread_create.c:312)
==29826==    by 0x5D7C38C: clone (clone.S:111)
 



 Comments   
Comment by Githook User [ 20/Aug/14 ]

Author:

{u'username': u'jrassi', u'name': u'Jason Rassi', u'email': u'rassi@10gen.com'}

Message: SERVER-14892 Fix memory leak in MatchExpressionParser::_parseElemMatch
Branch: master
https://github.com/mongodb/mongo/commit/71277e8a5d072fbfa956a74f3c0295f69ead120f

Comment by Githook User [ 20/Aug/14 ]

Author:

{u'username': u'jrassi', u'name': u'Jason Rassi', u'email': u'rassi@10gen.com'}

Message: SERVER-14892 Fix memory leak in MatchExpressionParser::_parseElemMatch

(cherry picked from commit cc80c4d9a0556926a3f3fa9556c95ffd56aa4814)
Branch: v2.6
https://github.com/mongodb/mongo/commit/2b23514f91c1ab0cc6b8f7e928138fbf513ce34e

Generated at Thu Feb 08 03:36:17 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.