1218887Sdim// MacOSXAPIChecker.h - Checks proper use of various MacOS X 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 MacOSXAPIChecker, which is an assortment of checks on calls 11249423Sdim// to various, widely used Apple APIs. 12218887Sdim// 13218887Sdim// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated 14218887Sdim// to here, using the new Checker interface. 15218887Sdim// 16218887Sdim//===----------------------------------------------------------------------===// 17218887Sdim 18218887Sdim#include "ClangSACheckers.h" 19249423Sdim#include "clang/Basic/TargetInfo.h" 20249423Sdim#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 21221345Sdim#include "clang/StaticAnalyzer/Core/Checker.h" 22218887Sdim#include "clang/StaticAnalyzer/Core/CheckerManager.h" 23219077Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" 24226633Sdim#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" 25218887Sdim#include "llvm/ADT/SmallString.h" 26218887Sdim#include "llvm/ADT/StringSwitch.h" 27218887Sdim#include "llvm/Support/raw_ostream.h" 28218887Sdim 29218887Sdimusing namespace clang; 30218887Sdimusing namespace ento; 31218887Sdim 32218887Sdimnamespace { 33221345Sdimclass MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > { 34234353Sdim mutable OwningPtr<BugType> BT_dispatchOnce; 35218887Sdim 36218887Sdimpublic: 37224145Sdim void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; 38218887Sdim 39224145Sdim void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, 40234353Sdim StringRef FName) const; 41224145Sdim 42224145Sdim typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &, 43224145Sdim const CallExpr *, 44234353Sdim StringRef FName) const; 45218887Sdim}; 46218887Sdim} //end anonymous namespace 47218887Sdim 48218887Sdim//===----------------------------------------------------------------------===// 49218887Sdim// dispatch_once and dispatch_once_f 50218887Sdim//===----------------------------------------------------------------------===// 51218887Sdim 52224145Sdimvoid MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, 53234353Sdim StringRef FName) const { 54218887Sdim if (CE->getNumArgs() < 1) 55218887Sdim return; 56218887Sdim 57218887Sdim // Check if the first argument is stack allocated. If so, issue a warning 58218887Sdim // because that's likely to be bad news. 59234353Sdim ProgramStateRef state = C.getState(); 60234353Sdim const MemRegion *R = 61234353Sdim state->getSVal(CE->getArg(0), C.getLocationContext()).getAsRegion(); 62218887Sdim if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) 63218887Sdim return; 64218887Sdim 65218887Sdim ExplodedNode *N = C.generateSink(state); 66218887Sdim if (!N) 67218887Sdim return; 68218887Sdim 69224145Sdim if (!BT_dispatchOnce) 70224145Sdim BT_dispatchOnce.reset(new BugType("Improper use of 'dispatch_once'", 71249423Sdim "API Misuse (Apple)")); 72224145Sdim 73243830Sdim // Handle _dispatch_once. In some versions of the OS X SDK we have the case 74243830Sdim // that dispatch_once is a macro that wraps a call to _dispatch_once. 75243830Sdim // _dispatch_once is then a function which then calls the real dispatch_once. 76243830Sdim // Users do not care; they just want the warning at the top-level call. 77243830Sdim if (CE->getLocStart().isMacroID()) { 78243830Sdim StringRef TrimmedFName = FName.ltrim("_"); 79243830Sdim if (TrimmedFName != FName) 80243830Sdim FName = TrimmedFName; 81243830Sdim } 82243830Sdim 83234353Sdim SmallString<256> S; 84218887Sdim llvm::raw_svector_ostream os(S); 85234353Sdim os << "Call to '" << FName << "' uses"; 86218887Sdim if (const VarRegion *VR = dyn_cast<VarRegion>(R)) 87218887Sdim os << " the local variable '" << VR->getDecl()->getName() << '\''; 88218887Sdim else 89218887Sdim os << " stack allocated memory"; 90218887Sdim os << " for the predicate value. Using such transient memory for " 91218887Sdim "the predicate is potentially dangerous."; 92218887Sdim if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) 93218887Sdim os << " Perhaps you intended to declare the variable as 'static'?"; 94218887Sdim 95226633Sdim BugReport *report = new BugReport(*BT_dispatchOnce, os.str(), N); 96218887Sdim report->addRange(CE->getArg(0)->getSourceRange()); 97243830Sdim C.emitReport(report); 98218887Sdim} 99218887Sdim 100218887Sdim//===----------------------------------------------------------------------===// 101218887Sdim// Central dispatch function. 102218887Sdim//===----------------------------------------------------------------------===// 103218887Sdim 104219077Sdimvoid MacOSXAPIChecker::checkPreStmt(const CallExpr *CE, 105219077Sdim CheckerContext &C) const { 106234353Sdim StringRef Name = C.getCalleeName(CE); 107234353Sdim if (Name.empty()) 108218887Sdim return; 109218887Sdim 110224145Sdim SubChecker SC = 111234353Sdim llvm::StringSwitch<SubChecker>(Name) 112243830Sdim .Cases("dispatch_once", 113243830Sdim "_dispatch_once", 114243830Sdim "dispatch_once_f", 115224145Sdim &MacOSXAPIChecker::CheckDispatchOnce) 116224145Sdim .Default(NULL); 117218887Sdim 118224145Sdim if (SC) 119234353Sdim (this->*SC)(C, CE, Name); 120218887Sdim} 121219077Sdim 122219077Sdim//===----------------------------------------------------------------------===// 123219077Sdim// Registration. 124219077Sdim//===----------------------------------------------------------------------===// 125219077Sdim 126219077Sdimvoid ento::registerMacOSXAPIChecker(CheckerManager &mgr) { 127219077Sdim mgr.registerChecker<MacOSXAPIChecker>(); 128219077Sdim} 129