1 //=======- RawPtrRefCallArgsChecker.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/Decl.h" 13 #include "clang/AST/DeclCXX.h" 14 #include "clang/AST/DynamicRecursiveASTVisitor.h" 15 #include "clang/Analysis/DomainSpecific/CocoaConventions.h" 16 #include "clang/Basic/SourceLocation.h" 17 #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" 18 #include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" 19 #include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" 20 #include "clang/StaticAnalyzer/Core/Checker.h" 21 #include "llvm/Support/SaveAndRestore.h" 22 #include <optional> 23 24 using namespace clang; 25 using namespace ento; 26 27 namespace { 28 29 class RawPtrRefCallArgsChecker 30 : public Checker<check::ASTDecl<TranslationUnitDecl>> { 31 BugType Bug; 32 mutable BugReporter *BR; 33 34 TrivialFunctionAnalysis TFA; 35 EnsureFunctionAnalysis EFA; 36 37 protected: 38 mutable std::optional<RetainTypeChecker> RTC; 39 40 public: 41 RawPtrRefCallArgsChecker(const char *description) 42 : Bug(this, description, "WebKit coding guidelines") {} 43 44 virtual std::optional<bool> isUnsafeType(QualType) const = 0; 45 virtual std::optional<bool> isUnsafePtr(QualType) const = 0; 46 virtual bool isSafePtr(const CXXRecordDecl *Record) const = 0; 47 virtual bool isSafePtrType(const QualType type) const = 0; 48 virtual bool isSafeExpr(const Expr *) const { return false; } 49 virtual const char *ptrKind() const = 0; 50 51 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, 52 BugReporter &BRArg) const { 53 BR = &BRArg; 54 55 // The calls to checkAST* from AnalysisConsumer don't 56 // visit template instantiations or lambda classes. We 57 // want to visit those, so we make our own RecursiveASTVisitor. 58 struct LocalVisitor : DynamicRecursiveASTVisitor { 59 const RawPtrRefCallArgsChecker *Checker; 60 Decl *DeclWithIssue{nullptr}; 61 62 explicit LocalVisitor(const RawPtrRefCallArgsChecker *Checker) 63 : Checker(Checker) { 64 assert(Checker); 65 ShouldVisitTemplateInstantiations = true; 66 ShouldVisitImplicitCode = false; 67 } 68 69 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override { 70 if (isSmartPtrClass(safeGetName(Decl))) 71 return true; 72 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl); 73 } 74 75 bool TraverseDecl(Decl *D) override { 76 llvm::SaveAndRestore SavedDecl(DeclWithIssue); 77 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D))) 78 DeclWithIssue = D; 79 return DynamicRecursiveASTVisitor::TraverseDecl(D); 80 } 81 82 bool VisitCallExpr(CallExpr *CE) override { 83 Checker->visitCallExpr(CE, DeclWithIssue); 84 return true; 85 } 86 87 bool VisitTypedefDecl(TypedefDecl *TD) override { 88 if (Checker->RTC) 89 Checker->RTC->visitTypedef(TD); 90 return true; 91 } 92 93 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr) override { 94 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue); 95 return true; 96 } 97 }; 98 99 LocalVisitor visitor(this); 100 if (RTC) 101 RTC->visitTranslationUnitDecl(TUD); 102 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); 103 } 104 105 void visitCallExpr(const CallExpr *CE, const Decl *D) const { 106 if (shouldSkipCall(CE)) 107 return; 108 109 if (auto *F = CE->getDirectCallee()) { 110 // Skip the first argument for overloaded member operators (e. g. lambda 111 // or std::function call operator). 112 unsigned ArgIdx = 113 isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F); 114 115 if (auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) { 116 if (auto *MD = MemberCallExpr->getMethodDecl()) { 117 auto name = safeGetName(MD); 118 if (name == "ref" || name == "deref") 119 return; 120 if (name == "incrementCheckedPtrCount" || 121 name == "decrementCheckedPtrCount") 122 return; 123 } 124 auto *E = MemberCallExpr->getImplicitObjectArgument(); 125 QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType(); 126 std::optional<bool> IsUnsafe = isUnsafeType(ArgType); 127 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E)) 128 reportBugOnThis(E, D); 129 } 130 131 for (auto P = F->param_begin(); 132 // FIXME: Also check variadic function parameters. 133 // FIXME: Also check default function arguments. Probably a different 134 // checker. In case there are default arguments the call can have 135 // fewer arguments than the callee has parameters. 136 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) { 137 // TODO: attributes. 138 // if ((*P)->hasAttr<SafeRefCntblRawPtrAttr>()) 139 // continue; 140 141 QualType ArgType = (*P)->getType(); 142 // FIXME: more complex types (arrays, references to raw pointers, etc) 143 std::optional<bool> IsUncounted = isUnsafePtr(ArgType); 144 if (!IsUncounted || !(*IsUncounted)) 145 continue; 146 147 const auto *Arg = CE->getArg(ArgIdx); 148 149 if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg)) 150 Arg = defaultArg->getExpr(); 151 152 if (isPtrOriginSafe(Arg)) 153 continue; 154 155 reportBug(Arg, *P, D); 156 } 157 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) { 158 const auto *Arg = CE->getArg(ArgIdx); 159 auto ArgType = Arg->getType(); 160 std::optional<bool> IsUncounted = isUnsafePtr(ArgType); 161 if (!IsUncounted || !(*IsUncounted)) 162 continue; 163 164 if (auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg)) 165 Arg = defaultArg->getExpr(); 166 167 if (isPtrOriginSafe(Arg)) 168 continue; 169 170 reportBug(Arg, nullptr, D); 171 } 172 } 173 } 174 175 void visitObjCMessageExpr(const ObjCMessageExpr *E, const Decl *D) const { 176 if (BR->getSourceManager().isInSystemHeader(E->getExprLoc())) 177 return; 178 179 auto Selector = E->getSelector(); 180 if (auto *Receiver = E->getInstanceReceiver()) { 181 std::optional<bool> IsUnsafe = isUnsafePtr(E->getReceiverType()); 182 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) { 183 if (auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) { 184 auto InnerSelector = InnerMsg->getSelector(); 185 if (InnerSelector.getNameForSlot(0) == "alloc" && 186 Selector.getNameForSlot(0).starts_with("init")) 187 return; 188 } 189 reportBugOnReceiver(Receiver, D); 190 } 191 } 192 193 auto *MethodDecl = E->getMethodDecl(); 194 if (!MethodDecl) 195 return; 196 197 auto ArgCount = E->getNumArgs(); 198 for (unsigned i = 0; i < ArgCount; ++i) { 199 auto *Arg = E->getArg(i); 200 bool hasParam = i < MethodDecl->param_size(); 201 auto *Param = hasParam ? MethodDecl->getParamDecl(i) : nullptr; 202 auto ArgType = Arg->getType(); 203 std::optional<bool> IsUnsafe = isUnsafePtr(ArgType); 204 if (!IsUnsafe || !(*IsUnsafe)) 205 continue; 206 if (isPtrOriginSafe(Arg)) 207 continue; 208 reportBug(Arg, Param, D); 209 } 210 } 211 212 bool isPtrOriginSafe(const Expr *Arg) const { 213 return tryToFindPtrOrigin( 214 Arg, /*StopAtFirstRefCountedObj=*/true, 215 [&](const clang::CXXRecordDecl *Record) { return isSafePtr(Record); }, 216 [&](const clang::QualType T) { return isSafePtrType(T); }, 217 [&](const clang::Expr *ArgOrigin, bool IsSafe) { 218 if (IsSafe) 219 return true; 220 if (isa<CXXNullPtrLiteralExpr>(ArgOrigin)) { 221 // foo(nullptr) 222 return true; 223 } 224 if (isa<IntegerLiteral>(ArgOrigin)) { 225 // FIXME: Check the value. 226 // foo(NULL) 227 return true; 228 } 229 if (isa<ObjCStringLiteral>(ArgOrigin)) 230 return true; 231 if (isASafeCallArg(ArgOrigin)) 232 return true; 233 if (EFA.isACallToEnsureFn(ArgOrigin)) 234 return true; 235 if (isSafeExpr(ArgOrigin)) 236 return true; 237 return false; 238 }); 239 } 240 241 bool shouldSkipCall(const CallExpr *CE) const { 242 const auto *Callee = CE->getDirectCallee(); 243 244 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc())) 245 return true; 246 247 if (Callee && TFA.isTrivial(Callee) && !Callee->isVirtualAsWritten()) 248 return true; 249 250 if (isTrivialBuiltinFunction(Callee)) 251 return true; 252 253 if (CE->getNumArgs() == 0) 254 return false; 255 256 // If an assignment is problematic we should warn about the sole existence 257 // of object on LHS. 258 if (auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) { 259 // Note: assignemnt to built-in type isn't derived from CallExpr. 260 if (MemberOp->getOperator() == 261 OO_Equal) { // Ignore assignment to Ref/RefPtr. 262 auto *callee = MemberOp->getDirectCallee(); 263 if (auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) { 264 if (const CXXRecordDecl *classDecl = calleeDecl->getParent()) { 265 if (isSafePtr(classDecl)) 266 return true; 267 } 268 } 269 } 270 if (MemberOp->isAssignmentOp()) 271 return false; 272 } 273 274 if (!Callee) 275 return false; 276 277 if (isMethodOnWTFContainerType(Callee)) 278 return true; 279 280 auto overloadedOperatorType = Callee->getOverloadedOperator(); 281 if (overloadedOperatorType == OO_EqualEqual || 282 overloadedOperatorType == OO_ExclaimEqual || 283 overloadedOperatorType == OO_LessEqual || 284 overloadedOperatorType == OO_GreaterEqual || 285 overloadedOperatorType == OO_Spaceship || 286 overloadedOperatorType == OO_AmpAmp || 287 overloadedOperatorType == OO_PipePipe) 288 return true; 289 290 if (isCtorOfSafePtr(Callee) || isPtrConversion(Callee)) 291 return true; 292 293 auto name = safeGetName(Callee); 294 if (name == "adoptRef" || name == "getPtr" || name == "WeakPtr" || 295 name == "is" || name == "equal" || name == "hash" || name == "isType" || 296 // FIXME: Most/all of these should be implemented via attributes. 297 name == "CFEqual" || name == "equalIgnoringASCIICase" || 298 name == "equalIgnoringASCIICaseCommon" || 299 name == "equalIgnoringNullity" || name == "toString") 300 return true; 301 302 return false; 303 } 304 305 bool isMethodOnWTFContainerType(const FunctionDecl *Decl) const { 306 if (!isa<CXXMethodDecl>(Decl)) 307 return false; 308 auto *ClassDecl = Decl->getParent(); 309 if (!ClassDecl || !isa<CXXRecordDecl>(ClassDecl)) 310 return false; 311 312 auto *NsDecl = ClassDecl->getParent(); 313 if (!NsDecl || !isa<NamespaceDecl>(NsDecl)) 314 return false; 315 316 auto MethodName = safeGetName(Decl); 317 auto ClsNameStr = safeGetName(ClassDecl); 318 StringRef ClsName = ClsNameStr; // FIXME: Make safeGetName return StringRef. 319 auto NamespaceName = safeGetName(NsDecl); 320 // FIXME: These should be implemented via attributes. 321 return NamespaceName == "WTF" && 322 (MethodName == "find" || MethodName == "findIf" || 323 MethodName == "reverseFind" || MethodName == "reverseFindIf" || 324 MethodName == "findIgnoringASCIICase" || MethodName == "get" || 325 MethodName == "inlineGet" || MethodName == "contains" || 326 MethodName == "containsIf" || 327 MethodName == "containsIgnoringASCIICase" || 328 MethodName == "startsWith" || MethodName == "endsWith" || 329 MethodName == "startsWithIgnoringASCIICase" || 330 MethodName == "endsWithIgnoringASCIICase" || 331 MethodName == "substring") && 332 (ClsName.ends_with("Vector") || ClsName.ends_with("Set") || 333 ClsName.ends_with("Map") || ClsName == "StringImpl" || 334 ClsName.ends_with("String")); 335 } 336 337 void reportBug(const Expr *CallArg, const ParmVarDecl *Param, 338 const Decl *DeclWithIssue) const { 339 assert(CallArg); 340 341 SmallString<100> Buf; 342 llvm::raw_svector_ostream Os(Buf); 343 344 const std::string paramName = safeGetName(Param); 345 Os << "Call argument"; 346 if (!paramName.empty()) { 347 Os << " for parameter "; 348 printQuotedQualifiedName(Os, Param); 349 } 350 Os << " is " << ptrKind() << " and unsafe."; 351 352 bool usesDefaultArgValue = isa<CXXDefaultArgExpr>(CallArg) && Param; 353 const SourceLocation SrcLocToReport = 354 usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc() 355 : CallArg->getSourceRange().getBegin(); 356 357 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); 358 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 359 Report->addRange(CallArg->getSourceRange()); 360 Report->setDeclWithIssue(DeclWithIssue); 361 BR->emitReport(std::move(Report)); 362 } 363 364 void reportBugOnThis(const Expr *CallArg, const Decl *DeclWithIssue) const { 365 assert(CallArg); 366 367 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); 368 369 SmallString<100> Buf; 370 llvm::raw_svector_ostream Os(Buf); 371 Os << "Call argument for 'this' parameter is " << ptrKind(); 372 Os << " and unsafe."; 373 374 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); 375 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 376 Report->addRange(CallArg->getSourceRange()); 377 Report->setDeclWithIssue(DeclWithIssue); 378 BR->emitReport(std::move(Report)); 379 } 380 381 void reportBugOnReceiver(const Expr *CallArg, 382 const Decl *DeclWithIssue) const { 383 assert(CallArg); 384 385 const SourceLocation SrcLocToReport = CallArg->getSourceRange().getBegin(); 386 387 SmallString<100> Buf; 388 llvm::raw_svector_ostream Os(Buf); 389 Os << "Reciever is " << ptrKind() << " and unsafe."; 390 391 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager()); 392 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc); 393 Report->addRange(CallArg->getSourceRange()); 394 Report->setDeclWithIssue(DeclWithIssue); 395 BR->emitReport(std::move(Report)); 396 } 397 }; 398 399 class UncountedCallArgsChecker final : public RawPtrRefCallArgsChecker { 400 public: 401 UncountedCallArgsChecker() 402 : RawPtrRefCallArgsChecker("Uncounted call argument for a raw " 403 "pointer/reference parameter") {} 404 405 std::optional<bool> isUnsafeType(QualType QT) const final { 406 return isUncounted(QT); 407 } 408 409 std::optional<bool> isUnsafePtr(QualType QT) const final { 410 return isUncountedPtr(QT.getCanonicalType()); 411 } 412 413 bool isSafePtr(const CXXRecordDecl *Record) const final { 414 return isRefCounted(Record) || isCheckedPtr(Record); 415 } 416 417 bool isSafePtrType(const QualType type) const final { 418 return isRefOrCheckedPtrType(type); 419 } 420 421 const char *ptrKind() const final { return "uncounted"; } 422 }; 423 424 class UncheckedCallArgsChecker final : public RawPtrRefCallArgsChecker { 425 public: 426 UncheckedCallArgsChecker() 427 : RawPtrRefCallArgsChecker("Unchecked call argument for a raw " 428 "pointer/reference parameter") {} 429 430 std::optional<bool> isUnsafeType(QualType QT) const final { 431 return isUnchecked(QT); 432 } 433 434 std::optional<bool> isUnsafePtr(QualType QT) const final { 435 return isUncheckedPtr(QT.getCanonicalType()); 436 } 437 438 bool isSafePtr(const CXXRecordDecl *Record) const final { 439 return isRefCounted(Record) || isCheckedPtr(Record); 440 } 441 442 bool isSafePtrType(const QualType type) const final { 443 return isRefOrCheckedPtrType(type); 444 } 445 446 bool isSafeExpr(const Expr *E) const final { 447 return isExprToGetCheckedPtrCapableMember(E); 448 } 449 450 const char *ptrKind() const final { return "unchecked"; } 451 }; 452 453 class UnretainedCallArgsChecker final : public RawPtrRefCallArgsChecker { 454 public: 455 UnretainedCallArgsChecker() 456 : RawPtrRefCallArgsChecker("Unretained call argument for a raw " 457 "pointer/reference parameter") { 458 RTC = RetainTypeChecker(); 459 } 460 461 std::optional<bool> isUnsafeType(QualType QT) const final { 462 return RTC->isUnretained(QT); 463 } 464 465 std::optional<bool> isUnsafePtr(QualType QT) const final { 466 return RTC->isUnretained(QT); 467 } 468 469 bool isSafePtr(const CXXRecordDecl *Record) const final { 470 return isRetainPtr(Record); 471 } 472 473 bool isSafePtrType(const QualType type) const final { 474 return isRetainPtrType(type); 475 } 476 477 bool isSafeExpr(const Expr *E) const final { 478 return ento::cocoa::isCocoaObjectRef(E->getType()) && 479 isa<ObjCMessageExpr>(E); 480 } 481 482 const char *ptrKind() const final { return "unretained"; } 483 }; 484 485 } // namespace 486 487 void ento::registerUncountedCallArgsChecker(CheckerManager &Mgr) { 488 Mgr.registerChecker<UncountedCallArgsChecker>(); 489 } 490 491 bool ento::shouldRegisterUncountedCallArgsChecker(const CheckerManager &) { 492 return true; 493 } 494 495 void ento::registerUncheckedCallArgsChecker(CheckerManager &Mgr) { 496 Mgr.registerChecker<UncheckedCallArgsChecker>(); 497 } 498 499 bool ento::shouldRegisterUncheckedCallArgsChecker(const CheckerManager &) { 500 return true; 501 } 502 503 void ento::registerUnretainedCallArgsChecker(CheckerManager &Mgr) { 504 Mgr.registerChecker<UnretainedCallArgsChecker>(); 505 } 506 507 bool ento::shouldRegisterUnretainedCallArgsChecker(const CheckerManager &) { 508 return true; 509 } 510