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