Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-14892

Invalid {$elemMatch: {$where}} query causes memory leak

    • Type: Icon: Bug Bug
    • Resolution: Done
    • Priority: Icon: Major - P3 Major - P3
    • 2.6.5, 2.7.6
    • Affects Version/s: 2.6.1, 2.6.4, 2.7.4
    • Component/s: Querying
    • ALL
    • Hide
      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
          }
      }
      
      Show
      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 } }

      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)
      
      

            Assignee:
            rassi J Rassi
            Reporter:
            kamran.khan Kamran K.
            Votes:
            0 Vote for this issue
            Watchers:
            2 Start watching this issue

              Created:
              Updated:
              Resolved: