RedDocMD updated this revision to Diff 347259.
RedDocMD added a comment.
Refactors to make code more stylistically accurate
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D97183/new/
https://reviews.llvm.org/D97183
Files:
clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
clang/test/Analysis/smart-ptr-text-output.cpp
Index: clang/test/Analysis/smart-ptr-text-output.cpp
===================================================================
--- clang/test/Analysis/smart-ptr-text-output.cpp
+++ clang/test/Analysis/smart-ptr-text-output.cpp
@@ -306,10 +306,81 @@
};
void derefAfterBranchingOnUnknownInnerPtr(std::unique_ptr<A> P) {
- A *RP = P.get();
+ A *RP = P.get(); // expected-note {{Obtained null inner pointer from 'P'}}
if (!RP) { // expected-note {{Assuming 'RP' is null}}
// expected-note@-1 {{Taking true branch}}
P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
// expected-note@-1{{Dereference of null smart pointer 'P'}}
}
}
+
+void multpleDeclsWithGet(std::unique_ptr<A> P) {
+ A *dummy1 = nullptr, *RP = P.get(), *dummy2; // expected-note {{Obtained null inner pointer from 'P'}}
+ if (!RP) { // expected-note {{Assuming 'RP' is null}}
+ // expected-note@-1 {{Taking true branch}}
+ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
+ // expected-note@-1{{Dereference of null smart pointer 'P'}}
+ }
+}
+
+void multipleGetsShouldNotAllHaveNotes(std::unique_ptr<A> P) {
+ A *RP = P.get(); // expected-note {{Obtained null inner pointer from 'P'}}
+ A *dummy1 = P.get();
+ A *dummy2 = P.get();
+ if (!RP) { // expected-note {{Assuming 'RP' is null}}
+ // expected-note@-1 {{Taking true branch}}
+ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
+ // expected-note@-1{{Dereference of null smart pointer 'P'}}
+ }
+}
+
+void getShouldNotAlwaysLeaveANote() {
+ std::unique_ptr<A> P; // expected-note {{Default constructed smart pointer 'P' is null}}
+ A *a = P.get();
+ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
+ // expected-note@-1{{Dereference of null smart pointer 'P'}}
+}
+
+void getShouldNotLeaveANoteAfterReset(std::unique_ptr<A> P) {
+ A *a = P.get();
+ P.reset(); // expected-note {{Smart pointer 'P' reset using a null value}}
+ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
+ // expected-note@-1{{Dereference of null smart pointer 'P'}}
+}
+
+void getShouldNotLeaveNoteWhenPtrNotUsed(std::unique_ptr<A> P) {
+ A *a = P.get();
+ if (!P) { // expected-note {{Taking true branch}}
+ // expected-note@-1 {{Assuming smart pointer 'P' is null}}
+ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
+ // expected-note@-1{{Dereference of null smart pointer 'P'}}
+ }
+}
+
+void getShouldLeaveANoteWithWhileLoop(std::unique_ptr<A> P) {
+ A *RP = P.get(); // expected-note {{Obtained null inner pointer from 'P'}}
+ while (!RP) { // expected-note {{Assuming 'RP' is null}}
+ // expected-note@-1 {{Loop condition is true. Entering loop body}}
+ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
+ // expected-note@-1{{Dereference of null smart pointer 'P'}}
+ }
+}
+
+void getShouldLeaveANoteWithForLoop(std::unique_ptr<A> P) {
+ for (A *RP = P.get(); !RP;) { // expected-note {{Assuming 'RP' is null}}
+ // expected-note@-1 {{Loop condition is true. Entering loop body}}
+ // expected-note@-2 {{Obtained null inner pointer from 'P'}}
+ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
+ // expected-note@-1{{Dereference of null smart pointer 'P'}}
+ }
+}
+
+void getShouldLeaveNoteOnChaining(std::unique_ptr<A> P) {
+ A *praw = P.get(), *other; // expected-note {{Obtained null inner pointer from 'P'}}
+ other = praw; // expected-note {{Obtained null value here}}
+ if (!other) { // expected-note {{Assuming 'other' is null}}
+ // expected-note@-1 {{Taking true branch}}
+ P->foo(); // expected-warning {{Dereference of null smart pointer 'P' [alpha.cplusplus.SmartPtr]}}
+ // expected-note@-1{{Dereference of null smart pointer 'P'}}
+ }
+}
\ No newline at end of file
Index: clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -17,6 +17,7 @@
#include "clang/AST/DeclCXX.h"
#include "clang/AST/DeclarationName.h"
#include "clang/AST/ExprCXX.h"
+#include "clang/AST/OperationKinds.h"
#include "clang/AST/Type.h"
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
@@ -76,11 +77,16 @@
{{"release"}, &SmartPtrModeling::handleRelease},
{{"swap", 1}, &SmartPtrModeling::handleSwap},
{{"get"}, &SmartPtrModeling::handleGet}};
+ mutable llvm::ImmutableSet<const Expr *>::Factory StmtSetFactory;
};
} // end of anonymous namespace
REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, SVal)
+// TODO: Get rid of this onece D97183 is settled
+REGISTER_MAP_WITH_PROGRAMSTATE(ExprsFromGet, const MemRegion *,
+ llvm::ImmutableSet<const Expr *>)
+
// Define the inter-checker API.
namespace clang {
namespace ento {
@@ -106,6 +112,164 @@
return InnerPointVal &&
!State->assume(InnerPointVal->castAs<DefinedOrUnknownSVal>(), true);
}
+
+bool isExprObtainedFromGet(const ProgramStateRef State,
+ const MemRegion *ThisRegion, const Expr *E) {
+ const auto *ExprSet = State->get<ExprsFromGet>(ThisRegion);
+ return ExprSet && ExprSet->contains(E);
+}
+
+PathDiagnosticPieceRef GetNoteVisitor::VisitNode(const ExplodedNode *Node,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) {
+
+ ProgramStateRef State = Node->getState();
+ const SVal *InnerPtr = State->get<TrackedRegionMap>(ThisRegion);
+ if (InnerPtr) {
+ SVal InnerPtrVal = *InnerPtr;
+ ProgramStateRef NullState, NonNullState;
+ std::tie(NullState, NonNullState) =
+ State->assume(InnerPtrVal.castAs<DefinedOrUnknownSVal>());
+
+ // Check whether we have a constraint on ThisRegion
+ if (NullState && NonNullState) {
+ if (const Stmt *S = Node->getStmtForDiagnostics()) {
+ // Skip if we already have covered this statement
+ auto ItInsertedPair = StmtsCovered.insert(S);
+ if (!ItInsertedPair.second)
+ return nullptr;
+
+ // If statement on raw pointer
+ visitIfStmt(Node, State, BRC, S, InnerPtrVal);
+
+ // Assignment operator
+ if (auto Report = visitAssgnStmt(Node, State, BRC, S, InnerPtrVal))
+ return Report;
+
+ // Variable declaration
+ if (auto Report = visitDeclStmt(Node, State, BRC, S, InnerPtrVal))
+ return Report;
+ }
+ }
+ }
+ return nullptr;
+}
+
+PathDiagnosticPieceRef GetNoteVisitor::bugReportOnGet(const ExplodedNode *Node,
+ BugReporterContext &BRC,
+ const Expr *E) {
+ if (const auto *MCE = llvm::dyn_cast<CXXMemberCallExpr>(E)) {
+ const auto *Method = MCE->getMethodDecl();
+ const auto *Record = MCE->getRecordDecl();
+ if (Method->getName() == "get" &&
+ Record->getDeclContext()->isStdNamespace() &&
+ Record->getName() == "unique_ptr") {
+ llvm::SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+ if (ThisRegion->canPrintPretty()) {
+ OS << "Obtained null inner pointer from ";
+ ThisRegion->printPretty(OS);
+ } else {
+ OS << "Obtained null inner pointer here";
+ }
+ const Stmt *S = Node->getStmtForDiagnostics();
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ Node->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
+ }
+ }
+ return std::shared_ptr<PathDiagnosticEventPiece>();
+}
+
+void GetNoteVisitor::visitIfStmt(const ExplodedNode *Node,
+ const ProgramStateRef State,
+ BugReporterContext &BRC, const Stmt *S,
+ const SVal InnerPtrVal) {
+ const auto *E = llvm::dyn_cast<ImplicitCastExpr>(S);
+ if (!E)
+ return;
+ if (E->getCastKind() != CastKind::CK_PointerToBoolean)
+ return;
+ // Check if we are tracking the expression being casted
+ const Expr *Sub = E->getSubExpr();
+ const Environment &Env = State->getEnvironment();
+ EnvironmentEntry Entry(Sub, Node->getLocationContext());
+ const SVal ExprVal = Env.getSVal(Entry, Bldr);
+ if (ExprVal != InnerPtrVal)
+ return;
+ // So we have a pointer being used in an if statement
+ // And that pointer is being tracked to the same SVal as
+ // ThisRegion Also it is at this if statement that the
+ // constraining starts So we know that this pointer points to
+ // P.get()
+ if (const auto *Ptr = llvm::dyn_cast<DeclRefExpr>(Sub->IgnoreParenCasts()))
+ PtrsTrackedAndConstrained.insert(Ptr->getDecl());
+}
+
+PathDiagnosticPieceRef GetNoteVisitor::visitAssgnStmt(
+ const ExplodedNode *Node, const ProgramStateRef State,
+ BugReporterContext &BRC, const Stmt *S, const SVal InnerPtrVal) {
+ const auto *BO = llvm::dyn_cast<BinaryOperator>(S);
+ if (!BO)
+ return nullptr;
+ if (BO->getOpcode() != BO_Assign)
+ return nullptr;
+ const Expr *LHS = BO->getLHS();
+ const auto *DeclRef = llvm::dyn_cast<DeclRefExpr>(LHS);
+ if (!DeclRef)
+ return nullptr;
+ if (!PtrsTrackedAndConstrained.contains(DeclRef->getDecl()))
+ return nullptr;
+ const Expr *RHS = BO->getRHS();
+ // So now we have an assignment of the form a = b,
+ // where a is known to be tracked and constrained
+
+ // If b is just a pointer, then we should add it to the set
+ if (const auto *ICE = llvm::dyn_cast<ImplicitCastExpr>(RHS)) {
+ const Expr *Sub = ICE->getSubExpr();
+
+ if (const auto *Ptr = llvm::dyn_cast<DeclRefExpr>(Sub)) {
+ PtrsTrackedAndConstrained.insert(Ptr->getDecl());
+ llvm::SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+ OS << "Obtained null value here";
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ Node->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
+ }
+ }
+
+ // If b is a get() expression, then we can return a note
+ if (auto Report = bugReportOnGet(Node, BRC, RHS))
+ return Report;
+
+ return nullptr;
+}
+
+PathDiagnosticPieceRef GetNoteVisitor::visitDeclStmt(
+ const ExplodedNode *Node, const ProgramStateRef State,
+ BugReporterContext &BRC, const Stmt *S, const SVal InnerPtrVal) {
+ const auto *DS = llvm::dyn_cast<DeclStmt>(S);
+ if (!DS)
+ return nullptr;
+ const Decl *D = DS->getSingleDecl();
+ assert(D && "DeclStmt should have at least one Decl");
+ const auto *VD = llvm::dyn_cast<VarDecl>(D);
+ if (!VD)
+ return nullptr;
+ for (const auto *I : PtrsTrackedAndConstrained) {
+ if (I->getName() == VD->getName()) {
+ const Expr *Init = VD->getAnyInitializer();
+ if (!Init)
+ continue;
+ if (auto Report = bugReportOnGet(Node, BRC, Init))
+ return Report;
+ break;
+ }
+ }
+ return nullptr;
+}
+
} // namespace smartptr
} // namespace ento
} // namespace clang
@@ -446,10 +610,10 @@
return;
SVal InnerPointerVal;
+ const auto *CallExpr = Call.getOriginExpr();
if (const auto *InnerValPtr = State->get<TrackedRegionMap>(ThisRegion)) {
InnerPointerVal = *InnerValPtr;
} else {
- const auto *CallExpr = Call.getOriginExpr();
InnerPointerVal = C.getSValBuilder().conjureSymbolVal(
CallExpr, C.getLocationContext(), Call.getResultType(), C.blockCount());
State = State->set<TrackedRegionMap>(ThisRegion, InnerPointerVal);
@@ -457,8 +621,20 @@
State = State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
InnerPointerVal);
- // TODO: Add NoteTag, for how the raw pointer got using 'get' method.
- C.addTransition(State);
+
+ // TODO: Get rid of this onece D97183 is settled
+ // Store the CallExpr as being obtained through get. It will be necessary in
+ // isExprObtainedFromGet().
+ if (const auto *StmtSet = State->get<ExprsFromGet>(ThisRegion)) {
+ auto NewStmtSet = StmtSetFactory.add(*StmtSet, CallExpr);
+ State = State->set<ExprsFromGet>(ThisRegion, NewStmtSet);
+ } else {
+ auto EmptySet = StmtSetFactory.getEmptySet();
+ auto NewStmtSet = StmtSetFactory.add(EmptySet, CallExpr);
+ State = State->set<ExprsFromGet>(ThisRegion, NewStmtSet);
+ }
+
+ C.addTransition(State); // The note is added later by a visitor.
}
bool SmartPtrModeling::handleAssignOp(const CallEvent &Call,
Index: clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/SmartPtrChecker.cpp
@@ -86,6 +86,8 @@
explainDereference(OS, DerefRegion, Call);
auto R = std::make_unique<PathSensitiveBugReport>(NullDereferenceBugType,
OS.str(), ErrNode);
+ R->addVisitor(std::make_unique<smartptr::GetNoteVisitor>(DerefRegion,
+ C.getSValBuilder()));
R->markInteresting(DerefRegion);
C.emitReport(std::move(R));
}
Index: clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
+++ clang/lib/StaticAnalyzer/Checkers/SmartPtr.h
@@ -28,6 +28,49 @@
const BugType *getNullDereferenceBugType();
+// TODO: Get rid after D97183
+// Returns whether E is of the form a.get(), where the MemRegion corresponding
+// to the smart-ptr a is ThisRegion.
+bool isExprObtainedFromGet(const ProgramStateRef State,
+ const MemRegion *ThisRegion, const Expr *E);
+
+class GetNoteVisitor : public BugReporterVisitor {
+private:
+ const MemRegion *ThisRegion;
+ SValBuilder &Bldr;
+ llvm::SmallPtrSet<const ValueDecl *, 4> PtrsTrackedAndConstrained;
+ llvm::SmallPtrSet<const Stmt *, 16> StmtsCovered;
+
+public:
+ GetNoteVisitor(const MemRegion *ThisRegion, SValBuilder &Bldr)
+ : ThisRegion{ThisRegion}, Bldr{Bldr}, PtrsTrackedAndConstrained{},
+ StmtsCovered{} {}
+ PathDiagnosticPieceRef VisitNode(const ExplodedNode *Node,
+ BugReporterContext &BRC,
+ PathSensitiveBugReport &BR) override;
+
+ PathDiagnosticPieceRef bugReportOnGet(const ExplodedNode *Node,
+ BugReporterContext &BRC, const Expr *E);
+
+ void visitIfStmt(const ExplodedNode *Node, const ProgramStateRef State,
+ BugReporterContext &BRC, const Stmt *S,
+ const SVal InnerPtrVal);
+
+ PathDiagnosticPieceRef visitAssgnStmt(const ExplodedNode *Node,
+ const ProgramStateRef State,
+ BugReporterContext &BRC, const Stmt *S,
+ const SVal InnerPtrVal);
+
+ PathDiagnosticPieceRef visitDeclStmt(const ExplodedNode *Node,
+ const ProgramStateRef State,
+ BugReporterContext &BRC, const Stmt *S,
+ const SVal InnerPtrVal);
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.Add(ThisRegion);
+ }
+};
+
} // namespace smartptr
} // namespace ento
} // namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits