Skip to content

Commit

Permalink
Merged master:06d100a69a08 into amd-gfx:9bd98790e775
Browse files Browse the repository at this point in the history
Local branch amd-gfx 9bd9879 Merged master:254e0abf5be2 into amd-gfx:ad883734e3bc
Remote branch master 06d100a [Analyzer] Support note tags for smart ptr checker
  • Loading branch information
Sw authored and Sw committed Aug 11, 2020
2 parents 9bd9879 + 06d100a commit 4a16eb2
Show file tree
Hide file tree
Showing 34 changed files with 577 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,26 @@ class CheckerContext {
IsPrunable);
}

/// A shorthand version of getNoteTag that accepts a lambda with stream for
/// note.
///
/// @param Cb Callback with 'BugReport &' and 'llvm::raw_ostream &'.
/// @param IsPrunable Whether the note is prunable. It allows BugReporter
/// to omit the note from the report if it would make the displayed
/// bug path significantly shorter.
const NoteTag *getNoteTag(
std::function<void(PathSensitiveBugReport &BR, llvm::raw_ostream &OS)> &&Cb,
bool IsPrunable = false) {
return getNoteTag(
[Cb](PathSensitiveBugReport &BR) -> std::string {
llvm::SmallString<128> Str;
llvm::raw_svector_ostream OS(Str);
Cb(BR, OS);
return std::string(OS.str());
},
IsPrunable);
}

/// Returns the word that should be used to refer to the declaration
/// in the report.
StringRef getDeclDescription(const Decl *D);
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ bool isStdSmartPtrCall(const CallEvent &Call);
/// Returns whether the smart pointer is null or not.
bool isNullSmartPtr(const ProgramStateRef State, const MemRegion *ThisRegion);

const BugType *getNullDereferenceBugType();

} // namespace smartptr
} // namespace ento
} // namespace clang
Expand Down
48 changes: 38 additions & 10 deletions clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,40 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "llvm/ADT/StringRef.h"

using namespace clang;
using namespace ento;

namespace {
class SmartPtrChecker : public Checker<check::PreCall> {
BugType NullDereferenceBugType{this, "Null SmartPtr dereference",
"C++ Smart Pointer"};

static const BugType *NullDereferenceBugTypePtr;

class SmartPtrChecker : public Checker<check::PreCall> {
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
BugType NullDereferenceBugType{this, "Null SmartPtr dereference",
"C++ Smart Pointer"};

private:
void reportBug(CheckerContext &C, const CallEvent &Call) const;
void reportBug(CheckerContext &C, const MemRegion *DerefRegion,
const CallEvent &Call) const;
void explainDereference(llvm::raw_ostream &OS, const MemRegion *DerefRegion,
const CallEvent &Call) const;
};
} // end of anonymous namespace

// Define the inter-checker API.
namespace clang {
namespace ento {
namespace smartptr {

const BugType *getNullDereferenceBugType() { return NullDereferenceBugTypePtr; }

} // namespace smartptr
} // namespace ento
} // namespace clang

void SmartPtrChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
if (!smartptr::isStdSmartPtrCall(Call))
Expand All @@ -55,23 +72,34 @@ void SmartPtrChecker::checkPreCall(const CallEvent &Call,
OverloadedOperatorKind OOK = OC->getOverloadedOperator();
if (OOK == OO_Star || OOK == OO_Arrow) {
if (smartptr::isNullSmartPtr(State, ThisRegion))
reportBug(C, Call);
reportBug(C, ThisRegion, Call);
}
}

void SmartPtrChecker::reportBug(CheckerContext &C,
void SmartPtrChecker::reportBug(CheckerContext &C, const MemRegion *DerefRegion,
const CallEvent &Call) const {
ExplodedNode *ErrNode = C.generateErrorNode();
if (!ErrNode)
return;

auto R = std::make_unique<PathSensitiveBugReport>(
NullDereferenceBugType, "Dereference of null smart pointer", ErrNode);
llvm::SmallString<128> Str;
llvm::raw_svector_ostream OS(Str);
explainDereference(OS, DerefRegion, Call);
auto R = std::make_unique<PathSensitiveBugReport>(NullDereferenceBugType,
OS.str(), ErrNode);
R->markInteresting(DerefRegion);
C.emitReport(std::move(R));
}

void SmartPtrChecker::explainDereference(llvm::raw_ostream &OS,
const MemRegion *DerefRegion,
const CallEvent &Call) const {
OS << "Dereference of null smart pointer ";
DerefRegion->printPretty(OS);
}

void ento::registerSmartPtrChecker(CheckerManager &Mgr) {
Mgr.registerChecker<SmartPtrChecker>();
SmartPtrChecker *Checker = Mgr.registerChecker<SmartPtrChecker>();
NullDereferenceBugTypePtr = &Checker->NullDereferenceBugType;
}

bool ento::shouldRegisterSmartPtrChecker(const CheckerManager &mgr) {
Expand Down
129 changes: 92 additions & 37 deletions clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,18 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include <string>

using namespace clang;
using namespace ento;
Expand All @@ -49,8 +53,6 @@ class SmartPtrModeling
const LocationContext *LCtx, const CallEvent *Call) const;

private:
ProgramStateRef updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
const MemRegion *ThisValRegion) const;
void handleReset(const CallEvent &Call, CheckerContext &C) const;
void handleRelease(const CallEvent &Call, CheckerContext &C) const;
void handleSwap(const CallEvent &Call, CheckerContext &C) const;
Expand Down Expand Up @@ -131,11 +133,11 @@ bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
bool SmartPtrModeling::evalCall(const CallEvent &Call,
CheckerContext &C) const {

ProgramStateRef State = C.getState();
if (!smartptr::isStdSmartPtrCall(Call))
return false;

if (isNullAfterMoveMethod(Call)) {
ProgramStateRef State = C.getState();
const MemRegion *ThisR =
cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();

Expand All @@ -159,12 +161,46 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
if (CC->getDecl()->isCopyOrMoveConstructor())
return false;

const MemRegion *ThisValRegion = CC->getCXXThisVal().getAsRegion();
if (!ThisValRegion)
const MemRegion *ThisRegion = CC->getCXXThisVal().getAsRegion();
if (!ThisRegion)
return false;

auto State = updateTrackedRegion(Call, C, ThisValRegion);
C.addTransition(State);
if (Call.getNumArgs() == 0) {
auto NullVal = C.getSValBuilder().makeNull();
State = State->set<TrackedRegionMap>(ThisRegion, NullVal);

C.addTransition(
State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
!BR.isInteresting(ThisRegion))
return;
OS << "Default constructed smart pointer ";
ThisRegion->printPretty(OS);
OS << " is null";
}));
} else {
const auto *TrackingExpr = Call.getArgExpr(0);
assert(TrackingExpr->getType()->isPointerType() &&
"Adding a non pointer value to TrackedRegionMap");
auto ArgVal = Call.getArgSVal(0);
State = State->set<TrackedRegionMap>(ThisRegion, ArgVal);

C.addTransition(State, C.getNoteTag([ThisRegion, TrackingExpr,
ArgVal](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
!BR.isInteresting(ThisRegion))
return;
bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
OS << "Smart pointer ";
ThisRegion->printPretty(OS);
if (ArgVal.isZeroConstant())
OS << " is constructed using a null value";
else
OS << " is constructed";
}));
}
return true;
}

