Coverage Report

Created: 2023-09-21 18:56

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//== InvalidPtrChecker.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
// This file defines InvalidPtrChecker which finds usages of possibly
10
// invalidated pointer.
11
// CERT SEI Rules ENV31-C and ENV34-C
12
// For more information see:
13
// https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
14
// https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
15
//===----------------------------------------------------------------------===//
16
17
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
18
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
19
#include "clang/StaticAnalyzer/Core/Checker.h"
20
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
21
#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
22
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
23
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
24
25
using namespace clang;
26
using namespace ento;
27
28
namespace {
29
30
class InvalidPtrChecker
31
    : public Checker<check::Location, check::BeginFunction, check::PostCall> {
32
private:
33
  BugType BT{this, "Use of invalidated pointer", categories::MemoryError};
34
35
  void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
36
37
  using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
38
                                                CheckerContext &C) const;
39
40
  // SEI CERT ENV31-C
41
  const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
42
      {{{"setenv"}, 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
43
      {{{"unsetenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
44
      {{{"putenv"}, 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
45
      {{{"_putenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
46
      {{{"_wputenv_s"}, 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
47
  };
48
49
  void postPreviousReturnInvalidatingCall(const CallEvent &Call,
50
                                          CheckerContext &C) const;
51
52
  // SEI CERT ENV34-C
53
  const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
54
      {{{"getenv"}, 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
55
      {{{"setlocale"}, 2},
56
       &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
57
      {{{"strerror"}, 1},
58
       &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
59
      {{{"localeconv"}, 0},
60
       &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
61
      {{{"asctime"}, 1},
62
       &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
63
  };
64
65
public:
66
  // Obtain the environment pointer from 'main()' (if present).
67
  void checkBeginFunction(CheckerContext &C) const;
68
69
  // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
70
  // pointer from 'main()'
71
  // Handle functions in PreviousCallInvalidatingFunctions.
72
  // Also, check if invalidated region is passed to a
73
  // conservatively evaluated function call as an argument.
74
  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
75
76
  // Check if invalidated region is being dereferenced.
77
  void checkLocation(SVal l, bool isLoad, const Stmt *S,
78
                     CheckerContext &C) const;
79
};
80
81
} // namespace
82
83
// Set of memory regions that were invalidated
84
REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
85
86
// Stores the region of the environment pointer of 'main' (if present).
87
REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const MemRegion *)
88
89
// Stores key-value pairs, where key is function declaration and value is
90
// pointer to memory region returned by previous call of this function
91
REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
92
                               const MemRegion *)
93
94
void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
95
5
                                             CheckerContext &C) const {
96
5
  StringRef FunctionName = Call.getCalleeIdentifier()->getName();
97
5
  ProgramStateRef State = C.getState();
98
5
  const MemRegion *SymbolicEnvPtrRegion = State->get<EnvPtrRegion>();
99
5
  if (!SymbolicEnvPtrRegion)
100
0
    return;
101
102
5
  State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
103
104
5
  const NoteTag *Note =
105
5
      C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
106
25
                       PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
107
25
        if (!BR.isInteresting(SymbolicEnvPtrRegion))
108
0
          return;
109
25
        Out << '\'' << FunctionName
110
25
            << "' call may invalidate the environment parameter of 'main'";
111
25
      });
112
113
5
  C.addTransition(State, Note);
114
5
}
115
116
void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
117
54
    const CallEvent &Call, CheckerContext &C) const {
118
54
  ProgramStateRef State = C.getState();
119
120
54
  const NoteTag *Note = nullptr;
121
54
  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
122
  // Invalidate the region of the previously returned pointer - if there was
123
  // one.
124
54
  if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
125
32
    const MemRegion *PrevReg = *Reg;
126
32
    State = State->add<InvalidMemoryRegions>(PrevReg);
127
32
    Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
128
32
                                      llvm::raw_ostream &Out) {
129
29
      if (!BR.isInteresting(PrevReg))
130
11
        return;
131
18
      Out << '\'';
132
18
      FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
133
18
      Out << "' call may invalidate the result of the previous " << '\'';
134
18
      FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
135
18
      Out << '\'';
136
18
    });
137
32
  }
138
139
54
  const LocationContext *LCtx = C.getLocationContext();
140
54
  const auto *CE = cast<CallExpr>(Call.getOriginExpr());
141
142
  // Function call will return a pointer to the new symbolic region.
143
54
  DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
144
54
      CE, LCtx, CE->getType(), C.blockCount());
145
54
  State = State->BindExpr(CE, LCtx, RetVal);
146
147
  // Remember to this region.
148
54
  const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
149
54
  const MemRegion *MR =
150
54
      const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
151
54
  State = State->set<PreviousCallResultMap>(FD, MR);
152
153
54
  ExplodedNode *Node = C.addTransition(State, Note);
154
54
  const NoteTag *PreviousCallNote =
155
54
      C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
156
47
        if (!BR.isInteresting(MR))
157
29
          return;
158
18
        Out << '\'' << "'previous function call was here" << '\'';
159
18
      });
160
161
54
  C.addTransition(State, Node, PreviousCallNote);
162
54
}
163
164
// TODO: This seems really ugly. Simplify this.
165
static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
166
265
                                                    const MemRegion *Reg) {
167
300
  while (Reg) {
168
300
    if (State->contains<InvalidMemoryRegions>(Reg))
169
43
      return Reg;
170
257
    const auto *SymBase = Reg->getSymbolicBase();
171
257
    if (!SymBase)
172
187
      break;
173
70
    const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
174
70
    if (!SRV)
175
35
      break;
176
35
    Reg = SRV->getRegion();
177
35
    if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
178
35
      Reg = VarReg;
179
35
  }
180
222
  return nullptr;
181
265
}
182
183
// Handle functions in EnvpInvalidatingFunctions, that invalidate environment
184
// pointer from 'main()' Also, check if invalidated region is passed to a
185
// function call as an argument.
186
void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
187
97
                                      CheckerContext &C) const {
188
  // Check if function invalidates 'envp' argument of 'main'
189
97
  if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
190
5
    (this->**Handler)(Call, C);
191
192
  // Check if function invalidates the result of previous call
193
97
  if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
194
54
    (this->**Handler)(Call, C);
195
196
  // Check if one of the arguments of the function call is invalidated
197
198
  // If call was inlined, don't report invalidated argument
199
97
  if (C.wasInlined)
200
11
    return;
201
202
86
  ProgramStateRef State = C.getState();
203
204
183
  for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; 
++I97
) {
205
206
97
    if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
207
97
            Call.getArgSVal(I).getAsRegion())) {
208
20
      if (const MemRegion *InvalidatedSymbolicBase =
209
20
              findInvalidatedSymbolicBase(State, SR)) {
210
8
        ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
211
8
        if (!ErrorNode)
212
0
          return;
213
214
8
        SmallString<256> Msg;
215
8
        llvm::raw_svector_ostream Out(Msg);
216
8
        Out << "use of invalidated pointer '";
217
8
        Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
218
8
                                        C.getASTContext().getPrintingPolicy());
219
8
        Out << "' in a function call";
220
221
8
        auto Report =
222
8
            std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
223
8
        Report->markInteresting(InvalidatedSymbolicBase);
224
8
        Report->addRange(Call.getArgSourceRange(I));
225
8
        C.emitReport(std::move(Report));
226
8
      }
