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