Coverage Report

Created: 2022-01-22 13:19

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