Expand Down Expand Up @@ -207,37 +243,65 @@ ProgramStateRef SmartPtrModeling::checkRegionChanges(

void SmartPtrModeling::handleReset(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
if (!IC)
return;

const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
if (!ThisValRegion)
const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
if (!ThisRegion)
return;
auto State = updateTrackedRegion(Call, C, ThisValRegion);
C.addTransition(State);

assert(Call.getArgExpr(0)->getType()->isPointerType() &&
"Adding a non pointer value to TrackedRegionMap");
State = State->set<TrackedRegionMap>(ThisRegion, Call.getArgSVal(0));
const auto *TrackingExpr = Call.getArgExpr(0);
C.addTransition(
State, C.getNoteTag([ThisRegion, TrackingExpr](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
!BR.isInteresting(ThisRegion))
return;
bugreporter::trackExpressionValue(BR.getErrorNode(), TrackingExpr, BR);
OS << "Smart pointer ";
ThisRegion->printPretty(OS);
OS << " reset using a null value";
}));
// TODO: Make sure to ivalidate the region in the Store if we don't have
// time to model all methods.
}

void SmartPtrModeling::handleRelease(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
const auto *IC = dyn_cast<CXXInstanceCall>(&Call);
if (!IC)
return;

const MemRegion *ThisValRegion = IC->getCXXThisVal().getAsRegion();
if (!ThisValRegion)
const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
if (!ThisRegion)
return;

auto State = updateTrackedRegion(Call, C, ThisValRegion);
const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisRegion);

const auto *InnerPointVal = State->get<TrackedRegionMap>(ThisValRegion);
if (InnerPointVal) {
State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
*InnerPointVal);
}
C.addTransition(State);

auto ValueToUpdate = C.getSValBuilder().makeNull();
State = State->set<TrackedRegionMap>(ThisRegion, ValueToUpdate);

C.addTransition(State, C.getNoteTag([ThisRegion](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
!BR.isInteresting(ThisRegion))
return;

OS << "Smart pointer ";
ThisRegion->printPretty(OS);
OS << " is released and set to null";
}));
// TODO: Add support to enable MallocChecker to start tracking the raw
// pointer.
}
Expand Down Expand Up @@ -267,27 +331,18 @@ void SmartPtrModeling::handleSwap(const CallEvent &Call,
State = updateSwappedRegion(State, ThisRegion, ArgRegionInnerPointerVal);
State = updateSwappedRegion(State, ArgRegion, ThisRegionInnerPointerVal);

C.addTransition(State);
}

ProgramStateRef
SmartPtrModeling::updateTrackedRegion(const CallEvent &Call, CheckerContext &C,
const MemRegion *ThisValRegion) const {
// TODO: Refactor and clean up handling too many things.
ProgramStateRef State = C.getState();
auto NumArgs = Call.getNumArgs();

if (NumArgs == 0) {
auto NullSVal = C.getSValBuilder().makeNull();
State = State->set<TrackedRegionMap>(ThisValRegion, NullSVal);
} else if (NumArgs == 1) {
auto ArgVal = Call.getArgSVal(0);
assert(Call.getArgExpr(0)->getType()->isPointerType() &&
"Adding a non pointer value to TrackedRegionMap");
State = State->set<TrackedRegionMap>(ThisValRegion, ArgVal);
}

return State;
C.addTransition(
State, C.getNoteTag([ThisRegion, ArgRegion](PathSensitiveBugReport &BR,
llvm::raw_ostream &OS) {
if (&BR.getBugType() != smartptr::getNullDereferenceBugType() ||
!BR.isInteresting(ThisRegion))
return;
BR.markInteresting(ArgRegion);
OS << "Swapped null smart pointer ";
ArgRegion->printPretty(OS);
OS << " with smart pointer ";
ThisRegion->printPretty(OS);
}));
}

void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
Expand Down
Loading

0 comments on commit 4a16eb2

Please sign in to comment.