17330f729Sjoerg //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- 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 // An AST checker that looks for common pitfalls when using 'CFArray',
107330f729Sjoerg // 'CFDictionary', 'CFSet' APIs.
117330f729Sjoerg //
127330f729Sjoerg //===----------------------------------------------------------------------===//
137330f729Sjoerg #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
147330f729Sjoerg #include "clang/AST/StmtVisitor.h"
157330f729Sjoerg #include "clang/Analysis/AnalysisDeclContext.h"
167330f729Sjoerg #include "clang/Basic/TargetInfo.h"
177330f729Sjoerg #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
187330f729Sjoerg #include "clang/StaticAnalyzer/Core/Checker.h"
197330f729Sjoerg #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
207330f729Sjoerg #include "llvm/ADT/SmallString.h"
217330f729Sjoerg #include "llvm/Support/raw_ostream.h"
227330f729Sjoerg
237330f729Sjoerg using namespace clang;
247330f729Sjoerg using namespace ento;
257330f729Sjoerg
267330f729Sjoerg namespace {
277330f729Sjoerg class WalkAST : public StmtVisitor<WalkAST> {
287330f729Sjoerg BugReporter &BR;
297330f729Sjoerg const CheckerBase *Checker;
307330f729Sjoerg AnalysisDeclContext* AC;
317330f729Sjoerg ASTContext &ASTC;
327330f729Sjoerg uint64_t PtrWidth;
337330f729Sjoerg
347330f729Sjoerg /// Check if the type has pointer size (very conservative).
isPointerSize(const Type * T)357330f729Sjoerg inline bool isPointerSize(const Type *T) {
367330f729Sjoerg if (!T)
377330f729Sjoerg return true;
387330f729Sjoerg if (T->isIncompleteType())
397330f729Sjoerg return true;
407330f729Sjoerg return (ASTC.getTypeSize(T) == PtrWidth);
417330f729Sjoerg }
427330f729Sjoerg
437330f729Sjoerg /// Check if the type is a pointer/array to pointer sized values.
hasPointerToPointerSizedType(const Expr * E)447330f729Sjoerg inline bool hasPointerToPointerSizedType(const Expr *E) {
457330f729Sjoerg QualType T = E->getType();
467330f729Sjoerg
477330f729Sjoerg // The type could be either a pointer or array.
487330f729Sjoerg const Type *TP = T.getTypePtr();
497330f729Sjoerg QualType PointeeT = TP->getPointeeType();
507330f729Sjoerg if (!PointeeT.isNull()) {
517330f729Sjoerg // If the type is a pointer to an array, check the size of the array
527330f729Sjoerg // elements. To avoid false positives coming from assumption that the
537330f729Sjoerg // values x and &x are equal when x is an array.
547330f729Sjoerg if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
557330f729Sjoerg if (isPointerSize(TElem))
567330f729Sjoerg return true;
577330f729Sjoerg
587330f729Sjoerg // Else, check the pointee size.
597330f729Sjoerg return isPointerSize(PointeeT.getTypePtr());
607330f729Sjoerg }
617330f729Sjoerg
627330f729Sjoerg if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
637330f729Sjoerg return isPointerSize(TElem);
647330f729Sjoerg
657330f729Sjoerg // The type must be an array/pointer type.
667330f729Sjoerg
677330f729Sjoerg // This could be a null constant, which is allowed.
687330f729Sjoerg return static_cast<bool>(
697330f729Sjoerg E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));
707330f729Sjoerg }
717330f729Sjoerg
727330f729Sjoerg public:
WalkAST(BugReporter & br,const CheckerBase * checker,AnalysisDeclContext * ac)737330f729Sjoerg WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
747330f729Sjoerg : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
757330f729Sjoerg PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
767330f729Sjoerg
777330f729Sjoerg // Statement visitor methods.
787330f729Sjoerg void VisitChildren(Stmt *S);
VisitStmt(Stmt * S)797330f729Sjoerg void VisitStmt(Stmt *S) { VisitChildren(S); }
807330f729Sjoerg void VisitCallExpr(CallExpr *CE);
817330f729Sjoerg };
827330f729Sjoerg } // end anonymous namespace
837330f729Sjoerg
getCalleeName(CallExpr * CE)847330f729Sjoerg static StringRef getCalleeName(CallExpr *CE) {
857330f729Sjoerg const FunctionDecl *FD = CE->getDirectCallee();
867330f729Sjoerg if (!FD)
877330f729Sjoerg return StringRef();
887330f729Sjoerg
897330f729Sjoerg IdentifierInfo *II = FD->getIdentifier();
907330f729Sjoerg if (!II) // if no identifier, not a simple C function
917330f729Sjoerg return StringRef();
927330f729Sjoerg
937330f729Sjoerg return II->getName();
947330f729Sjoerg }
957330f729Sjoerg
VisitCallExpr(CallExpr * CE)967330f729Sjoerg void WalkAST::VisitCallExpr(CallExpr *CE) {
977330f729Sjoerg StringRef Name = getCalleeName(CE);
987330f729Sjoerg if (Name.empty())
997330f729Sjoerg return;
1007330f729Sjoerg
1017330f729Sjoerg const Expr *Arg = nullptr;
1027330f729Sjoerg unsigned ArgNum;
1037330f729Sjoerg
1047330f729Sjoerg if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
1057330f729Sjoerg if (CE->getNumArgs() != 4)
1067330f729Sjoerg return;
1077330f729Sjoerg ArgNum = 1;
1087330f729Sjoerg Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
1097330f729Sjoerg if (hasPointerToPointerSizedType(Arg))
1107330f729Sjoerg return;
1117330f729Sjoerg } else if (Name.equals("CFDictionaryCreate")) {
1127330f729Sjoerg if (CE->getNumArgs() != 6)
1137330f729Sjoerg return;
1147330f729Sjoerg // Check first argument.
1157330f729Sjoerg ArgNum = 1;
1167330f729Sjoerg Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
1177330f729Sjoerg if (hasPointerToPointerSizedType(Arg)) {
1187330f729Sjoerg // Check second argument.
1197330f729Sjoerg ArgNum = 2;
1207330f729Sjoerg Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
1217330f729Sjoerg if (hasPointerToPointerSizedType(Arg))
1227330f729Sjoerg // Both are good, return.
1237330f729Sjoerg return;
1247330f729Sjoerg }
1257330f729Sjoerg }
1267330f729Sjoerg
1277330f729Sjoerg if (Arg) {
1287330f729Sjoerg assert(ArgNum == 1 || ArgNum == 2);
1297330f729Sjoerg
1307330f729Sjoerg SmallString<64> BufName;
1317330f729Sjoerg llvm::raw_svector_ostream OsName(BufName);
1327330f729Sjoerg OsName << " Invalid use of '" << Name << "'" ;
1337330f729Sjoerg
1347330f729Sjoerg SmallString<256> Buf;
1357330f729Sjoerg llvm::raw_svector_ostream Os(Buf);
1367330f729Sjoerg // Use "second" and "third" since users will expect 1-based indexing
1377330f729Sjoerg // for parameter names when mentioned in prose.
1387330f729Sjoerg Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '"
1397330f729Sjoerg << Name << "' must be a C array of pointer-sized values, not '"
1407330f729Sjoerg << Arg->getType().getAsString() << "'";
1417330f729Sjoerg
1427330f729Sjoerg PathDiagnosticLocation CELoc =
1437330f729Sjoerg PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
1447330f729Sjoerg BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
1457330f729Sjoerg categories::CoreFoundationObjectiveC, Os.str(), CELoc,
1467330f729Sjoerg Arg->getSourceRange());
1477330f729Sjoerg }
1487330f729Sjoerg
1497330f729Sjoerg // Recurse and check children.
1507330f729Sjoerg VisitChildren(CE);
1517330f729Sjoerg }
1527330f729Sjoerg
VisitChildren(Stmt * S)1537330f729Sjoerg void WalkAST::VisitChildren(Stmt *S) {
1547330f729Sjoerg for (Stmt *Child : S->children())
1557330f729Sjoerg if (Child)
1567330f729Sjoerg Visit(Child);
1577330f729Sjoerg }
1587330f729Sjoerg
1597330f729Sjoerg namespace {
1607330f729Sjoerg class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
1617330f729Sjoerg public:
1627330f729Sjoerg
checkASTCodeBody(const Decl * D,AnalysisManager & Mgr,BugReporter & BR) const1637330f729Sjoerg void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
1647330f729Sjoerg BugReporter &BR) const {
1657330f729Sjoerg WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
1667330f729Sjoerg walker.Visit(D->getBody());
1677330f729Sjoerg }
1687330f729Sjoerg };
1697330f729Sjoerg }
1707330f729Sjoerg
registerObjCContainersASTChecker(CheckerManager & mgr)1717330f729Sjoerg void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
1727330f729Sjoerg mgr.registerChecker<ObjCContainersASTChecker>();
1737330f729Sjoerg }
1747330f729Sjoerg
shouldRegisterObjCContainersASTChecker(const CheckerManager & mgr)175*e038c9c4Sjoerg bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager &mgr) {
1767330f729Sjoerg return true;
1777330f729Sjoerg }
178