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
26using namespace clang;
27using namespace ento;
28
29enum 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
39namespace {
40
41class 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
45public:
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
62class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
63public:
64  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
65
66private:
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
90static void LazyInitialize(const CheckerBase *Checker,
91                           std::unique_ptr<BugType> &BT,
92                           const char *name) {
93  if (BT)
94    return;
95  BT.reset(new BugType(Checker, name, categories::UnixAPI));
96}
97
98//===----------------------------------------------------------------------===//
99// "open" (man 2 open)
100//===----------------------------------------------------------------------===/
101
102void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE,
103                                        CheckerContext &C) const {
104  const FunctionDecl *FD = C.getCalleeDecl(CE);
105  if (!FD || FD->getKind() != Decl::Function)
106    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  const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
111  if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
112    return;
113
114  StringRef FName = C.getCalleeName(FD);
115  if (FName.empty())
116    return;
117
118  if (FName == "open")
119    CheckOpen(C, CE);
120
121  else if (FName == "openat")
122    CheckOpenAt(C, CE);
123
124  else if (FName == "pthread_once")
125    CheckPthreadOnce(C, CE);
126}
127void UnixAPIMisuseChecker::ReportOpenBug(CheckerContext &C,
128                                         ProgramStateRef State,
129                                         const char *Msg,
130                                         SourceRange SR) const {
131  ExplodedNode *N = C.generateErrorNode(State);
132  if (!N)
133    return;
134
135  LazyInitialize(this, BT_open, "Improper use of 'open'");
136
137  auto Report = std::make_unique<PathSensitiveBugReport>(*BT_open, Msg, N);
138  Report->addRange(SR);
139  C.emitReport(std::move(Report));
140}
141
142void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
143                                     const CallExpr *CE) const {
144  CheckOpenVariant(C, CE, OpenVariant::Open);
145}
146
147void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
148                                       const CallExpr *CE) const {
149  CheckOpenVariant(C, CE, OpenVariant::OpenAt);
150}
151
152void UnixAPIMisuseChecker::CheckOpenVariant(CheckerContext &C,
153                                            const CallExpr *CE,
154                                            OpenVariant Variant) const {
155  // The index of the argument taking the flags open flags (O_RDONLY,
156  // O_WRONLY, O_CREAT, etc.),
157  unsigned int FlagsArgIndex;
158  const char *VariantName;
159  switch (Variant) {
160  case OpenVariant::Open:
161    FlagsArgIndex = 1;
162    VariantName = "open";
163    break;
164  case OpenVariant::OpenAt:
165    FlagsArgIndex = 2;
166    VariantName = "openat";
167    break;
168  };
169
170  // All calls should at least provide arguments up to the 'flags' parameter.
171  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  unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
176
177  // The create mode argument should be the last argument.
178  unsigned int MaxArgCount = CreateModeArgIndex + 1;
179
180  ProgramStateRef state = C.getState();
181
182  if (CE->getNumArgs() < MinArgCount) {
183    // The frontend should issue a warning for this case. Just return.
184    return;
185  } else if (CE->getNumArgs() == MaxArgCount) {
186    const Expr *Arg = CE->getArg(CreateModeArgIndex);
187    QualType QT = Arg->getType();
188    if (!QT->isIntegerType()) {
189      SmallString<256> SBuf;
190      llvm::raw_svector_ostream OS(SBuf);
191      OS << "The " << CreateModeArgIndex + 1
192         << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
193         << " argument to '" << VariantName << "' is not an integer";
194
195      ReportOpenBug(C, state,
196                    SBuf.c_str(),
197                    Arg->getSourceRange());
198      return;
199    }
200  } else if (CE->getNumArgs() > MaxArgCount) {
201    SmallString<256> SBuf;
202    llvm::raw_svector_ostream OS(SBuf);
203    OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
204       << " arguments";
205
206    ReportOpenBug(C, state,
207                  SBuf.c_str(),
208                  CE->getArg(MaxArgCount)->getSourceRange());
209    return;
210  }
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  if (!Val_O_CREAT) {
215    if (C.getASTContext().getTargetInfo().getTriple().getVendor()
216                                                      == llvm::Triple::Apple)
217      Val_O_CREAT = 0x0200;
218    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      return;
224    }
225  }
226
227  // Now check if oflags has O_CREAT set.
228  const Expr *oflagsEx = CE->getArg(FlagsArgIndex);
229  const SVal V = C.getSVal(oflagsEx);
230  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    return;
234  }
235  NonLoc oflags = V.castAs<NonLoc>();
236  NonLoc ocreateFlag = C.getSValBuilder()
237                           .makeIntVal(*Val_O_CREAT, oflagsEx->getType())
238                           .castAs<NonLoc>();
239  SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
240                                                      oflags, ocreateFlag,
241                                                      oflagsEx->getType());
242  if (maskedFlagsUC.isUnknownOrUndef())
243    return;
244  DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
245
246  // Check if maskedFlags is non-zero.
247  ProgramStateRef trueState, falseState;
248  std::tie(trueState, falseState) = state->assume(maskedFlags);
249
250  // Only emit an error if the value of 'maskedFlags' is properly
251  // constrained;
252  if (!(trueState && !falseState))
253    return;
254
255  if (CE->getNumArgs() < MaxArgCount) {
256    SmallString<256> SBuf;
257    llvm::raw_svector_ostream OS(SBuf);
258    OS << "Call to '" << VariantName << "' requires a "
259       << CreateModeArgIndex + 1
260       << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
261       << " argument when the 'O_CREAT' flag is set";
262    ReportOpenBug(C, trueState,
263                  SBuf.c_str(),
264                  oflagsEx->getSourceRange());
265  }
266}
267
268//===----------------------------------------------------------------------===//
269// pthread_once
270//===----------------------------------------------------------------------===//
271
272void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
273                                      const CallExpr *CE) const {
274
275  // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
276  // They can possibly be refactored.
277
278  if (CE->getNumArgs() < 1)
279    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  ProgramStateRef state = C.getState();
284  const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
285  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
286    return;
287
288  ExplodedNode *N = C.generateErrorNode(state);
289  if (!N)
290    return;
291
292  SmallString<256> S;
293  llvm::raw_svector_ostream os(S);
294  os << "Call to 'pthread_once' uses";
295  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
296    os << " the local variable '" << VR->getDecl()->getName() << '\'';
297  else
298    os << " stack allocated memory";
299  os << " for the \"control\" value.  Using such transient memory for "
300  "the control value is potentially dangerous.";
301  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
302    os << "  Perhaps you intended to declare the variable as 'static'?";
303
304  LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'");
305
306  auto report =
307      std::make_unique<PathSensitiveBugReport>(*BT_pthreadOnce, os.str(), N);
308  report->addRange(CE->getArg(0)->getSourceRange());
309  C.emitReport(std::move(report));
310}
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.
322static bool IsZeroByteAllocation(ProgramStateRef state,
323                                 const SVal argVal,
324                                 ProgramStateRef *trueState,
325                                 ProgramStateRef *falseState) {
326  std::tie(*trueState, *falseState) =
327    state->assume(argVal.castAs<DefinedSVal>());
328
329  return (*falseState && !*trueState);
330}
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.
335bool UnixAPIPortabilityChecker::ReportZeroByteAllocation(
336                                                    CheckerContext &C,
337                                                    ProgramStateRef falseState,
338                                                    const Expr *arg,
339                                                    const char *fn_name) const {
340  ExplodedNode *N = C.generateErrorNode(falseState);
341  if (!N)
342    return false;
343
344  LazyInitialize(this, BT_mallocZero,
345                 "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
346
347  SmallString<256> S;
348  llvm::raw_svector_ostream os(S);
349  os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
350  auto report =
351      std::make_unique<PathSensitiveBugReport>(*BT_mallocZero, os.str(), N);
352
353  report->addRange(arg->getSourceRange());
354  bugreporter::trackExpressionValue(N, arg, *report);
355  C.emitReport(std::move(report));
356
357  return true;
358}
359
360// Does a basic check for 0-sized allocations suitable for most of the below
361// functions (modulo "calloc")
362void UnixAPIPortabilityChecker::BasicAllocationCheck(CheckerContext &C,
363                                                     const CallExpr *CE,
364                                                     const unsigned numArgs,
365                                                     const unsigned sizeArg,
366                                                     const char *fn) const {
367  // Check for the correct number of arguments.
368  if (CE->getNumArgs() != numArgs)
369    return;
370
371  // Check if the allocation size is 0.
372  ProgramStateRef state = C.getState();
373  ProgramStateRef trueState = nullptr, falseState = nullptr;
374  const Expr *arg = CE->getArg(sizeArg);
375  SVal argVal = C.getSVal(arg);
376
377  if (argVal.isUnknownOrUndef())
378    return;
379
380  // Is the value perfectly constrained to zero?
381  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
382    (void) ReportZeroByteAllocation(C, falseState, arg, fn);
383    return;
384  }
385  // Assume the value is non-zero going forward.
386  assert(trueState);
387  if (trueState != state)
388    C.addTransition(trueState);
389}
390
391void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
392                                                const CallExpr *CE) const {
393  unsigned int nArgs = CE->getNumArgs();
394  if (nArgs != 2)
395    return;
396
397  ProgramStateRef state = C.getState();
398  ProgramStateRef trueState = nullptr, falseState = nullptr;
399
400  unsigned int i;
401  for (i = 0; i < nArgs; i++) {
402    const Expr *arg = CE->getArg(i);
403    SVal argVal = C.getSVal(arg);
404    if (argVal.isUnknownOrUndef()) {
405      if (i == 0)
406        continue;
407      else
408        return;
409    }
410
411    if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
412      if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
413        return;
414      else if (i == 0)
415        continue;
416      else
417        return;
418    }
419  }
420
421  // Assume the value is non-zero going forward.
422  assert(trueState);
423  if (trueState != state)
424    C.addTransition(trueState);
425}
426
427void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
428                                                const CallExpr *CE) const {
429  BasicAllocationCheck(C, CE, 1, 0, "malloc");
430}
431
432void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
433                                                 const CallExpr *CE) const {
434  BasicAllocationCheck(C, CE, 2, 1, "realloc");
435}
436
437void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
438                                                  const CallExpr *CE) const {
439  BasicAllocationCheck(C, CE, 2, 1, "reallocf");
440}
441
442void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
443                                                const CallExpr *CE) const {
444  BasicAllocationCheck(C, CE, 1, 0, "alloca");
445}
446
447void UnixAPIPortabilityChecker::CheckAllocaWithAlignZero(
448                                                     CheckerContext &C,
449                                                     const CallExpr *CE) const {
450  BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
451}
452
453void UnixAPIPortabilityChecker::CheckVallocZero(CheckerContext &C,
454                                                const CallExpr *CE) const {
455  BasicAllocationCheck(C, CE, 1, 0, "valloc");
456}
457
458void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
459                                             CheckerContext &C) const {
460  const FunctionDecl *FD = C.getCalleeDecl(CE);
461  if (!FD || FD->getKind() != Decl::Function)
462    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  const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
467  if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
468    return;
469
470  StringRef FName = C.getCalleeName(FD);
471  if (FName.empty())
472    return;
473
474  if (FName == "calloc")
475    CheckCallocZero(C, CE);
476
477  else if (FName == "malloc")
478    CheckMallocZero(C, CE);
479
480  else if (FName == "realloc")
481    CheckReallocZero(C, CE);
482
483  else if (FName == "reallocf")
484    CheckReallocfZero(C, CE);
485
486  else if (FName == "alloca" || FName ==  "__builtin_alloca")
487    CheckAllocaZero(C, CE);
488
489  else if (FName == "__builtin_alloca_with_align")
490    CheckAllocaWithAlignZero(C, CE);
491
492  else if (FName == "valloc")
493    CheckVallocZero(C, CE);
494}
495
496//===----------------------------------------------------------------------===//
497// Registration.
498//===----------------------------------------------------------------------===//
499
500#define REGISTER_CHECKER(CHECKERNAME)                                          \
501  void ento::register##CHECKERNAME(CheckerManager &mgr) {                      \
502    mgr.registerChecker<CHECKERNAME>();                                        \
503  }                                                                            \
504                                                                               \
505  bool ento::shouldRegister##CHECKERNAME(const CheckerManager &mgr) {          \
506    return true;                                                               \
507  }
508
509REGISTER_CHECKER(UnixAPIMisuseChecker)
510REGISTER_CHECKER(UnixAPIPortabilityChecker)
511