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/Basic/SourceLocation.h" 18 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 20 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 21 #include "clang/StaticAnalyzer/Core/Checker.h" 22 #include <optional> 23 24 using namespace clang; 25 using namespace ento; 26 27 namespace { 28 29 // FIXME: should be defined by anotations in the future 30 bool isRefcountedStringsHack(const VarDecl *V) { 31 assert(V); 32 auto safeClass = [](const std::string &className) { 33 return className == "String" || className == "AtomString" || 34 className == "UniquedString" || className == "Identifier"; 35 }; 36 QualType QT = V->getType(); 37 auto *T = QT.getTypePtr(); 38 if (auto *CXXRD = T->getAsCXXRecordDecl()) { 39 if (safeClass(safeGetName(CXXRD))) 40 return true; 41 } 42 if (T->isPointerType() || T->isReferenceType()) { 43 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) { 44 if (safeClass(safeGetName(CXXRD))) 45 return true; 46 } 47 } 48 return false; 49 } 50 51 struct GuardianVisitor : DynamicRecursiveASTVisitor { 52 const VarDecl *Guardian{nullptr}; 53 54 explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) { 55 assert(Guardian); 56 } 57 58 bool VisitBinaryOperator(BinaryOperator *BO) override { 59 if (BO->isAssignmentOp()) { 60 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) { 61 if (VarRef->getDecl() == Guardian) 62 return false; 63 } 64 } 65 return true; 66 } 67 68 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override { 69 if (auto *Ctor = CE->getConstructor()) { 70 if (Ctor->isMoveConstructor() && CE->getNumArgs() == 1) { 71 auto *Arg = CE->getArg(0)->IgnoreParenCasts(); 72 if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) { 73 if (VarRef->getDecl() == Guardian) 74 return false; 75 } 76 } 77 } 78 return true; 79 } 80 81 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override { 82 auto MethodName = safeGetName(MCE->getMethodDecl()); 83 if (MethodName == "swap" || MethodName == "leakRef" || 84 MethodName == "releaseNonNull") { 85 auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts(); 86 if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) { 87 if (VarRef->getDecl() == Guardian) 88 return false; 89 } 90 } 91 return true; 92 } 93 94 bool VisitCXXOperatorCallExpr(CXXOperatorCallExpr *OCE) override { 95 if (OCE->isAssignmentOp()) { 96 assert(OCE->getNumArgs() == 2); 97 auto *ThisArg = OCE->getArg(0)->IgnoreParenCasts(); 98 if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) { 99 if (VarRef->getDecl() == Guardian) 100 return false; 101 } 102 } 103 return true; 104 } 105 }; 106 107 bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded, 108 const VarDecl *MaybeGuardian) { 109 assert(Guarded); 110 assert(MaybeGuardian); 111 112 if (!MaybeGuardian->isLocalVarDecl()) 113 return false; 114 115 const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr; 116 117 ASTContext &ctx = MaybeGuardian->getASTContext(); 118 119 for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian); 120 !guardianAncestors.empty(); 121 guardianAncestors = ctx.getParents( 122 *guardianAncestors 123 .begin()) // FIXME - should we handle all of the parents? 124 ) { 125 for (auto &guardianAncestor : guardianAncestors) { 126 if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) { 127 guardiansClosestCompStmtAncestor = CStmtParentAncestor; 128 break; 129 } 130 } 131 if (guardiansClosestCompStmtAncestor) 132 break; 133 } 134 135 if (!guardiansClosestCompStmtAncestor) 136 return false; 137 138 // We need to skip the first CompoundStmt to avoid situation when guardian is 139 // defined in the same scope as guarded variable. 140 const CompoundStmt *FirstCompondStmt = nullptr; 141 for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded); 142 !guardedVarAncestors.empty(); 143 guardedVarAncestors = ctx.getParents( 144 *guardedVarAncestors 145 .begin()) // FIXME - should we handle all of the parents? 146 ) { 147 for (auto &guardedVarAncestor : guardedVarAncestors) { 148 if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) { 149 if (!FirstCompondStmt) { 150 FirstCompondStmt = CStmtAncestor; 151 continue; 152 } 153 if (CStmtAncestor == guardiansClosestCompStmtAncestor) { 154 GuardianVisitor guardianVisitor(MaybeGuardian); 155 auto *GuardedScope = const_cast<CompoundStmt *>(FirstCompondStmt); 156 return guardianVisitor.TraverseCompoundStmt(GuardedScope); 157 } 158 } 159 } 160 } 161 162 return false; 163 } 164 165 class RawPtrRefLocalVarsChecker 166 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 167 BugType Bug; 168 mutable BugReporter *BR; 169 EnsureFunctionAnalysis EFA; 170 171 public: 172 RawPtrRefLocalVarsChecker(const char *description) 173 : Bug(this, description, "WebKit coding guidelines") {} 174 175 virtual std::optional<bool> isUnsafePtr(const QualType T) const = 0; 176 virtual const char *ptrKind() const = 0; 177 178 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 179 BugReporter &BRArg) const { 180 BR = &BRArg; 181 182 // The calls to checkAST* from AnalysisConsumer don't 183 // visit template instantiations or lambda classes. We 184 // want to visit those, so we make our own RecursiveASTVisitor. 185 struct LocalVisitor : DynamicRecursiveASTVisitor { 186 const RawPtrRefLocalVarsChecker *Checker; 187 Decl *DeclWithIssue{nullptr}; 188 189 TrivialFunctionAnalysis TFA; 190 191 explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker) 192 : Checker(Checker) { 193 assert(Checker); 194 ShouldVisitTemplateInstantiations = true; 195 ShouldVisitImplicitCode = false; 196 } 197 198 bool TraverseDecl(Decl *D) override { 199 llvm::SaveAndRestore SavedDecl(DeclWithIssue); 200 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D))) 201 DeclWithIssue = D; 202 return DynamicRecursiveASTVisitor::TraverseDecl(D); 203 } 204 205 bool VisitVarDecl(VarDecl *V) override { 206 auto *Init = V->getInit(); 207 if (Init && V->isLocalVarDecl()) 208 Checker->visitVarDecl(V, Init, DeclWithIssue); 209 return true; 210 } 211 212 bool VisitBinaryOperator(BinaryOperator *BO) override { 213 if (BO->isAssignmentOp()) { 214 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) { 215 if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl())) 216 Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue); 217 } 218 } 219 return true; 220 } 221 222 bool TraverseIfStmt(IfStmt *IS) override { 223 if (!TFA.isTrivial(IS)) 224 return DynamicRecursiveASTVisitor::TraverseIfStmt(IS); 225 return true; 226 } 227 228 bool TraverseForStmt(ForStmt *FS) override { 229 if (!TFA.isTrivial(FS)) 230 return DynamicRecursiveASTVisitor::TraverseForStmt(FS); 231 return true; 232 } 233 234 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override { 235 if (!TFA.isTrivial(FRS)) 236 return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS); 237 return true; 238 } 239 240 bool TraverseWhileStmt(WhileStmt *WS) override { 241 if (!TFA.isTrivial(WS)) 242 return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS); 243 return true; 244 } 245 246 bool TraverseCompoundStmt(CompoundStmt *CS) override { 247 if (!TFA.isTrivial(CS)) 248 return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS); 249 return true; 250 } 251 }; 252 253 LocalVisitor visitor(this); 254 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 255 } 256 257 void visitVarDecl(const VarDecl *V, const Expr *Value, 258 const Decl *DeclWithIssue) const { 259 if (shouldSkipVarDecl(V)) 260 return; 261 262 std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType()); 263 if (IsUncountedPtr && *IsUncountedPtr) { 264 if (tryToFindPtrOrigin( 265 Value, /*StopAtFirstRefCountedObj=*/false, 266 [&](const clang::Expr *InitArgOrigin, bool IsSafe) { 267 if (!InitArgOrigin || IsSafe) 268 return true; 269 270 if (isa<CXXThisExpr>(InitArgOrigin)) 271 return true; 272 273 if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin)) 274 return true; 275 276 if (isa<IntegerLiteral>(InitArgOrigin)) 277 return true; 278 279 if (isConstOwnerPtrMemberExpr(InitArgOrigin)) 280 return true; 281 282 if (EFA.isACallToEnsureFn(InitArgOrigin)) 283 return true; 284 285 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) { 286 if (auto *MaybeGuardian = 287 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) { 288 const auto *MaybeGuardianArgType = 289 MaybeGuardian->getType().getTypePtr(); 290 if (MaybeGuardianArgType) { 291 const CXXRecordDecl *const MaybeGuardianArgCXXRecord = 292 MaybeGuardianArgType->getAsCXXRecordDecl(); 293 if (MaybeGuardianArgCXXRecord) { 294 if (MaybeGuardian->isLocalVarDecl() && 295 (isRefCounted(MaybeGuardianArgCXXRecord) || 296 isCheckedPtr(MaybeGuardianArgCXXRecord) || 297 isRefcountedStringsHack(MaybeGuardian)) && 298 isGuardedScopeEmbeddedInGuardianScope( 299 V, MaybeGuardian)) 300 return true; 301 } 302 } 303 304 // Parameters are guaranteed to be safe for the duration of 305 // the call by another checker. 306 if (isa<ParmVarDecl>(MaybeGuardian)) 307 return true; 308 } 309 } 310 311 return false; 312 })) 313 return; 314 315 reportBug(V, Value, DeclWithIssue); 316 } 317 } 318 319 bool shouldSkipVarDecl(const VarDecl *V) const { 320 assert(V); 321 return BR->getSourceManager().isInSystemHeader(V->getLocation()); 322 } 323 324 void reportBug(const VarDecl *V, const Expr *Value, 325 const Decl *DeclWithIssue) const { 326 assert(V); 327 SmallString<100> Buf; 328 llvm::raw_svector_ostream Os(Buf); 329 330 if (dyn_cast<ParmVarDecl>(V)) { 331 Os << "Assignment to an " << ptrKind() << " parameter "; 332 printQuotedQualifiedName(Os, V); 333 Os << " is unsafe."; 334 335 PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager()); 336 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 337 Report->addRange(Value->getSourceRange()); 338 BR->emitReport(std::move(Report)); 339 } else { 340 if (V->hasLocalStorage()) 341 Os << "Local variable "; 342 else if (V->isStaticLocal()) 343 Os << "Static local variable "; 344 else if (V->hasGlobalStorage()) 345 Os << "Global variable "; 346 else 347 Os << "Variable "; 348 printQuotedQualifiedName(Os, V); 349 Os << " is " << ptrKind() << " and unsafe."; 350 351 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager()); 352 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 353 Report->addRange(V->getSourceRange()); 354 Report->setDeclWithIssue(DeclWithIssue); 355 BR->emitReport(std::move(Report)); 356 } 357 } 358 }; 359 360 class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker { 361 public: 362 UncountedLocalVarsChecker() 363 : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not " 364 "provably backed by ref-counted variable") {} 365 std::optional<bool> isUnsafePtr(const QualType T) const final { 366 return isUncountedPtr(T); 367 } 368 const char *ptrKind() const final { return "uncounted"; } 369 }; 370 371 class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker { 372 public: 373 UncheckedLocalVarsChecker() 374 : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not " 375 "provably backed by checked variable") {} 376 std::optional<bool> isUnsafePtr(const QualType T) const final { 377 return isUncheckedPtr(T); 378 } 379 const char *ptrKind() const final { return "unchecked"; } 380 }; 381 382 } // namespace 383 384 void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) { 385 Mgr.registerChecker<UncountedLocalVarsChecker>(); 386 } 387 388 bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) { 389 return true; 390 } 391 392 void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) { 393 Mgr.registerChecker<UncheckedLocalVarsChecker>(); 394 } 395 396 bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) { 397 return true; 398 } 399