-
Type: Bug
-
Resolution: Fixed
-
Priority: Major - P3
-
Affects Version/s: None
-
Component/s: Internal Code
-
None
-
Server Security
-
Fully Compatible
-
ALL
-
v7.1, v7.0
-
Security 2023-09-04, Security 2023-09-18
-
5
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.