Coverage Report

Created: 2022-01-18 06:27

/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}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
58
      {{"localeconv", 0},
59
       &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
60
      {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
61
  };
62
63
public:
64
  // Obtain the environment pointer from 'main()' (if present).
65
  void checkBeginFunction(CheckerContext &C) const;
66
67
  // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
68
  // pointer from 'main()'
69
  // Handle functions in PreviousCallInvalidatingFunctions.
70
  // Also, check if invalidated region is passed to a
71
  // conservatively evaluated function call as an argument.
72
  void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
73
74
  // Check if invalidated region is being dereferenced.
75
  void checkLocation(SVal l, bool isLoad, const Stmt *S,
76
                     CheckerContext &C) const;
77
};
78
79
} // namespace
80
81
// Set of memory regions that were invalidated
82
REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
83
84
// Stores the region of the environment pointer of 'main' (if present).
85
// Note: This pointer has type 'const MemRegion *', however the trait is only
86
// specialized to 'const void*' and 'void*'
87
REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const void *)
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 auto *Reg = State->get<EnvPtrRegion>();
99
5
  if (!Reg)
100
0
    return;
101
5
  const auto *SymbolicEnvPtrRegion =
102
5
      reinterpret_cast<const MemRegion *>(const_cast<const void *>(Reg));
103
104
5
  State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
105
106
5
  const NoteTag *Note =
107
5
      C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
108
25
                       PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
109
25
        if (!BR.isInteresting(SymbolicEnvPtrRegion))
110
0
          return;
111
25
        Out << '\'' << FunctionName
112
25
            << "' call may invalidate the environment parameter of 'main'";
113
25
      });
114
115
5
  C.addTransition(State, Note);
116
5
}
117
118
void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
119
54
    const CallEvent &Call, CheckerContext &C) const {
120
54
  ProgramStateRef State = C.getState();
121
122
54
  const NoteTag *Note = nullptr;
123
54
  const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
124
  // Invalidate the region of the previously returned pointer - if there was
125
  // one.
126
54
  if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
127
32
    const MemRegion *PrevReg = *Reg;
128
32
    State = State->add<InvalidMemoryRegions>(PrevReg);
129
32
    Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
130
32
                                      llvm::raw_ostream &Out) {
131
29
      if (!BR.isInteresting(PrevReg))
132
11
        return;
133
18
      Out << '\'';
134
18
      FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
135
18
      Out << "' call may invalidate the the result of the previous " << '\'';
136
18
      FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
137
18
      Out << '\'';
138
18
    });
139
32
  }
140
141
54
  const LocationContext *LCtx = C.getLocationContext();
142
54
  const auto *CE = cast<CallExpr>(Call.getOriginExpr());
143
144
  // Function call will return a pointer to the new symbolic region.
145
54
  DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
146
54
      CE, LCtx, CE->getType(), C.blockCount());
147
54
  State = State->BindExpr(CE, LCtx, RetVal);
148
149
  // Remember to this region.
150
54
  const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
151
54
  const MemRegion *MR =
152
54
      const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
153
54
  State = State->set<PreviousCallResultMap>(FD, MR);
154
155
54
  ExplodedNode *Node = C.addTransition(State, Note);
156
54
  const NoteTag *PreviousCallNote =
157
54
      C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
158
47
        if (!BR.isInteresting(MR))
159
29
          return;
160
18
        Out << '\'' << "'previous function call was here" << '\'';
161
18
      });
162
163
54
  C.addTransition(State, Node, PreviousCallNote);
164
54
}
165
166
// TODO: This seems really ugly. Simplify this.
167
static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
168
265
                                                    const MemRegion *Reg) {
169
300
  while (Reg) {
170
300
    if (State->contains<InvalidMemoryRegions>(Reg))
171
43
      return Reg;
172
257
    const auto *SymBase = Reg->getSymbolicBase();
173
257
    if (!SymBase)
174
187
      break;
175
70
    const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
176
70
    if (!SRV)
177
35
      break;
178
35
    Reg = SRV->getRegion();
179
35
    if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
180
35
      Reg = VarReg;
181
35
  }
182
222
  return nullptr;
183
265
}
184
185
// Handle functions in EnvpInvalidatingFunctions, that invalidate environment
186
// pointer from 'main()' Also, check if invalidated region is passed to a
187
// function call as an argument.
188
void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
189
97
                                      CheckerContext &C) const {
190
  // Check if function invalidates 'envp' argument of 'main'
191
97
  if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
192
5
    (this->**Handler)(Call, C);
193
194
  // Check if function invalidates the result of previous call
195
97
  if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
196
54
    (this->**Handler)(Call, C);
197
198
  // Check if one of the arguments of the function call is invalidated
199
200
  // If call was inlined, don't report invalidated argument
201
97
  if (C.wasInlined)
202
11
    return;
203
204
86
  ProgramStateRef State = C.getState();
205
206
183
  for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; 
++I97
) {
207
208
97
    if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
209
97
            Call.getArgSVal(I).getAsRegion())) {
210
20
      if (const MemRegion *InvalidatedSymbolicBase =
211
20
              findInvalidatedSymbolicBase(State, SR)) {
212
8
        ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
213
8
        if (!ErrorNode)
214
0
          return;
215
216
8
        SmallString<256> Msg;
217
8
        llvm::raw_svector_ostream Out(Msg);
218
8
        Out << "use of invalidated pointer '";
219
8
        Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
220
8
                                        C.getASTContext().getPrintingPolicy());
221
8
        Out << "' in a function call";
222
223
8
        auto Report =
224
8
            std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
225
8
        Report->markInteresting(InvalidatedSymbolicBase);
226
8
        Report->addRange(Call.getArgSourceRange(I));
227
8
        C.emitReport(std::move(Report));
228
8
      }
229
20
    }
230
97
  }
231
86
}
232
233
// Obtain the environment pointer from 'main()', if present.
234
38
void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
235
38
  if (!C.inTopFrame())
236
11
    return;
237
238
27
  const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
239
27
  if (!FD || FD->param_size() != 3 || 
!FD->isMain()5
)
240
22
    return;
241
242
5
  ProgramStateRef State = C.getState();
243
5
  const MemRegion *EnvpReg =
244
5
      State->getRegion(FD->parameters()[2], C.getLocationContext());
245
246
  // Save the memory region pointed by the environment pointer parameter of
247
  // 'main'.
248
5
  State = State->set<EnvPtrRegion>(
249
5
      reinterpret_cast<void *>(const_cast<MemRegion *>(EnvpReg)));
250
5
  C.addTransition(State);
251
5
}
252
253
// Check if invalidated region is being dereferenced.
254
void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
255
245
                                      CheckerContext &C) const {
256
245
  ProgramStateRef State = C.getState();
257
258
  // Ignore memory operations involving 'non-invalidated' locations.
259
245
  const MemRegion *InvalidatedSymbolicBase =
260
245
      findInvalidatedSymbolicBase(State, Loc.getAsRegion());
261
245
  if (!InvalidatedSymbolicBase)
262
210
    return;
263
264
35
  ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
265
35
  if (!ErrorNode)
266
0
    return;
267
268
35
  auto Report = std::make_unique<PathSensitiveBugReport>(
269
35
      BT, "dereferencing an invalid pointer", ErrorNode);
270
35
  Report->markInteresting(InvalidatedSymbolicBase);
271
35
  C.emitReport(std::move(Report));
272
35
}
273
274
7
void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
275
7
  Mgr.registerChecker<InvalidPtrChecker>();
276
7
}
277
278
14
bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
279
14
  return true;
280
14
}