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