1//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-//
2//
3//                     The LLVM Compiler Infrastructure
4//
5// This file is distributed under the University of Illinois Open Source
6// License. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9//
10// This defines UnixAPIChecker, which is an assortment of checks on calls
11// to various, widely used UNIX/Posix functions.
12//
13//===----------------------------------------------------------------------===//
14
15#include "ClangSACheckers.h"
16#include "clang/Basic/TargetInfo.h"
17#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18#include "clang/StaticAnalyzer/Core/Checker.h"
19#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21#include "llvm/ADT/Optional.h"
22#include "llvm/ADT/STLExtras.h"
23#include "llvm/ADT/SmallString.h"
24#include "llvm/ADT/StringSwitch.h"
25#include "llvm/Support/raw_ostream.h"
26#include <fcntl.h>
27
28using namespace clang;
29using namespace ento;
30
31namespace {
32class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > {
33  mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero;
34  mutable Optional<uint64_t> Val_O_CREAT;
35
36public:
37  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
38
39  void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
40  void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
41  void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
42  void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
43  void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
44  void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
45  void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
46  void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
47
48  typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &,
49                                             const CallExpr *) const;
50private:
51  bool ReportZeroByteAllocation(CheckerContext &C,
52                                ProgramStateRef falseState,
53                                const Expr *arg,
54                                const char *fn_name) const;
55  void BasicAllocationCheck(CheckerContext &C,
56                            const CallExpr *CE,
57                            const unsigned numArgs,
58                            const unsigned sizeArg,
59                            const char *fn) const;
60  void LazyInitialize(std::unique_ptr<BugType> &BT, const char *name) const {
61    if (BT)
62      return;
63    BT.reset(new BugType(this, name, categories::UnixAPI));
64  }
65  void ReportOpenBug(CheckerContext &C,
66                     ProgramStateRef State,
67                     const char *Msg,
68                     SourceRange SR) const;
69};
70} //end anonymous namespace
71
72//===----------------------------------------------------------------------===//
73// "open" (man 2 open)
74//===----------------------------------------------------------------------===//
75
76void UnixAPIChecker::ReportOpenBug(CheckerContext &C,
77                                   ProgramStateRef State,
78                                   const char *Msg,
79                                   SourceRange SR) const {
80  ExplodedNode *N = C.generateErrorNode(State);
81  if (!N)
82    return;
83
84  LazyInitialize(BT_open, "Improper use of 'open'");
85
86  auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N);
87  Report->addRange(SR);
88  C.emitReport(std::move(Report));
89}
90
91void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
92  ProgramStateRef state = C.getState();
93
94  if (CE->getNumArgs() < 2) {
95    // The frontend should issue a warning for this case, so this is a sanity
96    // check.
97    return;
98  } else if (CE->getNumArgs() == 3) {
99    const Expr *Arg = CE->getArg(2);
100    QualType QT = Arg->getType();
101    if (!QT->isIntegerType()) {
102      ReportOpenBug(C, state,
103                    "Third argument to 'open' is not an integer",
104                    Arg->getSourceRange());
105      return;
106    }
107  } else if (CE->getNumArgs() > 3) {
108    ReportOpenBug(C, state,
109                  "Call to 'open' with more than three arguments",
110                  CE->getArg(3)->getSourceRange());
111    return;
112  }
113
114  // The definition of O_CREAT is platform specific.  We need a better way
115  // of querying this information from the checking environment.
116  if (!Val_O_CREAT.hasValue()) {
117    if (C.getASTContext().getTargetInfo().getTriple().getVendor()
118                                                      == llvm::Triple::Apple)
119      Val_O_CREAT = 0x0200;
120    else {
121      // FIXME: We need a more general way of getting the O_CREAT value.
122      // We could possibly grovel through the preprocessor state, but
123      // that would require passing the Preprocessor object to the ExprEngine.
124      // See also: MallocChecker.cpp / M_ZERO.
125      return;
126    }
127  }
128
129  // Now check if oflags has O_CREAT set.
130  const Expr *oflagsEx = CE->getArg(1);
131  const SVal V = state->getSVal(oflagsEx, C.getLocationContext());
132  if (!V.getAs<NonLoc>()) {
133    // The case where 'V' can be a location can only be due to a bad header,
134    // so in this case bail out.
135    return;
136  }
137  NonLoc oflags = V.castAs<NonLoc>();
138  NonLoc ocreateFlag = C.getSValBuilder()
139      .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>();
140  SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
141                                                      oflags, ocreateFlag,
142                                                      oflagsEx->getType());
143  if (maskedFlagsUC.isUnknownOrUndef())
144    return;
145  DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
146
147  // Check if maskedFlags is non-zero.
148  ProgramStateRef trueState, falseState;
149  std::tie(trueState, falseState) = state->assume(maskedFlags);
150
151  // Only emit an error if the value of 'maskedFlags' is properly
152  // constrained;
153  if (!(trueState && !falseState))
154    return;
155
156  if (CE->getNumArgs() < 3) {
157    ReportOpenBug(C, trueState,
158                  "Call to 'open' requires a third argument when "
159                  "the 'O_CREAT' flag is set",
160                  oflagsEx->getSourceRange());
161  }
162}
163
164//===----------------------------------------------------------------------===//
165// pthread_once
166//===----------------------------------------------------------------------===//
167
168void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
169                                      const CallExpr *CE) const {
170
171  // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
172  // They can possibly be refactored.
173
174  if (CE->getNumArgs() < 1)
175    return;
176
177  // Check if the first argument is stack allocated.  If so, issue a warning
178  // because that's likely to be bad news.
179  ProgramStateRef state = C.getState();
180  const MemRegion *R =
181    state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
182  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
183    return;
184
185  ExplodedNode *N = C.generateErrorNode(state);
186  if (!N)
187    return;
188
189  SmallString<256> S;
190  llvm::raw_svector_ostream os(S);
191  os << "Call to 'pthread_once' uses";
192  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
193    os << " the local variable '" << VR->getDecl()->getName() << '\'';
194  else
195    os << " stack allocated memory";
196  os << " for the \"control\" value.  Using such transient memory for "
197  "the control value is potentially dangerous.";
198  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
199    os << "  Perhaps you intended to declare the variable as 'static'?";
200
201  LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'");
202
203  auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N);
204  report->addRange(CE->getArg(0)->getSourceRange());
205  C.emitReport(std::move(report));
206}
207
208//===----------------------------------------------------------------------===//
209// "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
210// with allocation size 0
211//===----------------------------------------------------------------------===//
212// FIXME: Eventually these should be rolled into the MallocChecker, but right now
213// they're more basic and valuable for widespread use.
214
215// Returns true if we try to do a zero byte allocation, false otherwise.
216// Fills in trueState and falseState.
217static bool IsZeroByteAllocation(ProgramStateRef state,
218                                const SVal argVal,
219                                ProgramStateRef *trueState,
220                                ProgramStateRef *falseState) {
221  std::tie(*trueState, *falseState) =
222    state->assume(argVal.castAs<DefinedSVal>());
223
224  return (*falseState && !*trueState);
225}
226
227// Generates an error report, indicating that the function whose name is given
228// will perform a zero byte allocation.
229// Returns false if an error occurred, true otherwise.
230bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
231                                              ProgramStateRef falseState,
232                                              const Expr *arg,
233                                              const char *fn_name) const {
234  ExplodedNode *N = C.generateErrorNode(falseState);
235  if (!N)
236    return false;
237
238  LazyInitialize(BT_mallocZero,
239                 "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
240
241  SmallString<256> S;
242  llvm::raw_svector_ostream os(S);
243  os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
244  auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N);
245
246  report->addRange(arg->getSourceRange());
247  bugreporter::trackNullOrUndefValue(N, arg, *report);
248  C.emitReport(std::move(report));
249
250  return true;
251}
252
253// Does a basic check for 0-sized allocations suitable for most of the below
254// functions (modulo "calloc")
255void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
256                                          const CallExpr *CE,
257                                          const unsigned numArgs,
258                                          const unsigned sizeArg,
259                                          const char *fn) const {
260  // Sanity check for the correct number of arguments
261  if (CE->getNumArgs() != numArgs)
262    return;
263
264  // Check if the allocation size is 0.
265  ProgramStateRef state = C.getState();
266  ProgramStateRef trueState = nullptr, falseState = nullptr;
267  const Expr *arg = CE->getArg(sizeArg);
268  SVal argVal = state->getSVal(arg, C.getLocationContext());
269
270  if (argVal.isUnknownOrUndef())
271    return;
272
273  // Is the value perfectly constrained to zero?
274  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
275    (void) ReportZeroByteAllocation(C, falseState, arg, fn);
276    return;
277  }
278  // Assume the value is non-zero going forward.
279  assert(trueState);
280  if (trueState != state)
281    C.addTransition(trueState);
282}
283
284void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
285                                     const CallExpr *CE) const {
286  unsigned int nArgs = CE->getNumArgs();
287  if (nArgs != 2)
288    return;
289
290  ProgramStateRef state = C.getState();
291  ProgramStateRef trueState = nullptr, falseState = nullptr;
292
293  unsigned int i;
294  for (i = 0; i < nArgs; i++) {
295    const Expr *arg = CE->getArg(i);
296    SVal argVal = state->getSVal(arg, C.getLocationContext());
297    if (argVal.isUnknownOrUndef()) {
298      if (i == 0)
299        continue;
300      else
301        return;
302    }
303
304    if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
305      if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
306        return;
307      else if (i == 0)
308        continue;
309      else
310        return;
311    }
312  }
313
314  // Assume the value is non-zero going forward.
315  assert(trueState);
316  if (trueState != state)
317    C.addTransition(trueState);
318}
319
320void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
321                                     const CallExpr *CE) const {
322  BasicAllocationCheck(C, CE, 1, 0, "malloc");
323}
324
325void UnixAPIChecker::CheckReallocZero(CheckerContext &C,
326                                      const CallExpr *CE) const {
327  BasicAllocationCheck(C, CE, 2, 1, "realloc");
328}
329
330void UnixAPIChecker::CheckReallocfZero(CheckerContext &C,
331                                       const CallExpr *CE) const {
332  BasicAllocationCheck(C, CE, 2, 1, "reallocf");
333}
334
335void UnixAPIChecker::CheckAllocaZero(CheckerContext &C,
336                                     const CallExpr *CE) const {
337  BasicAllocationCheck(C, CE, 1, 0, "alloca");
338}
339
340void UnixAPIChecker::CheckVallocZero(CheckerContext &C,
341                                     const CallExpr *CE) const {
342  BasicAllocationCheck(C, CE, 1, 0, "valloc");
343}
344
345
346//===----------------------------------------------------------------------===//
347// Central dispatch function.
348//===----------------------------------------------------------------------===//
349
350void UnixAPIChecker::checkPreStmt(const CallExpr *CE,
351                                  CheckerContext &C) const {
352  const FunctionDecl *FD = C.getCalleeDecl(CE);
353  if (!FD || FD->getKind() != Decl::Function)
354    return;
355
356  StringRef FName = C.getCalleeName(FD);
357  if (FName.empty())
358    return;
359
360  SubChecker SC =
361    llvm::StringSwitch<SubChecker>(FName)
362      .Case("open", &UnixAPIChecker::CheckOpen)
363      .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
364      .Case("calloc", &UnixAPIChecker::CheckCallocZero)
365      .Case("malloc", &UnixAPIChecker::CheckMallocZero)
366      .Case("realloc", &UnixAPIChecker::CheckReallocZero)
367      .Case("reallocf", &UnixAPIChecker::CheckReallocfZero)
368      .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero)
369      .Case("valloc", &UnixAPIChecker::CheckVallocZero)
370      .Default(nullptr);
371
372  if (SC)
373    (this->*SC)(C, CE);
374}
375
376//===----------------------------------------------------------------------===//
377// Registration.
378//===----------------------------------------------------------------------===//
379
380void ento::registerUnixAPIChecker(CheckerManager &mgr) {
381  mgr.registerChecker<UnixAPIChecker>();
382}
383