Coverage Report

Created: 2023-09-12 09:32

/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
Line
Count
Source (jump to first uncovered line)
1
//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- 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 defines UnixAPIChecker, which is an assortment of checks on calls
10
// to various, widely used UNIX/Posix functions.
11
//
12
//===----------------------------------------------------------------------===//
13
14
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
15
#include "clang/Basic/TargetInfo.h"
16
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
17
#include "clang/StaticAnalyzer/Core/Checker.h"
18
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
19
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
20
#include "llvm/ADT/STLExtras.h"
21
#include "llvm/ADT/SmallString.h"
22
#include "llvm/ADT/StringExtras.h"
23
#include "llvm/Support/raw_ostream.h"
24
#include <optional>
25
26
using namespace clang;
27
using namespace ento;
28
29
enum class OpenVariant {
30
  /// The standard open() call:
31
  ///    int open(const char *path, int oflag, ...);
32
  Open,
33
34
  /// The variant taking a directory file descriptor and a relative path:
35
  ///    int openat(int fd, const char *path, int oflag, ...);
36
  OpenAt
37
};
38
39
namespace {
40
41
class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > {
42
  mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce;
43
  mutable std::optional<uint64_t> Val_O_CREAT;
44
45
public:
46
  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
47
48
  void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
49
  void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const;
50
  void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
51
52
  void CheckOpenVariant(CheckerContext &C,
53
                        const CallExpr *CE, OpenVariant Variant) const;
54
55
  void ReportOpenBug(CheckerContext &C,
56
                     ProgramStateRef State,
57
                     const char *Msg,
58
                     SourceRange SR) const;
59
60
};
61
62
class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
63
public:
64
  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
65
66
private:
67
  mutable std::unique_ptr<BugType> BT_mallocZero;
68
69
  void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
70
  void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
71
  void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
72
  void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
73
  void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
74
  void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const;
75
  void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
76
77
  bool ReportZeroByteAllocation(CheckerContext &C,
78
                                ProgramStateRef falseState,
79
                                const Expr *arg,
80
                                const char *fn_name) const;
81
  void BasicAllocationCheck(CheckerContext &C,
82
                            const CallExpr *CE,
83
                            const unsigned numArgs,
84
                            const unsigned sizeArg,
85
                            const char *fn) const;
86
};
87
88
} //end anonymous namespace
89
90
static void LazyInitialize(const CheckerBase *Checker,
91
                           std::unique_ptr<BugType> &BT,
92
37
                           const char *name) {
93
37
  if (BT)
94
28
    return;
95
9
  BT.reset(new BugType(Checker, name, categories::UnixAPI));
96
9
}
97
98
//===----------------------------------------------------------------------===//
99
// "open" (man 2 open)
100
//===----------------------------------------------------------------------===/
101
102
void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE,
103
1.61k
                                        CheckerContext &C) const {
104
1.61k
  const FunctionDecl *FD = C.getCalleeDecl(CE);
105
1.61k
  if (!FD || 
FD->getKind() != Decl::Function1.59k
)
106
30
    return;
107
108
  // Don't treat functions in namespaces with the same name a Unix function
109
  // as a call to the Unix function.
110
1.58k
  const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
111
1.58k
  if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
112
29
    return;
113
114
1.55k
  StringRef FName = C.getCalleeName(FD);
115
1.55k
  if (FName.empty())
116
1
    return;
117
118
1.55k
  if (FName == "open")
119
17
    CheckOpen(C, CE);
120
121
1.54k
  else if (FName == "openat")
122
8
    CheckOpenAt(C, CE);
123
124
1.53k
  else if (FName == "pthread_once")
125
4
    CheckPthreadOnce(C, CE);
126
1.55k
}
127
void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
128
                                         ProgramStateRef State,
129
                                         const char *Msg,
130
18
                                         SourceRange SR) const {
131
18
  ExplodedNode *N = C.generateErrorNode(State);
132
18
  if (!N)
133
0
    return;
134
135
18
  LazyInitialize(this, BT_open, "Improper use of 'open'");
136
137
18
  auto Report = std::make_unique<PathSensitiveBugReport>(*BT_open, Msg, N);
138
18
  Report->addRange(SR);
139
18
  C.emitReport(std::move(Report));
140
18
}
141
142
void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
143
17
                                     const CallExpr *CE) const {
144
17
  CheckOpenVariant(C, CE, OpenVariant::Open);
145
17
}
146
147
void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
148
8
                                       const CallExpr *CE) const {
149
8
  CheckOpenVariant(C, CE, OpenVariant::OpenAt);
150
8
}
151
152
void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
153
                                            const CallExpr *CE,
