Details
-
Bug
-
Resolution: Fixed
-
Major - P3
-
None
-
None
-
Server Security
-
Fully Compatible
-
ALL
-
v7.1, v7.0
-
Security 2023-09-04, Security 2023-09-18
-
5
Description
OpDebug is a non-synchronized structure, designed to hold debug information for an operation. This structure is not intended to be visible to any thread other than the one that is running the operation:
/**
|
* Returns a structure containing data used for profiling, accessed only by a thread
|
* currently executing the operation context associated with this CurOp.
|
*/
|
OpDebug& debug() {
|
return _debug; |
}
|
SERVER-74962 and SERVER-75265 introduced a non-thread-safe usage of OpDebug by having the operation thread write a state that is later accessed/read by any thread that calls into CurOp::reportState:
StatusWith<CursorResponse> ClusterFind::runGetMore(OperationContext* opCtx, ...) {
|
...
|
{
|
CurOp::get(opCtx)->debug().nShards = pinnedCursor.getValue()->getNumRemotes();
|
CurOp::get(opCtx)->debug().cursorid = cursorId;
|
CurOp::get(opCtx)->debug().shouldOmitDiagnosticInformation =
|
pinnedCursor.getValue()->shouldOmitDiagnosticInformation();
|
stdx::lock_guard<Client> lk(*opCtx->getClient());
|
CurOp::get(opCtx)->setOriginatingCommand_inlock(
|
pinnedCursor.getValue()->getOriginatingCommand());
|
CurOp::get(opCtx)->setGenericCursor_inlock(pinnedCursor.getValue().toGenericCursor());
|
}
|
...
|
}
|
|
|
void CurOp::reportState(BSONObjBuilder* builder, ...) { |
...
|
bool omitAndRedactInformation = CurOp::get(opCtx)->debug().shouldOmitDiagnosticInformation; |
builder->append("redacted", omitAndRedactInformation); |
...
|
}
|
The above is identified by TSAN as a read-after-write data race. A possible fix is to move any shared data into CurOp, currently protected by the Client lock.