DebugIteratorModeling.cpp revision 360784
1//===-- DebugIteratorModeling.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
21using namespace clang;
22using namespace ento;
23using namespace iterator;
24
25namespace {
26
27class DebugIteratorModeling
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  template <typename Getter>
38  void analyzerIteratorDataField(const CallExpr *CE, CheckerContext &C,
39                                 Getter get, SVal Default) const;
40  void analyzerIteratorPosition(const CallExpr *CE, CheckerContext &C) const;
41  void analyzerIteratorContainer(const CallExpr *CE, CheckerContext &C) const;
42  void analyzerIteratorValidity(const CallExpr *CE, CheckerContext &C) const;
43  ExplodedNode *reportDebugMsg(llvm::StringRef Msg, CheckerContext &C) const;
44
45  typedef void (DebugIteratorModeling::*FnCheck)(const CallExpr *,
46                                                 CheckerContext &) const;
47
48  CallDescriptionMap<FnCheck> Callbacks = {
49    {{0, "clang_analyzer_container_begin", 1},
50     &DebugIteratorModeling::analyzerContainerBegin},
51    {{0, "clang_analyzer_container_end", 1},
52     &DebugIteratorModeling::analyzerContainerEnd},
53    {{0, "clang_analyzer_iterator_position", 1},
54     &DebugIteratorModeling::analyzerIteratorPosition},
55    {{0, "clang_analyzer_iterator_container", 1},
56     &DebugIteratorModeling::analyzerIteratorContainer},
57    {{0, "clang_analyzer_iterator_validity", 1},
58     &DebugIteratorModeling::analyzerIteratorValidity},
59  };
60
61public:
62  DebugIteratorModeling();
63
64  bool evalCall(const CallEvent &Call, CheckerContext &C) const;
65};
66
67} //namespace
68
69DebugIteratorModeling::DebugIteratorModeling() {
70  DebugMsgBugType.reset(
71      new BugType(this, "Checking analyzer assumptions", "debug",
72                  /*SuppressOnSink=*/true));
73}
74
75bool DebugIteratorModeling::evalCall(const CallEvent &Call,
76                                     CheckerContext &C) const {
77  const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
78  if (!CE)
79    return false;
80
81  const FnCheck *Handler = Callbacks.lookup(Call);
82  if (!Handler)
83    return false;
84
85  (this->**Handler)(CE, C);
86  return true;
87}
88
89template <typename Getter>
90void DebugIteratorModeling::analyzerContainerDataField(const CallExpr *CE,
91                                                       CheckerContext &C,
92                                                       Getter get) const {
93  if (CE->getNumArgs() == 0) {
94    reportDebugMsg("Missing container argument", C);
95    return;
96  }
97
98  auto State = C.getState();
99  const MemRegion *Cont = C.getSVal(CE->getArg(0)).getAsRegion();
100  if (Cont) {
101    const auto *Data = getContainerData(State, Cont);
102    if (Data) {
103      SymbolRef Field = get(Data);
104      if (Field) {
105        State = State->BindExpr(CE, C.getLocationContext(),
106                                nonloc::SymbolVal(Field));
107        C.addTransition(State);
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
118void DebugIteratorModeling::analyzerContainerBegin(const CallExpr *CE,
119                                                   CheckerContext &C) const {
120  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
121      return D->getBegin();
122    });
123}
124
125void DebugIteratorModeling::analyzerContainerEnd(const CallExpr *CE,
126                                                 CheckerContext &C) const {
127  analyzerContainerDataField(CE, C, [](const ContainerData *D) {
128      return D->getEnd();
129    });
130}
131
132template <typename Getter>
133void DebugIteratorModeling::analyzerIteratorDataField(const CallExpr *CE,
134                                                      CheckerContext &C,
135                                                      Getter get,
136                                                      SVal Default) const {
137  if (CE->getNumArgs() == 0) {
138    reportDebugMsg("Missing iterator argument", C);
139    return;
140  }
141
142  auto State = C.getState();
143  SVal V = C.getSVal(CE->getArg(0));
144  const auto *Pos = getIteratorPosition(State, V);
145  if (Pos) {
146    State = State->BindExpr(CE, C.getLocationContext(), get(Pos));
147  } else {
148    State = State->BindExpr(CE, C.getLocationContext(), Default);
149  }
150  C.addTransition(State);
151}
152
153void DebugIteratorModeling::analyzerIteratorPosition(const CallExpr *CE,
154                                                     CheckerContext &C) const {
155  auto &BVF = C.getSValBuilder().getBasicValueFactory();
156  analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
157      return nonloc::SymbolVal(P->getOffset());
158    }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
159}
160
161void DebugIteratorModeling::analyzerIteratorContainer(const CallExpr *CE,
162                                                      CheckerContext &C) const {
163  auto &BVF = C.getSValBuilder().getBasicValueFactory();
164  analyzerIteratorDataField(CE, C, [](const IteratorPosition *P) {
165      return loc::MemRegionVal(P->getContainer());
166    }, loc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
167}
168
169void DebugIteratorModeling::analyzerIteratorValidity(const CallExpr *CE,
170                                                     CheckerContext &C) const {
171  auto &BVF = C.getSValBuilder().getBasicValueFactory();
172  analyzerIteratorDataField(CE, C, [&BVF](const IteratorPosition *P) {
173      return
174        nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get((P->isValid()))));
175    }, nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))));
176}
177
178ExplodedNode *DebugIteratorModeling::reportDebugMsg(llvm::StringRef Msg,
179                                                    CheckerContext &C) const {
180  ExplodedNode *N = C.generateNonFatalErrorNode();
181  if (!N)
182    return nullptr;
183
184  auto &BR = C.getBugReporter();
185  BR.emitReport(std::make_unique<PathSensitiveBugReport>(*DebugMsgBugType,
186                                                         Msg, N));
187  return N;
188}
189
190void ento::registerDebugIteratorModeling(CheckerManager &mgr) {
191  mgr.registerChecker<DebugIteratorModeling>();
192}
193
194bool ento::shouldRegisterDebugIteratorModeling(const LangOptions &LO) {
195  return true;
196}
197