123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177 |
- //== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- //
- // An AST checker that looks for common pitfalls when using 'CFArray',
- // 'CFDictionary', 'CFSet' APIs.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
- #include "clang/AST/StmtVisitor.h"
- #include "clang/Analysis/AnalysisDeclContext.h"
- #include "clang/Basic/TargetInfo.h"
- #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
- #include "clang/StaticAnalyzer/Core/Checker.h"
- #include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
- #include "llvm/ADT/SmallString.h"
- #include "llvm/Support/raw_ostream.h"
- using namespace clang;
- using namespace ento;
- namespace {
- class WalkAST : public StmtVisitor<WalkAST> {
- BugReporter &BR;
- const CheckerBase *Checker;
- AnalysisDeclContext* AC;
- ASTContext &ASTC;
- uint64_t PtrWidth;
- /// Check if the type has pointer size (very conservative).
- inline bool isPointerSize(const Type *T) {
- if (!T)
- return true;
- if (T->isIncompleteType())
- return true;
- return (ASTC.getTypeSize(T) == PtrWidth);
- }
- /// Check if the type is a pointer/array to pointer sized values.
- inline bool hasPointerToPointerSizedType(const Expr *E) {
- QualType T = E->getType();
- // The type could be either a pointer or array.
- const Type *TP = T.getTypePtr();
- QualType PointeeT = TP->getPointeeType();
- if (!PointeeT.isNull()) {
- // If the type is a pointer to an array, check the size of the array
- // elements. To avoid false positives coming from assumption that the
- // values x and &x are equal when x is an array.
- if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
- if (isPointerSize(TElem))
- return true;
- // Else, check the pointee size.
- return isPointerSize(PointeeT.getTypePtr());
- }
- if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
- return isPointerSize(TElem);
- // The type must be an array/pointer type.
- // This could be a null constant, which is allowed.
- return static_cast<bool>(
- E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));
- }
- public:
- WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
- : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
- PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
- // Statement visitor methods.
- void VisitChildren(Stmt *S);
- void VisitStmt(Stmt *S) { VisitChildren(S); }
- void VisitCallExpr(CallExpr *CE);
- };
- } // end anonymous namespace
- static StringRef getCalleeName(CallExpr *CE) {
- const FunctionDecl *FD = CE->getDirectCallee();
- if (!FD)
- return StringRef();
- IdentifierInfo *II = FD->getIdentifier();
- if (!II) // if no identifier, not a simple C function
- return StringRef();
- return II->getName();
- }
- void WalkAST::VisitCallExpr(CallExpr *CE) {
- StringRef Name = getCalleeName(CE);
- if (Name.empty())
- return;
- const Expr *Arg = nullptr;
- unsigned ArgNum;
- if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
- if (CE->getNumArgs() != 4)
- return;
- ArgNum = 1;
- Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
- if (hasPointerToPointerSizedType(Arg))
- return;
- } else if (Name.equals("CFDictionaryCreate")) {
- if (CE->getNumArgs() != 6)
- return;
- // Check first argument.
- ArgNum = 1;
- Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
- if (hasPointerToPointerSizedType(Arg)) {
- // Check second argument.
- ArgNum = 2;
- Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
- if (hasPointerToPointerSizedType(Arg))
- // Both are good, return.
- return;
- }
- }
- if (Arg) {
- assert(ArgNum == 1 || ArgNum == 2);
- SmallString<64> BufName;
- llvm::raw_svector_ostream OsName(BufName);
- OsName << " Invalid use of '" << Name << "'" ;
- SmallString<256> Buf;
- llvm::raw_svector_ostream Os(Buf);
- // Use "second" and "third" since users will expect 1-based indexing
- // for parameter names when mentioned in prose.
- Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '"
- << Name << "' must be a C array of pointer-sized values, not '"
- << Arg->getType().getAsString() << "'";
- PathDiagnosticLocation CELoc =
- PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
- BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
- categories::CoreFoundationObjectiveC, Os.str(), CELoc,
- Arg->getSourceRange());
- }
- // Recurse and check children.
- VisitChildren(CE);
- }
- void WalkAST::VisitChildren(Stmt *S) {
- for (Stmt *Child : S->children())
- if (Child)
- Visit(Child);
- }
- namespace {
- class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
- public:
- void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
- BugReporter &BR) const {
- WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
- walker.Visit(D->getBody());
- }
- };
- }
- void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
- mgr.registerChecker<ObjCContainersASTChecker>();
- }
- bool ento::shouldRegisterObjCContainersASTChecker(const CheckerManager &mgr) {
- return true;
- }
|