Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 1 | //===-- DeleteWithNonVirtualDtorChecker.cpp -----------------------*- C++ -*--// |
| 2 | // |
Chandler Carruth | 2946cd7 | 2019-01-19 08:50:56 +0000 | [diff] [blame] | 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | // |
| 9 | // Defines a checker for the OOP52-CPP CERT rule: Do not delete a polymorphic |
| 10 | // object without a virtual destructor. |
| 11 | // |
| 12 | // Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor report if |
| 13 | // an object with a virtual function but a non-virtual destructor exists or is |
| 14 | // deleted, respectively. |
| 15 | // |
| 16 | // This check exceeds them by comparing the dynamic and static types of the |
| 17 | // object at the point of destruction and only warns if it happens through a |
| 18 | // pointer to a base type without a virtual destructor. The check places a note |
| 19 | // at the last point where the conversion from derived to base happened. |
| 20 | // |
| 21 | //===----------------------------------------------------------------------===// |
| 22 | |
Kristof Umann | 76a2150 | 2018-12-15 16:23:51 +0000 | [diff] [blame] | 23 | #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 24 | #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" |
| 25 | #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" |
| 26 | #include "clang/StaticAnalyzer/Core/Checker.h" |
| 27 | #include "clang/StaticAnalyzer/Core/CheckerManager.h" |
| 28 | #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" |
| 29 | #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" |
Csaba Dabis | e4bf456 | 2019-08-22 00:36:42 +0000 | [diff] [blame^] | 30 | #include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h" |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 31 | #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" |
| 32 | |
| 33 | using namespace clang; |
| 34 | using namespace ento; |
| 35 | |
| 36 | namespace { |
| 37 | class DeleteWithNonVirtualDtorChecker |
| 38 | : public Checker<check::PreStmt<CXXDeleteExpr>> { |
| 39 | mutable std::unique_ptr<BugType> BT; |
| 40 | |
George Karpenkov | 70ec1dd | 2018-06-26 21:12:08 +0000 | [diff] [blame] | 41 | class DeleteBugVisitor : public BugReporterVisitor { |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 42 | public: |
| 43 | DeleteBugVisitor() : Satisfied(false) {} |
| 44 | void Profile(llvm::FoldingSetNodeID &ID) const override { |
| 45 | static int X = 0; |
| 46 | ID.AddPointer(&X); |
| 47 | } |
Kristof Umann | 6d716ef | 2019-08-13 16:45:48 +0000 | [diff] [blame] | 48 | PathDiagnosticPieceRef VisitNode(const ExplodedNode *N, |
| 49 | BugReporterContext &BRC, |
| 50 | BugReport &BR) override; |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 51 | |
| 52 | private: |
| 53 | bool Satisfied; |
| 54 | }; |
| 55 | |
| 56 | public: |
| 57 | void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; |
| 58 | }; |
| 59 | } // end anonymous namespace |
| 60 | |
| 61 | void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE, |
| 62 | CheckerContext &C) const { |
| 63 | const Expr *DeletedObj = DE->getArgument(); |
| 64 | const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); |
| 65 | if (!MR) |
| 66 | return; |
| 67 | |
| 68 | const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); |
| 69 | const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); |
| 70 | if (!BaseClassRegion || !DerivedClassRegion) |
| 71 | return; |
| 72 | |
| 73 | const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); |
| 74 | const auto *DerivedClass = |
| 75 | DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); |
| 76 | if (!BaseClass || !DerivedClass) |
| 77 | return; |
| 78 | |
| 79 | if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) |
| 80 | return; |
| 81 | |
| 82 | if (BaseClass->getDestructor()->isVirtual()) |
| 83 | return; |
| 84 | |
| 85 | if (!DerivedClass->isDerivedFrom(BaseClass)) |
| 86 | return; |
| 87 | |
| 88 | if (!BT) |
| 89 | BT.reset(new BugType(this, |
| 90 | "Destruction of a polymorphic object with no " |
| 91 | "virtual destructor", |
| 92 | "Logic error")); |
| 93 | |
| 94 | ExplodedNode *N = C.generateNonFatalErrorNode(); |
Jonas Devlieghere | 2b3d49b | 2019-08-14 23:04:18 +0000 | [diff] [blame] | 95 | auto R = std::make_unique<BugReport>(*BT, BT->getName(), N); |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 96 | |
| 97 | // Mark region of problematic base class for later use in the BugVisitor. |
| 98 | R->markInteresting(BaseClassRegion); |
Jonas Devlieghere | 2b3d49b | 2019-08-14 23:04:18 +0000 | [diff] [blame] | 99 | R->addVisitor(std::make_unique<DeleteBugVisitor>()); |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 100 | C.emitReport(std::move(R)); |
| 101 | } |
| 102 | |
Kristof Umann | 6d716ef | 2019-08-13 16:45:48 +0000 | [diff] [blame] | 103 | PathDiagnosticPieceRef |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 104 | DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( |
Kristof Umann | 6d716ef | 2019-08-13 16:45:48 +0000 | [diff] [blame] | 105 | const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 106 | // Stop traversal after the first conversion was found on a path. |
| 107 | if (Satisfied) |
| 108 | return nullptr; |
| 109 | |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 110 | const Stmt *S = PathDiagnosticLocation::getStmt(N); |
| 111 | if (!S) |
| 112 | return nullptr; |
| 113 | |
| 114 | const auto *CastE = dyn_cast<CastExpr>(S); |
| 115 | if (!CastE) |
| 116 | return nullptr; |
| 117 | |
| 118 | // Only interested in DerivedToBase implicit casts. |
| 119 | // Explicit casts can have different CastKinds. |
| 120 | if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) { |
| 121 | if (ImplCastE->getCastKind() != CK_DerivedToBase) |
| 122 | return nullptr; |
| 123 | } |
| 124 | |
| 125 | // Region associated with the current cast expression. |
George Karpenkov | d703ec9 | 2018-01-17 20:27:29 +0000 | [diff] [blame] | 126 | const MemRegion *M = N->getSVal(CastE).getAsRegion(); |
Gabor Horvath | 61e7ade | 2017-09-22 10:16:33 +0000 | [diff] [blame] | 127 | if (!M) |
| 128 | return nullptr; |
| 129 | |
| 130 | // Check if target region was marked as problematic previously. |
| 131 | if (!BR.isInteresting(M)) |
| 132 | return nullptr; |
| 133 | |
| 134 | // Stop traversal on this path. |
| 135 | Satisfied = true; |
| 136 | |
| 137 | SmallString<256> Buf; |
| 138 | llvm::raw_svector_ostream OS(Buf); |
| 139 | OS << "Conversion from derived to base happened here"; |
| 140 | PathDiagnosticLocation Pos(S, BRC.getSourceManager(), |
| 141 | N->getLocationContext()); |
| 142 | return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, |
| 143 | nullptr); |
| 144 | } |
| 145 | |
| 146 | void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { |
| 147 | mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); |
| 148 | } |
Kristof Umann | 058a7a4 | 2019-01-26 14:23:08 +0000 | [diff] [blame] | 149 | |
| 150 | bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker( |
| 151 | const LangOptions &LO) { |
| 152 | return true; |
| 153 | } |