xref: /freebsd-src/contrib/llvm-project/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp (revision 647cbc5de815c5651677bf8582797f716ec7b48d)
10b57cec5SDimitry Andric // MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-//
20b57cec5SDimitry Andric //
30b57cec5SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
40b57cec5SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
50b57cec5SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
60b57cec5SDimitry Andric //
70b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
80b57cec5SDimitry Andric //
90b57cec5SDimitry Andric // This defines MacOSXAPIChecker, which is an assortment of checks on calls
100b57cec5SDimitry Andric // to various, widely used Apple APIs.
110b57cec5SDimitry Andric //
120b57cec5SDimitry Andric // FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated
130b57cec5SDimitry Andric // to here, using the new Checker interface.
140b57cec5SDimitry Andric //
150b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
160b57cec5SDimitry Andric 
17480093f4SDimitry Andric #include "clang/AST/Attr.h"
180b57cec5SDimitry Andric #include "clang/Basic/TargetInfo.h"
19480093f4SDimitry Andric #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
200b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
21*647cbc5dSDimitry Andric #include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
220b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/Checker.h"
230b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/CheckerManager.h"
240b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
250b57cec5SDimitry Andric #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
260b57cec5SDimitry Andric #include "llvm/ADT/SmallString.h"
270b57cec5SDimitry Andric #include "llvm/ADT/StringSwitch.h"
280b57cec5SDimitry Andric #include "llvm/Support/raw_ostream.h"
290b57cec5SDimitry Andric 
300b57cec5SDimitry Andric using namespace clang;
310b57cec5SDimitry Andric using namespace ento;
320b57cec5SDimitry Andric 
330b57cec5SDimitry Andric namespace {
340b57cec5SDimitry Andric class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
35*647cbc5dSDimitry Andric   const BugType BT_dispatchOnce{this, "Improper use of 'dispatch_once'",
36*647cbc5dSDimitry Andric                                 categories::AppleAPIMisuse};
370b57cec5SDimitry Andric 
380b57cec5SDimitry Andric   static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R);
390b57cec5SDimitry Andric 
400b57cec5SDimitry Andric public:
410b57cec5SDimitry Andric   void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
420b57cec5SDimitry Andric 
430b57cec5SDimitry Andric   void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
440b57cec5SDimitry Andric                          StringRef FName) const;
450b57cec5SDimitry Andric 
460b57cec5SDimitry Andric   typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &,
470b57cec5SDimitry Andric                                                const CallExpr *,
480b57cec5SDimitry Andric                                                StringRef FName) const;
490b57cec5SDimitry Andric };
500b57cec5SDimitry Andric } //end anonymous namespace
510b57cec5SDimitry Andric 
520b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
530b57cec5SDimitry Andric // dispatch_once and dispatch_once_f
540b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
550b57cec5SDimitry Andric 
560b57cec5SDimitry Andric const ObjCIvarRegion *
getParentIvarRegion(const MemRegion * R)570b57cec5SDimitry Andric MacOSXAPIChecker::getParentIvarRegion(const MemRegion *R) {
580b57cec5SDimitry Andric   const SubRegion *SR = dyn_cast<SubRegion>(R);
590b57cec5SDimitry Andric   while (SR) {
600b57cec5SDimitry Andric     if (const ObjCIvarRegion *IR = dyn_cast<ObjCIvarRegion>(SR))
610b57cec5SDimitry Andric       return IR;
620b57cec5SDimitry Andric     SR = dyn_cast<SubRegion>(SR->getSuperRegion());
630b57cec5SDimitry Andric   }
640b57cec5SDimitry Andric   return nullptr;
650b57cec5SDimitry Andric }
660b57cec5SDimitry Andric 
CheckDispatchOnce(CheckerContext & C,const CallExpr * CE,StringRef FName) const670b57cec5SDimitry Andric void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
680b57cec5SDimitry Andric                                          StringRef FName) const {
690b57cec5SDimitry Andric   if (CE->getNumArgs() < 1)
700b57cec5SDimitry Andric     return;
710b57cec5SDimitry Andric 
720b57cec5SDimitry Andric   // Check if the first argument is improperly allocated.  If so, issue a
730b57cec5SDimitry Andric   // warning because that's likely to be bad news.
740b57cec5SDimitry Andric   const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
750b57cec5SDimitry Andric   if (!R)
760b57cec5SDimitry Andric     return;
770b57cec5SDimitry Andric 
780b57cec5SDimitry Andric   // Global variables are fine.
790b57cec5SDimitry Andric   const MemRegion *RB = R->getBaseRegion();
800b57cec5SDimitry Andric   const MemSpaceRegion *RS = RB->getMemorySpace();
810b57cec5SDimitry Andric   if (isa<GlobalsSpaceRegion>(RS))
820b57cec5SDimitry Andric     return;
830b57cec5SDimitry Andric 
840b57cec5SDimitry Andric   // Handle _dispatch_once.  In some versions of the OS X SDK we have the case
850b57cec5SDimitry Andric   // that dispatch_once is a macro that wraps a call to _dispatch_once.
860b57cec5SDimitry Andric   // _dispatch_once is then a function which then calls the real dispatch_once.
870b57cec5SDimitry Andric   // Users do not care; they just want the warning at the top-level call.
880b57cec5SDimitry Andric   if (CE->getBeginLoc().isMacroID()) {
890b57cec5SDimitry Andric     StringRef TrimmedFName = FName.ltrim('_');
900b57cec5SDimitry Andric     if (TrimmedFName != FName)
910b57cec5SDimitry Andric       FName = TrimmedFName;
920b57cec5SDimitry Andric   }
930b57cec5SDimitry Andric 
940b57cec5SDimitry Andric   SmallString<256> S;
950b57cec5SDimitry Andric   llvm::raw_svector_ostream os(S);
960b57cec5SDimitry Andric   bool SuggestStatic = false;
970b57cec5SDimitry Andric   os << "Call to '" << FName << "' uses";
980b57cec5SDimitry Andric   if (const VarRegion *VR = dyn_cast<VarRegion>(RB)) {
990b57cec5SDimitry Andric     const VarDecl *VD = VR->getDecl();
1000b57cec5SDimitry Andric     // FIXME: These should have correct memory space and thus should be filtered
1010b57cec5SDimitry Andric     // out earlier. This branch only fires when we're looking from a block,
1020b57cec5SDimitry Andric     // which we analyze as a top-level declaration, onto a static local
1030b57cec5SDimitry Andric     // in a function that contains the block.
1040b57cec5SDimitry Andric     if (VD->isStaticLocal())
1050b57cec5SDimitry Andric       return;
1060b57cec5SDimitry Andric     // We filtered out globals earlier, so it must be a local variable
1070b57cec5SDimitry Andric     // or a block variable which is under UnknownSpaceRegion.
1080b57cec5SDimitry Andric     if (VR != R)
1090b57cec5SDimitry Andric       os << " memory within";
1100b57cec5SDimitry Andric     if (VD->hasAttr<BlocksAttr>())
1110b57cec5SDimitry Andric       os << " the block variable '";
1120b57cec5SDimitry Andric     else
1130b57cec5SDimitry Andric       os << " the local variable '";
1140b57cec5SDimitry Andric     os << VR->getDecl()->getName() << '\'';
1150b57cec5SDimitry Andric     SuggestStatic = true;
1160b57cec5SDimitry Andric   } else if (const ObjCIvarRegion *IVR = getParentIvarRegion(R)) {
1170b57cec5SDimitry Andric     if (IVR != R)
1180b57cec5SDimitry Andric       os << " memory within";
1190b57cec5SDimitry Andric     os << " the instance variable '" << IVR->getDecl()->getName() << '\'';
1200b57cec5SDimitry Andric   } else if (isa<HeapSpaceRegion>(RS)) {
1210b57cec5SDimitry Andric     os << " heap-allocated memory";
1220b57cec5SDimitry Andric   } else if (isa<UnknownSpaceRegion>(RS)) {
1230b57cec5SDimitry Andric     // Presence of an IVar superregion has priority over this branch, because
1240b57cec5SDimitry Andric     // ObjC objects are on the heap even if the core doesn't realize this.
1250b57cec5SDimitry Andric     // Presence of a block variable base region has priority over this branch,
1260b57cec5SDimitry Andric     // because block variables are known to be either on stack or on heap
1270b57cec5SDimitry Andric     // (might actually move between the two, hence UnknownSpace).
1280b57cec5SDimitry Andric     return;
1290b57cec5SDimitry Andric   } else {
1300b57cec5SDimitry Andric     os << " stack allocated memory";
1310b57cec5SDimitry Andric   }
1320b57cec5SDimitry Andric   os << " for the predicate value.  Using such transient memory for "
1330b57cec5SDimitry Andric         "the predicate is potentially dangerous.";
1340b57cec5SDimitry Andric   if (SuggestStatic)
1350b57cec5SDimitry Andric     os << "  Perhaps you intended to declare the variable as 'static'?";
1360b57cec5SDimitry Andric 
1370b57cec5SDimitry Andric   ExplodedNode *N = C.generateErrorNode();
1380b57cec5SDimitry Andric   if (!N)
1390b57cec5SDimitry Andric     return;
1400b57cec5SDimitry Andric 
141a7dea167SDimitry Andric   auto report =
142*647cbc5dSDimitry Andric       std::make_unique<PathSensitiveBugReport>(BT_dispatchOnce, os.str(), N);
1430b57cec5SDimitry Andric   report->addRange(CE->getArg(0)->getSourceRange());
1440b57cec5SDimitry Andric   C.emitReport(std::move(report));
1450b57cec5SDimitry Andric }
1460b57cec5SDimitry Andric 
1470b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1480b57cec5SDimitry Andric // Central dispatch function.
1490b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1500b57cec5SDimitry Andric 
checkPreStmt(const CallExpr * CE,CheckerContext & C) const1510b57cec5SDimitry Andric void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
1520b57cec5SDimitry Andric                                     CheckerContext &C) const {
1530b57cec5SDimitry Andric   StringRef Name = C.getCalleeName(CE);
1540b57cec5SDimitry Andric   if (Name.empty())
1550b57cec5SDimitry Andric     return;
1560b57cec5SDimitry Andric 
1570b57cec5SDimitry Andric   SubChecker SC =
1580b57cec5SDimitry Andric     llvm::StringSwitch<SubChecker>(Name)
1590b57cec5SDimitry Andric       .Cases("dispatch_once",
1600b57cec5SDimitry Andric              "_dispatch_once",
1610b57cec5SDimitry Andric              "dispatch_once_f",
1620b57cec5SDimitry Andric              &MacOSXAPIChecker::CheckDispatchOnce)
1630b57cec5SDimitry Andric       .Default(nullptr);
1640b57cec5SDimitry Andric 
1650b57cec5SDimitry Andric   if (SC)
1660b57cec5SDimitry Andric     (this->*SC)(C, CE, Name);
1670b57cec5SDimitry Andric }
1680b57cec5SDimitry Andric 
1690b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1700b57cec5SDimitry Andric // Registration.
1710b57cec5SDimitry Andric //===----------------------------------------------------------------------===//
1720b57cec5SDimitry Andric 
registerMacOSXAPIChecker(CheckerManager & mgr)1730b57cec5SDimitry Andric void ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
1740b57cec5SDimitry Andric   mgr.registerChecker<MacOSXAPIChecker>();
1750b57cec5SDimitry Andric }
1760b57cec5SDimitry Andric 
shouldRegisterMacOSXAPIChecker(const CheckerManager & mgr)1775ffd83dbSDimitry Andric bool ento::shouldRegisterMacOSXAPIChecker(const CheckerManager &mgr) {
1780b57cec5SDimitry Andric   return true;
1790b57cec5SDimitry Andric }
180