2019-03-01 14:49:51 +08:00
|
|
|
//===-- DereferenceChecker.cpp - Null dereference checker -----------------===//
|
2009-10-31 01:24:47 +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-10-31 01:24:47 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2010-12-23 02:53:44 +08:00
|
|
|
// This defines NullDerefChecker, a builtin check in ExprEngine that performs
|
2009-10-31 01:24:47 +08:00
|
|
|
// checks for null pointers at loads and stores.
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
[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-01-28 20:06:22 +08:00
|
|
|
#include "clang/AST/ExprObjC.h"
|
2015-08-25 22:24:04 +08:00
|
|
|
#include "clang/AST/ExprOpenMP.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-03-01 01:36:18 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
|
2015-11-06 19:16:31 +08:00
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
|
2012-02-04 21:45:25 +08:00
|
|
|
#include "llvm/ADT/SmallString.h"
|
2012-12-02 01:12:56 +08:00
|
|
|
#include "llvm/Support/raw_ostream.h"
|
2009-10-31 01:24:47 +08:00
|
|
|
|
|
|
|
using namespace clang;
|
2010-12-23 15:20:52 +08:00
|
|
|
using namespace ento;
|
2009-10-31 01:24:47 +08:00
|
|
|
|
2009-11-11 11:26:34 +08:00
|
|
|
namespace {
|
2011-03-01 01:36:18 +08:00
|
|
|
class DereferenceChecker
|
2011-03-01 09:16:21 +08:00
|
|
|
: public Checker< check::Location,
|
2012-08-03 05:33:42 +08:00
|
|
|
check::Bind,
|
|
|
|
EventDispatcher<ImplicitNullDerefEvent> > {
|
2020-08-11 15:03:22 +08:00
|
|
|
enum DerefKind { NullPointer, UndefinedPointerValue };
|
|
|
|
|
2020-07-30 14:31:39 +08:00
|
|
|
BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
|
|
|
|
BugType BT_Undef{this, "Dereference of undefined pointer value",
|
|
|
|
categories::LogicError};
|
2011-03-01 01:36:18 +08:00
|
|
|
|
2020-08-11 15:03:22 +08:00
|
|
|
void reportBug(DerefKind K, ProgramStateRef State, const Stmt *S,
|
|
|
|
CheckerContext &C) const;
|
2012-08-03 05:33:42 +08:00
|
|
|
|
2009-11-11 11:26:34 +08:00
|
|
|
public:
|
2011-10-06 08:43:15 +08:00
|
|
|
void checkLocation(SVal location, bool isLoad, const Stmt* S,
|
|
|
|
CheckerContext &C) const;
|
2012-08-03 05:33:42 +08:00
|
|
|
void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
|
2011-03-01 01:36:18 +08:00
|
|
|
|
2012-09-06 06:31:49 +08:00
|
|
|
static void AddDerefSource(raw_ostream &os,
|
2011-07-23 18:55:15 +08:00
|
|
|
SmallVectorImpl<SourceRange> &Ranges,
|
2012-03-09 09:13:14 +08:00
|
|
|
const Expr *Ex, const ProgramState *state,
|
|
|
|
const LocationContext *LCtx,
|
|
|
|
bool loadedFrom = false);
|
2009-11-11 11:26:34 +08:00
|
|
|
};
|
|
|
|
} // end anonymous namespace
|
2009-10-31 01:24:47 +08:00
|
|
|
|
2012-09-06 06:31:49 +08:00
|
|
|
void
|
2012-03-09 09:13:14 +08:00
|
|
|
DereferenceChecker::AddDerefSource(raw_ostream &os,
|
|
|
|
SmallVectorImpl<SourceRange> &Ranges,
|
|
|
|
const Expr *Ex,
|
|
|
|
const ProgramState *state,
|
|
|
|
const LocationContext *LCtx,
|
|
|
|
bool loadedFrom) {
|
2010-12-04 11:47:34 +08:00
|
|
|
Ex = Ex->IgnoreParenLValueCasts();
|
2010-10-26 08:06:13 +08:00
|
|
|
switch (Ex->getStmtClass()) {
|
|
|
|
default:
|
2012-03-09 09:13:14 +08:00
|
|
|
break;
|
2010-10-26 08:06:13 +08:00
|
|
|
case Stmt::DeclRefExprClass: {
|
|
|
|
const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
|
|
|
|
if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
|
|
|
|
os << " (" << (loadedFrom ? "loaded from" : "from")
|
|
|
|
<< " variable '" << VD->getName() << "')";
|
|
|
|
Ranges.push_back(DR->getSourceRange());
|
|
|
|
}
|
2012-03-09 09:13:14 +08:00
|
|
|
break;
|
2010-10-26 08:06:13 +08:00
|
|
|
}
|
|
|
|
case Stmt::MemberExprClass: {
|
|
|
|
const MemberExpr *ME = cast<MemberExpr>(Ex);
|
|
|
|
os << " (" << (loadedFrom ? "loaded from" : "via")
|
|
|
|
<< " field '" << ME->getMemberNameInfo() << "')";
|
|
|
|
SourceLocation L = ME->getMemberLoc();
|
|
|
|
Ranges.push_back(SourceRange(L, L));
|
|
|
|
break;
|
|
|
|
}
|
2013-02-24 15:21:01 +08:00
|
|
|
case Stmt::ObjCIvarRefExprClass: {
|
|
|
|
const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
|
|
|
|
os << " (" << (loadedFrom ? "loaded from" : "via")
|
|
|
|
<< " ivar '" << IV->getDecl()->getName() << "')";
|
|
|
|
SourceLocation L = IV->getLocation();
|
|
|
|
Ranges.push_back(SourceRange(L, L));
|
|
|
|
break;
|
2015-09-08 11:50:52 +08:00
|
|
|
}
|
2010-10-26 08:06:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-06 08:32:49 +08:00
|
|
|
static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
|
|
|
|
const Expr *E = nullptr;
|
|
|
|
|
|
|
|
// Walk through lvalue casts to get the original expression
|
|
|
|
// that syntactically caused the load.
|
|
|
|
if (const Expr *expr = dyn_cast<Expr>(S))
|
|
|
|
E = expr->IgnoreParenLValueCasts();
|
|
|
|
|
|
|
|
if (IsBind) {
|
|
|
|
const VarDecl *VD;
|
|
|
|
const Expr *Init;
|
|
|
|
std::tie(VD, Init) = parseAssignment(S);
|
|
|
|
if (VD && Init)
|
|
|
|
E = Init;
|
|
|
|
}
|
|
|
|
return E;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool suppressReport(const Expr *E) {
|
|
|
|
// Do not report dereferences on memory in non-default address spaces.
|
2019-12-07 00:48:15 +08:00
|
|
|
return E->getType().hasAddressSpace();
|
2016-01-06 08:32:49 +08:00
|
|
|
}
|
|
|
|
|
2018-10-24 02:24:53 +08:00
|
|
|
static bool isDeclRefExprToReference(const Expr *E) {
|
|
|
|
if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
|
|
|
|
return DRE->getDecl()->getType()->isReferenceType();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-08-11 15:03:22 +08:00
|
|
|
void DereferenceChecker::reportBug(DerefKind K, ProgramStateRef State,
|
|
|
|
const Stmt *S, CheckerContext &C) const {
|
|
|
|
const BugType *BT = nullptr;
|
|
|
|
llvm::StringRef DerefStr1;
|
|
|
|
llvm::StringRef DerefStr2;
|
|
|
|
switch (K) {
|
|
|
|
case DerefKind::NullPointer:
|
|
|
|
BT = &BT_Null;
|
|
|
|
DerefStr1 = " results in a null pointer dereference";
|
|
|
|
DerefStr2 = " results in a dereference of a null pointer";
|
|
|
|
break;
|
|
|
|
case DerefKind::UndefinedPointerValue:
|
|
|
|
BT = &BT_Undef;
|
|
|
|
DerefStr1 = " results in an undefined pointer dereference";
|
|
|
|
DerefStr2 = " results in a dereference of an undefined pointer value";
|
|
|
|
break;
|
|
|
|
};
|
|
|
|
|
2012-08-03 05:33:42 +08:00
|
|
|
// Generate an error node.
|
2015-09-17 06:03:05 +08:00
|
|
|
ExplodedNode *N = C.generateErrorNode(State);
|
2012-08-03 05:33:42 +08:00
|
|
|
if (!N)
|
|
|
|
return;
|
|
|
|
|
|
|
|
SmallString<100> buf;
|
2012-09-22 09:24:38 +08:00
|
|
|
llvm::raw_svector_ostream os(buf);
|
|
|
|
|
2012-08-03 05:33:42 +08:00
|
|
|
SmallVector<SourceRange, 2> Ranges;
|
|
|
|
|
|
|
|
switch (S->getStmtClass()) {
|
|
|
|
case Stmt::ArraySubscriptExprClass: {
|
|
|
|
os << "Array access";
|
|
|
|
const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
|
2012-09-06 06:31:49 +08:00
|
|
|
AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
|
2014-07-05 11:08:06 +08:00
|
|
|
State.get(), N->getLocationContext());
|
2020-08-11 15:03:22 +08:00
|
|
|
os << DerefStr1;
|
2012-08-03 05:33:42 +08:00
|
|
|
break;
|
|
|
|
}
|
2015-08-25 22:24:04 +08:00
|
|
|
case Stmt::OMPArraySectionExprClass: {
|
|
|
|
os << "Array access";
|
|
|
|
const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
|
|
|
|
AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
|
|
|
|
State.get(), N->getLocationContext());
|
2020-08-11 15:03:22 +08:00
|
|
|
os << DerefStr1;
|
2015-08-25 22:24:04 +08:00
|
|
|
break;
|
|
|
|
}
|
2012-08-03 05:33:42 +08:00
|
|
|
case Stmt::UnaryOperatorClass: {
|
2020-08-11 15:03:22 +08:00
|
|
|
os << BT->getDescription();
|
2012-08-03 05:33:42 +08:00
|
|
|
const UnaryOperator *U = cast<UnaryOperator>(S);
|
2012-09-06 06:31:49 +08:00
|
|
|
AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
|
2014-07-05 11:08:06 +08:00
|
|
|
State.get(), N->getLocationContext(), true);
|
2012-08-03 05:33:42 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Stmt::MemberExprClass: {
|
|
|
|
const MemberExpr *M = cast<MemberExpr>(S);
|
2018-10-24 02:24:53 +08:00
|
|
|
if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
|
2020-08-11 15:03:22 +08:00
|
|
|
os << "Access to field '" << M->getMemberNameInfo() << "'" << DerefStr2;
|
2012-09-06 06:31:49 +08:00
|
|
|
AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
|
2014-07-05 11:08:06 +08:00
|
|
|
State.get(), N->getLocationContext(), true);
|
2012-08-03 05:33:42 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Stmt::ObjCIvarRefExprClass: {
|
|
|
|
const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
|
2020-08-11 15:03:22 +08:00
|
|
|
os << "Access to instance variable '" << *IV->getDecl() << "'" << DerefStr2;
|
2012-09-22 09:24:38 +08:00
|
|
|
AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
|
2014-07-05 11:08:06 +08:00
|
|
|
State.get(), N->getLocationContext(), true);
|
2012-08-03 05:33:42 +08:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2019-09-10 04:34:40 +08:00
|
|
|
auto report = std::make_unique<PathSensitiveBugReport>(
|
2020-08-11 15:03:22 +08:00
|
|
|
*BT, buf.empty() ? BT->getDescription() : StringRef(buf), N);
|
2012-08-03 05:33:42 +08:00
|
|
|
|
2018-10-24 02:24:53 +08:00
|
|
|
bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
|
2012-08-03 05:33:42 +08:00
|
|
|
|
|
|
|
for (SmallVectorImpl<SourceRange>::iterator
|
|
|
|
I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
|
|
|
|
report->addRange(*I);
|
|
|
|
|
2015-06-23 21:15:32 +08:00
|
|
|
C.emitReport(std::move(report));
|
2012-08-03 05:33:42 +08:00
|
|
|
}
|
|
|
|
|
2011-10-06 08:43:15 +08:00
|
|
|
void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
|
2011-03-01 01:36:18 +08:00
|
|
|
CheckerContext &C) const {
|
2009-11-11 11:26:34 +08:00
|
|
|
// Check for dereference of an undefined value.
|
|
|
|
if (l.isUndef()) {
|
2020-08-11 15:03:22 +08:00
|
|
|
const Expr *DerefExpr = getDereferenceExpr(S);
|
|
|
|
if (!suppressReport(DerefExpr))
|
|
|
|
reportBug(DerefKind::UndefinedPointerValue, C.getState(), DerefExpr, C);
|
2009-11-11 11:26:34 +08:00
|
|
|
return;
|
|
|
|
}
|
2010-03-23 09:11:38 +08:00
|
|
|
|
2013-02-20 13:52:05 +08:00
|
|
|
DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
|
2010-03-23 09:11:38 +08:00
|
|
|
|
|
|
|
// Check for null dereferences.
|
2013-02-20 13:52:05 +08:00
|
|
|
if (!location.getAs<Loc>())
|
2009-11-11 11:26:34 +08:00
|
|
|
return;
|
2010-03-23 09:11:38 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef state = C.getState();
|
2012-08-03 05:33:42 +08:00
|
|
|
|
2012-01-27 05:29:00 +08:00
|
|
|
ProgramStateRef notNullState, nullState;
|
2014-03-02 21:01:17 +08:00
|
|
|
std::tie(notNullState, nullState) = state->assume(location);
|
2010-03-23 09:11:38 +08:00
|
|
|
|
2009-11-11 11:26:34 +08:00
|
|
|
if (nullState) {
|
2010-03-23 09:11:38 +08:00
|
|
|
if (!notNullState) {
|
2020-07-30 14:31:39 +08:00
|
|
|
// We know that 'location' can only be null. This is what
|
|
|
|
// we call an "explicit" null dereference.
|
2016-01-06 08:32:49 +08:00
|
|
|
const Expr *expr = getDereferenceExpr(S);
|
|
|
|
if (!suppressReport(expr)) {
|
2020-08-11 15:03:22 +08:00
|
|
|
reportBug(DerefKind::NullPointer, nullState, expr, C);
|
2016-01-06 08:32:49 +08:00
|
|
|
return;
|
|
|
|
}
|
2012-08-03 05:33:42 +08:00
|
|
|
}
|
2009-11-04 02:41:06 +08:00
|
|
|
|
2012-08-03 05:33:42 +08:00
|
|
|
// Otherwise, we have the case where the location could either be
|
|
|
|
// null or not-null. Record the error node as an "implicit" null
|
|
|
|
// dereference.
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
|
2015-08-28 02:49:07 +08:00
|
|
|
ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
|
2016-01-30 02:43:15 +08:00
|
|
|
/*IsDirectDereference=*/true};
|
2012-08-03 05:33:42 +08:00
|
|
|
dispatchEvent(event);
|
|
|
|
}
|
|
|
|
}
|
2009-11-24 09:33:10 +08:00
|
|
|
|
2012-08-03 05:33:42 +08:00
|
|
|
// From this point forward, we know that the location is not null.
|
|
|
|
C.addTransition(notNullState);
|
|
|
|
}
|
2010-03-23 09:11:38 +08:00
|
|
|
|
2012-08-03 05:33:42 +08:00
|
|
|
void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
|
|
|
|
CheckerContext &C) const {
|
2012-08-04 08:25:30 +08:00
|
|
|
// If we're binding to a reference, check if the value is known to be null.
|
2012-08-03 05:33:42 +08:00
|
|
|
if (V.isUndef())
|
|
|
|
return;
|
2010-03-23 09:11:38 +08:00
|
|
|
|
2012-08-03 05:33:42 +08:00
|
|
|
const MemRegion *MR = L.getAsRegion();
|
|
|
|
const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
|
|
|
|
if (!TVR)
|
|
|
|
return;
|
2012-03-09 09:13:14 +08:00
|
|
|
|
2012-08-03 05:33:42 +08:00
|
|
|
if (!TVR->getValueType()->isReferenceType())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ProgramStateRef State = C.getState();
|
|
|
|
|
|
|
|
ProgramStateRef StNonNull, StNull;
|
2014-03-02 21:01:17 +08:00
|
|
|
std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
|
2012-08-03 05:33:42 +08:00
|
|
|
|
|
|
|
if (StNull) {
|
|
|
|
if (!StNonNull) {
|
2016-01-06 08:32:49 +08:00
|
|
|
const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
|
|
|
|
if (!suppressReport(expr)) {
|
2020-08-11 15:03:22 +08:00
|
|
|
reportBug(DerefKind::NullPointer, StNull, expr, C);
|
2016-01-06 08:32:49 +08:00
|
|
|
return;
|
|
|
|
}
|
2009-11-21 09:50:48 +08:00
|
|
|
}
|
2012-08-03 05:33:42 +08:00
|
|
|
|
|
|
|
// At this point the value could be either null or non-null.
|
|
|
|
// Record this as an "implicit" null dereference.
|
2015-09-17 06:03:05 +08:00
|
|
|
if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
|
2015-08-28 02:49:07 +08:00
|
|
|
ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
|
|
|
|
&C.getBugReporter(),
|
2016-01-30 02:43:15 +08:00
|
|
|
/*IsDirectDereference=*/true};
|
2012-08-03 05:33:42 +08:00
|
|
|
dispatchEvent(event);
|
2009-11-04 02:41:06 +08:00
|
|
|
}
|
|
|
|
}
|
2010-03-23 09:11:38 +08:00
|
|
|
|
2012-08-04 08:25:30 +08:00
|
|
|
// Unlike a regular null dereference, initializing a reference with a
|
|
|
|
// dereferenced null pointer does not actually cause a runtime exception in
|
|
|
|
// Clang's implementation of references.
|
|
|
|
//
|
|
|
|
// int &r = *p; // safe??
|
|
|
|
// if (p != NULL) return; // uh-oh
|
|
|
|
// r = 5; // trap here
|
|
|
|
//
|
|
|
|
// The standard says this is invalid as soon as we try to create a "null
|
|
|
|
// reference" (there is no such thing), but turning this into an assumption
|
|
|
|
// that 'p' is never null will not match our actual runtime behavior.
|
|
|
|
// So we do not record this assumption, allowing us to warn on the last line
|
|
|
|
// of this example.
|
|
|
|
//
|
|
|
|
// We do need to add a transition because we may have generated a sink for
|
|
|
|
// the "implicit" null dereference.
|
|
|
|
C.addTransition(State, this);
|
2009-11-04 02:41:06 +08:00
|
|
|
}
|
2011-03-01 01:36:18 +08:00
|
|
|
|
|
|
|
void ento::registerDereferenceChecker(CheckerManager &mgr) {
|
|
|
|
mgr.registerChecker<DereferenceChecker>();
|
|
|
|
}
|
2019-01-26 22:23:08 +08:00
|
|
|
|
2020-03-27 21:29:31 +08:00
|
|
|
bool ento::shouldRegisterDereferenceChecker(const CheckerManager &mgr) {
|
2019-01-26 22:23:08 +08:00
|
|
|
return true;
|
|
|
|
}
|