UnixAPIChecker.cpp revision 243830
1218887Sdim//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-//
2218887Sdim//
3218887Sdim//                     The LLVM Compiler Infrastructure
4218887Sdim//
5218887Sdim// This file is distributed under the University of Illinois Open Source
6218887Sdim// License. See LICENSE.TXT for details.
7218887Sdim//
8218887Sdim//===----------------------------------------------------------------------===//
9218887Sdim//
10218887Sdim// This defines UnixAPIChecker, which is an assortment of checks on calls
11218887Sdim// to various, widely used UNIX/Posix functions.
12218887Sdim//
13218887Sdim//===----------------------------------------------------------------------===//
14218887Sdim
15218887Sdim#include "ClangSACheckers.h"
16221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
17218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
18219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19218887Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20219077Sdim#include "clang/Basic/TargetInfo.h"
21218887Sdim#include "llvm/ADT/Optional.h"
22234353Sdim#include "llvm/ADT/SmallString.h"
23234353Sdim#include "llvm/ADT/STLExtras.h"
24218887Sdim#include "llvm/ADT/StringSwitch.h"
25218887Sdim#include <fcntl.h>
26218887Sdim
27218887Sdimusing namespace clang;
28218887Sdimusing namespace ento;
29218887Sdimusing llvm::Optional;
30218887Sdim
31218887Sdimnamespace {
32221345Sdimclass UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > {
33234353Sdim  mutable OwningPtr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero;
34219077Sdim  mutable Optional<uint64_t> Val_O_CREAT;
35218887Sdim
36218887Sdimpublic:
37224145Sdim  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
38218887Sdim
39224145Sdim  void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
40224145Sdim  void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
41234353Sdim  void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
42224145Sdim  void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
43234353Sdim  void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
44243830Sdim  void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
45234353Sdim  void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
46234353Sdim  void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
47224145Sdim
48224145Sdim  typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &,
49224145Sdim                                             const CallExpr *) const;
50234353Sdimprivate:
51234353Sdim  bool ReportZeroByteAllocation(CheckerContext &C,
52234353Sdim                                ProgramStateRef falseState,
53234353Sdim                                const Expr *arg,
54234353Sdim                                const char *fn_name) const;
55234353Sdim  void BasicAllocationCheck(CheckerContext &C,
56234353Sdim                            const CallExpr *CE,
57234353Sdim                            const unsigned numArgs,
58234353Sdim                            const unsigned sizeArg,
59234353Sdim                            const char *fn) const;
60218887Sdim};
61218887Sdim} //end anonymous namespace
62218887Sdim
63218887Sdim//===----------------------------------------------------------------------===//
64218887Sdim// Utility functions.
65218887Sdim//===----------------------------------------------------------------------===//
66218887Sdim
67234353Sdimstatic inline void LazyInitialize(OwningPtr<BugType> &BT,
68224145Sdim                                  const char *name) {
69218887Sdim  if (BT)
70218887Sdim    return;
71234353Sdim  BT.reset(new BugType(name, categories::UnixAPI));
72218887Sdim}
73218887Sdim
74218887Sdim//===----------------------------------------------------------------------===//
75218887Sdim// "open" (man 2 open)
76218887Sdim//===----------------------------------------------------------------------===//
77218887Sdim
78224145Sdimvoid UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
79218887Sdim  // The definition of O_CREAT is platform specific.  We need a better way
80218887Sdim  // of querying this information from the checking environment.
81224145Sdim  if (!Val_O_CREAT.hasValue()) {
82226633Sdim    if (C.getASTContext().getTargetInfo().getTriple().getVendor()
83226633Sdim                                                      == llvm::Triple::Apple)
84224145Sdim      Val_O_CREAT = 0x0200;
85218887Sdim    else {
86218887Sdim      // FIXME: We need a more general way of getting the O_CREAT value.
87218887Sdim      // We could possibly grovel through the preprocessor state, but
88218887Sdim      // that would require passing the Preprocessor object to the ExprEngine.
89218887Sdim      return;
90218887Sdim    }
91218887Sdim  }
92218887Sdim
93218887Sdim  // Look at the 'oflags' argument for the O_CREAT flag.
94234353Sdim  ProgramStateRef state = C.getState();
95218887Sdim
96218887Sdim  if (CE->getNumArgs() < 2) {
97218887Sdim    // The frontend should issue a warning for this case, so this is a sanity
98218887Sdim    // check.
99218887Sdim    return;
100218887Sdim  }
101218887Sdim
102218887Sdim  // Now check if oflags has O_CREAT set.
103218887Sdim  const Expr *oflagsEx = CE->getArg(1);
104234353Sdim  const SVal V = state->getSVal(oflagsEx, C.getLocationContext());
105218887Sdim  if (!isa<NonLoc>(V)) {
106218887Sdim    // The case where 'V' can be a location can only be due to a bad header,
107218887Sdim    // so in this case bail out.
108218887Sdim    return;
109218887Sdim  }
110218887Sdim  NonLoc oflags = cast<NonLoc>(V);
111218887Sdim  NonLoc ocreateFlag =
112224145Sdim    cast<NonLoc>(C.getSValBuilder().makeIntVal(Val_O_CREAT.getValue(),
113218887Sdim                                                oflagsEx->getType()));
114218887Sdim  SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
115218887Sdim                                                      oflags, ocreateFlag,
116218887Sdim                                                      oflagsEx->getType());
117218887Sdim  if (maskedFlagsUC.isUnknownOrUndef())
118218887Sdim    return;
119218887Sdim  DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC);
120218887Sdim
121218887Sdim  // Check if maskedFlags is non-zero.
122234353Sdim  ProgramStateRef trueState, falseState;
123218887Sdim  llvm::tie(trueState, falseState) = state->assume(maskedFlags);
124218887Sdim
125218887Sdim  // Only emit an error if the value of 'maskedFlags' is properly
126218887Sdim  // constrained;
127218887Sdim  if (!(trueState && !falseState))
128218887Sdim    return;
129218887Sdim
130218887Sdim  if (CE->getNumArgs() < 3) {
131218887Sdim    ExplodedNode *N = C.generateSink(trueState);
132218887Sdim    if (!N)
133218887Sdim      return;
134218887Sdim
135224145Sdim    LazyInitialize(BT_open, "Improper use of 'open'");
136224145Sdim
137226633Sdim    BugReport *report =
138226633Sdim      new BugReport(*BT_open,
139218887Sdim                            "Call to 'open' requires a third argument when "
140218887Sdim                            "the 'O_CREAT' flag is set", N);
141218887Sdim    report->addRange(oflagsEx->getSourceRange());
142243830Sdim    C.emitReport(report);
143218887Sdim  }
144218887Sdim}
145218887Sdim
146218887Sdim//===----------------------------------------------------------------------===//
147218887Sdim// pthread_once
148218887Sdim//===----------------------------------------------------------------------===//
149218887Sdim
150224145Sdimvoid UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
151224145Sdim                                      const CallExpr *CE) const {
152218887Sdim
153218887Sdim  // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
154218887Sdim  // They can possibly be refactored.
155218887Sdim
156218887Sdim  if (CE->getNumArgs() < 1)
157218887Sdim    return;
158218887Sdim
159218887Sdim  // Check if the first argument is stack allocated.  If so, issue a warning
160218887Sdim  // because that's likely to be bad news.
161234353Sdim  ProgramStateRef state = C.getState();
162234353Sdim  const MemRegion *R =
163234353Sdim    state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
164218887Sdim  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
165218887Sdim    return;
166218887Sdim
167218887Sdim  ExplodedNode *N = C.generateSink(state);
168218887Sdim  if (!N)
169218887Sdim    return;
170218887Sdim
171234353Sdim  SmallString<256> S;
172218887Sdim  llvm::raw_svector_ostream os(S);
173218887Sdim  os << "Call to 'pthread_once' uses";
174218887Sdim  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
175218887Sdim    os << " the local variable '" << VR->getDecl()->getName() << '\'';
176218887Sdim  else
177218887Sdim    os << " stack allocated memory";
178218887Sdim  os << " for the \"control\" value.  Using such transient memory for "
179218887Sdim  "the control value is potentially dangerous.";
180218887Sdim  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
181218887Sdim    os << "  Perhaps you intended to declare the variable as 'static'?";
182218887Sdim
183224145Sdim  LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'");
184224145Sdim
185226633Sdim  BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N);
186218887Sdim  report->addRange(CE->getArg(0)->getSourceRange());
187243830Sdim  C.emitReport(report);
188218887Sdim}
189218887Sdim
190218887Sdim//===----------------------------------------------------------------------===//
191243830Sdim// "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
192243830Sdim// with allocation size 0
193218887Sdim//===----------------------------------------------------------------------===//
194234353Sdim// FIXME: Eventually these should be rolled into the MallocChecker, but right now
195234353Sdim// they're more basic and valuable for widespread use.
196218887Sdim
197234353Sdim// Returns true if we try to do a zero byte allocation, false otherwise.
198234353Sdim// Fills in trueState and falseState.
199234353Sdimstatic bool IsZeroByteAllocation(ProgramStateRef state,
200234353Sdim                                const SVal argVal,
201234353Sdim                                ProgramStateRef *trueState,
202234353Sdim                                ProgramStateRef *falseState) {
203234353Sdim  llvm::tie(*trueState, *falseState) =
204234353Sdim    state->assume(cast<DefinedSVal>(argVal));
205234353Sdim
206234353Sdim  return (*falseState && !*trueState);
207234353Sdim}
208218887Sdim
209234353Sdim// Generates an error report, indicating that the function whose name is given
210234353Sdim// will perform a zero byte allocation.
211234353Sdim// Returns false if an error occured, true otherwise.
212234353Sdimbool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
213234353Sdim                                              ProgramStateRef falseState,
214234353Sdim                                              const Expr *arg,
215234353Sdim                                              const char *fn_name) const {
216234353Sdim  ExplodedNode *N = C.generateSink(falseState);
217234353Sdim  if (!N)
218234353Sdim    return false;
219234353Sdim
220234353Sdim  LazyInitialize(BT_mallocZero,
221234353Sdim    "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
222234353Sdim
223234353Sdim  SmallString<256> S;
224234353Sdim  llvm::raw_svector_ostream os(S);
225234353Sdim  os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
226234353Sdim  BugReport *report = new BugReport(*BT_mallocZero, os.str(), N);
227234353Sdim
228234353Sdim  report->addRange(arg->getSourceRange());
229243830Sdim  bugreporter::trackNullOrUndefValue(N, arg, *report);
230243830Sdim  C.emitReport(report);
231234353Sdim
232234353Sdim  return true;
233234353Sdim}
234234353Sdim
235234353Sdim// Does a basic check for 0-sized allocations suitable for most of the below
236234353Sdim// functions (modulo "calloc")
237234353Sdimvoid UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
238234353Sdim                                          const CallExpr *CE,
239234353Sdim                                          const unsigned numArgs,
240234353Sdim                                          const unsigned sizeArg,
241234353Sdim                                          const char *fn) const {
242234353Sdim  // Sanity check for the correct number of arguments
243234353Sdim  if (CE->getNumArgs() != numArgs)
244218887Sdim    return;
245218887Sdim
246218887Sdim  // Check if the allocation size is 0.
247234353Sdim  ProgramStateRef state = C.getState();
248234353Sdim  ProgramStateRef trueState = NULL, falseState = NULL;
249234353Sdim  const Expr *arg = CE->getArg(sizeArg);
250234353Sdim  SVal argVal = state->getSVal(arg, C.getLocationContext());
251218887Sdim
252218887Sdim  if (argVal.isUnknownOrUndef())
253218887Sdim    return;
254234353Sdim
255218887Sdim  // Is the value perfectly constrained to zero?
256234353Sdim  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
257234353Sdim    (void) ReportZeroByteAllocation(C, falseState, arg, fn);
258234353Sdim    return;
259234353Sdim  }
260239462Sdim  // Assume the value is non-zero going forward.
261234353Sdim  assert(trueState);
262234353Sdim  if (trueState != state)
263234353Sdim    C.addTransition(trueState);
264234353Sdim}
265218887Sdim
266234353Sdimvoid UnixAPIChecker::CheckCallocZero(CheckerContext &C,
267234353Sdim                                     const CallExpr *CE) const {
268234353Sdim  unsigned int nArgs = CE->getNumArgs();
269234353Sdim  if (nArgs != 2)
270218887Sdim    return;
271234353Sdim
272234353Sdim  ProgramStateRef state = C.getState();
273234353Sdim  ProgramStateRef trueState = NULL, falseState = NULL;
274234353Sdim
275234353Sdim  unsigned int i;
276234353Sdim  for (i = 0; i < nArgs; i++) {
277234353Sdim    const Expr *arg = CE->getArg(i);
278234353Sdim    SVal argVal = state->getSVal(arg, C.getLocationContext());
279234353Sdim    if (argVal.isUnknownOrUndef()) {
280234353Sdim      if (i == 0)
281234353Sdim        continue;
282234353Sdim      else
283234353Sdim        return;
284234353Sdim    }
285234353Sdim
286234353Sdim    if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
287234353Sdim      if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
288234353Sdim        return;
289234353Sdim      else if (i == 0)
290234353Sdim        continue;
291234353Sdim      else
292234353Sdim        return;
293234353Sdim    }
294218887Sdim  }
295234353Sdim
296239462Sdim  // Assume the value is non-zero going forward.
297218887Sdim  assert(trueState);
298234353Sdim  if (trueState != state)
299218887Sdim    C.addTransition(trueState);
300218887Sdim}
301234353Sdim
302234353Sdimvoid UnixAPIChecker::CheckMallocZero(CheckerContext &C,
303234353Sdim                                     const CallExpr *CE) const {
304234353Sdim  BasicAllocationCheck(C, CE, 1, 0, "malloc");
305234353Sdim}
306234353Sdim
307234353Sdimvoid UnixAPIChecker::CheckReallocZero(CheckerContext &C,
308234353Sdim                                      const CallExpr *CE) const {
309234353Sdim  BasicAllocationCheck(C, CE, 2, 1, "realloc");
310234353Sdim}
311234353Sdim
312243830Sdimvoid UnixAPIChecker::CheckReallocfZero(CheckerContext &C,
313243830Sdim                                       const CallExpr *CE) const {
314243830Sdim  BasicAllocationCheck(C, CE, 2, 1, "reallocf");
315243830Sdim}
316243830Sdim
317234353Sdimvoid UnixAPIChecker::CheckAllocaZero(CheckerContext &C,
318234353Sdim                                     const CallExpr *CE) const {
319234353Sdim  BasicAllocationCheck(C, CE, 1, 0, "alloca");
320234353Sdim}
321234353Sdim
322234353Sdimvoid UnixAPIChecker::CheckVallocZero(CheckerContext &C,
323234353Sdim                                     const CallExpr *CE) const {
324234353Sdim  BasicAllocationCheck(C, CE, 1, 0, "valloc");
325234353Sdim}
326234353Sdim
327234353Sdim
328218887Sdim//===----------------------------------------------------------------------===//
329218887Sdim// Central dispatch function.
330218887Sdim//===----------------------------------------------------------------------===//
331218887Sdim
332234353Sdimvoid UnixAPIChecker::checkPreStmt(const CallExpr *CE,
333234353Sdim                                  CheckerContext &C) const {
334239462Sdim  const FunctionDecl *FD = C.getCalleeDecl(CE);
335239462Sdim  if (!FD || FD->getKind() != Decl::Function)
336239462Sdim    return;
337239462Sdim
338239462Sdim  StringRef FName = C.getCalleeName(FD);
339234353Sdim  if (FName.empty())
340218887Sdim    return;
341218887Sdim
342224145Sdim  SubChecker SC =
343234353Sdim    llvm::StringSwitch<SubChecker>(FName)
344224145Sdim      .Case("open", &UnixAPIChecker::CheckOpen)
345224145Sdim      .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
346234353Sdim      .Case("calloc", &UnixAPIChecker::CheckCallocZero)
347224145Sdim      .Case("malloc", &UnixAPIChecker::CheckMallocZero)
348234353Sdim      .Case("realloc", &UnixAPIChecker::CheckReallocZero)
349243830Sdim      .Case("reallocf", &UnixAPIChecker::CheckReallocfZero)
350234353Sdim      .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero)
351234353Sdim      .Case("valloc", &UnixAPIChecker::CheckVallocZero)
352224145Sdim      .Default(NULL);
353218887Sdim
354224145Sdim  if (SC)
355224145Sdim    (this->*SC)(C, CE);
356218887Sdim}
357219077Sdim
358219077Sdim//===----------------------------------------------------------------------===//
359219077Sdim// Registration.
360219077Sdim//===----------------------------------------------------------------------===//
361219077Sdim
362219077Sdimvoid ento::registerUnixAPIChecker(CheckerManager &mgr) {
363219077Sdim  mgr.registerChecker<UnixAPIChecker>();
364219077Sdim}
365