227
20
    }
228
97
  }
229
86
}
230
231
// Obtain the environment pointer from 'main()', if present.
232
38
void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
233
38
  if (!C.inTopFrame())
234
11
    return;
235
236
27
  const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
237
27
  if (!FD || FD->param_size() != 3 || 
!FD->isMain()5
)
238
22
    return;
239
240
5
  ProgramStateRef State = C.getState();
241
5
  const MemRegion *EnvpReg =
242
5
      State->getRegion(FD->parameters()[2], C.getLocationContext());
243
244
  // Save the memory region pointed by the environment pointer parameter of
245
  // 'main'.
246
5
  C.addTransition(State->set<EnvPtrRegion>(EnvpReg));
247
5
}
248
249
// Check if invalidated region is being dereferenced.
250
void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
251
245
                                      CheckerContext &C) const {
252
245
  ProgramStateRef State = C.getState();
253
254
  // Ignore memory operations involving 'non-invalidated' locations.
255
245
  const MemRegion *InvalidatedSymbolicBase =
256
245
      findInvalidatedSymbolicBase(State, Loc.getAsRegion());
257
245
  if (!InvalidatedSymbolicBase)
258
210
    return;
259
260
35
  ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
261
35
  if (!ErrorNode)
262
0
    return;
263
264
35
  auto Report = std::make_unique<PathSensitiveBugReport>(
265
35
      BT, "dereferencing an invalid pointer", ErrorNode);
266
35
  Report->markInteresting(InvalidatedSymbolicBase);
267
35
  C.emitReport(std::move(Report));
268
35
}
269
270
7
void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
271
7
  Mgr.registerChecker<InvalidPtrChecker>();
272
7
}
273
274
14
bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
275
14
  return true;
276
14
}