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-exception 6 // 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 25 using namespace clang; 26 using namespace ento; 27 28 namespace { 29 30 // FIXME: should be defined by anotations in the future 31 bool 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 52 struct 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 108 bool 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 *guardianAncestors 124 .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 is 140 // 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 *guardedVarAncestors 146 .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 166 class RawPtrRefLocalVarsChecker 167 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 168 BugType Bug; 169 mutable BugReporter *BR; 170 EnsureFunctionAnalysis EFA; 171 172 protected: 173 mutable std::optional<RetainTypeChecker> RTC; 174 175 public: 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 const char *ptrKind() const = 0; 184 185 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 186 BugReporter &BRArg) const { 187 BR = &BRArg; 188 189 // The calls to checkAST* from AnalysisConsumer don't 190 // visit template instantiations or lambda classes. We 191 // want to visit those, so we make our own RecursiveASTVisitor. 192 struct LocalVisitor : DynamicRecursiveASTVisitor { 193 const RawPtrRefLocalVarsChecker *Checker; 194 Decl *DeclWithIssue{nullptr}; 195 196 TrivialFunctionAnalysis TFA; 197 198 explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker) 199 : Checker(Checker) { 200 assert(Checker); 201 ShouldVisitTemplateInstantiations = true; 202 ShouldVisitImplicitCode = false; 203 } 204 205 bool TraverseDecl(Decl *D) override { 206 llvm::SaveAndRestore SavedDecl(DeclWithIssue); 207 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D))) 208 DeclWithIssue = D; 209 return DynamicRecursiveASTVisitor::TraverseDecl(D); 210 } 211 212 bool VisitTypedefDecl(TypedefDecl *TD) override { 213 if (Checker->RTC) 214 Checker->RTC->visitTypedef(TD); 215 return true; 216 } 217 218 bool VisitVarDecl(VarDecl *V) override { 219 auto *Init = V->getInit(); 220 if (Init && V->isLocalVarDecl()) 221 Checker->visitVarDecl(V, Init, DeclWithIssue); 222 return true; 223 } 224 225 bool VisitBinaryOperator(BinaryOperator *BO) override { 226 if (BO->isAssignmentOp()) { 227 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) { 228 if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl())) 229 Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue); 230 } 231 } 232 return true; 233 } 234 235 bool TraverseIfStmt(IfStmt *IS) override { 236 if (!TFA.isTrivial(IS)) 237 return DynamicRecursiveASTVisitor::TraverseIfStmt(IS); 238 return true; 239 } 240 241 bool TraverseForStmt(ForStmt *FS) override { 242 if (!TFA.isTrivial(FS)) 243 return DynamicRecursiveASTVisitor::TraverseForStmt(FS); 244 return true; 245 } 246 247 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override { 248 if (!TFA.isTrivial(FRS)) 249 return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS); 250 return true; 251 } 252 253 bool TraverseWhileStmt(WhileStmt *WS) override { 254 if (!TFA.isTrivial(WS)) 255 return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS); 256 return true; 257 } 258 259 bool TraverseCompoundStmt(CompoundStmt *CS) override { 260 if (!TFA.isTrivial(CS)) 261 return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS); 262 return true; 263 } 264 265 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override { 266 if (isSmartPtrClass(safeGetName(Decl))) 267 return true; 268 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl); 269 } 270 }; 271 272 LocalVisitor visitor(this); 273 if (RTC) 274 RTC->visitTranslationUnitDecl(TUD); 275 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 276 } 277 278 void visitVarDecl(const VarDecl *V, const Expr *Value, 279 const Decl *DeclWithIssue) const { 280 if (shouldSkipVarDecl(V)) 281 return; 282 283 std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType()); 284 if (IsUncountedPtr && *IsUncountedPtr) { 285 if (tryToFindPtrOrigin( 286 Value, /*StopAtFirstRefCountedObj=*/false, 287 [&](const clang::CXXRecordDecl *Record) { 288 return isSafePtr(Record); 289 }, 290 [&](const clang::QualType Type) { return isSafePtrType(Type); }, 291 [&](const clang::Expr *InitArgOrigin, bool IsSafe) { 292 if (!InitArgOrigin || IsSafe) 293 return true; 294 295 if (isa<CXXThisExpr>(InitArgOrigin)) 296 return true; 297 298 if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin)) 299 return true; 300 301 if (isa<IntegerLiteral>(InitArgOrigin)) 302 return true; 303 304 if (isConstOwnerPtrMemberExpr(InitArgOrigin)) 305 return true; 306 307 if (EFA.isACallToEnsureFn(InitArgOrigin)) 308 return true; 309 310 if (isSafeExpr(InitArgOrigin)) 311 return true; 312 313 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) { 314 if (auto *MaybeGuardian = 315 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) { 316 const auto *MaybeGuardianArgType = 317 MaybeGuardian->getType().getTypePtr(); 318 if (MaybeGuardianArgType) { 319 const CXXRecordDecl *const MaybeGuardianArgCXXRecord = 320 MaybeGuardianArgType->getAsCXXRecordDecl(); 321 if (MaybeGuardianArgCXXRecord) { 322 if (MaybeGuardian->isLocalVarDecl() && 323 (isSafePtr(MaybeGuardianArgCXXRecord) || 324 isRefcountedStringsHack(MaybeGuardian)) && 325 isGuardedScopeEmbeddedInGuardianScope( 326 V, MaybeGuardian)) 327 return true; 328 } 329 } 330 331 // Parameters are guaranteed to be safe for the duration of 332 // the call by another checker. 333 if (isa<ParmVarDecl>(MaybeGuardian)) 334 return true; 335 } 336 } 337 338 return false; 339 })) 340 return; 341 342 reportBug(V, Value, DeclWithIssue); 343 } 344 } 345 346 bool shouldSkipVarDecl(const VarDecl *V) const { 347 assert(V); 348 if (isa<ImplicitParamDecl>(V)) 349 return true; 350 return BR->getSourceManager().isInSystemHeader(V->getLocation()); 351 } 352 353 void reportBug(const VarDecl *V, const Expr *Value, 354 const Decl *DeclWithIssue) const { 355 assert(V); 356 SmallString<100> Buf; 357 llvm::raw_svector_ostream Os(Buf); 358 359 if (isa<ParmVarDecl>(V)) { 360 Os << "Assignment to an " << ptrKind() << " parameter "; 361 printQuotedQualifiedName(Os, V); 362 Os << " is unsafe."; 363 364 PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager()); 365 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 366 Report->addRange(Value->getSourceRange()); 367 BR->emitReport(std::move(Report)); 368 } else { 369 if (V->hasLocalStorage()) 370 Os << "Local variable "; 371 else if (V->isStaticLocal()) 372 Os << "Static local variable "; 373 else if (V->hasGlobalStorage()) 374 Os << "Global variable "; 375 else 376 Os << "Variable "; 377 printQuotedQualifiedName(Os, V); 378 Os << " is " << ptrKind() << " and unsafe."; 379 380 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager()); 381 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 382 Report->addRange(V->getSourceRange()); 383 Report->setDeclWithIssue(DeclWithIssue); 384 BR->emitReport(std::move(Report)); 385 } 386 } 387 }; 388 389 class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker { 390 public: 391 UncountedLocalVarsChecker() 392 : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not " 393 "provably backed by ref-counted variable") {} 394 std::optional<bool> isUnsafePtr(const QualType T) const final { 395 return isUncountedPtr(T); 396 } 397 bool isSafePtr(const CXXRecordDecl *Record) const final { 398 return isRefCounted(Record) || isCheckedPtr(Record); 399 } 400 bool isSafePtrType(const QualType type) const final { 401 return isRefOrCheckedPtrType(type); 402 } 403 const char *ptrKind() const final { return "uncounted"; } 404 }; 405 406 class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker { 407 public: 408 UncheckedLocalVarsChecker() 409 : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not " 410 "provably backed by checked variable") {} 411 std::optional<bool> isUnsafePtr(const QualType T) const final { 412 return isUncheckedPtr(T); 413 } 414 bool isSafePtr(const CXXRecordDecl *Record) const final { 415 return isRefCounted(Record) || isCheckedPtr(Record); 416 } 417 bool isSafePtrType(const QualType type) const final { 418 return isRefOrCheckedPtrType(type); 419 } 420 bool isSafeExpr(const Expr *E) const final { 421 return isExprToGetCheckedPtrCapableMember(E); 422 } 423 const char *ptrKind() const final { return "unchecked"; } 424 }; 425 426 class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker { 427 public: 428 UnretainedLocalVarsChecker() 429 : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not " 430 "provably backed by a RetainPtr") { 431 RTC = RetainTypeChecker(); 432 } 433 std::optional<bool> isUnsafePtr(const QualType T) const final { 434 return RTC->isUnretained(T); 435 } 436 bool isSafePtr(const CXXRecordDecl *Record) const final { 437 return isRetainPtr(Record); 438 } 439 bool isSafePtrType(const QualType type) const final { 440 return isRetainPtrType(type); 441 } 442 bool isSafeExpr(const Expr *E) const final { 443 return ento::cocoa::isCocoaObjectRef(E->getType()) && 444 isa<ObjCMessageExpr>(E); 445 } 446 const char *ptrKind() const final { return "unretained"; } 447 }; 448 449 } // namespace 450 451 void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) { 452 Mgr.registerChecker<UncountedLocalVarsChecker>(); 453 } 454 455 bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) { 456 return true; 457 } 458 459 void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) { 460 Mgr.registerChecker<UncheckedLocalVarsChecker>(); 461 } 462 463 bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) { 464 return true; 465 } 466 467 void ento::registerUnretainedLocalVarsChecker(CheckerManager &Mgr) { 468 Mgr.registerChecker<UnretainedLocalVarsChecker>(); 469 } 470 471 bool ento::shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &) { 472 return true; 473 } 474