154
25
                                            OpenVariant Variant) const {
155
  // The index of the argument taking the flags open flags (O_RDONLY,
156
  // O_WRONLY, O_CREAT, etc.),
157
25
  unsigned int FlagsArgIndex;
158
25
  const char *VariantName;
159
25
  switch (Variant) {
160
17
  case OpenVariant::Open:
161
17
    FlagsArgIndex = 1;
162
17
    VariantName = "open";
163
17
    break;
164
8
  case OpenVariant::OpenAt:
165
8
    FlagsArgIndex = 2;
166
8
    VariantName = "openat";
167
8
    break;
168
25
  };
169
170
  // All calls should at least provide arguments up to the 'flags' parameter.
171
25
  unsigned int MinArgCount = FlagsArgIndex + 1;
172
173
  // If the flags has O_CREAT set then open/openat() require an additional
174
  // argument specifying the file mode (permission bits) for the created file.
175
25
  unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
176
177
  // The create mode argument should be the last argument.
178
25
  unsigned int MaxArgCount = CreateModeArgIndex + 1;
179
180
25
  ProgramStateRef state = C.getState();
181
182
25
  if (CE->getNumArgs() < MinArgCount) {
183
    // The frontend should issue a warning for this case. Just return.
184
0
    return;
185
25
  } else if (CE->getNumArgs() == MaxArgCount) {
186
7
    const Expr *Arg = CE->getArg(CreateModeArgIndex);
187
7
    QualType QT = Arg->getType();
188
7
    if (!QT->isIntegerType()) {
189
6
      SmallString<256> SBuf;
190
6
      llvm::raw_svector_ostream OS(SBuf);
191
6
      OS << "The " << CreateModeArgIndex + 1
192
6
         << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
193
6
         << " argument to '" << VariantName << "' is not an integer";
194
195
6
      ReportOpenBug(C, state,
196
6
                    SBuf.c_str(),
197
6
                    Arg->getSourceRange());
198
6
      return;
199
6
    }
200
18
  } else if (CE->getNumArgs() > MaxArgCount) {
201
4
    SmallString<256> SBuf;
202
4
    llvm::raw_svector_ostream OS(SBuf);
203
4
    OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
204
4
       << " arguments";
205
206
4
    ReportOpenBug(C, state,
207
4
                  SBuf.c_str(),
208
4
                  CE->getArg(MaxArgCount)->getSourceRange());
209
4
    return;
210
4
  }
211
212
  // The definition of O_CREAT is platform specific.  We need a better way
213
  // of querying this information from the checking environment.
214
15
  if (!Val_O_CREAT) {
215
4
    if (C.getASTContext().getTargetInfo().getTriple().getVendor()
216
4
                                                      == llvm::Triple::Apple)
217
4
      Val_O_CREAT = 0x0200;
218
0
    else {
219
      // FIXME: We need a more general way of getting the O_CREAT value.
220
      // We could possibly grovel through the preprocessor state, but
221
      // that would require passing the Preprocessor object to the ExprEngine.
222
      // See also: MallocChecker.cpp / M_ZERO.
223
0
      return;
224
0
    }
225
4
  }
226
227
  // Now check if oflags has O_CREAT set.
228
15
  const Expr *oflagsEx = CE->getArg(FlagsArgIndex);
229
15
  const SVal V = C.getSVal(oflagsEx);
230
15
  if (!isa<NonLoc>(V)) {
231
    // The case where 'V' can be a location can only be due to a bad header,
232
    // so in this case bail out.
233
0
    return;
234
0
  }
235
15
  NonLoc oflags = V.castAs<NonLoc>();
236
15
  NonLoc ocreateFlag = C.getSValBuilder()
237
15
                           .makeIntVal(*Val_O_CREAT, oflagsEx->getType())
238
15
                           .castAs<NonLoc>();
239
15
  SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
240
15
                                                      oflags, ocreateFlag,
241
15
                                                      oflagsEx->getType());
242
15
  if (maskedFlagsUC.isUnknownOrUndef())
243
0
    return;
244
15
  DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
245
246
  // Check if maskedFlags is non-zero.
247
15
  ProgramStateRef trueState, falseState;
248
15
  std::tie(trueState, falseState) = state->assume(maskedFlags);
249
250
  // Only emit an error if the value of 'maskedFlags' is properly
251
  // constrained;
252
15
  if (!(trueState && 
!falseState8
))
253
7
    return;
254
255
8
  if (CE->getNumArgs() < MaxArgCount) {
256
8
    SmallString<256> SBuf;
257
8
    llvm::raw_svector_ostream OS(SBuf);
258
8
    OS << "Call to '" << VariantName << "' requires a "
259
8
       << CreateModeArgIndex + 1
260
8
       << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
261
8
       << " argument when the 'O_CREAT' flag is set";
262
8
    ReportOpenBug(C, trueState,
263
8
                  SBuf.c_str(),
264
8
                  oflagsEx->getSourceRange());
265
8
  }
