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