2013-03-09 11:23:14 +08:00
|
|
|
//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===//
|
2009-11-03 15:35:33 +08:00
|
|
|
//
|
2019-01-19 16:50:56 +08:00
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2009-11-03 15:35:33 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2013-03-09 11:23:14 +08:00
|
|
|
// This defines NonNullParamChecker, which checks for arguments expected not to
|
|
|
|
// be null due to:
|
|
|
|
// - the corresponding parameters being declared to have nonnull attribute
|
|
|
|
// - the corresponding parameters being references; since the call would form
|
|
|
|
// a reference to a null pointer
|
2009-11-03 15:35:33 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
[analyzer][NFC] Move CheckerRegistry from the Core directory to Frontend
ClangCheckerRegistry is a very non-obvious, poorly documented, weird concept.
It derives from CheckerRegistry, and is placed in lib/StaticAnalyzer/Frontend,
whereas it's base is located in lib/StaticAnalyzer/Core. It was, from what I can
imagine, used to circumvent the problem that the registry functions of the
checkers are located in the clangStaticAnalyzerCheckers library, but that
library depends on clangStaticAnalyzerCore. However, clangStaticAnalyzerFrontend
depends on both of those libraries.
One can make the observation however, that CheckerRegistry has no place in Core,
it isn't used there at all! The only place where it is used is Frontend, which
is where it ultimately belongs.
This move implies that since
include/clang/StaticAnalyzer/Checkers/ClangCheckers.h only contained a single function:
class CheckerRegistry;
void registerBuiltinCheckers(CheckerRegistry ®istry);
it had to re purposed, as CheckerRegistry is no longer available to
clangStaticAnalyzerCheckers. It was renamed to BuiltinCheckerRegistration.h,
which actually describes it a lot better -- it does not contain the registration
functions for checkers, but only those generated by the tblgen files.
Differential Revision: https://reviews.llvm.org/D54436
llvm-svn: 349275
2018-12-16 00:23:51 +08:00
|
|
|
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
|
2012-12-01 23:09:41 +08:00
|
|
|
#include "clang/AST/Attr.h"
|
2012-12-04 17:13:33 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
|
2011-03-01 09:16:21 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/Checker.h"
|
2011-02-28 09:28:01 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
2012-07-27 05:39:41 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
|
2011-02-28 09:28:01 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2009-11-03 15:35:33 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2009-11-03 15:35:33 +08:00
|
|
|
|
2009-11-11 13:50:44 +08:00
|
|
|
namespace {
|
2013-03-09 11:23:14 +08:00
|
|
|
class NonNullParamChecker
|
2015-08-28 02:49:07 +08:00
|
|
|
: public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > {
|
2014-03-08 04:03:18 +08:00
|
|
|
mutable std::unique_ptr<BugType> BTAttrNonNull;
|
|
|
|
mutable std::unique_ptr<BugType> BTNullRefArg;
|
|
|
|
|
2009-11-11 13:50:44 +08:00
|
|
|
public:
|
2011-02-28 09:28:01 +08:00
|
|
|
|
2012-07-03 03:28:21 +08:00
|
|
|
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
|
2013-03-07 11:02:36 +08:00
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
std::unique_ptr<BugReport>
|
|
|
|
genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const;
|
|
|
|
std::unique_ptr<BugReport>
|
|
|
|
genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
|
|
|
|
const Expr *ArgE) const;
|
2009-11-11 13:50:44 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
|
2018-03-08 03:27:32 +08:00
|
|
|
/// \return Bitvector marking non-null attributes.
|
|
|
|
static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
|
2012-07-03 03:28:21 +08:00
|
|
|
const Decl *FD = Call.getDecl();
|
2014-10-14 03:38:02 +08:00
|
|
|
unsigned NumArgs = Call.getNumArgs();
|
|
|
|
llvm::SmallBitVector AttrNonNull(NumArgs);
|
|
|
|
for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
|
|
|
|
if (!NonNull->args_size()) {
|
|
|
|
AttrNonNull.set(0, NumArgs);
|
|
|
|
break;
|
|
|
|
}
|
2018-03-13 22:51:22 +08:00
|
|
|
for (const ParamIdx &Idx : NonNull->args()) {
|
|
|
|
unsigned IdxAST = Idx.getASTIndex();
|
|
|
|
if (IdxAST >= NumArgs)
|
2014-10-14 03:38:02 +08:00
|
|
|
continue;
|
2018-03-13 22:51:22 +08:00
|
|
|
AttrNonNull.set(IdxAST);
|
2014-10-14 03:38:02 +08:00
|
|
|
}
|
|
|
|
}
|
2018-03-08 03:27:32 +08:00
|
|
|
return AttrNonNull;
|
|
|
|
}
|
2009-11-03 15:35:33 +08:00
|
|
|
|
2018-03-08 03:27:32 +08:00
|
|
|
void NonNullParamChecker::checkPreCall(const CallEvent &Call,
|
|
|
|
CheckerContext &C) const {
|
|
|
|
if (!Call.getDecl())
|
|
|
|
return;
|
|
|
|
|
|
|
|
llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call);
|
|
|
|
unsigned NumArgs = Call.getNumArgs();
|
2009-11-03 15:35:33 +08:00
|
|
|
|
2018-03-08 03:27:32 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
|
|
|
ArrayRef<ParmVarDecl*> parms = Call.parameters();
|
2013-03-07 11:02:36 +08:00
|
|
|
|
2014-10-14 03:38:02 +08:00
|
|
|
for (unsigned idx = 0; idx < NumArgs; ++idx) {
|
2018-03-08 03:27:32 +08:00
|
|
|
// For vararg functions, a corresponding parameter decl may not exist.
|
|
|
|
bool HasParam = idx < parms.size();
|
2013-03-07 11:02:36 +08:00
|
|
|
|
|
|
|
// Check if the parameter is a reference. We want to report when reference
|
2017-03-30 22:13:19 +08:00
|
|
|
// to a null pointer is passed as a parameter.
|
2018-03-08 03:27:32 +08:00
|
|
|
bool haveRefTypeParam =
|
|
|
|
HasParam ? parms[idx]->getType()->isReferenceType() : false;
|
2014-10-14 03:38:02 +08:00
|
|
|
bool haveAttrNonNull = AttrNonNull[idx];
|
2013-03-07 11:02:36 +08:00
|
|
|
|
2018-03-08 03:27:32 +08:00
|
|
|
// Check if the parameter is also marked 'nonnull'.
|
|
|
|
if (!haveAttrNonNull && HasParam)
|
|
|
|
haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
|
|
|
|
|
|
|
|
if (!haveAttrNonNull && !haveRefTypeParam)
|
2009-11-03 15:35:33 +08:00
|
|
|
continue;
|
|
|
|
|
2013-03-07 11:02:36 +08:00
|
|
|
// If the value is unknown or undefined, we can't perform this check.
|
|
|
|
const Expr *ArgE = Call.getArgExpr(idx);
|
2012-07-03 03:28:21 +08:00
|
|
|
SVal V = Call.getArgSVal(idx);
|
2018-03-08 03:27:32 +08:00
|
|
|
auto DV = V.getAs<DefinedSVal>();
|
2010-06-22 04:08:28 +08:00
|
|
|
if (!DV)
|
|
|
|
continue;
|
2009-11-03 15:35:33 +08:00
|
|
|
|
2013-03-07 11:02:36 +08:00
|
|
|
assert(!haveRefTypeParam || DV->getAs<Loc>());
|
|
|
|
|
2018-03-08 03:27:32 +08:00
|
|
|
// Process the case when the argument is not a location.
|
2013-03-07 11:02:36 +08:00
|
|
|
if (haveAttrNonNull && !DV->getAs<Loc>()) {
|
2010-11-09 10:11:43 +08:00
|
|
|
// If the argument is a union type, we want to handle a potential
|
2012-07-03 03:28:21 +08:00
|
|
|
// transparent_union GCC extension.
|
|
|
|
if (!ArgE)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
QualType T = ArgE->getType();
|
2010-11-09 10:11:43 +08:00
|
|
|
const RecordType *UT = T->getAsUnionType();
|
|
|
|
if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
|
|
|
|
continue;
|
2012-07-03 03:28:21 +08:00
|
|
|
|
2018-03-08 03:27:32 +08:00
|
|
|
auto CSV = DV->getAs<nonloc::CompoundVal>();
|
|
|
|
|
|
|
|
// FIXME: Handle LazyCompoundVals?
|
|
|
|
if (!CSV)
|
2010-11-09 10:11:43 +08:00
|
|
|
continue;
|
2018-03-08 03:27:32 +08:00
|
|
|
|
|
|
|
V = *(CSV->begin());
|
|
|
|
DV = V.getAs<DefinedSVal>();
|
|
|
|
assert(++CSV->begin() == CSV->end());
|
|
|
|
// FIXME: Handle (some_union){ some_other_union_val }, which turns into
|
|
|
|
// a LazyCompoundVal inside a CompoundVal.
|
|
|
|
if (!V.getAs<Loc>())
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Retrieve the corresponding expression.
|
|
|
|
if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
|
|
|
|
if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))
|
|
|
|
ArgE = dyn_cast<Expr>(*(IE->begin()));
|
2010-11-09 10:11:43 +08:00
|
|
|
}
|
|
|
|
|
2009-11-03 15:35:33 +08:00
|
|
|
ConstraintManager &CM = C.getConstraintManager();
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef stateNotNull, stateNull;
|
2014-03-02 21:01:17 +08:00
|
|
|
std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
|
2009-11-03 15:35:33 +08:00
|
|
|
|
2018-03-08 03:27:32 +08:00
|
|
|
// Generate an error node. Check for a null node in case
|
|
|
|
// we cache out.
|
|
|
|
if (stateNull && !stateNotNull) {
|
|
|
|
if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
|
|
|
|
|
|
|
|
std::unique_ptr<BugReport> R;
|
|
|
|
if (haveAttrNonNull)
|
|
|
|
R = genReportNullAttrNonNull(errorNode, ArgE);
|
|
|
|
else if (haveRefTypeParam)
|
|
|
|
R = genReportReferenceToNullPointer(errorNode, ArgE);
|
|
|
|
|
|
|
|
// Highlight the range of the argument that was null.
|
|
|
|
R->addRange(Call.getArgSourceRange(idx));
|
|
|
|
|
|
|
|
// Emit the bug report.
|
|
|
|
C.emitReport(std::move(R));
|
2015-08-28 02:49:07 +08:00
|
|
|
}
|
2018-03-08 03:27:32 +08:00
|
|
|
|
|
|
|
// Always return. Either we cached out or we just emitted an error.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (stateNull) {
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
|
2015-08-28 02:49:07 +08:00
|
|
|
ImplicitNullDerefEvent event = {
|
2018-03-08 03:27:32 +08:00
|
|
|
V, false, N, &C.getBugReporter(),
|
|
|
|
/*IsDirectDereference=*/haveRefTypeParam};
|
2015-08-28 02:49:07 +08:00
|
|
|
dispatchEvent(event);
|
|
|
|
}
|
2009-11-03 15:35:33 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// If a pointer value passed the check we should assume that it is
|
|
|
|
// indeed not null from this point forward.
|
|
|
|
state = stateNotNull;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we reach here all of the arguments passed the nonnull check.
|
|
|
|
// If 'state' has been updated generated a new node.
|
2011-10-27 05:06:34 +08:00
|
|
|
C.addTransition(state);
|
2009-11-03 15:35:33 +08:00
|
|
|
}
|
2011-02-28 09:28:01 +08:00
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
std::unique_ptr<BugReport>
|
|
|
|
NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
|
|
|
|
const Expr *ArgE) const {
|
2013-03-07 11:02:36 +08:00
|
|
|
// Lazily allocate the BugType object if it hasn't already been
|
|
|
|
// created. Ownership is transferred to the BugReporter object once
|
|
|
|
// the BugReport is passed to 'EmitWarning'.
|
|
|
|
if (!BTAttrNonNull)
|
|
|
|
BTAttrNonNull.reset(new BugType(
|
2014-02-12 05:49:21 +08:00
|
|
|
this, "Argument with 'nonnull' attribute passed null", "API"));
|
2013-03-07 11:02:36 +08:00
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
auto R = llvm::make_unique<BugReport>(
|
|
|
|
*BTAttrNonNull,
|
|
|
|
"Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode);
|
2013-03-07 11:02:36 +08:00
|
|
|
if (ArgE)
|
2018-10-24 02:24:53 +08:00
|
|
|
bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);
|
2013-03-07 11:02:36 +08:00
|
|
|
|
|
|
|
return R;
|
|
|
|
}
|
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer(
|
|
|
|
const ExplodedNode *ErrorNode, const Expr *ArgE) const {
|
2013-03-07 11:02:36 +08:00
|
|
|
if (!BTNullRefArg)
|
2014-02-12 05:49:21 +08:00
|
|
|
BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
|
2013-03-07 11:02:36 +08:00
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
auto R = llvm::make_unique<BugReport>(
|
|
|
|
*BTNullRefArg, "Forming reference to null pointer", ErrorNode);
|
2013-03-07 11:02:36 +08:00
|
|
|
if (ArgE) {
|
|
|
|
const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
|
2014-05-27 10:45:47 +08:00
|
|
|
if (!ArgEDeref)
|
2013-03-07 11:02:36 +08:00
|
|
|
ArgEDeref = ArgE;
|
2018-10-24 02:24:53 +08:00
|
|
|
bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R);
|
2013-03-07 11:02:36 +08:00
|
|
|
}
|
|
|
|
return R;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2013-03-09 11:23:14 +08:00
|
|
|
void ento::registerNonNullParamChecker(CheckerManager &mgr) {
|
|
|
|
mgr.registerChecker<NonNullParamChecker>();
|
2011-02-28 09:28:01 +08:00
|
|
|
}
|