Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -24,6 +24,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/Support/raw_ostream.h" using namespace clang; @@ -159,6 +160,79 @@ return std::move(P); } +namespace { + +/// Put a diagnostic on return statement of the inlined function \p F +/// iff the region of interest \p R was not stored into inside \p F. +class NoStoreFuncVisitor final + : public BugReporterVisitorImpl { + + const MemRegion *RegionOfInterest; + bool Reported = false; + + public: + + NoStoreFuncVisitor(const MemRegion *R) : RegionOfInterest(R) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int Tag = 0; + ID.AddPointer(&Tag); + } + + + std::shared_ptr VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext &BRC, + BugReport &BR) override { + if (Reported) + return nullptr; + + const LocationContext* Ctx = N->getLocationContext(); + const StackFrameContext *SCtx = Ctx->getCurrentStackFrame(); + const Stmt *S = PathDiagnosticLocation::getStmt(N); + + auto *FD = dyn_cast(SCtx->getDecl()); + auto *CE = dyn_cast_or_null(SCtx->getCallSite()); + auto *RS = dyn_cast_or_null(S); + if (!FD || !CE || !RS) + return nullptr; + + const LocationContext* PCtx = Ctx->getParent(); + unsigned int ParamIdx = 0; + for (const Expr *arg : CE->arguments()) { + const MemRegion *R = N->getState()->getSVal(arg, PCtx).getAsRegion(); + + const ParmVarDecl *PVD = FD->getParamDecl(ParamIdx); + if (R == RegionOfInterest) { + Reported = true; + return generateDiagnostics(Ctx, BRC, S, PVD); + } + ParamIdx++; + } + + return nullptr; + } + +private: + + std::shared_ptr generateDiagnostics( + const LocationContext *Ctx, + BugReporterContext &BRC, + const Stmt *S, + const ParmVarDecl *PVD) { + PathDiagnosticLocation L = PathDiagnosticLocation::createBegin( + S, BRC.getSourceManager(), Ctx); + SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "parameter '"; + PVD->printQualifiedName(os); + os << "' not written into"; + return std::make_shared(L, os.str()); + } +}; + + +} namespace { /// Emits an extra note at the return statement of an interesting stack frame. @@ -1086,6 +1160,7 @@ if (R) { // Mark both the variable region and its contents as interesting. SVal V = LVState->getRawSVal(loc::MemRegionVal(R)); + report.addVisitor(llvm::make_unique(R)); report.markInteresting(R); report.markInteresting(V); Index: test/Analysis/diagnostics/undef-value-param.c =================================================================== --- test/Analysis/diagnostics/undef-value-param.c +++ test/Analysis/diagnostics/undef-value-param.c @@ -12,7 +12,7 @@ if (c) //expected-note@-1{{Assuming 'c' is not equal to 0}} //expected-note@-2{{Taking true branch}} - return; + return; // expected-note{{parameter 'x' not written into}} *x = 5; } Index: test/Analysis/diagnostics/undef-value-param.m =================================================================== --- test/Analysis/diagnostics/undef-value-param.m +++ test/Analysis/diagnostics/undef-value-param.m @@ -69,7 +69,7 @@ //expected-note@-1{{Assuming 'err' is not equal to 0}} //expected-note@-2{{Taking true branch}} CFRelease(ref); - return; + return; // expected-note{{parameter 'storeRef' not written into}} } *storeRef = ref; } Index: test/Analysis/no-store-func-path-notes.c =================================================================== --- /dev/null +++ test/Analysis/no-store-func-path-notes.c @@ -0,0 +1,44 @@ +// RUN: %clang_analyze_cc1 -x c -analyzer-checker=core -analyzer-output=text -verify %s + +int initializer1(int *p, int x) { + if (x) { // expected-note{{Taking false branch}} + *p = 1; + return 0; + } else { + return 1; // expected-note {{parameter 'p' not written into}} + } +} + +int param_not_initialized_by_func() { + int p; // expected-note {{'p' declared without an initial value}} + int out = initializer1(&p, 0); // expected-note{{Calling 'initializer1'}} + // expected-note@-1{{Returning from 'initializer1'}} + return p; // expected-note{{Undefined or garbage value returned to caller}} + // expected-warning@-1{{Undefined or garbage value returned to caller}} + +} + +int param_initialized_properly() { + int p; + int out = initializer1(&p, 1); + return p; //no-warning +} + +static int global; + +int initializer2(int **p, int x) { + if (x) { // expected-note{{Taking false branch}} + *p = &global; + return 0; + } else { + return 1; // expected-note {{parameter 'p' not written into}} + } +} + +int param_not_written_into_by_func() { + int *p = 0; // expected-note{{'p' initialized to a null pointer value}} + int out = initializer2(&p, 0); // expected-note{{Calling 'initializer2'}} + // expected-note@-1{{Returning from 'initializer2'}} + return *p; // expected-warning{{Dereference of null pointer (loaded from variable 'p')}} + // expected-note@-1{{Dereference of null pointer (loaded from variable 'p')}} +} Index: test/Analysis/no-store-func-path-notes.cpp =================================================================== --- /dev/null +++ test/Analysis/no-store-func-path-notes.cpp @@ -0,0 +1,37 @@ +// RUN: %clang_analyze_cc1 -x c++ -analyzer-checker=core -analyzer-output=text -verify %s + +int initializer1(int &p, int x) { + if (x) { // expected-note{{Taking false branch}} + p = 1; + return 0; + } else { + return 1; // expected-note {{parameter 'p' not written into}} + } +} + +int param_not_initialized_by_func() { + int p; // expected-note {{'p' declared without an initial value}} + int out = initializer1(p, 0); // expected-note{{Calling 'initializer1'}} + // expected-note@-1{{Returning from 'initializer1'}} + return p; // expected-note{{Undefined or garbage value returned to caller}} + // expected-warning@-1{{Undefined or garbage value returned to caller}} + +} + +struct S { + int initialize(int *p, int param) { + if (param) { //expected-note{{Taking false branch}} + *p = 1; + return 1; + } + return 0; // expected-note{{parameter 'p' not written into}} + } +}; + +int use(S* s) { + int p; //expected-note{{'p' declared without an initial value}} + s->initialize(&p, 0); //expected-note{{Calling 'S::initialize'}} + //expected-note@-1{{Returning from 'S::initialize'}} + return p; // expected-warning{{Undefined or garbage value returned to caller}} + // expected-note@-1{{Undefined or garbage value returned to caller}} +} Index: test/Analysis/no-store-func-path-notes.m =================================================================== --- /dev/null +++ test/Analysis/no-store-func-path-notes.m @@ -0,0 +1,21 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core -analyzer-output=text -verify -Wno-objc-root-class -fblocks %s + +@interface I +- (void)initVar:(int *)var param:(int)param; +@end + +@implementation I +- (void)initVar:(int *)var param:(int)param { + if (param) { // expected-note{{Taking false branch}} + *var = 1; + } +} +@end + +int foo(I* i) { + int x; //expected-note{{'x' declared without an initial value}} + [i initVar:&x param:0]; //expected-note{{Calling 'initVar:param:'}} + //expected-note@-1{{Returning from 'initVar:param:'}} + return x; //expected-warning{{Undefined or garbage value returned to caller}} + //expected-note@-1{{Undefined or garbage value returned to caller}} +}