UnixAPIChecker.cpp revision 5eca482fe895ea57bc82410222e6426c09e63284
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/StaticAnalyzer/Core/Checker.h"
17#include "clang/StaticAnalyzer/Core/CheckerManager.h"
18#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20#include "clang/Basic/TargetInfo.h"
21#include "llvm/ADT/Optional.h"
22#include "llvm/ADT/StringSwitch.h"
23#include <fcntl.h>
24
25using namespace clang;
26using namespace ento;
27using llvm::Optional;
28
29namespace {
30class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > {
31  mutable llvm::OwningPtr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero;
32  mutable Optional<uint64_t> Val_O_CREAT;
33
34public:
35  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
36
37  void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
38  void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
39  void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
40  void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
41  void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
42
43  typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &,
44                                             const CallExpr *) const;
45private:
46  bool ReportZeroByteAllocation(CheckerContext &C,
47                                const ProgramState *falseState,
48                                const Expr *arg,
49                                const char *fn_name) const;
50};
51} //end anonymous namespace
52
53//===----------------------------------------------------------------------===//
54// Utility functions.
55//===----------------------------------------------------------------------===//
56
57static inline void LazyInitialize(llvm::OwningPtr<BugType> &BT,
58                                  const char *name) {
59  if (BT)
60    return;
61  BT.reset(new BugType(name, "Unix API"));
62}
63
64//===----------------------------------------------------------------------===//
65// "open" (man 2 open)
66//===----------------------------------------------------------------------===//
67
68void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
69  // The definition of O_CREAT is platform specific.  We need a better way
70  // of querying this information from the checking environment.
71  if (!Val_O_CREAT.hasValue()) {
72    if (C.getASTContext().getTargetInfo().getTriple().getVendor()
73                                                      == llvm::Triple::Apple)
74      Val_O_CREAT = 0x0200;
75    else {
76      // FIXME: We need a more general way of getting the O_CREAT value.
77      // We could possibly grovel through the preprocessor state, but
78      // that would require passing the Preprocessor object to the ExprEngine.
79      return;
80    }
81  }
82
83  // Look at the 'oflags' argument for the O_CREAT flag.
84  const ProgramState *state = C.getState();
85
86  if (CE->getNumArgs() < 2) {
87    // The frontend should issue a warning for this case, so this is a sanity
88    // check.
89    return;
90  }
91
92  // Now check if oflags has O_CREAT set.
93  const Expr *oflagsEx = CE->getArg(1);
94  const SVal V = state->getSVal(oflagsEx, C.getLocationContext());
95  if (!isa<NonLoc>(V)) {
96    // The case where 'V' can be a location can only be due to a bad header,
97    // so in this case bail out.
98    return;
99  }
100  NonLoc oflags = cast<NonLoc>(V);
101  NonLoc ocreateFlag =
102    cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(),
103                                                oflagsEx->getType()));
104  SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
105                                                      oflags, ocreateFlag,
106                                                      oflagsEx->getType());
107  if (maskedFlagsUC.isUnknownOrUndef())
108    return;
109  DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC);
110
111  // Check if maskedFlags is non-zero.
112  const ProgramState *trueState, *falseState;
113  llvm::tie(trueState, falseState) = state->assume(maskedFlags);
114
115  // Only emit an error if the value of 'maskedFlags' is properly
116  // constrained;
117  if (!(trueState && !falseState))
118    return;
119
120  if (CE->getNumArgs() < 3) {
121    ExplodedNode *N = C.generateSink(trueState);
122    if (!N)
123      return;
124
125    LazyInitialize(BT_open, "Improper use of 'open'");
126
127    BugReport *report =
128      new BugReport(*BT_open,
129                            "Call to 'open' requires a third argument when "
130                            "the 'O_CREAT' flag is set", N);
131    report->addRange(oflagsEx->getSourceRange());
132    C.EmitReport(report);
133  }
134}
135
136//===----------------------------------------------------------------------===//
137// pthread_once
138//===----------------------------------------------------------------------===//
139
140void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
141                                      const CallExpr *CE) const {
142
143  // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
144  // They can possibly be refactored.
145
146  if (CE->getNumArgs() < 1)
147    return;
148
149  // Check if the first argument is stack allocated.  If so, issue a warning
150  // because that's likely to be bad news.
151  const ProgramState *state = C.getState();
152  const MemRegion *R =
153    state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
154  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
155    return;
156
157  ExplodedNode *N = C.generateSink(state);
158  if (!N)
159    return;
160
161  llvm::SmallString<256> S;
162  llvm::raw_svector_ostream os(S);
163  os << "Call to 'pthread_once' uses";
164  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
165    os << " the local variable '" << VR->getDecl()->getName() << '\'';
166  else
167    os << " stack allocated memory";
168  os << " for the \"control\" value.  Using such transient memory for "
169  "the control value is potentially dangerous.";
170  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
171    os << "  Perhaps you intended to declare the variable as 'static'?";
172
173  LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'");
174
175  BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N);
176  report->addRange(CE->getArg(0)->getSourceRange());
177  C.EmitReport(report);
178}
179
180//===----------------------------------------------------------------------===//
181// "calloc",  "malloc" and "realloc" with allocation size 0
182//===----------------------------------------------------------------------===//
183
184// Returns true if we try to do a zero byte allocation, false otherwise.
185// Fills in trueState and falseState.
186static bool IsZeroByteAllocation(const ProgramState *state,
187                                const SVal argVal,
188                                const ProgramState **trueState,
189                                const ProgramState **falseState) {
190  llvm::tie(*trueState, *falseState) = state->assume(cast<DefinedSVal>(argVal));
191  return (*falseState && !*trueState);
192}
193
194// Generates an error report, indicating that the function whose name is given
195// will perform a zero byte allocation.
196// Returns false if an error occured, true otherwise.
197bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
198                                              const ProgramState *falseState,
199                                              const Expr *arg,
200                                              const char *fn_name) const {
201  ExplodedNode *N = C.generateSink(falseState);
202  if (!N)
203    return false;
204
205  // FIXME: Add reference to CERT advisory, and/or C99 standard in bug
206  // output.
207  LazyInitialize(BT_mallocZero, "Undefined allocation of 0 bytes");
208
209  llvm::SmallString<256> S;
210  llvm::raw_svector_ostream os(S);
211  os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
212  BugReport *report = new BugReport(*BT_mallocZero, os.str(), N);
213
214  report->addRange(arg->getSourceRange());
215  report->addVisitor(bugreporter::getTrackNullOrUndefValueVisitor(N, arg));
216  C.EmitReport(report);
217
218  return true;
219}
220
221void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
222                                     const CallExpr *CE) const {
223  unsigned int nArgs = CE->getNumArgs();
224  if (nArgs != 2)
225    return;
226
227  const ProgramState *state = C.getState();
228  const ProgramState *trueState = NULL, *falseState = NULL;
229
230  unsigned int i;
231  for (i = 0; i < nArgs; i++) {
232    const Expr *arg = CE->getArg(i);
233    SVal argVal = state->getSVal(arg, C.getLocationContext());
234    if (argVal.isUnknownOrUndef()) {
235      if (i == 0)
236        continue;
237      else
238        return;
239    }
240
241    if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
242      if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
243        return;
244      else if (i == 0)
245        continue;
246      else
247        return;
248    }
249  }
250
251  // Assume the the value is non-zero going forward.
252  assert(trueState);
253  if (trueState != state)
254    C.addTransition(trueState);
255}
256
257// FIXME: Eventually this should be rolled into the MallocChecker, but this
258// check is more basic and is valuable for widespread use.
259void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
260                                     const CallExpr *CE) const {
261  // Sanity check that malloc takes one argument.
262  if (CE->getNumArgs() != 1)
263    return;
264
265  // Check if the allocation size is 0.
266  const ProgramState *state = C.getState();
267  const ProgramState *trueState = NULL, *falseState = NULL;
268  const Expr *arg = CE->getArg(0);
269  SVal argVal = state->getSVal(arg, C.getLocationContext());
270
271  if (argVal.isUnknownOrUndef())
272    return;
273
274  // Is the value perfectly constrained to zero?
275  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
276    (void) ReportZeroByteAllocation(C, falseState, arg, "malloc");
277    return;
278  }
279  // Assume the the value is non-zero going forward.
280  assert(trueState);
281  if (trueState != state)
282    C.addTransition(trueState);
283}
284
285void UnixAPIChecker::CheckReallocZero(CheckerContext &C,
286                                      const CallExpr *CE) const {
287  if (CE->getNumArgs() != 2)
288    return;
289
290  const ProgramState *state = C.getState();
291  const ProgramState *trueState = NULL, *falseState = NULL;
292  const Expr *arg = CE->getArg(1);
293  SVal argVal = state->getSVal(arg, C.getLocationContext());
294
295  if (argVal.isUnknownOrUndef())
296    return;
297
298  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
299    ReportZeroByteAllocation(C, falseState, arg, "realloc");
300    return;
301  }
302
303  // Assume the the value is non-zero going forward.
304  assert(trueState);
305  if (trueState != state)
306    C.addTransition(trueState);
307}
308
309//===----------------------------------------------------------------------===//
310// Central dispatch function.
311//===----------------------------------------------------------------------===//
312
313void UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
314  StringRef FName = C.getCalleeName(CE);
315  if (FName.empty())
316    return;
317
318  SubChecker SC =
319    llvm::StringSwitch<SubChecker>(FName)
320      .Case("open", &UnixAPIChecker::CheckOpen)
321      .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
322      .Case("calloc", &UnixAPIChecker::CheckCallocZero)
323      .Case("malloc", &UnixAPIChecker::CheckMallocZero)
324      .Case("realloc", &UnixAPIChecker::CheckReallocZero)
325      .Default(NULL);
326
327  if (SC)
328    (this->*SC)(C, CE);
329}
330
331//===----------------------------------------------------------------------===//
332// Registration.
333//===----------------------------------------------------------------------===//
334
335void ento::registerUnixAPIChecker(CheckerManager &mgr) {
336  mgr.registerChecker<UnixAPIChecker>();
337}
338