266
8
}
267
268
//===----------------------------------------------------------------------===//
269
// pthread_once
270
//===----------------------------------------------------------------------===//
271
272
void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
273
4
                                      const CallExpr *CE) const {
274
275
  // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
276
  // They can possibly be refactored.
277
278
4
  if (CE->getNumArgs() < 1)
279
0
    return;
280
281
  // Check if the first argument is stack allocated.  If so, issue a warning
282
  // because that's likely to be bad news.
283
4
  ProgramStateRef state = C.getState();
284
4
  const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
285
4
  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
286
2
    return;
287
288
2
  ExplodedNode *N = C.generateErrorNode(state);
289
2
  if (!N)
290
0
    return;
291
292
2
  SmallString<256> S;
293
2
  llvm::raw_svector_ostream os(S);
294
2
  os << "Call to 'pthread_once' uses";
295
2
  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
296
2
    os << " the local variable '" << VR->getDecl()->getName() << '\'';
297
0
  else
298
0
    os << " stack allocated memory";
299
2
  os << " for the \"control\" value.  Using such transient memory for "
300
2
  "the control value is potentially dangerous.";
301
2
  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
302
2
    os << "  Perhaps you intended to declare the variable as 'static'?";
303
304
2
  LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'");
305
306
2
  auto report =
307
2
      std::make_unique<PathSensitiveBugReport>(*BT_pthreadOnce, os.str(), N);
308
2
  report->addRange(CE->getArg(0)->getSourceRange());
309
2
  C.emitReport(std::move(report));
310
2
}
311
312
//===----------------------------------------------------------------------===//
313
// "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
314
// with allocation size 0
315
//===----------------------------------------------------------------------===//
316
317
// FIXME: Eventually these should be rolled into the MallocChecker, but right now
318
// they're more basic and valuable for widespread use.
319
320
// Returns true if we try to do a zero byte allocation, false otherwise.
321
// Fills in trueState and falseState.
322
static bool IsZeroByteAllocation(ProgramStateRef state,
323
                                 const SVal argVal,
324
                                 ProgramStateRef *trueState,
325
38
                                 ProgramStateRef *falseState) {
326
38
  std::tie(*trueState, *falseState) =
327
38
    state->assume(argVal.castAs<DefinedSVal>());
328
329
38
  return (*falseState && 
!*trueState34
);
330
38
}
331
332
// Generates an error report, indicating that the function whose name is given
333
// will perform a zero byte allocation.
334
// Returns false if an error occurred, true otherwise.
335
bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
336
                                                    CheckerContext &C,
337
                                                    ProgramStateRef falseState,
338
                                                    const Expr *arg,
339
17
                                                    const char *fn_name) const {
340
17
  ExplodedNode *N = C.generateErrorNode(falseState);
341
17
  if (!N)
342
0
    return false;
343
344
17
  LazyInitialize(this, BT_mallocZero,
345
17
                 "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
346
347
17
  SmallString<256> S;
348
17
  llvm::raw_svector_ostream os(S);
349
17
  os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
350
17
  auto report =
351
17
      std::make_unique<PathSensitiveBugReport>(*BT_mallocZero, os.str(), N);
352
353
17
  report->addRange(arg->getSourceRange());
354
17
  bugreporter::trackExpressionValue(N, arg, *report);
355
17
  C.emitReport(std::move(report));
356
357
17
  return true;
358
17
}
359
360
// Does a basic check for 0-sized allocations suitable for most of the below
361
// functions (modulo "calloc")
362
void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
363
                                                     const CallExpr *CE,
364
                                                     const unsigned numArgs,
365
                                                     const unsigned sizeArg,
366
28
                                                     const char *fn) const {
367
  // Check for the correct number of arguments.
368
28
  if (CE->getNumArgs() != numArgs)
369
0
    return;
370
371
  // Check if the allocation size is 0.
372
28
  ProgramStateRef state = C.getState();
373
28
  ProgramStateRef trueState = nullptr, falseState = nullptr;
374
28
  const Expr *arg = CE->getArg(sizeArg);
375
28
  SVal argVal = C.getSVal(arg);
376
377
28
  if (argVal.isUnknownOrUndef())
378
0
    return;
379
380
  // Is the value perfectly constrained to zero?
381
28
  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
382
13
    (void) ReportZeroByteAllocation(C, falseState, arg, fn);
383
13
    return;
384
13
  }
385
  // Assume the value is non-zero going forward.
386
15
  assert(trueState);
387
15
  if (trueState != state)
388
13
    C.addTransition(trueState);
