1 //==-- DebugContainerModeling.cpp ---------------------------------*- C++ -*--// 2 // 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 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Defines a checker for debugging iterator modeling. 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 14 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 15 #include "clang/StaticAnalyzer/Core/Checker.h" 16 #include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" 17 #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 18 19 #include "Iterator.h" 20 21 using namespace clang; 22 using namespace ento; 23 using namespace iterator; 24 25 namespace { 26 27 class DebugContainerModeling 28 : public Checker<eval::Call> { 29 30 std::unique_ptr<BugType> DebugMsgBugType; 31 32 template <typename Getter> 33 void analyzerContainerDataField(const CallExpr *CE, CheckerContext &C, 34 Getter get) const; 35 void analyzerContainerBegin(const CallExpr *CE, CheckerContext &C) const; 36 void analyzerContainerEnd(const CallExpr *CE, CheckerContext &C) const; 37 ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const; 38 39 typedef void (DebugContainerModeling::*FnCheck)(const CallExpr *, 40 CheckerContext &) const; 41 42 CallDescriptionMap<FnCheck> Callbacks = { 43 {{0, "clang_analyzer_container_begin", 1}, 44 &DebugContainerModeling::analyzerContainerBegin}, 45 {{0, "clang_analyzer_container_end", 1}, 46 &DebugContainerModeling::analyzerContainerEnd}, 47 }; 48 49 public: 50 DebugContainerModeling(); 51 52 bool evalCall(const CallEvent &Call, CheckerContext &C) const; 53 }; 54 55 } //namespace 56 57 DebugContainerModeling::DebugContainerModeling() { 58 DebugMsgBugType.reset( 59 new BugType(this, "Checking analyzer assumptions", "debug", 60 /*SuppressOnSink=*/true)); 61 } 62 63 bool DebugContainerModeling::evalCall(const CallEvent &Call, 64 CheckerContext &C) const { 65 const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); 66 if (!CE) 67 return false; 68 69 const FnCheck *Handler = Callbacks.lookup(Call); 70 if (!Handler) 71 return false; 72 73 (this->**Handler)(CE, C); 74 return true; 75 } 76 77 template <typename Getter> 78 void DebugContainerModeling::analyzerContainerDataField(const CallExpr *CE, 79 CheckerContext &C, 80 Getter get) const { 81 if (CE->getNumArgs() == 0) { 82 reportDebugMsg("Missing container argument", C); 83 return; 84 } 85 86 auto State = C.getState(); 87 const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion(); 88 if (Cont) { 89 const auto *Data = getContainerData(State, Cont); 90 if (Data) { 91 SymbolRef Field = get(Data); 92 if (Field) { 93 State = State->BindExpr(CE, C.getLocationContext(), 94 nonloc::SymbolVal(Field)); 95 96 // Progpagate interestingness from the container's data (marked 97 // interesting by an `ExprInspection` debug call to the container 98 // itself. 99 const NoteTag *InterestingTag = 100 C.getNoteTag( 101 [Cont, Field](PathSensitiveBugReport &BR) -> std::string { 102 if (BR.isInteresting(Field)) { 103 BR.markInteresting(Cont); 104 } 105 return ""; 106 }); 107 C.addTransition(State, InterestingTag); 108 return; 109 } 110 } 111 } 112 113 auto &BVF = C.getSValBuilder().getBasicValueFactory(); 114 State = State->BindExpr(CE, C.getLocationContext(), 115 nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0)))); 116 } 117 118 void DebugContainerModeling::analyzerContainerBegin(const CallExpr *CE, 119 CheckerContext &C) const { 120 analyzerContainerDataField(CE, C, [](const ContainerData *D) { 121 return D->getBegin(); 122 }); 123 } 124 125 void DebugContainerModeling::analyzerContainerEnd(const CallExpr *CE, 126 CheckerContext &C) const { 127 analyzerContainerDataField(CE, C, [](const ContainerData *D) { 128 return D->getEnd(); 129 }); 130 } 131 132 ExplodedNode *DebugContainerModeling::reportDebugMsg(llvm::StringRef Msg, 133 CheckerContext &C) const { 134 ExplodedNode *N = C.generateNonFatalErrorNode(); 135 if (!N) 136 return nullptr; 137 138 auto &BR = C.getBugReporter(); 139 BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType, 140 Msg, N)); 141 return N; 142 } 143 144 void ento::registerDebugContainerModeling(CheckerManager &mgr) { 145 mgr.registerChecker<DebugContainerModeling>(); 146 } 147 148 bool ento::shouldRegisterDebugContainerModeling(const CheckerManager &mgr) { 149 return true; 150 } 151