-
Type: Bug
-
Resolution: Fixed
-
Priority: Critical - P2
-
Affects Version/s: 3.6.0-rc4
-
Component/s: Aggregation Framework
-
None
-
Fully Compatible
-
ALL
-
v3.6
-
Query 2017-12-04
-
0
In general, aggregation expressions are owned by a single thread responsible for executing the aggregate command. However, this is not the case for a document validator which uses $expr. A thread running a create or collMod commands sets up the validator. Subsequent writes to the collection can concurrently evaluate the aggregation expression, as part of $expr. Note that this is the only the case for storage engines which support document-level concurrency; on MMAPv1, writes will take an exclusive collection lock and thus will not evaluate the $expr concurrently.
Expression::evaluate() is marked as const and therefore is expected to be thread-safe, but ExpressionDateFromString's evaluate implementation has a subtle data race. This expression holds a subexpression by boost::intrusive_ptr:
It passes this intrusive_ptr by value to the makeTimeZone() helper:
This causes the intrusive_ptr refcount to be incremented when this function is called, and decremented when the function returns. This increment/decrement pair amounts to mutating the object inside a const method, and it results in a data race. The Expression class inherits from IntrusiveCounterUnsigned in order to interface with instrusive_ptr. IntrusiveCounterUnsigned writes directly to an unsigned integer on increment and decrement:
This is in contrast to our RefCountable class, whose refcount is an AtomicUInt32.