ID.AddPointer(RegionOfInterest);
}
+ void *getTag() const {
+ static int Tag = 0;
+ return static_cast<void *>(&Tag);
+ }
+
std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
BugReporterContext &BR,
- BugReport &) override {
+ BugReport &R) override {
const LocationContext *Ctx = N->getLocationContext();
const StackFrameContext *SCtx = Ctx->getStackFrame();
CallEventRef<> Call =
BR.getStateManager().getCallEventManager().getCaller(SCtx, State);
- if (Call->isInSystemHeader())
- return nullptr;
-
// Region of interest corresponds to an IVar, exiting a method
// which could have written into that IVar, but did not.
if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) {
if (RegionOfInterest->isSubRegionOf(SelfRegion) &&
potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),
IvarR->getDecl()))
- return notModifiedDiagnostics(N, {}, SelfRegion, "self",
- /*FirstIsReferenceType=*/false, 1);
+ return maybeEmitNode(R, *Call, N, {}, SelfRegion, "self",
+ /*FirstIsReferenceType=*/false, 1);
}
}
const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion();
if (RegionOfInterest->isSubRegionOf(ThisR)
&& !CCall->getDecl()->isImplicit())
- return notModifiedDiagnostics(N, {}, ThisR, "this",
- /*FirstIsReferenceType=*/false, 1);
+ return maybeEmitNode(R, *Call, N, {}, ThisR, "this",
+ /*FirstIsReferenceType=*/false, 1);
// Do not generate diagnostics for not modified parameters in
// constructors.
ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) {
const ParmVarDecl *PVD = parameters[I];
- SVal S = Call->getArgSVal(I);
+ SVal V = Call->getArgSVal(I);
bool ParamIsReferenceType = PVD->getType()->isReferenceType();
std::string ParamName = PVD->getNameAsString();
int IndirectionLevel = 1;
QualType T = PVD->getType();
- while (const MemRegion *R = S.getAsRegion()) {
- if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T))
- return notModifiedDiagnostics(N, {}, R, ParamName,
- ParamIsReferenceType, IndirectionLevel);
+ while (const MemRegion *MR = V.getAsRegion()) {
+ if (RegionOfInterest->isSubRegionOf(MR) && !isPointerToConst(T))
+ return maybeEmitNode(R, *Call, N, {}, MR, ParamName,
+ ParamIsReferenceType, IndirectionLevel);
QualType PT = T->getPointeeType();
if (PT.isNull() || PT->isVoidType()) break;
if (const RecordDecl *RD = PT->getAsRecordDecl())
- if (auto P = findRegionOfInterestInRecord(RD, State, R))
- return notModifiedDiagnostics(N, *P, RegionOfInterest, ParamName,
- ParamIsReferenceType,
- IndirectionLevel);
+ if (auto P = findRegionOfInterestInRecord(RD, State, MR))
+ return maybeEmitNode(R, *Call, N, *P, RegionOfInterest, ParamName,
+ ParamIsReferenceType, IndirectionLevel);
- S = State->getSVal(R, PT);
+ V = State->getSVal(MR, PT);
T = PT;
IndirectionLevel++;
}
Ty->getPointeeType().getCanonicalType().isConstQualified();
}
- /// \return Diagnostics piece for region not modified in the current function.
+ /// Consume the information on the no-store stack frame in order to
+ /// either emit a note or suppress the report enirely.
+ /// \return Diagnostics piece for region not modified in the current function,
+ /// if it decides to emit one.
std::shared_ptr<PathDiagnosticPiece>
- notModifiedDiagnostics(const ExplodedNode *N, const RegionVector &FieldChain,
- const MemRegion *MatchedRegion, StringRef FirstElement,
- bool FirstIsReferenceType, unsigned IndirectionLevel) {
+ maybeEmitNode(BugReport &R, const CallEvent &Call, const ExplodedNode *N,
+ const RegionVector &FieldChain, const MemRegion *MatchedRegion,
+ StringRef FirstElement, bool FirstIsReferenceType,
+ unsigned IndirectionLevel) {
+ // Optimistically suppress uninitialized value bugs that result
+ // from system headers having a chance to initialize the value
+ // but failing to do so. It's too unlikely a system header's fault.
+ // It's much more likely a situation in which the function has a failure
+ // mode that the user decided not to check. If we want to hunt such
+ // omitted checks, we should provide an explicit function-specific note
+ // describing the precondition under which the function isn't supposed to
+ // initialize its out-parameter, and additionally check that such
+ // precondition can actually be fulfilled on the current path.
+ if (Call.isInSystemHeader()) {
+ // We make an exception for system header functions that have no branches,
+ // i.e. have exactly 3 CFG blocks: begin, all its code, end. Such
+ // functions unconditionally fail to initialize the variable.
+ // If they call other functions that have more paths within them,
+ // this suppression would still apply when we visit these inner functions.
+ // One common example of a standard function that doesn't ever initialize
+ // its out parameter is operator placement new; it's up to the follow-up
+ // constructor (if any) to initialize the memory.
+ if (N->getStackFrame()->getCFG()->size() > 3)
+ R.markInvalid(getTag(), nullptr);
+ return nullptr;
+ }
PathDiagnosticLocation L =
PathDiagnosticLocation::create(N->getLocation(), SM);