xref: /netbsd-src/external/apache2/llvm/dist/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp (revision e038c9c4676b0f19b1b7dd08a940c6ed64a6d5ae)
17330f729Sjoerg // MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-//
27330f729Sjoerg //
37330f729Sjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
47330f729Sjoerg // See https://llvm.org/LICENSE.txt for license information.
57330f729Sjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
67330f729Sjoerg //
77330f729Sjoerg //===----------------------------------------------------------------------===//
87330f729Sjoerg //
97330f729Sjoerg // This defines MacOSXAPIChecker, which is an assortment of checks on calls
107330f729Sjoerg // to various, widely used Apple APIs.
117330f729Sjoerg //
127330f729Sjoerg // FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated
137330f729Sjoerg // to here, using the new Checker interface.
147330f729Sjoerg //
157330f729Sjoerg //===----------------------------------------------------------------------===//
167330f729Sjoerg 
17*e038c9c4Sjoerg #include "clang/AST/Attr.h"
187330f729Sjoerg #include "clang/Basic/TargetInfo.h"
19*e038c9c4Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
207330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
217330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
227330f729Sjoerg #include "clang/StaticAnalyzer/Core/CheckerManager.h"
237330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
247330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
257330f729Sjoerg #include "llvm/ADT/SmallString.h"
267330f729Sjoerg #include "llvm/ADT/StringSwitch.h"
277330f729Sjoerg #include "llvm/Support/raw_ostream.h"
287330f729Sjoerg 
297330f729Sjoerg using namespace clang;
307330f729Sjoerg using namespace ento;
317330f729Sjoerg 
327330f729Sjoerg namespace {
337330f729Sjoerg class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
347330f729Sjoerg   mutable std::unique_ptr<BugType> BT_dispatchOnce;
357330f729Sjoerg 
367330f729Sjoerg   static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R);
377330f729Sjoerg 
387330f729Sjoerg public:
397330f729Sjoerg   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
407330f729Sjoerg 
417330f729Sjoerg   void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
427330f729Sjoerg                          StringRef FName) const;
437330f729Sjoerg 
447330f729Sjoerg   typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &,
457330f729Sjoerg                                                const CallExpr *,
467330f729Sjoerg                                                StringRef FName) const;
477330f729Sjoerg };
487330f729Sjoerg } //end anonymous namespace
497330f729Sjoerg 
507330f729Sjoerg //===----------------------------------------------------------------------===//
517330f729Sjoerg // dispatch_once and dispatch_once_f
527330f729Sjoerg //===----------------------------------------------------------------------===//
537330f729Sjoerg 
547330f729Sjoerg const ObjCIvarRegion *
getParentIvarRegion(const MemRegion * R)557330f729Sjoerg MacOSXAPIChecker::getParentIvarRegion(const MemRegion *R) {
567330f729Sjoerg   const SubRegion *SR = dyn_cast<SubRegion>(R);
577330f729Sjoerg   while (SR) {
587330f729Sjoerg     if (const ObjCIvarRegion *IR = dyn_cast<ObjCIvarRegion>(SR))
597330f729Sjoerg       return IR;
607330f729Sjoerg     SR = dyn_cast<SubRegion>(SR->getSuperRegion());
617330f729Sjoerg   }
627330f729Sjoerg   return nullptr;
637330f729Sjoerg }
647330f729Sjoerg 
CheckDispatchOnce(CheckerContext & C,const CallExpr * CE,StringRef FName) const657330f729Sjoerg void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
667330f729Sjoerg                                          StringRef FName) const {
677330f729Sjoerg   if (CE->getNumArgs() < 1)
687330f729Sjoerg     return;
697330f729Sjoerg 
707330f729Sjoerg   // Check if the first argument is improperly allocated.  If so, issue a
717330f729Sjoerg   // warning because that's likely to be bad news.
727330f729Sjoerg   const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
737330f729Sjoerg   if (!R)
747330f729Sjoerg     return;
757330f729Sjoerg 
767330f729Sjoerg   // Global variables are fine.
777330f729Sjoerg   const MemRegion *RB = R->getBaseRegion();
787330f729Sjoerg   const MemSpaceRegion *RS = RB->getMemorySpace();
797330f729Sjoerg   if (isa<GlobalsSpaceRegion>(RS))
807330f729Sjoerg     return;
817330f729Sjoerg 
827330f729Sjoerg   // Handle _dispatch_once.  In some versions of the OS X SDK we have the case
837330f729Sjoerg   // that dispatch_once is a macro that wraps a call to _dispatch_once.
847330f729Sjoerg   // _dispatch_once is then a function which then calls the real dispatch_once.
857330f729Sjoerg   // Users do not care; they just want the warning at the top-level call.
867330f729Sjoerg   if (CE->getBeginLoc().isMacroID()) {
877330f729Sjoerg     StringRef TrimmedFName = FName.ltrim('_');
887330f729Sjoerg     if (TrimmedFName != FName)
897330f729Sjoerg       FName = TrimmedFName;
907330f729Sjoerg   }
917330f729Sjoerg 
927330f729Sjoerg   SmallString<256> S;
937330f729Sjoerg   llvm::raw_svector_ostream os(S);
947330f729Sjoerg   bool SuggestStatic = false;
957330f729Sjoerg   os << "Call to '" << FName << "' uses";
967330f729Sjoerg   if (const VarRegion *VR = dyn_cast<VarRegion>(RB)) {
977330f729Sjoerg     const VarDecl *VD = VR->getDecl();
987330f729Sjoerg     // FIXME: These should have correct memory space and thus should be filtered
997330f729Sjoerg     // out earlier. This branch only fires when we're looking from a block,
1007330f729Sjoerg     // which we analyze as a top-level declaration, onto a static local
1017330f729Sjoerg     // in a function that contains the block.
1027330f729Sjoerg     if (VD->isStaticLocal())
1037330f729Sjoerg       return;
1047330f729Sjoerg     // We filtered out globals earlier, so it must be a local variable
1057330f729Sjoerg     // or a block variable which is under UnknownSpaceRegion.
1067330f729Sjoerg     if (VR != R)
1077330f729Sjoerg       os << " memory within";
1087330f729Sjoerg     if (VD->hasAttr<BlocksAttr>())
1097330f729Sjoerg       os << " the block variable '";
1107330f729Sjoerg     else
1117330f729Sjoerg       os << " the local variable '";
1127330f729Sjoerg     os << VR->getDecl()->getName() << '\'';
1137330f729Sjoerg     SuggestStatic = true;
1147330f729Sjoerg   } else if (const ObjCIvarRegion *IVR = getParentIvarRegion(R)) {
1157330f729Sjoerg     if (IVR != R)
1167330f729Sjoerg       os << " memory within";
1177330f729Sjoerg     os << " the instance variable '" << IVR->getDecl()->getName() << '\'';
1187330f729Sjoerg   } else if (isa<HeapSpaceRegion>(RS)) {
1197330f729Sjoerg     os << " heap-allocated memory";
1207330f729Sjoerg   } else if (isa<UnknownSpaceRegion>(RS)) {
1217330f729Sjoerg     // Presence of an IVar superregion has priority over this branch, because
1227330f729Sjoerg     // ObjC objects are on the heap even if the core doesn't realize this.
1237330f729Sjoerg     // Presence of a block variable base region has priority over this branch,
1247330f729Sjoerg     // because block variables are known to be either on stack or on heap
1257330f729Sjoerg     // (might actually move between the two, hence UnknownSpace).
1267330f729Sjoerg     return;
1277330f729Sjoerg   } else {
1287330f729Sjoerg     os << " stack allocated memory";
1297330f729Sjoerg   }
1307330f729Sjoerg   os << " for the predicate value.  Using such transient memory for "
1317330f729Sjoerg         "the predicate is potentially dangerous.";
1327330f729Sjoerg   if (SuggestStatic)
1337330f729Sjoerg     os << "  Perhaps you intended to declare the variable as 'static'?";
1347330f729Sjoerg 
1357330f729Sjoerg   ExplodedNode *N = C.generateErrorNode();
1367330f729Sjoerg   if (!N)
1377330f729Sjoerg     return;
1387330f729Sjoerg 
1397330f729Sjoerg   if (!BT_dispatchOnce)
1407330f729Sjoerg     BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
1417330f729Sjoerg                                       "API Misuse (Apple)"));
1427330f729Sjoerg 
1437330f729Sjoerg   auto report =
1447330f729Sjoerg       std::make_unique<PathSensitiveBugReport>(*BT_dispatchOnce, os.str(), N);
1457330f729Sjoerg   report->addRange(CE->getArg(0)->getSourceRange());
1467330f729Sjoerg   C.emitReport(std::move(report));
1477330f729Sjoerg }
1487330f729Sjoerg 
1497330f729Sjoerg //===----------------------------------------------------------------------===//
1507330f729Sjoerg // Central dispatch function.
1517330f729Sjoerg //===----------------------------------------------------------------------===//
1527330f729Sjoerg 
checkPreStmt(const CallExpr * CE,CheckerContext & C) const1537330f729Sjoerg void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
1547330f729Sjoerg                                     CheckerContext &C) const {
1557330f729Sjoerg   StringRef Name = C.getCalleeName(CE);
1567330f729Sjoerg   if (Name.empty())
1577330f729Sjoerg     return;
1587330f729Sjoerg 
1597330f729Sjoerg   SubChecker SC =
1607330f729Sjoerg     llvm::StringSwitch<SubChecker>(Name)
1617330f729Sjoerg       .Cases("dispatch_once",
1627330f729Sjoerg              "_dispatch_once",
1637330f729Sjoerg              "dispatch_once_f",
1647330f729Sjoerg              &MacOSXAPIChecker::CheckDispatchOnce)
1657330f729Sjoerg       .Default(nullptr);
1667330f729Sjoerg 
1677330f729Sjoerg   if (SC)
1687330f729Sjoerg     (this->*SC)(C, CE, Name);
1697330f729Sjoerg }
1707330f729Sjoerg 
1717330f729Sjoerg //===----------------------------------------------------------------------===//
1727330f729Sjoerg // Registration.
1737330f729Sjoerg //===----------------------------------------------------------------------===//
1747330f729Sjoerg 
registerMacOSXAPIChecker(CheckerManager & mgr)1757330f729Sjoerg void ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
1767330f729Sjoerg   mgr.registerChecker<MacOSXAPIChecker>();
1777330f729Sjoerg }
1787330f729Sjoerg 
shouldRegisterMacOSXAPIChecker(const CheckerManager & mgr)179*e038c9c4Sjoerg bool ento::shouldRegisterMacOSXAPIChecker(const CheckerManager &mgr) {
1807330f729Sjoerg   return true;
1817330f729Sjoerg }
182