480 lines · cpp
1//=======- UncountedLocalVarsChecker.cpp -------------------------*- C++ -*-==//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ASTUtils.h"10#include "DiagOutputUtils.h"11#include "PtrTypesSemantics.h"12#include "clang/AST/CXXInheritance.h"13#include "clang/AST/Decl.h"14#include "clang/AST/DeclCXX.h"15#include "clang/AST/DynamicRecursiveASTVisitor.h"16#include "clang/AST/ParentMapContext.h"17#include "clang/Analysis/DomainSpecific/CocoaConventions.h"18#include "clang/Basic/SourceLocation.h"19#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"20#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"21#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"22#include "clang/StaticAnalyzer/Core/Checker.h"23#include <optional>24 25using namespace clang;26using namespace ento;27 28namespace {29 30// FIXME: should be defined by anotations in the future31bool isRefcountedStringsHack(const VarDecl *V) {32 assert(V);33 auto safeClass = [](const std::string &className) {34 return className == "String" || className == "AtomString" ||35 className == "UniquedString" || className == "Identifier";36 };37 QualType QT = V->getType();38 auto *T = QT.getTypePtr();39 if (auto *CXXRD = T->getAsCXXRecordDecl()) {40 if (safeClass(safeGetName(CXXRD)))41 return true;42 }43 if (T->isPointerType() || T->isReferenceType()) {44 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {45 if (safeClass(safeGetName(CXXRD)))46 return true;47 }48 }49 return false;50}51 52struct GuardianVisitor : DynamicRecursiveASTVisitor {53 const VarDecl *Guardian{nullptr};54 55 explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) {56 assert(Guardian);57 }58 59 bool VisitBinaryOperator(BinaryOperator *BO) override {60 if (BO->isAssignmentOp()) {61 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {62 if (VarRef->getDecl() == Guardian)63 return false;64 }65 }66 return true;67 }68 69 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {70 if (auto *Ctor = CE->getConstructor()) {71 if (Ctor->isMoveConstructor() && CE->getNumArgs() == 1) {72 auto *Arg = CE->getArg(0)->IgnoreParenCasts();73 if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) {74 if (VarRef->getDecl() == Guardian)75 return false;76 }77 }78 }79 return true;80 }81 82 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {83 auto MethodName = safeGetName(MCE->getMethodDecl());84 if (MethodName == "swap" || MethodName == "leakRef" ||85 MethodName == "releaseNonNull" || MethodName == "clear") {86 auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts();87 if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {88 if (VarRef->getDecl() == Guardian)89 return false;90 }91 }92 return true;93 }94 95 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) override {96 if (OCE->isAssignmentOp()) {97 assert(OCE->getNumArgs() == 2);98 auto *ThisArg = OCE->getArg(0)->IgnoreParenCasts();99 if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {100 if (VarRef->getDecl() == Guardian)101 return false;102 }103 }104 return true;105 }106};107 108bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,109 const VarDecl *MaybeGuardian) {110 assert(Guarded);111 assert(MaybeGuardian);112 113 if (!MaybeGuardian->isLocalVarDecl())114 return false;115 116 const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;117 118 ASTContext &ctx = MaybeGuardian->getASTContext();119 120 for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);121 !guardianAncestors.empty();122 guardianAncestors = ctx.getParents(123 *guardianAncestors124 .begin()) // FIXME - should we handle all of the parents?125 ) {126 for (auto &guardianAncestor : guardianAncestors) {127 if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {128 guardiansClosestCompStmtAncestor = CStmtParentAncestor;129 break;130 }131 }132 if (guardiansClosestCompStmtAncestor)133 break;134 }135 136 if (!guardiansClosestCompStmtAncestor)137 return false;138 139 // We need to skip the first CompoundStmt to avoid situation when guardian is140 // defined in the same scope as guarded variable.141 const CompoundStmt *FirstCompondStmt = nullptr;142 for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);143 !guardedVarAncestors.empty();144 guardedVarAncestors = ctx.getParents(145 *guardedVarAncestors146 .begin()) // FIXME - should we handle all of the parents?147 ) {148 for (auto &guardedVarAncestor : guardedVarAncestors) {149 if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {150 if (!FirstCompondStmt) {151 FirstCompondStmt = CStmtAncestor;152 continue;153 }154 if (CStmtAncestor == guardiansClosestCompStmtAncestor) {155 GuardianVisitor guardianVisitor(MaybeGuardian);156 auto *GuardedScope = const_cast<CompoundStmt *>(FirstCompondStmt);157 return guardianVisitor.TraverseCompoundStmt(GuardedScope);158 }159 }160 }161 }162 163 return false;164}165 166class RawPtrRefLocalVarsChecker167 : public Checker<check::ASTDecl<TranslationUnitDecl>> {168 BugType Bug;169 EnsureFunctionAnalysis EFA;170 171protected:172 mutable BugReporter *BR;173 mutable std::optional<RetainTypeChecker> RTC;174 175public:176 RawPtrRefLocalVarsChecker(const char *description)177 : Bug(this, description, "WebKit coding guidelines") {}178 179 virtual std::optional<bool> isUnsafePtr(const QualType T) const = 0;180 virtual bool isSafePtr(const CXXRecordDecl *) const = 0;181 virtual bool isSafePtrType(const QualType) const = 0;182 virtual bool isSafeExpr(const Expr *) const { return false; }183 virtual bool isSafeDecl(const Decl *) const { return false; }184 virtual const char *ptrKind() const = 0;185 186 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,187 BugReporter &BRArg) const {188 BR = &BRArg;189 190 // The calls to checkAST* from AnalysisConsumer don't191 // visit template instantiations or lambda classes. We192 // want to visit those, so we make our own RecursiveASTVisitor.193 struct LocalVisitor : DynamicRecursiveASTVisitor {194 const RawPtrRefLocalVarsChecker *Checker;195 Decl *DeclWithIssue{nullptr};196 197 TrivialFunctionAnalysis TFA;198 199 explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker)200 : Checker(Checker) {201 assert(Checker);202 ShouldVisitTemplateInstantiations = true;203 ShouldVisitImplicitCode = false;204 }205 206 bool TraverseDecl(Decl *D) override {207 llvm::SaveAndRestore SavedDecl(DeclWithIssue);208 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))209 DeclWithIssue = D;210 return DynamicRecursiveASTVisitor::TraverseDecl(D);211 }212 213 bool VisitTypedefDecl(TypedefDecl *TD) override {214 if (Checker->RTC)215 Checker->RTC->visitTypedef(TD);216 return true;217 }218 219 bool VisitVarDecl(VarDecl *V) override {220 auto *Init = V->getInit();221 if (Init && V->isLocalVarDecl())222 Checker->visitVarDecl(V, Init, DeclWithIssue);223 return true;224 }225 226 bool VisitBinaryOperator(BinaryOperator *BO) override {227 if (BO->isAssignmentOp()) {228 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {229 if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))230 Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue);231 }232 }233 return true;234 }235 236 bool TraverseIfStmt(IfStmt *IS) override {237 if (!TFA.isTrivial(IS))238 return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);239 return true;240 }241 242 bool TraverseForStmt(ForStmt *FS) override {243 if (!TFA.isTrivial(FS))244 return DynamicRecursiveASTVisitor::TraverseForStmt(FS);245 return true;246 }247 248 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override {249 if (!TFA.isTrivial(FRS))250 return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS);251 return true;252 }253 254 bool TraverseWhileStmt(WhileStmt *WS) override {255 if (!TFA.isTrivial(WS))256 return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS);257 return true;258 }259 260 bool TraverseCompoundStmt(CompoundStmt *CS) override {261 if (!TFA.isTrivial(CS))262 return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS);263 return true;264 }265 266 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {267 if (isSmartPtrClass(safeGetName(Decl)))268 return true;269 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);270 }271 };272 273 LocalVisitor visitor(this);274 if (RTC)275 RTC->visitTranslationUnitDecl(TUD);276 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));277 }278 279 void visitVarDecl(const VarDecl *V, const Expr *Value,280 const Decl *DeclWithIssue) const {281 if (shouldSkipVarDecl(V))282 return;283 284 std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType());285 if (IsUncountedPtr && *IsUncountedPtr) {286 if (tryToFindPtrOrigin(287 Value, /*StopAtFirstRefCountedObj=*/false,288 [&](const clang::CXXRecordDecl *Record) {289 return isSafePtr(Record);290 },291 [&](const clang::QualType Type) { return isSafePtrType(Type); },292 [&](const clang::Decl *D) { return isSafeDecl(D); },293 [&](const clang::Expr *InitArgOrigin, bool IsSafe) {294 if (!InitArgOrigin || IsSafe)295 return true;296 297 if (isa<CXXThisExpr>(InitArgOrigin))298 return true;299 300 if (isNullPtr(InitArgOrigin))301 return true;302 303 if (isa<IntegerLiteral>(InitArgOrigin))304 return true;305 306 if (isConstOwnerPtrMemberExpr(InitArgOrigin))307 return true;308 309 if (EFA.isACallToEnsureFn(InitArgOrigin))310 return true;311 312 if (isSafeExpr(InitArgOrigin))313 return true;314 315 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {316 if (auto *MaybeGuardian =317 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {318 const auto *MaybeGuardianArgType =319 MaybeGuardian->getType().getTypePtr();320 if (MaybeGuardianArgType) {321 const CXXRecordDecl *const MaybeGuardianArgCXXRecord =322 MaybeGuardianArgType->getAsCXXRecordDecl();323 if (MaybeGuardianArgCXXRecord) {324 if (MaybeGuardian->isLocalVarDecl() &&325 (isSafePtr(MaybeGuardianArgCXXRecord) ||326 isRefcountedStringsHack(MaybeGuardian)) &&327 isGuardedScopeEmbeddedInGuardianScope(328 V, MaybeGuardian))329 return true;330 }331 }332 333 // Parameters are guaranteed to be safe for the duration of334 // the call by another checker.335 if (isa<ParmVarDecl>(MaybeGuardian))336 return true;337 }338 }339 340 return false;341 }))342 return;343 344 reportBug(V, Value, DeclWithIssue);345 }346 }347 348 bool shouldSkipVarDecl(const VarDecl *V) const {349 assert(V);350 if (isa<ImplicitParamDecl>(V))351 return true;352 return BR->getSourceManager().isInSystemHeader(V->getLocation());353 }354 355 void reportBug(const VarDecl *V, const Expr *Value,356 const Decl *DeclWithIssue) const {357 assert(V);358 SmallString<100> Buf;359 llvm::raw_svector_ostream Os(Buf);360 361 if (isa<ParmVarDecl>(V)) {362 Os << "Assignment to an " << ptrKind() << " parameter ";363 printQuotedQualifiedName(Os, V);364 Os << " is unsafe.";365 366 PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager());367 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);368 Report->addRange(Value->getSourceRange());369 BR->emitReport(std::move(Report));370 } else {371 if (V->hasLocalStorage())372 Os << "Local variable ";373 else if (V->isStaticLocal())374 Os << "Static local variable ";375 else if (V->hasGlobalStorage())376 Os << "Global variable ";377 else378 Os << "Variable ";379 printQuotedQualifiedName(Os, V);380 Os << " is " << ptrKind() << " and unsafe.";381 382 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());383 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);384 Report->addRange(V->getSourceRange());385 Report->setDeclWithIssue(DeclWithIssue);386 BR->emitReport(std::move(Report));387 }388 }389};390 391class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {392public:393 UncountedLocalVarsChecker()394 : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not "395 "provably backed by ref-counted variable") {}396 std::optional<bool> isUnsafePtr(const QualType T) const final {397 return isUncountedPtr(T);398 }399 bool isSafePtr(const CXXRecordDecl *Record) const final {400 return isRefCounted(Record) || isCheckedPtr(Record);401 }402 bool isSafePtrType(const QualType type) const final {403 return isRefOrCheckedPtrType(type);404 }405 const char *ptrKind() const final { return "uncounted"; }406};407 408class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {409public:410 UncheckedLocalVarsChecker()411 : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not "412 "provably backed by checked variable") {}413 std::optional<bool> isUnsafePtr(const QualType T) const final {414 return isUncheckedPtr(T);415 }416 bool isSafePtr(const CXXRecordDecl *Record) const final {417 return isRefCounted(Record) || isCheckedPtr(Record);418 }419 bool isSafePtrType(const QualType type) const final {420 return isRefOrCheckedPtrType(type);421 }422 bool isSafeExpr(const Expr *E) const final {423 return isExprToGetCheckedPtrCapableMember(E);424 }425 const char *ptrKind() const final { return "unchecked"; }426};427 428class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {429public:430 UnretainedLocalVarsChecker()431 : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not "432 "provably backed by a RetainPtr") {433 RTC = RetainTypeChecker();434 }435 std::optional<bool> isUnsafePtr(const QualType T) const final {436 return RTC->isUnretained(T);437 }438 bool isSafePtr(const CXXRecordDecl *Record) const final {439 return isRetainPtrOrOSPtr(Record);440 }441 bool isSafePtrType(const QualType type) const final {442 return isRetainPtrOrOSPtrType(type);443 }444 bool isSafeExpr(const Expr *E) const final {445 return ento::cocoa::isCocoaObjectRef(E->getType()) &&446 isa<ObjCMessageExpr>(E);447 }448 bool isSafeDecl(const Decl *D) const final {449 // Treat NS/CF globals in system header as immortal.450 return BR->getSourceManager().isInSystemHeader(D->getLocation());451 }452 const char *ptrKind() const final { return "unretained"; }453};454 455} // namespace456 457void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {458 Mgr.registerChecker<UncountedLocalVarsChecker>();459}460 461bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {462 return true;463}464 465void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) {466 Mgr.registerChecker<UncheckedLocalVarsChecker>();467}468 469bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) {470 return true;471}472 473void ento::registerUnretainedLocalVarsChecker(CheckerManager &Mgr) {474 Mgr.registerChecker<UnretainedLocalVarsChecker>();475}476 477bool ento::shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &) {478 return true;479}480