UnixAPIChecker.cpp revision 219077
1199482Srdivacky//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-//
2199482Srdivacky//
3199482Srdivacky//                     The LLVM Compiler Infrastructure
4199482Srdivacky//
5199482Srdivacky// This file is distributed under the University of Illinois Open Source
6199482Srdivacky// License. See LICENSE.TXT for details.
7199482Srdivacky//
8199482Srdivacky//===----------------------------------------------------------------------===//
9199482Srdivacky//
10199482Srdivacky// This defines UnixAPIChecker, which is an assortment of checks on calls
11212904Sdim// to various, widely used UNIX/Posix functions.
12199482Srdivacky//
13218893Sdim//===----------------------------------------------------------------------===//
14199482Srdivacky
15199482Srdivacky#include "ClangSACheckers.h"
16199482Srdivacky#include "clang/StaticAnalyzer/Core/CheckerV2.h"
17199482Srdivacky#include "clang/StaticAnalyzer/Core/CheckerManager.h"
18199482Srdivacky#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
19218893Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
20218893Sdim#include "clang/Basic/TargetInfo.h"
21212904Sdim#include "llvm/ADT/Optional.h"
22218893Sdim#include "llvm/ADT/StringSwitch.h"
23221345Sdim#include <fcntl.h>
24199482Srdivacky
25199482Srdivackyusing namespace clang;
26199482Srdivackyusing namespace ento;
27199482Srdivackyusing llvm::Optional;
28199482Srdivacky
29199482Srdivackynamespace {
30218893Sdimclass UnixAPIChecker : public CheckerV2< check::PreStmt<CallExpr> > {
31218893Sdim  enum SubChecks {
32218893Sdim    OpenFn = 0,
33218893Sdim    PthreadOnceFn = 1,
34218893Sdim    MallocZero = 2,
35218893Sdim    NumChecks
36218893Sdim  };
37218893Sdim
38218893Sdim  mutable BugType *BTypes[NumChecks];
39218893Sdim
40218893Sdimpublic:
41218893Sdim  mutable Optional<uint64_t> Val_O_CREAT;
42218893Sdim
43218893Sdimpublic:
44218893Sdim  UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); }
45218893Sdim  ~UnixAPIChecker() {
46218893Sdim    for (unsigned i=0; i != NumChecks; ++i)
47218893Sdim      delete BTypes[i];
48218893Sdim  }
49218893Sdim
50218893Sdim  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
51218893Sdim};
52218893Sdim} //end anonymous namespace
53218893Sdim
54218893Sdim//===----------------------------------------------------------------------===//
55218893Sdim// Utility functions.
56218893Sdim//===----------------------------------------------------------------------===//
57218893Sdim
58218893Sdimstatic inline void LazyInitialize(BugType *&BT, const char *name) {
59218893Sdim  if (BT)
60218893Sdim    return;
61218893Sdim  BT = new BugType(name, "Unix API");
62218893Sdim}
63218893Sdim
64218893Sdim//===----------------------------------------------------------------------===//
65218893Sdim// "open" (man 2 open)
66218893Sdim//===----------------------------------------------------------------------===//
67218893Sdim
68218893Sdimstatic void CheckOpen(CheckerContext &C, const UnixAPIChecker &UC,
69218893Sdim                      const CallExpr *CE, BugType *&BT) {
70218893Sdim  // The definition of O_CREAT is platform specific.  We need a better way
71218893Sdim  // of querying this information from the checking environment.
72218893Sdim  if (!UC.Val_O_CREAT.hasValue()) {
73218893Sdim    if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple)
74218893Sdim      UC.Val_O_CREAT = 0x0200;
75218893Sdim    else {
76218893Sdim      // FIXME: We need a more general way of getting the O_CREAT value.
77218893Sdim      // We could possibly grovel through the preprocessor state, but
78218893Sdim      // that would require passing the Preprocessor object to the ExprEngine.
79218893Sdim      return;
80218893Sdim    }
81199990Srdivacky  }
82199482Srdivacky
83199482Srdivacky  LazyInitialize(BT, "Improper use of 'open'");
84199482Srdivacky
85210299Sed  // Look at the 'oflags' argument for the O_CREAT flag.
86210299Sed  const GRState *state = C.getState();
87199482Srdivacky
88210299Sed  if (CE->getNumArgs() < 2) {
89199482Srdivacky    // The frontend should issue a warning for this case, so this is a sanity
90199482Srdivacky    // check.
91199482Srdivacky    return;
92218893Sdim  }
93218893Sdim
94218893Sdim  // Now check if oflags has O_CREAT set.
95218893Sdim  const Expr *oflagsEx = CE->getArg(1);
96218893Sdim  const SVal V = state->getSVal(oflagsEx);
97218893Sdim  if (!isa<NonLoc>(V)) {
98218893Sdim    // The case where 'V' can be a location can only be due to a bad header,
99218893Sdim    // so in this case bail out.
100218893Sdim    return;
101218893Sdim  }
102218893Sdim  NonLoc oflags = cast<NonLoc>(V);
103218893Sdim  NonLoc ocreateFlag =
104218893Sdim    cast<NonLoc>(C.getSValBuilder().makeIntVal(UC.Val_O_CREAT.getValue(),
105218893Sdim                                                oflagsEx->getType()));
106218893Sdim  SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
107218893Sdim                                                      oflags, ocreateFlag,
108218893Sdim                                                      oflagsEx->getType());
109218893Sdim  if (maskedFlagsUC.isUnknownOrUndef())
110218893Sdim    return;
111218893Sdim  DefinedSVal maskedFlags = cast<DefinedSVal>(maskedFlagsUC);
112218893Sdim
113218893Sdim  // Check if maskedFlags is non-zero.
114218893Sdim  const GRState *trueState, *falseState;
115218893Sdim  llvm::tie(trueState, falseState) = state->assume(maskedFlags);
116218893Sdim
117218893Sdim  // Only emit an error if the value of 'maskedFlags' is properly
118218893Sdim  // constrained;
119218893Sdim  if (!(trueState && !falseState))
120218893Sdim    return;
121218893Sdim
122218893Sdim  if (CE->getNumArgs() < 3) {
123218893Sdim    ExplodedNode *N = C.generateSink(trueState);
124218893Sdim    if (!N)
125199482Srdivacky      return;
126199482Srdivacky
127210299Sed    EnhancedBugReport *report =
128199482Srdivacky      new EnhancedBugReport(*BT,
129199482Srdivacky                            "Call to 'open' requires a third argument when "
130210299Sed                            "the 'O_CREAT' flag is set", N);
131199482Srdivacky    report->addRange(oflagsEx->getSourceRange());
132199482Srdivacky    C.EmitReport(report);
133199482Srdivacky  }
134199482Srdivacky}
135210299Sed
136199482Srdivacky//===----------------------------------------------------------------------===//
137199482Srdivacky// pthread_once
138210299Sed//===----------------------------------------------------------------------===//
139210299Sed
140199482Srdivackystatic void CheckPthreadOnce(CheckerContext &C, const UnixAPIChecker &,
141206275Srdivacky                             const CallExpr *CE, BugType *&BT) {
142199482Srdivacky
143218893Sdim  // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
144218893Sdim  // They can possibly be refactored.
145200583Srdivacky
146199482Srdivacky  LazyInitialize(BT, "Improper use of 'pthread_once'");
147199482Srdivacky
148210299Sed  if (CE->getNumArgs() < 1)
149199482Srdivacky    return;
150199482Srdivacky
151199482Srdivacky  // Check if the first argument is stack allocated.  If so, issue a warning
152199482Srdivacky  // because that's likely to be bad news.
153199482Srdivacky  const GRState *state = C.getState();
154199482Srdivacky  const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion();
155199482Srdivacky  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
156199482Srdivacky    return;
157199482Srdivacky
158199482Srdivacky  ExplodedNode *N = C.generateSink(state);
159199482Srdivacky  if (!N)
160199482Srdivacky    return;
161199482Srdivacky
162218893Sdim  llvm::SmallString<256> S;
163199482Srdivacky  llvm::raw_svector_ostream os(S);
164199482Srdivacky  os << "Call to 'pthread_once' uses";
165199482Srdivacky  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
166199482Srdivacky    os << " the local variable '" << VR->getDecl()->getName() << '\'';
167199482Srdivacky  else
168199482Srdivacky    os << " stack allocated memory";
169210299Sed  os << " for the \"control\" value.  Using such transient memory for "
170210299Sed  "the control value is potentially dangerous.";
171210299Sed  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
172210299Sed    os << "  Perhaps you intended to declare the variable as 'static'?";
173218893Sdim
174210299Sed  EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N);
175210299Sed  report->addRange(CE->getArg(0)->getSourceRange());
176210299Sed  C.EmitReport(report);
177210299Sed}
178210299Sed
179210299Sed//===----------------------------------------------------------------------===//
180210299Sed// "malloc" with allocation size 0
181210299Sed//===----------------------------------------------------------------------===//
182210299Sed
183210299Sed// FIXME: Eventually this should be rolled into the MallocChecker, but this
184210299Sed// check is more basic and is valuable for widespread use.
185210299Sedstatic void CheckMallocZero(CheckerContext &C, const UnixAPIChecker &UC,
186210299Sed                            const CallExpr *CE, BugType *&BT) {
187210299Sed
188210299Sed  // Sanity check that malloc takes one argument.
189210299Sed  if (CE->getNumArgs() != 1)
190210299Sed    return;
191210299Sed
192210299Sed  // Check if the allocation size is 0.
193199482Srdivacky  const GRState *state = C.getState();
194199482Srdivacky  SVal argVal = state->getSVal(CE->getArg(0));
195199482Srdivacky
196199482Srdivacky  if (argVal.isUnknownOrUndef())
197199482Srdivacky    return;
198199482Srdivacky
199199482Srdivacky  const GRState *trueState, *falseState;
200199482Srdivacky  llvm::tie(trueState, falseState) = state->assume(cast<DefinedSVal>(argVal));
201199482Srdivacky
202199482Srdivacky  // Is the value perfectly constrained to zero?
203199482Srdivacky  if (falseState && !trueState) {
204199482Srdivacky    ExplodedNode *N = C.generateSink(falseState);
205199482Srdivacky    if (!N)
206218893Sdim      return;
207218893Sdim
208218893Sdim    // FIXME: Add reference to CERT advisory, and/or C99 standard in bug
209218893Sdim    // output.
210212904Sdim
211218893Sdim    LazyInitialize(BT, "Undefined allocation of 0 bytes");
212218893Sdim
213221345Sdim    EnhancedBugReport *report =
214221345Sdim      new EnhancedBugReport(*BT, "Call to 'malloc' has an allocation size"
215221345Sdim                                 " of 0 bytes", N);
216221345Sdim    report->addRange(CE->getArg(0)->getSourceRange());
217221345Sdim    report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue,
218221345Sdim                              CE->getArg(0));
219221345Sdim    C.EmitReport(report);
220221345Sdim    return;
221221345Sdim  }
222221345Sdim  // Assume the the value is non-zero going forward.
223199482Srdivacky  assert(trueState);
224218893Sdim  if (trueState != state) {
225218893Sdim    C.addTransition(trueState);
226218893Sdim  }
227218893Sdim}
228218893Sdim
229218893Sdim//===----------------------------------------------------------------------===//
230218893Sdim// Central dispatch function.
231218893Sdim//===----------------------------------------------------------------------===//
232218893Sdim
233199482Srdivackytypedef void (*SubChecker)(CheckerContext &C, const UnixAPIChecker &UC,
234212904Sdim                           const CallExpr *CE, BugType *&BT);
235212904Sdimnamespace {
236218893Sdim  class SubCheck {
237218893Sdim    SubChecker SC;
238199482Srdivacky    const UnixAPIChecker *UC;
239199482Srdivacky    BugType **BT;
240199482Srdivacky  public:
241210299Sed    SubCheck(SubChecker sc, const UnixAPIChecker *uc, BugType *& bt)
242212904Sdim      : SC(sc), UC(uc), BT(&bt) {}
243210299Sed    SubCheck() : SC(NULL), UC(NULL), BT(NULL) {}
244210299Sed
245199482Srdivacky    void run(CheckerContext &C, const CallExpr *CE) const {
246199482Srdivacky      if (SC)
247199482Srdivacky        SC(C, *UC, CE, *BT);
248199482Srdivacky    }
249199482Srdivacky  };
250199482Srdivacky} // end anonymous namespace
251199482Srdivacky
252218893Sdimvoid UnixAPIChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
253199482Srdivacky  // Get the callee.  All the functions we care about are C functions
254199482Srdivacky  // with simple identifiers.
255199482Srdivacky  const GRState *state = C.getState();
256199482Srdivacky  const Expr *Callee = CE->getCallee();
257199482Srdivacky  const FunctionTextRegion *Fn =
258199482Srdivacky    dyn_cast_or_null<FunctionTextRegion>(state->getSVal(Callee).getAsRegion());
259199482Srdivacky
260199482Srdivacky  if (!Fn)
261221345Sdim    return;
262221345Sdim
263221345Sdim  const IdentifierInfo *FI = Fn->getDecl()->getIdentifier();
264221345Sdim  if (!FI)
265199482Srdivacky    return;
266199482Srdivacky
267199482Srdivacky  const SubCheck &SC =
268210299Sed    llvm::StringSwitch<SubCheck>(FI->getName())
269199482Srdivacky      .Case("open",
270199482Srdivacky            SubCheck(CheckOpen, this, BTypes[OpenFn]))
271199482Srdivacky      .Case("pthread_once",
272199482Srdivacky            SubCheck(CheckPthreadOnce, this, BTypes[PthreadOnceFn]))
273199482Srdivacky      .Case("malloc",
274199482Srdivacky            SubCheck(CheckMallocZero, this, BTypes[MallocZero]))
275199482Srdivacky      .Default(SubCheck());
276199482Srdivacky
277199482Srdivacky  SC.run(C, CE);
278199482Srdivacky}
279199482Srdivacky
280199482Srdivacky//===----------------------------------------------------------------------===//
281199482Srdivacky// Registration.
282199482Srdivacky//===----------------------------------------------------------------------===//
283199482Srdivacky
284199482Srdivackyvoid ento::registerUnixAPIChecker(CheckerManager &mgr) {
285206275Srdivacky  mgr.registerChecker<UnixAPIChecker>();
286199482Srdivacky}
287199482Srdivacky