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/ParentMapContext.h" 16 #include "clang/AST/RecursiveASTVisitor.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 bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded, 52 const VarDecl *MaybeGuardian) { 53 assert(Guarded); 54 assert(MaybeGuardian); 55 56 if (!MaybeGuardian->isLocalVarDecl()) 57 return false; 58 59 const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr; 60 61 ASTContext &ctx = MaybeGuardian->getASTContext(); 62 63 for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian); 64 !guardianAncestors.empty(); 65 guardianAncestors = ctx.getParents( 66 *guardianAncestors 67 .begin()) // FIXME - should we handle all of the parents? 68 ) { 69 for (auto &guardianAncestor : guardianAncestors) { 70 if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) { 71 guardiansClosestCompStmtAncestor = CStmtParentAncestor; 72 break; 73 } 74 } 75 if (guardiansClosestCompStmtAncestor) 76 break; 77 } 78 79 if (!guardiansClosestCompStmtAncestor) 80 return false; 81 82 // We need to skip the first CompoundStmt to avoid situation when guardian is 83 // defined in the same scope as guarded variable. 84 bool HaveSkippedFirstCompoundStmt = false; 85 for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded); 86 !guardedVarAncestors.empty(); 87 guardedVarAncestors = ctx.getParents( 88 *guardedVarAncestors 89 .begin()) // FIXME - should we handle all of the parents? 90 ) { 91 for (auto &guardedVarAncestor : guardedVarAncestors) { 92 if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) { 93 if (!HaveSkippedFirstCompoundStmt) { 94 HaveSkippedFirstCompoundStmt = true; 95 continue; 96 } 97 if (CStmtAncestor == guardiansClosestCompStmtAncestor) 98 return true; 99 } 100 } 101 } 102 103 return false; 104 } 105 106 class UncountedLocalVarsChecker 107 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 108 BugType Bug{this, 109 "Uncounted raw pointer or reference not provably backed by " 110 "ref-counted variable", 111 "WebKit coding guidelines"}; 112 mutable BugReporter *BR; 113 114 public: 115 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 116 BugReporter &BRArg) const { 117 BR = &BRArg; 118 119 // The calls to checkAST* from AnalysisConsumer don't 120 // visit template instantiations or lambda classes. We 121 // want to visit those, so we make our own RecursiveASTVisitor. 122 struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { 123 const UncountedLocalVarsChecker *Checker; 124 125 TrivialFunctionAnalysis TFA; 126 127 using Base = RecursiveASTVisitor<LocalVisitor>; 128 129 explicit LocalVisitor(const UncountedLocalVarsChecker *Checker) 130 : Checker(Checker) { 131 assert(Checker); 132 } 133 134 bool shouldVisitTemplateInstantiations() const { return true; } 135 bool shouldVisitImplicitCode() const { return false; } 136 137 bool VisitVarDecl(VarDecl *V) { 138 auto *Init = V->getInit(); 139 if (Init && V->isLocalVarDecl()) 140 Checker->visitVarDecl(V, Init); 141 return true; 142 } 143 144 bool VisitBinaryOperator(const BinaryOperator *BO) { 145 if (BO->isAssignmentOp()) { 146 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) { 147 if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl())) 148 Checker->visitVarDecl(V, BO->getRHS()); 149 } 150 } 151 return true; 152 } 153 154 bool TraverseIfStmt(IfStmt *IS) { 155 if (!TFA.isTrivial(IS)) 156 return Base::TraverseIfStmt(IS); 157 return true; 158 } 159 160 bool TraverseForStmt(ForStmt *FS) { 161 if (!TFA.isTrivial(FS)) 162 return Base::TraverseForStmt(FS); 163 return true; 164 } 165 166 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) { 167 if (!TFA.isTrivial(FRS)) 168 return Base::TraverseCXXForRangeStmt(FRS); 169 return true; 170 } 171 172 bool TraverseWhileStmt(WhileStmt *WS) { 173 if (!TFA.isTrivial(WS)) 174 return Base::TraverseWhileStmt(WS); 175 return true; 176 } 177 178 bool TraverseCompoundStmt(CompoundStmt *CS) { 179 if (!TFA.isTrivial(CS)) 180 return Base::TraverseCompoundStmt(CS); 181 return true; 182 } 183 }; 184 185 LocalVisitor visitor(this); 186 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 187 } 188 189 void visitVarDecl(const VarDecl *V, const Expr *Value) const { 190 if (shouldSkipVarDecl(V)) 191 return; 192 193 const auto *ArgType = V->getType().getTypePtr(); 194 if (!ArgType) 195 return; 196 197 std::optional<bool> IsUncountedPtr = isUncountedPtr(ArgType); 198 if (IsUncountedPtr && *IsUncountedPtr) { 199 if (tryToFindPtrOrigin( 200 Value, /*StopAtFirstRefCountedObj=*/false, 201 [&](const clang::Expr *InitArgOrigin, bool IsSafe) { 202 if (!InitArgOrigin) 203 return true; 204 205 if (isa<CXXThisExpr>(InitArgOrigin)) 206 return true; 207 208 if (isa<CXXNullPtrLiteralExpr>(InitArgOrigin)) 209 return true; 210 211 if (isa<IntegerLiteral>(InitArgOrigin)) 212 return true; 213 214 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) { 215 if (auto *MaybeGuardian = 216 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) { 217 const auto *MaybeGuardianArgType = 218 MaybeGuardian->getType().getTypePtr(); 219 if (MaybeGuardianArgType) { 220 const CXXRecordDecl *const MaybeGuardianArgCXXRecord = 221 MaybeGuardianArgType->getAsCXXRecordDecl(); 222 if (MaybeGuardianArgCXXRecord) { 223 if (MaybeGuardian->isLocalVarDecl() && 224 (isRefCounted(MaybeGuardianArgCXXRecord) || 225 isRefcountedStringsHack(MaybeGuardian)) && 226 isGuardedScopeEmbeddedInGuardianScope( 227 V, MaybeGuardian)) 228 return true; 229 } 230 } 231 232 // Parameters are guaranteed to be safe for the duration of 233 // the call by another checker. 234 if (isa<ParmVarDecl>(MaybeGuardian)) 235 return true; 236 } 237 } 238 239 return false; 240 })) 241 return; 242 243 reportBug(V, Value); 244 } 245 } 246 247 bool shouldSkipVarDecl(const VarDecl *V) const { 248 assert(V); 249 return BR->getSourceManager().isInSystemHeader(V->getLocation()); 250 } 251 252 void reportBug(const VarDecl *V, const Expr *Value) const { 253 assert(V); 254 SmallString<100> Buf; 255 llvm::raw_svector_ostream Os(Buf); 256 257 if (dyn_cast<ParmVarDecl>(V)) { 258 Os << "Assignment to an uncounted parameter "; 259 printQuotedQualifiedName(Os, V); 260 Os << " is unsafe."; 261 262 PathDiagnosticLocation BSLoc(Value->getExprLoc(), BR->getSourceManager()); 263 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 264 Report->addRange(Value->getSourceRange()); 265 BR->emitReport(std::move(Report)); 266 } else { 267 if (V->hasLocalStorage()) 268 Os << "Local variable "; 269 else if (V->isStaticLocal()) 270 Os << "Static local variable "; 271 else if (V->hasGlobalStorage()) 272 Os << "Global variable "; 273 else 274 Os << "Variable "; 275 printQuotedQualifiedName(Os, V); 276 Os << " is uncounted and unsafe."; 277 278 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager()); 279 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 280 Report->addRange(V->getSourceRange()); 281 BR->emitReport(std::move(Report)); 282 } 283 } 284 }; 285 } // namespace 286 287 void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) { 288 Mgr.registerChecker<UncountedLocalVarsChecker>(); 289 } 290 291 bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) { 292 return true; 293 } 294