clang 24.0.0git
RawPtrRefLocalVarsChecker.cpp
Go to the documentation of this file.
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"
14#include "clang/AST/Decl.h"
15#include "clang/AST/DeclCXX.h"
24#include <optional>
25
26using namespace clang;
27using namespace ento;
28
29namespace {
30
31// FIXME: should be defined by anotations in the future
32bool isRefcountedStringsHack(const VarDecl *V) {
33 assert(V);
34 auto safeClass = [](const std::string &className) {
35 return className == "String" || className == "AtomString" ||
36 className == "UniquedString" || className == "Identifier";
37 };
38 QualType QT = V->getType();
39 auto *T = QT.getTypePtr();
40 if (auto *CXXRD = T->getAsCXXRecordDecl()) {
41 if (safeClass(safeGetName(CXXRD)))
42 return true;
43 }
44 if (T->isPointerType() || T->isReferenceType()) {
45 if (auto *CXXRD = T->getPointeeCXXRecordDecl()) {
46 if (safeClass(safeGetName(CXXRD)))
47 return true;
48 }
49 }
50 return false;
51}
52
53struct GuardianVisitor : DynamicRecursiveASTVisitor {
54 const VarDecl *Guardian{nullptr};
55
56 explicit GuardianVisitor(const VarDecl *Guardian) : Guardian(Guardian) {
57 assert(Guardian);
58 }
59
60 bool VisitBinaryOperator(BinaryOperator *BO) override {
61 if (BO->isAssignmentOp()) {
62 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
63 if (VarRef->getDecl() == Guardian)
64 return false;
65 }
66 }
67 return true;
68 }
69
70 bool VisitCXXConstructExpr(CXXConstructExpr *CE) override {
71 auto *Ctor = CE->getConstructor();
72 if (!Ctor)
73 return false;
74 unsigned ArgIndex = 0;
75 for (auto *Arg : CE->arguments()) {
76 ParmVarDecl *Parm = nullptr;
77 if (ArgIndex < Ctor->getNumParams())
78 Parm = Ctor->getParamDecl(ArgIndex);
79 if (mutatesGuardian(Arg, Parm))
80 return false;
81 ArgIndex++;
82 }
83 return true;
84 }
85
86 bool VisitCallExpr(CallExpr *CE) override {
87 auto *Callee = CE->getDirectCallee();
88 if (!Callee)
89 return false;
90 if (isPtrConversion(Callee))
91 return true;
92 unsigned ArgIndex = 0;
93 unsigned ArgOffset = isa<CXXOperatorCallExpr>(CE);
94 for (auto *Arg : CE->arguments()) {
95 ParmVarDecl *Parm = nullptr;
96 if (ArgIndex >= ArgOffset) {
97 unsigned ParmIndex = ArgIndex - ArgOffset;
98 if (ParmIndex < Callee->getNumParams())
99 Parm = Callee->getParamDecl(ParmIndex);
100 }
101 if (mutatesGuardian(Arg, Parm))
102 return false;
103 ArgIndex++;
104 }
105 return true;
106 }
107
108 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE) override {
109 auto *Method = MCE->getMethodDecl();
110 auto ObjType = MCE->getObjectType();
111 if (ObjType.isConstQualified())
112 return true;
113 auto *ThisArg = MCE->getImplicitObjectArgument()->IgnoreParenCasts();
114 if (auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
115 if (!isa<CXXConversionDecl>(Method) && VarRef->getDecl() == Guardian)
116 return false;
117 }
118 return true;
119 }
120
121private:
122 bool mutatesGuardian(const Expr *Arg, const ParmVarDecl *ParmDecl) {
123 Arg = Arg->IgnoreParenCasts();
124 if (auto *VarRef = dyn_cast<DeclRefExpr>(Arg)) {
125 if (VarRef->getDecl() == Guardian) {
126 auto ArgType = ParmDecl ? ParmDecl->getType() : Arg->getType();
127 if (!ArgType.isConstQualified())
128 return true;
129 }
130 }
131 return false;
132 }
133};
134
135bool isGuardedScopeEmbeddedInGuardianScope(const VarDecl *Guarded,
136 const VarDecl *MaybeGuardian) {
137 assert(Guarded);
138 assert(MaybeGuardian);
139
140 if (!MaybeGuardian->isLocalVarDecl())
141 return false;
142
143 const CompoundStmt *guardiansClosestCompStmtAncestor = nullptr;
144
145 ASTContext &ctx = MaybeGuardian->getASTContext();
146
147 for (DynTypedNodeList guardianAncestors = ctx.getParents(*MaybeGuardian);
148 !guardianAncestors.empty();
149 guardianAncestors = ctx.getParents(
150 *guardianAncestors
151 .begin()) // FIXME - should we handle all of the parents?
152 ) {
153 for (auto &guardianAncestor : guardianAncestors) {
154 if (auto *CStmtParentAncestor = guardianAncestor.get<CompoundStmt>()) {
155 guardiansClosestCompStmtAncestor = CStmtParentAncestor;
156 break;
157 }
158 }
159 if (guardiansClosestCompStmtAncestor)
160 break;
161 }
162
163 if (!guardiansClosestCompStmtAncestor)
164 return false;
165
166 // We need to skip the first CompoundStmt to avoid situation when guardian is
167 // defined in the same scope as guarded variable.
168 const CompoundStmt *FirstCompondStmt = nullptr;
169 for (DynTypedNodeList guardedVarAncestors = ctx.getParents(*Guarded);
170 !guardedVarAncestors.empty();
171 guardedVarAncestors = ctx.getParents(
172 *guardedVarAncestors
173 .begin()) // FIXME - should we handle all of the parents?
174 ) {
175 for (auto &guardedVarAncestor : guardedVarAncestors) {
176 if (auto *CStmtAncestor = guardedVarAncestor.get<CompoundStmt>()) {
177 if (!FirstCompondStmt) {
178 FirstCompondStmt = CStmtAncestor;
179 continue;
180 }
181 if (CStmtAncestor == guardiansClosestCompStmtAncestor) {
182 GuardianVisitor guardianVisitor(MaybeGuardian);
183 auto *GuardedScope = const_cast<CompoundStmt *>(FirstCompondStmt);
184 return guardianVisitor.TraverseCompoundStmt(GuardedScope);
185 }
186 }
187 }
188 }
189
190 return false;
191}
192
193class RawPtrRefLocalVarsChecker
194 : public Checker<check::ASTDecl<TranslationUnitDecl>> {
195 BugType Bug;
196 EnsureFunctionAnalysis EFA;
197
198protected:
199 mutable BugReporter *BR;
200 const std::unique_ptr<PtrRefSafetyModel> Model;
201
202public:
203 RawPtrRefLocalVarsChecker(const char *description,
204 std::unique_ptr<PtrRefSafetyModel> Model)
205 : Bug(this, description, "WebKit coding guidelines"),
206 Model(std::move(Model)) {}
207
208 std::optional<bool> isUnsafePtr(QualType T) const {
209 return isUnsafePtrForStorage(*Model, T);
210 }
211
212 void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
213 BugReporter &BRArg) const {
214 BR = &BRArg;
215
216 // The calls to checkAST* from AnalysisConsumer don't
217 // visit template instantiations or lambda classes. We
218 // want to visit those, so we make our own RecursiveASTVisitor.
219 struct LocalVisitor : DynamicRecursiveASTVisitor {
220 const RawPtrRefLocalVarsChecker *Checker;
221 Decl *DeclWithIssue{nullptr};
222
223 TrivialFunctionAnalysis TFA;
224
225 explicit LocalVisitor(const RawPtrRefLocalVarsChecker *Checker)
226 : Checker(Checker) {
227 assert(Checker);
228 ShouldVisitTemplateInstantiations = true;
229 ShouldVisitImplicitCode = false;
230 }
231
232 bool TraverseDecl(Decl *D) override {
233 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
234 if (D && (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)))
235 DeclWithIssue = D;
237 }
238
239 bool VisitTypedefDecl(TypedefDecl *TD) override {
240 if (auto *RTC = Checker->Model->retainTypeChecker())
241 RTC->visitTypedef(TD);
242 return true;
243 }
244
245 bool VisitVarDecl(VarDecl *V) override {
246 auto *Init = V->getInit();
247 if (V->isLocalVarDecl())
248 Checker->visitVarDecl(V, Init, DeclWithIssue);
249 return true;
250 }
251
252 bool VisitBinaryOperator(BinaryOperator *BO) override {
253 if (BO->isAssignmentOp()) {
254 if (auto *VarRef = dyn_cast<DeclRefExpr>(BO->getLHS())) {
255 if (auto *V = dyn_cast<VarDecl>(VarRef->getDecl()))
256 Checker->visitVarDecl(V, BO->getRHS(), DeclWithIssue);
257 }
258 }
259 return true;
260 }
261
262 bool TraverseIfStmt(IfStmt *IS) override {
263 if (IS->getConditionVariable()) {
264 // This code currently does not explicitly check the "else" statement
265 // since getConditionVariable returns nullptr when there is a
266 // condition defined after ";" as in "if (auto foo = ~; !foo)". If
267 // this semantics change, we should add an explicit check for "else".
268 if (auto *Then = IS->getThen(); !Then || TFA.isTrivial(Then))
269 return true;
270 }
271 if (!TFA.isTrivial(IS))
272 return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);
273 return true;
274 }
275
276 bool TraverseForStmt(ForStmt *FS) override {
277 if (!TFA.isTrivial(FS))
278 return DynamicRecursiveASTVisitor::TraverseForStmt(FS);
279 return true;
280 }
281
282 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS) override {
283 if (!TFA.isTrivial(FRS))
284 return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS);
285 return true;
286 }
287
288 bool TraverseWhileStmt(WhileStmt *WS) override {
289 if (!TFA.isTrivial(WS))
290 return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS);
291 return true;
292 }
293
294 bool TraverseCompoundStmt(CompoundStmt *CS) override {
295 if (!TFA.isTrivial(CS))
296 return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS);
297 return true;
298 }
299
300 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl) override {
301 if (isSmartPtrClass(safeGetName(Decl)))
302 return true;
303 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
304 }
305 };
306
307 LocalVisitor visitor(this);
308 if (auto *RTC = Model->retainTypeChecker())
309 RTC->visitTranslationUnitDecl(TUD);
310 visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
311 }
312
313 void visitVarDecl(const VarDecl *V, const Expr *Value,
314 const Decl *DeclWithIssue) const {
315 if (shouldSkipVarDecl(V))
316 return;
317
318 if (auto *DD = dyn_cast<DecompositionDecl>(V)) {
319 for (auto *BD : DD->bindings()) {
320 auto *Binding = BD->getBinding();
321 if (!Binding)
322 continue;
323 std::optional<bool> IsUncountedPtr = isUnsafePtr(Binding->getType());
324 if (!IsUncountedPtr || !*IsUncountedPtr)
325 continue;
326 reportBug(V, nullptr, BD, DeclWithIssue);
327 }
328 }
329
330 std::optional<bool> IsUncountedPtr = isUnsafePtr(V->getType());
331 if (IsUncountedPtr && *IsUncountedPtr) {
332 if (Value && isPtrOriginSafe(V, Value, DeclWithIssue))
333 return;
334 reportBug(V, Value, nullptr, DeclWithIssue);
335 }
336 }
337
338 bool isPtrOriginSafe(const VarDecl *V, const Expr *Value,
339 const Decl *DeclWithIssue) const {
340 return tryToFindPtrOrigin(
341 Value, /*StopAtFirstRefCountedObj=*/false,
342 [&](const clang::CXXRecordDecl *Record) {
343 return Model->isSafePtr(Record);
344 },
345 [&](const clang::QualType Type) { return Model->isSafePtrType(Type); },
346 [&](const clang::Decl *D) {
347 return Model->isSafeDecl(D, BR->getSourceManager());
348 },
349 [&](const clang::Expr *InitArgOrigin, bool IsSafe) {
350 if (!InitArgOrigin || IsSafe)
351 return true;
352
353 if (isa<CXXThisExpr>(InitArgOrigin))
354 return true;
355
356 if (isNullPtr(InitArgOrigin))
357 return true;
358
359 if (isa<IntegerLiteral>(InitArgOrigin))
360 return true;
361
362 if (isConstOwnerPtrMemberExpr(InitArgOrigin))
363 return true;
364
365 if (EFA.isACallToEnsureFn(InitArgOrigin))
366 return true;
367
368 if (Model->isSafeExpr(InitArgOrigin))
369 return true;
370
371 if (auto *Ref = llvm::dyn_cast<DeclRefExpr>(InitArgOrigin)) {
372 if (auto *MaybeGuardian =
373 dyn_cast_or_null<VarDecl>(Ref->getFoundDecl())) {
374 const auto *MaybeGuardianArgType =
375 MaybeGuardian->getType().getTypePtr();
376 if (MaybeGuardianArgType) {
377 const CXXRecordDecl *const MaybeGuardianArgCXXRecord =
378 MaybeGuardianArgType->getAsCXXRecordDecl();
379 if (MaybeGuardianArgCXXRecord) {
380 if (MaybeGuardian->isLocalVarDecl() &&
381 (Model->isSafePtr(MaybeGuardianArgCXXRecord) ||
382 isRefcountedStringsHack(MaybeGuardian)) &&
383 isGuardedScopeEmbeddedInGuardianScope(V, MaybeGuardian))
384 return true;
385 }
386 }
387
388 if (isa<ParmVarDecl>(MaybeGuardian)) {
389 if (auto *FD = dyn_cast<FunctionDecl>(DeclWithIssue)) {
390 if (GuardianVisitor{MaybeGuardian}.TraverseStmt(
391 FD->getBody()))
392 return true;
393 }
394 if (auto *MD = dyn_cast<ObjCMethodDecl>(DeclWithIssue)) {
395 if (GuardianVisitor{MaybeGuardian}.TraverseStmt(
396 MD->getBody()))
397 return true;
398 }
399 }
400 }
401 }
402
403 return false;
404 });
405 }
406
407 bool shouldSkipVarDecl(const VarDecl *V) const {
408 assert(V);
410 return true;
411 return BR->getSourceManager().isInSystemHeader(V->getLocation());
412 }
413
414 void reportBug(const VarDecl *V, const Expr *Value, const Decl *BindingDecl,
415 const Decl *DeclWithIssue) const {
416 assert(V);
417 SmallString<100> Buf;
418 llvm::raw_svector_ostream Os(Buf);
419
420 if (isa<ParmVarDecl>(V)) {
421 Os << "Parameter ";
423 Os << " is a ";
424 printPointerTypeAndType(Os, V->getType());
425
426 SourceLocation ExprLoc = (Value) ? Value->getExprLoc() : V->getLocation();
427 PathDiagnosticLocation BSLoc(ExprLoc, BR->getSourceManager());
428 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
429 if (Value)
430 Report->addRange(Value->getSourceRange());
431 BR->emitReport(std::move(Report));
432 } else {
433 if (V->hasLocalStorage())
434 Os << "Local variable ";
435 else if (V->isStaticLocal())
436 Os << "Static local variable ";
437 else if (V->hasGlobalStorage())
438 Os << "Global variable ";
439 else
440 Os << "Variable ";
441 if (BindingDecl)
442 Os << "'" << safeGetName(BindingDecl) << "'";
443 else
445 Os << " is a ";
446 printPointerTypeAndType(Os, V->getType());
447
448 PathDiagnosticLocation BSLoc(V->getLocation(), BR->getSourceManager());
449 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
450 Report->addRange(V->getSourceRange());
451 Report->setDeclWithIssue(DeclWithIssue);
452 BR->emitReport(std::move(Report));
453 }
454 }
455
456 void printPointerTypeAndType(llvm::raw_svector_ostream &Os,
457 QualType QT) const {
458 auto *VarType = QT.getTypePtr();
459 auto *RTC = Model->retainTypeChecker();
460 if (RTC && isa<TypedefType>(VarType)) {
461 Os << Model->typeName() << " ";
462 if (auto *Decl = RTC->getCanonicalDecl(QT)) {
463 printQuotedQualifiedName(Os, Decl);
464 } else {
465 auto Typedef = VarType->getAs<TypedefType>();
466 assert(Typedef);
467 printQuotedQualifiedName(Os, Typedef->getDecl());
468 }
469 } else {
470 auto *DesugaredType = VarType->getUnqualifiedDesugaredType();
471 bool IsPtr = isa<PointerType, ObjCObjectPointerType>(DesugaredType);
472 Os << "raw " << (IsPtr ? "pointer" : "reference") << " to ";
473 Os << Model->typeName() << " ";
474 printTypeName(Os, QT);
475 }
476 }
477};
478
479class UncountedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
480public:
481 UncountedLocalVarsChecker()
482 : RawPtrRefLocalVarsChecker("Uncounted raw pointer or reference not "
483 "provably backed by ref-counted variable",
485};
486
487class UncheckedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
488public:
489 UncheckedLocalVarsChecker()
490 : RawPtrRefLocalVarsChecker("Unchecked raw pointer or reference not "
491 "provably backed by checked variable",
493};
494
495class UnretainedLocalVarsChecker final : public RawPtrRefLocalVarsChecker {
496public:
497 UnretainedLocalVarsChecker()
498 : RawPtrRefLocalVarsChecker("Unretained raw pointer or reference not "
499 "provably backed by a RetainPtr",
501};
502
503} // namespace
504
505void ento::registerUncountedLocalVarsChecker(CheckerManager &Mgr) {
506 Mgr.registerChecker<UncountedLocalVarsChecker>();
507}
508
509bool ento::shouldRegisterUncountedLocalVarsChecker(const CheckerManager &) {
510 return true;
511}
512
513void ento::registerUncheckedLocalVarsChecker(CheckerManager &Mgr) {
514 Mgr.registerChecker<UncheckedLocalVarsChecker>();
515}
516
517bool ento::shouldRegisterUncheckedLocalVarsChecker(const CheckerManager &) {
518 return true;
519}
520
521void ento::registerUnretainedLocalVarsChecker(CheckerManager &Mgr) {
522 Mgr.registerChecker<UnretainedLocalVarsChecker>();
523}
524
525bool ento::shouldRegisterUnretainedLocalVarsChecker(const CheckerManager &) {
526 return true;
527}
#define V(N, I)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
llvm::MachO::Record Record
Definition MachO.h:31
Defines the clang::SourceLocation class and associated facilities.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:223
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
Expr * getLHS() const
Definition Expr.h:4094
Expr * getRHS() const
Definition Expr.h:4096
static bool isAssignmentOp(Opcode Opc)
Definition Expr.h:4180
arg_range arguments()
Definition ExprCXX.h:1675
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
Definition ExprCXX.h:1614
CXXMethodDecl * getMethodDecl() const
Retrieve the declaration of the called method.
Definition ExprCXX.cpp:748
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
Definition ExprCXX.cpp:729
QualType getObjectType() const
Retrieve the type of the object argument.
Definition ExprCXX.cpp:741
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition Expr.h:3132
arg_range arguments()
Definition Expr.h:3201
CompoundStmt - This represents a group of statements like { stmt stmt }.
Definition Stmt.h:1749
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:550
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
virtual bool TraverseDecl(MaybeConst< Decl > *D)
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
Definition Expr.cpp:3106
QualType getType() const
Definition Expr.h:144
Stmt * getThen()
Definition Stmt.h:2357
VarDecl * getConditionVariable()
Retrieve the variable declared in this "if" statement, if any.
Definition Stmt.cpp:1068
A (possibly-)qualified type.
Definition TypeBase.h:938
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8495
bool isTrivial(const Decl *D, const Stmt **OffendingStmt=nullptr) const
QualType getType() const
Definition Decl.h:723
Represents a variable declaration or definition.
Definition Decl.h:932
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition Decl.h:1274
CHECKER * registerChecker(AT &&...Args)
Register a single-part checker (derived from Checker): construct its singleton instance,...
Simple checker classes that implement one frontend (i.e.
Definition Checker.h:565
std::variant< struct RequiresDecl, struct HeaderDecl, struct UmbrellaDirDecl, struct ModuleDecl, struct ExcludeDecl, struct ExportDecl, struct ExportAsDecl, struct ExternModuleDecl, struct UseDecl, struct LinkDecl, struct ConfigMacrosDecl, struct ConflictDecl > Decl
All declarations that can appear in a module declaration.
The JSON file list parser is used to communicate input to InstallAPI.
bool isa(CodeGen::Address addr)
Definition Address.h:330
bool isPtrConversion(const FunctionDecl *F)
std::unique_ptr< PtrRefSafetyModel > makeCheckedPtrSafetyModel()
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
std::optional< bool > isUnsafePtrForStorage(const PtrRefSafetyModel &Model, QualType T, bool IgnoreARC=false)
Applies the memory-management exemptions that hold for a variable, member, or lambda capture (but not...
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, std::function< bool(const clang::CXXRecordDecl *)> isSafePtr, std::function< bool(const clang::QualType)> isSafePtrType, std::function< bool(const clang::Decl *)> isSafeGlobalDecl, std::function< bool(const clang::Expr *, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
Definition ASTUtils.cpp:25
const FunctionProtoType * T
bool isSmartPtrClass(const std::string &Name)
@ Type
The name was classified as a type.
Definition Sema.h:564
void printTypeName(llvm::raw_ostream &Os, const QualType QT)
std::string safeGetName(const T *ASTNode)
Definition ASTUtils.h:98
bool isNullPtr(const clang::Expr *E)
Definition ASTUtils.cpp:287
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
std::unique_ptr< PtrRefSafetyModel > makeRefPtrSafetyModel()
std::unique_ptr< PtrRefSafetyModel > makeRetainPtrSafetyModel()
bool isConstOwnerPtrMemberExpr(const clang::Expr *E)
Definition ASTUtils.cpp:297