1df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek// MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-//
2df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//
3df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//                     The LLVM Compiler Infrastructure
4df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//
5df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek// This file is distributed under the University of Illinois Open Source
6df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek// License. See LICENSE.TXT for details.
7df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//
8df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//===----------------------------------------------------------------------===//
9df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//
10df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek// This defines MacOSXAPIChecker, which is an assortment of checks on calls
11df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek// to various, widely used Mac OS X functions.
12df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//
13df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated
14df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek// to here, using the new Checker interface.
15df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//
16df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//===----------------------------------------------------------------------===//
17df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
18027a6abdd6cedc0b8203da72eed6d15c796dce9dArgyrios Kyrtzidis#include "ClangSACheckers.h"
1955fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/Basic/TargetInfo.h"
2055fc873017f10f6f566b182b70f6fc22aefa3464Chandler Carruth#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21ec8605f1d7ec846dbf51047bfd5c56d32d1ff91cArgyrios Kyrtzidis#include "clang/StaticAnalyzer/Core/Checker.h"
22695fb502825a53ccd178ec1c85c77929d88acb71Argyrios Kyrtzidis#include "clang/StaticAnalyzer/Core/CheckerManager.h"
23983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
2418c66fdc3c4008d335885695fe36fb5353c5f672Ted Kremenek#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
25df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek#include "llvm/ADT/SmallString.h"
26df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek#include "llvm/ADT/StringSwitch.h"
27df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek#include "llvm/Support/raw_ostream.h"
28df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
29df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenekusing namespace clang;
309ef6537a894c33003359b1f9b9676e9178e028b7Ted Kremenekusing namespace ento;
31df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
32df61b580cea757cc72723ae95c1d5da603129f2cTed Kremeneknamespace {
33ec8605f1d7ec846dbf51047bfd5c56d32d1ff91cArgyrios Kyrtzidisclass MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
346f42b62b6194f53bcbc349f5d17388e1936535d7Dylan Noblesmith  mutable OwningPtr<BugType> BT_dispatchOnce;
35df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
36df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenekpublic:
37983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis  void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
3857964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose
3957964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose  void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
40b805c8ff133ef0c62df032fa711d6b13c5afd7f4Anna Zaks                         StringRef FName) const;
4157964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose
4257964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose  typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &,
4357964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose                                               const CallExpr *,
44b805c8ff133ef0c62df032fa711d6b13c5afd7f4Anna Zaks                                               StringRef FName) const;
45df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek};
46df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek} //end anonymous namespace
47df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
48df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//===----------------------------------------------------------------------===//
49df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek// dispatch_once and dispatch_once_f
50df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//===----------------------------------------------------------------------===//
51df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
5257964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rosevoid MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
53b805c8ff133ef0c62df032fa711d6b13c5afd7f4Anna Zaks                                         StringRef FName) const {
54df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  if (CE->getNumArgs() < 1)
55df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek    return;
56df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
57df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  // Check if the first argument is stack allocated.  If so, issue a warning
58df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  // because that's likely to be bad news.
598bef8238181a30e52dea380789a7e2d760eac532Ted Kremenek  ProgramStateRef state = C.getState();
605eca482fe895ea57bc82410222e6426c09e63284Ted Kremenek  const MemRegion *R =
615eca482fe895ea57bc82410222e6426c09e63284Ted Kremenek    state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion();
62df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
63df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek    return;
64df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
65d048c6ef5b6cfaa0cecb8cc1d4bdace32ed21d07Ted Kremenek  ExplodedNode *N = C.generateSink(state);
66df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  if (!N)
67df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek    return;
68df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
6957964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose  if (!BT_dispatchOnce)
7057964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose    BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'",
7157964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose                                      "Mac OS X API"));
7257964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose
7345b76bad757d8b9f93df2b21ca012c309810d206Ted Kremenek  // Handle _dispatch_once.  In some versions of the OS X SDK we have the case
7445b76bad757d8b9f93df2b21ca012c309810d206Ted Kremenek  // that dispatch_once is a macro that wraps a call to _dispatch_once.
7545b76bad757d8b9f93df2b21ca012c309810d206Ted Kremenek  // _dispatch_once is then a function which then calls the real dispatch_once.
7645b76bad757d8b9f93df2b21ca012c309810d206Ted Kremenek  // Users do not care; they just want the warning at the top-level call.
77be879727893994532b4a643bfae6fb656742057fTed Kremenek  if (CE->getLocStart().isMacroID()) {
78be879727893994532b4a643bfae6fb656742057fTed Kremenek    StringRef TrimmedFName = FName.ltrim("_");
79be879727893994532b4a643bfae6fb656742057fTed Kremenek    if (TrimmedFName != FName)
80be879727893994532b4a643bfae6fb656742057fTed Kremenek      FName = TrimmedFName;
81be879727893994532b4a643bfae6fb656742057fTed Kremenek  }
82be879727893994532b4a643bfae6fb656742057fTed Kremenek
83f7ccbad5d9949e7ddd1cbef43d482553b811e026Dylan Noblesmith  SmallString<256> S;
84df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  llvm::raw_svector_ostream os(S);
85b805c8ff133ef0c62df032fa711d6b13c5afd7f4Anna Zaks  os << "Call to '" << FName << "' uses";
86df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  if (const VarRegion *VR = dyn_cast<VarRegion>(R))
87df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek    os << " the local variable '" << VR->getDecl()->getName() << '\'';
88df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  else
89df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek    os << " stack allocated memory";
90df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  os << " for the predicate value.  Using such transient memory for "
91df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek        "the predicate is potentially dangerous.";
92df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
93df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek    os << "  Perhaps you intended to declare the variable as 'static'?";
94df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
95e172e8b9e7fc67d7d03589af7e92fe777afcf33aAnna Zaks  BugReport *report = new BugReport(*BT_dispatchOnce, os.str(), N);
96df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek  report->addRange(CE->getArg(0)->getSourceRange());
97785950e59424dca7ce0081bebf13c0acd2c4fff6Jordan Rose  C.emitReport(report);
98df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek}
99df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
100df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//===----------------------------------------------------------------------===//
101df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek// Central dispatch function.
102df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek//===----------------------------------------------------------------------===//
103df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
104983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidisvoid MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
105983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis                                    CheckerContext &C) const {
106b805c8ff133ef0c62df032fa711d6b13c5afd7f4Anna Zaks  StringRef Name = C.getCalleeName(CE);
107b805c8ff133ef0c62df032fa711d6b13c5afd7f4Anna Zaks  if (Name.empty())
108df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek    return;
109df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
11057964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose  SubChecker SC =
111b805c8ff133ef0c62df032fa711d6b13c5afd7f4Anna Zaks    llvm::StringSwitch<SubChecker>(Name)
112be879727893994532b4a643bfae6fb656742057fTed Kremenek      .Cases("dispatch_once",
113be879727893994532b4a643bfae6fb656742057fTed Kremenek             "_dispatch_once",
114be879727893994532b4a643bfae6fb656742057fTed Kremenek             "dispatch_once_f",
11557964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose             &MacOSXAPIChecker::CheckDispatchOnce)
11657964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose      .Default(NULL);
117df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek
11857964bda54c9b1e10090cae94d776a6b9b7eca33Jordy Rose  if (SC)
119b805c8ff133ef0c62df032fa711d6b13c5afd7f4Anna Zaks    (this->*SC)(C, CE, Name);
120df61b580cea757cc72723ae95c1d5da603129f2cTed Kremenek}
121983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis
122983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis//===----------------------------------------------------------------------===//
123983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis// Registration.
124983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis//===----------------------------------------------------------------------===//
125983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis
126983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidisvoid ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
127983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis  mgr.registerChecker<MacOSXAPIChecker>();
128983326f32c746f5e47161a73758e4d363263dd2cArgyrios Kyrtzidis}
129