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"
16249423Sdim#include "clang/Basic/TargetInfo.h"
17249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
18221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h"
19218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h"
20219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
21218887Sdim#include "llvm/ADT/Optional.h"
22249423Sdim#include "llvm/ADT/STLExtras.h"
23234353Sdim#include "llvm/ADT/SmallString.h"
24218887Sdim#include "llvm/ADT/StringSwitch.h"
25249423Sdim#include "llvm/Support/raw_ostream.h"
26218887Sdim#include <fcntl.h>
27218887Sdim
28218887Sdimusing namespace clang;
29218887Sdimusing namespace ento;
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());
105249423Sdim  if (!V.getAs<NonLoc>()) {
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  }
110249423Sdim  NonLoc oflags = V.castAs<NonLoc>();
111249423Sdim  NonLoc ocreateFlag = C.getSValBuilder()
112249423Sdim      .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>();
113218887Sdim  SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
114218887Sdim                                                      oflags, ocreateFlag,
115218887Sdim                                                      oflagsEx->getType());
116218887Sdim  if (maskedFlagsUC.isUnknownOrUndef())
117218887Sdim    return;
118249423Sdim  DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
119218887Sdim
120218887Sdim  // Check if maskedFlags is non-zero.
121234353Sdim  ProgramStateRef trueState, falseState;
122218887Sdim  llvm::tie(trueState, falseState) = state->assume(maskedFlags);
123218887Sdim
124218887Sdim  // Only emit an error if the value of 'maskedFlags' is properly
125218887Sdim  // constrained;
126218887Sdim  if (!(trueState && !falseState))
127218887Sdim    return;
128218887Sdim
129218887Sdim  if (CE->getNumArgs() < 3) {
130218887Sdim    ExplodedNode *N = C.generateSink(trueState);
131218887Sdim    if (!N)
132218887Sdim      return;
133218887Sdim
134224145Sdim    LazyInitialize(BT_open, "Improper use of 'open'");
135224145Sdim
136226633Sdim    BugReport *report =
137226633Sdim      new BugReport(*BT_open,
138218887Sdim                            "Call to 'open' requires a third argument when "
139218887Sdim                            "the 'O_CREAT' flag is set", N);
140218887Sdim    report->addRange(oflagsEx->getSourceRange());
141243830Sdim    C.emitReport(report);
142218887Sdim  }
143218887Sdim}
144218887Sdim
145218887Sdim//===----------------------------------------------------------------------===//
146218887Sdim// pthread_once
147218887Sdim//===----------------------------------------------------------------------===//
148218887Sdim
149224145Sdimvoid UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
150224145Sdim                                      const CallExpr *CE) const {
151218887Sdim
152218887Sdim  // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
153218887Sdim  // They can possibly be refactored.
154218887Sdim
155218887Sdim  if (CE->getNumArgs() < 1)
156218887Sdim    return;
157218887Sdim
158218887Sdim  // Check if the first argument is stack allocated.  If so, issue a warning
159218887Sdim  // because that's likely to be bad news.
160234353Sdim  ProgramStateRef state = C.getState();
161234353Sdim  const MemRegion *R =
162234353Sdim    state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
163218887Sdim  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
164218887Sdim    return;
165218887Sdim
166218887Sdim  ExplodedNode *N = C.generateSink(state);
167218887Sdim  if (!N)
168218887Sdim    return;
169218887Sdim
170234353Sdim  SmallString<256> S;
171218887Sdim  llvm::raw_svector_ostream os(S);
172218887Sdim  os << "Call to 'pthread_once' uses";
173218887Sdim  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
174218887Sdim    os << " the local variable '" << VR->getDecl()->getName() << '\'';
175218887Sdim  else
176218887Sdim    os << " stack allocated memory";
177218887Sdim  os << " for the \"control\" value.  Using such transient memory for "
178218887Sdim  "the control value is potentially dangerous.";
179218887Sdim  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
180218887Sdim    os << "  Perhaps you intended to declare the variable as 'static'?";
181218887Sdim
182224145Sdim  LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'");
183224145Sdim
184226633Sdim  BugReport *report = new BugReport(*BT_pthreadOnce, os.str(), N);
185218887Sdim  report->addRange(CE->getArg(0)->getSourceRange());
186243830Sdim  C.emitReport(report);
187218887Sdim}
188218887Sdim
189218887Sdim//===----------------------------------------------------------------------===//
190243830Sdim// "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
191243830Sdim// with allocation size 0
192218887Sdim//===----------------------------------------------------------------------===//
193234353Sdim// FIXME: Eventually these should be rolled into the MallocChecker, but right now
194234353Sdim// they're more basic and valuable for widespread use.
195218887Sdim
196234353Sdim// Returns true if we try to do a zero byte allocation, false otherwise.
197234353Sdim// Fills in trueState and falseState.
198234353Sdimstatic bool IsZeroByteAllocation(ProgramStateRef state,
199234353Sdim                                const SVal argVal,
200234353Sdim                                ProgramStateRef *trueState,
201234353Sdim                                ProgramStateRef *falseState) {
202234353Sdim  llvm::tie(*trueState, *falseState) =
203249423Sdim    state->assume(argVal.castAs<DefinedSVal>());
204234353Sdim
205234353Sdim  return (*falseState && !*trueState);
206234353Sdim}
207218887Sdim
208234353Sdim// Generates an error report, indicating that the function whose name is given
209234353Sdim// will perform a zero byte allocation.
210234353Sdim// Returns false if an error occured, true otherwise.
211234353Sdimbool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
212234353Sdim                                              ProgramStateRef falseState,
213234353Sdim                                              const Expr *arg,
214234353Sdim                                              const char *fn_name) const {
215234353Sdim  ExplodedNode *N = C.generateSink(falseState);
216234353Sdim  if (!N)
217234353Sdim    return false;
218234353Sdim
219234353Sdim  LazyInitialize(BT_mallocZero,
220234353Sdim    "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
221234353Sdim
222234353Sdim  SmallString<256> S;
223234353Sdim  llvm::raw_svector_ostream os(S);
224234353Sdim  os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
225234353Sdim  BugReport *report = new BugReport(*BT_mallocZero, os.str(), N);
226234353Sdim
227234353Sdim  report->addRange(arg->getSourceRange());
228243830Sdim  bugreporter::trackNullOrUndefValue(N, arg, *report);
229243830Sdim  C.emitReport(report);
230234353Sdim
231234353Sdim  return true;
232234353Sdim}
233234353Sdim
234234353Sdim// Does a basic check for 0-sized allocations suitable for most of the below
235234353Sdim// functions (modulo "calloc")
236234353Sdimvoid UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
237234353Sdim                                          const CallExpr *CE,
238234353Sdim                                          const unsigned numArgs,
239234353Sdim                                          const unsigned sizeArg,
240234353Sdim                                          const char *fn) const {
241234353Sdim  // Sanity check for the correct number of arguments
242234353Sdim  if (CE->getNumArgs() != numArgs)
243218887Sdim    return;
244218887Sdim
245218887Sdim  // Check if the allocation size is 0.
246234353Sdim  ProgramStateRef state = C.getState();
247234353Sdim  ProgramStateRef trueState = NULL, falseState = NULL;
248234353Sdim  const Expr *arg = CE->getArg(sizeArg);
249234353Sdim  SVal argVal = state->getSVal(arg, C.getLocationContext());
250218887Sdim
251218887Sdim  if (argVal.isUnknownOrUndef())
252218887Sdim    return;
253234353Sdim
254218887Sdim  // Is the value perfectly constrained to zero?
255234353Sdim  if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
256234353Sdim    (void) ReportZeroByteAllocation(C, falseState, arg, fn);
257234353Sdim    return;
258234353Sdim  }
259239462Sdim  // Assume the value is non-zero going forward.
260234353Sdim  assert(trueState);
261234353Sdim  if (trueState != state)
262234353Sdim    C.addTransition(trueState);
263234353Sdim}
264218887Sdim
265234353Sdimvoid UnixAPIChecker::CheckCallocZero(CheckerContext &C,
266234353Sdim                                     const CallExpr *CE) const {
267234353Sdim  unsigned int nArgs = CE->getNumArgs();
268234353Sdim  if (nArgs != 2)
269218887Sdim    return;
270234353Sdim
271234353Sdim  ProgramStateRef state = C.getState();
272234353Sdim  ProgramStateRef trueState = NULL, falseState = NULL;
273234353Sdim
274234353Sdim  unsigned int i;
275234353Sdim  for (i = 0; i < nArgs; i++) {
276234353Sdim    const Expr *arg = CE->getArg(i);
277234353Sdim    SVal argVal = state->getSVal(arg, C.getLocationContext());
278234353Sdim    if (argVal.isUnknownOrUndef()) {
279234353Sdim      if (i == 0)
280234353Sdim        continue;
281234353Sdim      else
282234353Sdim        return;
283234353Sdim    }
284234353Sdim
285234353Sdim    if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
286234353Sdim      if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
287234353Sdim        return;
288234353Sdim      else if (i == 0)
289234353Sdim        continue;
290234353Sdim      else
291234353Sdim        return;
292234353Sdim    }
293218887Sdim  }
294234353Sdim
295239462Sdim  // Assume the value is non-zero going forward.
296218887Sdim  assert(trueState);
297234353Sdim  if (trueState != state)
298218887Sdim    C.addTransition(trueState);
299218887Sdim}
300234353Sdim
301234353Sdimvoid UnixAPIChecker::CheckMallocZero(CheckerContext &C,
302234353Sdim                                     const CallExpr *CE) const {
303234353Sdim  BasicAllocationCheck(C, CE, 1, 0, "malloc");
304234353Sdim}
305234353Sdim
306234353Sdimvoid UnixAPIChecker::CheckReallocZero(CheckerContext &C,
307234353Sdim                                      const CallExpr *CE) const {
308234353Sdim  BasicAllocationCheck(C, CE, 2, 1, "realloc");
309234353Sdim}
310234353Sdim
311243830Sdimvoid UnixAPIChecker::CheckReallocfZero(CheckerContext &C,
312243830Sdim                                       const CallExpr *CE) const {
313243830Sdim  BasicAllocationCheck(C, CE, 2, 1, "reallocf");
314243830Sdim}
315243830Sdim
316234353Sdimvoid UnixAPIChecker::CheckAllocaZero(CheckerContext &C,
317234353Sdim                                     const CallExpr *CE) const {
318234353Sdim  BasicAllocationCheck(C, CE, 1, 0, "alloca");
319234353Sdim}
320234353Sdim
321234353Sdimvoid UnixAPIChecker::CheckVallocZero(CheckerContext &C,
322234353Sdim                                     const CallExpr *CE) const {
323234353Sdim  BasicAllocationCheck(C, CE, 1, 0, "valloc");
324234353Sdim}
325234353Sdim
326234353Sdim
327218887Sdim//===----------------------------------------------------------------------===//
328218887Sdim// Central dispatch function.
329218887Sdim//===----------------------------------------------------------------------===//
330218887Sdim
331234353Sdimvoid UnixAPIChecker::checkPreStmt(const CallExpr *CE,
332234353Sdim                                  CheckerContext &C) const {
333239462Sdim  const FunctionDecl *FD = C.getCalleeDecl(CE);
334239462Sdim  if (!FD || FD->getKind() != Decl::Function)
335239462Sdim    return;
336239462Sdim
337239462Sdim  StringRef FName = C.getCalleeName(FD);
338234353Sdim  if (FName.empty())
339218887Sdim    return;
340218887Sdim
341224145Sdim  SubChecker SC =
342234353Sdim    llvm::StringSwitch<SubChecker>(FName)
343224145Sdim      .Case("open", &UnixAPIChecker::CheckOpen)
344224145Sdim      .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
345234353Sdim      .Case("calloc", &UnixAPIChecker::CheckCallocZero)
346224145Sdim      .Case("malloc", &UnixAPIChecker::CheckMallocZero)
347234353Sdim      .Case("realloc", &UnixAPIChecker::CheckReallocZero)
348243830Sdim      .Case("reallocf", &UnixAPIChecker::CheckReallocfZero)
349234353Sdim      .Cases("alloca", "__builtin_alloca", &UnixAPIChecker::CheckAllocaZero)
350234353Sdim      .Case("valloc", &UnixAPIChecker::CheckVallocZero)
351224145Sdim      .Default(NULL);
352218887Sdim
353224145Sdim  if (SC)
354224145Sdim    (this->*SC)(C, CE);
355218887Sdim}
356219077Sdim
357219077Sdim//===----------------------------------------------------------------------===//
358219077Sdim// Registration.
359219077Sdim//===----------------------------------------------------------------------===//
360219077Sdim
361219077Sdimvoid ento::registerUnixAPIChecker(CheckerManager &mgr) {
362219077Sdim  mgr.registerChecker<UnixAPIChecker>();
363219077Sdim}
364