// Coverity model file for MongoDB-specific patterns.
//
// This file is compiled into an xmldb via cov-make-library (see evergreen/coverity_build.sh)
// and placed at <covIdir>/config/user_models.xmldb so cov-analyze picks it up automatically.
//
// Models tried and retired (confirmed ineffective in practice):
//
// mongo::error_details::invariant*, uasserted*, fassert* — redundant. These functions
// are already decorated with [[noreturn]] / MONGO_COMPILER_NORETURN, which Coverity
// understands natively. The models had zero measurable effect on UNINIT/FORWARD_NULL counts.
//
// mongo::idl::preparsedValue<T> — C++ template models require exact mangled-name matches
// per concrete instantiation. A single template stub does not generate those mangled names,
// so the model never matched any call site. UNINIT_CTOR in IDL-generated constructors is
// instead addressed with inline // coverity[uninit_ctor] annotations in
// buildscripts/idl/idl/generator.py.
//
// mongo::Future<T>::then() — auto return type + template parameter makes the mangled
// name unique per instantiation. Cannot be matched with a single model stub. The
// related UNINIT_CTOR pattern in future_util.h is handled with inline annotations.
//
// Add new models here when a Coverity FP pattern is identified that:
// (a) cannot be suppressed with an inline annotation at the source location, AND
// (b) is driven by a MongoDB-specific function whose semantics Coverity cannot infer.
//
// cov-make-library runs a standalone front-end with no system include paths.
// Use the std::string stub below if signatures require it; do not include standard headers.
// Minimal stubs for types used in function signatures below.
namespace std {
class string {
public:
string();
string(const char*);
~string();
const char* c_str() const;
bool empty() const;
};
} // namespace std
namespace mongo {
// ---------------------------------------------------------------------------
// Wire-protocol taint — source + sink models
//
// IMPORTANT LIMITATION (discovered through testing, 2026-05):
// In Coverity's C/C++ analysis, TAINTED_SCALAR tracks two distinct properties:
// (1) "potentially uninitialized" — used by the UNINIT checker
// (2) "network-derived/untrusted" — used by the TAINTED_SCALAR checker
//
// __coverity_mark_pointee_as_tainted__ sets property (1) only. It is NOT
// sufficient to make TAINTED_SCALAR fire. TAINTED_SCALAR sources in C/C++
// are driven exclusively by Coverity's built-in recognition of system-level
// calls (recv, read, fread, etc.). Adding new TAINTED_SCALAR sources requires
// Coverity's Security Directives mechanism, not user model files.
//
// These models are kept because:
// (a) The SharedBuffer::allocate sink model fires when tainted data from a
// BUILT-IN Coverity source reaches an allocation without bounds-checking.
// (b) The CompressionHeader source model increases UNINIT sensitivity on
// network-derived fields, flagging downstream unvalidated use.
// (c) The CodeXM checker (etc/coverity_models/codexm/) directly detects the
// CVE-2025-14847 pattern without relying on TAINTED_SCALAR propagation.
//
// Reference: src/mongo/rpc/op_compressed.h — CompressionHeader(ConstDataRangeCursor*)
// ---------------------------------------------------------------------------
class ConstDataRangeCursor {
public:
ConstDataRangeCursor();
};
struct CompressionHeader {
int originalOpCode;
int uncompressedSize;
unsigned char compressorId;
CompressionHeader(ConstDataRangeCursor* cursor) {
__coverity_mark_pointee_as_tainted__(this, TAINT_TYPE_NETWORK);
}
};
class SharedBuffer {
public:
// size_t = unsigned long on x86_64 Linux; matches real allocate(size_t, Allocator={})
static SharedBuffer allocate(unsigned long bytes, ...) {
__coverity_taint_sink__(&bytes, TAINTED_SCALAR_GENERIC);
return SharedBuffer();
}
};
// ---------------------------------------------------------------------------
// MessageCompressorBase — decompressData / compressData
//
// These pure virtual functions (message_compressor_base.h) account for 12
// RW.ROUTINE_NOT_EMITTED findings. They appear 3 more times in
// message_compressor_manager.cpp. Coverity treats them as opaque black boxes,
// so reads from the output DataRange after a decompress/compress call are
// flagged as UNINIT because Coverity does not know the call wrote to the buffer.
//
// The model marks the output buffer as written (via __coverity_writeall__) so
// that downstream reads from the decompressed/compressed data do not generate
// false-positive UNINIT findings.
//
// Note: DataRange wraps a (char*, size_t) pair. The model stubs provide enough
// structure for the output buffer pointer to be accessible.
//
// References:
// src/mongo/transport/message_compressor_base.h
// src/mongo/transport/message_compressor_zlib.cpp (decompresses wire data)
// src/mongo/transport/message_compressor_snappy.cpp
// src/mongo/transport/message_compressor_zstd.cpp
// ---------------------------------------------------------------------------
class ConstDataRange {
public:
ConstDataRange();
const char* data() const;
unsigned long length() const;
};
class DataRange {
public:
DataRange();
char* data() const;
unsigned long length() const;
};
// StatusWith<size_t> stub — only needs to be constructible for return modelling.
class StatusWithSizeT {
public:
StatusWithSizeT();
bool isOK() const;
unsigned long getValue() const;
};
class MessageCompressorBase {
public:
// Model: decompressData writes compressed-wire-data into the output DataRange.
// Mark the output buffer as fully written so reads from it after this call
// do not trigger UNINIT findings in callers (e.g. decompressMessage).
virtual StatusWithSizeT decompressData(ConstDataRange input, DataRange output) {
char* buf = output.data();
__coverity_writeall__(buf);
StatusWithSizeT result;
__coverity_writeall__(&result);
return result;
}
// Model: compressData writes the compressed output into the output DataRange.
virtual StatusWithSizeT compressData(ConstDataRange input, DataRange output) {
char* buf = output.data();
__coverity_writeall__(buf);
StatusWithSizeT result;
__coverity_writeall__(&result);
return result;
}
};
// ---------------------------------------------------------------------------
// OutOfLineExecutor::schedule — service executor callback dispatch
//
// ServiceExecutorAdaptive, ServiceExecutorReserved, and ServiceExecutorSynchronous
// all implement OutOfLineExecutor::schedule(Task func). There are 42
// RW.ROUTINE_NOT_EMITTED findings across service_executor_adaptive.cpp (.h),
// service_executor_reserved.cpp, and service_executor_synchronous.cpp.
//
// When Coverity cannot emit these functions it treats schedule() as a black box:
// values captured by the Task lambda appear to Coverity as never used, generating
// UNINIT findings in callers that create a task, schedule it, then use its output.
//
// The model tells Coverity that schedule() synchronously invokes func with an
// OK-like status. This is a conservative approximation — in reality the call is
// asynchronous — but it is sufficient to propagate initialization/taint state
// through the callback boundary in Coverity's analysis.
//
// Reference: src/mongo/util/out_of_line_executor.h — OutOfLineExecutor::schedule
// ---------------------------------------------------------------------------
class Status {
public:
Status();
bool isOK() const;
static Status OK();
};
// unique_function<void(Status)> stub — the real Task type; only needs
// to be callable with a Status for the model to express the invocation.
class Task {
public:
Task();
void operator()(Status s);
};
class OutOfLineExecutor {
public:
// Model: schedule invokes func with an OK Status, then returns.
// This tells Coverity that values captured by the lambda ARE used,
// eliminating false-positive UNINIT findings in callers.
virtual void schedule(Task func) {
Status s;
__coverity_writeall__(&s);
func(s);
}
};
// ---------------------------------------------------------------------------
// BSONObj — objdata / getOwned
//
// BSONObjBuilder has 66 RW.ROUTINE_NOT_EMITTED findings. Many arise from template
// specialisations in bsonobjbuilder.h that Coverity cannot emit. The downstream
// effect is that BSONObj values produced by BSONObjBuilder::obj() appear
// uninitialised to Coverity because it cannot trace through the builder's
// template machinery.
//
// These models mark the raw BSON data buffer and the BSONObj itself as fully
// written, eliminating UNINIT findings on fields extracted from BSON objects
// constructed via the builder.
//
// References:
// src/mongo/bson/bsonobj.h — BSONObj::objdata()
// src/mongo/bson/bsonobjbuilder.h — BSONObjBuilder::obj()
// ---------------------------------------------------------------------------
class BSONObj {
public:
BSONObj();
// Model: objdata() returns a pointer to fully-written BSON bytes.
// Marks the returned buffer as initialized to suppress UNINIT on
// field reads that follow a call to objdata().
const char* objdata() const {
const char* result;
__coverity_writeall__(&result);
return result;
}
// Model: getOwned() returns an initialized copy of this BSONObj.
BSONObj getOwned() const {
BSONObj result;
__coverity_writeall__(&result);
return result;
}
};
} // namespace mongo