Index: clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -306,9 +306,14 @@ ID.AddPointer(RegionOfInterest); } + void* getTag() const { + static int Tag = 0; + return static_cast(&Tag); + } + std::shared_ptr VisitNode(const ExplodedNode *N, BugReporterContext &BR, - BugReport &) override { + BugReport &R) override { const LocationContext *Ctx = N->getLocationContext(); const StackFrameContext *SCtx = Ctx->getStackFrame(); @@ -322,9 +327,6 @@ 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(Call)) { @@ -333,8 +335,8 @@ if (RegionOfInterest->isSubRegionOf(SelfRegion) && potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), IvarR->getDecl())) - return notModifiedDiagnostics(N, {}, SelfRegion, "self", - /*FirstIsReferenceType=*/false, 1); + return emitNoteOrSuppress(R, *Call, N, {}, SelfRegion, "self", + /*FirstIsReferenceType=*/false, 1); } } @@ -342,8 +344,8 @@ const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); if (RegionOfInterest->isSubRegionOf(ThisR) && !CCall->getDecl()->isImplicit()) - return notModifiedDiagnostics(N, {}, ThisR, "this", - /*FirstIsReferenceType=*/false, 1); + return emitNoteOrSuppress(R, *Call, N, {}, ThisR, "this", + /*FirstIsReferenceType=*/false, 1); // Do not generate diagnostics for not modified parameters in // constructors. @@ -359,21 +361,20 @@ 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 = S.getAsRegion()) { + if (RegionOfInterest->isSubRegionOf(MR) && !isPointerToConst(T)) + return emitNoteOrSuppress(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 emitNoteOrSuppress(R, *Call, N, *P, RegionOfInterest, ParamName, + ParamIsReferenceType, IndirectionLevel); - S = State->getSVal(R, PT); + S = State->getSVal(MR, PT); T = PT; IndirectionLevel++; } @@ -545,9 +546,29 @@ /// \return Diagnostics piece for region not modified in the current function. std::shared_ptr - notModifiedDiagnostics(const ExplodedNode *N, const RegionVector &FieldChain, - const MemRegion *MatchedRegion, StringRef FirstElement, - bool FirstIsReferenceType, unsigned IndirectionLevel) { + emitNoteOrSuppress(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. + if (N->getStackFrame()->getCFG()->size() > 3) + R.markInvalid(getTag(), nullptr); + return nullptr; + } PathDiagnosticLocation L = PathDiagnosticLocation::create(N->getLocation(), SM); Index: clang/test/Analysis/Inputs/no-store-suppression.h =================================================================== --- /dev/null +++ clang/test/Analysis/Inputs/no-store-suppression.h @@ -0,0 +1,17 @@ +#pragma clang system_header + +namespace std { +class istream { +public: + bool is_eof(); + char get_char(); +}; + +istream &operator>>(istream &is, char &c) { + if (is.is_eof()) + return; + c = is.get_char(); +} + +extern istream cin; +}; Index: clang/test/Analysis/no-store-suppression.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/no-store-suppression.cpp @@ -0,0 +1,17 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -verify %s + +// expected-no-diagnostics + +#include "Inputs/no-store-suppression.h" + +using namespace std; + +namespace value_uninitialized_after_stream_shift { +void use(char c); + +void foo() { + char c; + std::cin >> c; + use(c); // no-warning +} +} // namespace value_uninitialized_after_stream_shift