[analyzer] Warn when passing pointers to const but uninitialized memory.
Passing a pointer to an uninitialized memory buffer is normally okay,
but if the function is declared to take a pointer-to-const then it's
very unlikely it will be modifying the buffer. In this case the analyzer
should warn that there will likely be a read of uninitialized memory.
This doesn't check all elements of an array, only the first one.
It also doesn't yet check Objective-C methods, only C functions and
C++ methods.
This is controlled by a new check: alpha.core.CallAndMessageUnInitRefArg.
Patch by Per Viberg!
llvm-svn: 203822
diff --git a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index 26e1956..907f516 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -27,6 +27,15 @@
using namespace ento;
namespace {
+
+struct ChecksFilter {
+ DefaultBool Check_CallAndMessageUnInitRefArg;
+ DefaultBool Check_CallAndMessageChecker;
+
+ CheckName CheckName_CallAndMessageUnInitRefArg;
+ CheckName CheckName_CallAndMessageChecker;
+};
+
class CallAndMessageChecker
: public Checker< check::PreStmt<CallExpr>,
check::PreStmt<CXXDeleteExpr>,
@@ -46,6 +55,7 @@
mutable std::unique_ptr<BugType> BT_call_few_args;
public:
+ ChecksFilter Filter;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
@@ -53,10 +63,11 @@
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
private:
- bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange argRange,
- const Expr *argEx, bool IsFirstArgument,
- bool checkUninitFields, const CallEvent &Call,
- std::unique_ptr<BugType> &BT) const;
+ bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
+ const Expr *ArgEx, bool IsFirstArgument,
+ bool CheckUninitFields, const CallEvent &Call,
+ std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl) const;
static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE);
void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
@@ -70,6 +81,10 @@
if (!BT)
BT.reset(new BuiltinBug(this, desc));
}
+ bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
+ const SourceRange &ArgRange,
+ const Expr *ArgEx, std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl, const char *BD) const;
};
} // end anonymous namespace
@@ -114,27 +129,86 @@
}
}
-bool CallAndMessageChecker::PreVisitProcessArg(
- CheckerContext &C, SVal V, SourceRange argRange, const Expr *argEx,
- bool IsFirstArgument, bool checkUninitFields, const CallEvent &Call,
- std::unique_ptr<BugType> &BT) const {
+bool CallAndMessageChecker::uninitRefOrPointer(CheckerContext &C,
+ const SVal &V,
+ const SourceRange &ArgRange,
+ const Expr *ArgEx,
+ std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl,
+ const char *BD) const {
+ if (!Filter.Check_CallAndMessageUnInitRefArg)
+ return false;
+
+ // No parameter declaration available, i.e. variadic function argument.
+ if(!ParamDecl)
+ return false;
+
+ // If parameter is declared as pointer to const in function declaration,
+ // then check if corresponding argument in function call is
+ // pointing to undefined symbol value (uninitialized memory).
+ StringRef Message;
+
+ if (ParamDecl->getType()->isPointerType()) {
+ Message = "Function call argument is a pointer to uninitialized value";
+ } else if (ParamDecl->getType()->isReferenceType()) {
+ Message = "Function call argument is an uninitialized value";
+ } else
+ return false;
+
+ if(!ParamDecl->getType()->getPointeeType().isConstQualified())
+ return false;
+
+ if (const MemRegion *SValMemRegion = V.getAsRegion()) {
+ const ProgramStateRef State = C.getState();
+ const SVal PSV = State->getSVal(SValMemRegion);
+ if (PSV.isUndef()) {
+ if (ExplodedNode *N = C.generateSink()) {
+ LazyInit_BT(BD, BT);
+ BugReport *R = new BugReport(*BT, Message, N);
+ R->addRange(ArgRange);
+ if (ArgEx) {
+ bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
+ }
+ C.emitReport(R);
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
+ SVal V,
+ SourceRange ArgRange,
+ const Expr *ArgEx,
+ bool IsFirstArgument,
+ bool CheckUninitFields,
+ const CallEvent &Call,
+ std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl
+ ) const {
+ const char *BD = "Uninitialized argument value";
+
+ if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD))
+ return true;
+
if (V.isUndef()) {
if (ExplodedNode *N = C.generateSink()) {
- LazyInit_BT("Uninitialized argument value", BT);
+ LazyInit_BT(BD, BT);
// Generate a report for this bug.
- StringRef Desc = describeUninitializedArgumentInCall(Call,
- IsFirstArgument);
+ StringRef Desc =
+ describeUninitializedArgumentInCall(Call, IsFirstArgument);
BugReport *R = new BugReport(*BT, Desc, N);
- R->addRange(argRange);
- if (argEx)
- bugreporter::trackNullOrUndefValue(N, argEx, *R);
+ R->addRange(ArgRange);
+ if (ArgEx)
+ bugreporter::trackNullOrUndefValue(N, ArgEx, *R);
C.emitReport(R);
}
return true;
}
- if (!checkUninitFields)
+ if (!CheckUninitFields)
return false;
if (Optional<nonloc::LazyCompoundVal> LV =
@@ -185,7 +259,7 @@
if (F.Find(D->getRegion())) {
if (ExplodedNode *N = C.generateSink()) {
- LazyInit_BT("Uninitialized argument value", BT);
+ LazyInit_BT(BD, BT);
SmallString<512> Str;
llvm::raw_svector_ostream os(Str);
os << "Passed-by-value struct argument contains uninitialized data";
@@ -208,7 +282,7 @@
// Generate a report for this bug.
BugReport *R = new BugReport(*BT, os.str(), N);
- R->addRange(argRange);
+ R->addRange(ArgRange);
// FIXME: enhance track back for uninitialized value for arbitrary
// memregions
@@ -308,7 +382,8 @@
}
const Decl *D = Call.getDecl();
- if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D)) {
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
+ if (FD) {
// If we have a declaration, we can make sure we pass enough parameters to
// the function.
unsigned Params = FD->getNumParams();
@@ -343,11 +418,15 @@
else
BT = &BT_call_arg;
- for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i)
+ for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
+ const ParmVarDecl *ParamDecl = NULL;
+ if(FD && i < FD->getNumParams())
+ ParamDecl = FD->getParamDecl(i);
if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
Call.getArgExpr(i), /*IsFirstArgument=*/i == 0,
- checkUninitFields, Call, *BT))
+ checkUninitFields, Call, *BT, ParamDecl))
return;
+ }
// If we make it here, record our assumptions about the callee.
C.addTransition(State);
@@ -507,6 +586,13 @@
C.addTransition(state);
}
-void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
- mgr.registerChecker<CallAndMessageChecker>();
-}
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ CallAndMessageChecker *Checker = \
+ mgr.registerChecker<CallAndMessageChecker>(); \
+ Checker->Filter.Check_##name = true; \
+ Checker->Filter.CheckName_##name = mgr.getCurrentCheckName(); \
+ }
+
+REGISTER_CHECKER(CallAndMessageUnInitRefArg)
+REGISTER_CHECKER(CallAndMessageChecker)