389
15
}
390
391
void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
392
6
                                                const CallExpr *CE) const {
393
6
  unsigned int nArgs = CE->getNumArgs();
394
6
  if (nArgs != 2)
395
0
    return;
396
397
6
  ProgramStateRef state = C.getState();
398
6
  ProgramStateRef trueState = nullptr, falseState = nullptr;
399
400
6
  unsigned int i;
401
12
  for (i = 0; i < nArgs; 
i++6
) {
402
10
    const Expr *arg = CE->getArg(i);
403
10
    SVal argVal = C.getSVal(arg);
404
10
    if (argVal.isUnknownOrUndef()) {
405
0
      if (i == 0)
406
0
        continue;
407
0
      else
408
0
        return;
409
0
    }
410
411
10
    if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
412
4
      if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
413
4
        return;
414
0
      else if (i == 0)
415
0
        continue;
416
0
      else
417
0
        return;
418
4
    }
419
10
  }
420
421
  // Assume the value is non-zero going forward.
422
2
  assert(trueState);
423
2
  if (trueState != state)
424
2
    C.addTransition(trueState);
425
2
}
426
427
void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
428
8
                                                const CallExpr *CE) const {
429
8
  BasicAllocationCheck(C, CE, 1, 0, "malloc");
430
8
}
431
432
void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
433
4
                                                 const CallExpr *CE) const {
434
4
  BasicAllocationCheck(C, CE, 2, 1, "realloc");
435
4
}
436
437
void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
438
4
                                                  const CallExpr *CE) const {
439
4
  BasicAllocationCheck(C, CE, 2, 1, "reallocf");
440
4
}
441
442
void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
443
8
                                                const CallExpr *CE) const {
444
8
  BasicAllocationCheck(C, CE, 1, 0, "alloca");
445
8
}
446
447
void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
448
                                                     CheckerContext &C,
449
0
                                                     const CallExpr *CE) const {
450
0
  BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
451
0
}
452
453
void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
454
4
                                                const CallExpr *CE) const {
455
4
  BasicAllocationCheck(C, CE, 1, 0, "valloc");
456
4
}
457
458
void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
459
95
                                             CheckerContext &C) const {
460
95
  const FunctionDecl *FD = C.getCalleeDecl(CE);
461
95
  if (!FD || 
FD->getKind() != Decl::Function87
)
462
8
    return;
463
464
  // Don't treat functions in namespaces with the same name a Unix function
465
  // as a call to the Unix function.
466
87
  const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
467
87
  if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
468
0
    return;
469
470
87
  StringRef FName = C.getCalleeName(FD);
471
87
  if (FName.empty())
472
0
    return;
473
474
87
  if (FName == "calloc")
475
6
    CheckCallocZero(C, CE);
476
477
81
  else if (FName == "malloc")
478
8
    CheckMallocZero(C, CE);
479
480
73
  else if (FName == "realloc")
481
4
    CheckReallocZero(C, CE);
482
483
69
  else if (FName == "reallocf")
484
4
    CheckReallocfZero(C, CE);
485
486
65
  else if (FName == "alloca" || 
FName == "__builtin_alloca"61
)
487
8
    CheckAllocaZero(C, CE);
488
489
57
  else if (FName == "__builtin_alloca_with_align")
490
0
    CheckAllocaWithAlignZero(C, CE);
491
492
57
  else if (FName == "valloc")
493
4
    CheckVallocZero(C, CE);
494
87
}
495
496
//===----------------------------------------------------------------------===//
497
// Registration.
498
//===----------------------------------------------------------------------===//
499
500
#define REGISTER_CHECKER(CHECKERNAME)                                          \
501
74
  void ento::register##CHECKERNAME(CheckerManager &mgr) {                      \
502
74
    mgr.registerChecker<CHECKERNAME>();                                        \
503
74
  }                                                                            \
clang::ento::registerUnixAPIMisuseChecker(clang::ento::CheckerManager&)
Line
Count
Source
501
71
  void ento::register##CHECKERNAME(CheckerManager &mgr) {                      \
502
71
    mgr.registerChecker<CHECKERNAME>();                                        \
503
71
  }                                                                            \
clang::ento::registerUnixAPIPortabilityChecker(clang::ento::CheckerManager&)
Line
Count
Source
501
3
  void ento::register##CHECKERNAME(CheckerManager &mgr) {                      \
502
3
    mgr.registerChecker<CHECKERNAME>();                                        \
503
3
  }                                                                            \
504
                                                                               \
505
152
  bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) {          \
506
152
    return true;                                                               \
507
152
  }
clang::ento::shouldRegisterUnixAPIMisuseChecker(clang::ento::CheckerManager const&)
Line
Count
Source
505
146
  bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) {          \
506
146
    return true;                                                               \
507
146
  }
clang::ento::shouldRegisterUnixAPIPortabilityChecker(clang::ento::CheckerManager const&)
Line
Count
Source
505
6
  bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) {          \
506
6
    return true;                                                               \
507
6
  }
508
509
REGISTER_CHECKER(UnixAPIMisuseChecker)
510
REGISTER_CHECKER(UnixAPIPortabilityChecker)