34bool isRefcountedStringsHack(
const VarDecl *
V) {
36 auto safeClass = [](
const std::string &className) {
37 return className ==
"String" || className ==
"AtomString" ||
38 className ==
"UniquedString" || className ==
"Identifier";
42 if (
auto *CXXRD =
T->getAsCXXRecordDecl()) {
46 if (
T->isPointerType() ||
T->isReferenceType()) {
47 if (
auto *CXXRD =
T->getPointeeCXXRecordDecl()) {
56 const VarDecl *Guardian{
nullptr};
57 bool GuardianIsRawPtrOrRef{
false};
59 explicit GuardianVisitor(
const VarDecl *Guardian,
60 bool GuardianIsRawPtrOrRef =
false)
61 : Guardian(Guardian), GuardianIsRawPtrOrRef(GuardianIsRawPtrOrRef) {
65 bool VisitBinaryOperator(BinaryOperator *BO)
override {
67 if (
auto *VarRef = dyn_cast<DeclRefExpr>(BO->
getLHS())) {
68 if (VarRef->getDecl() == Guardian)
75 bool VisitCXXConstructExpr(CXXConstructExpr *CE)
override {
79 unsigned ArgIndex = 0;
81 ParmVarDecl *Parm =
nullptr;
82 if (ArgIndex < Ctor->getNumParams())
83 Parm = Ctor->getParamDecl(ArgIndex);
84 if (mutatesGuardian(Arg, Parm))
91 bool VisitCallExpr(CallExpr *CE)
override {
97 if (
auto *
Method = dyn_cast<CXXMethodDecl>(Callee)) {
102 auto *OperatorMethod = dyn_cast<CXXMethodDecl>(Callee);
104 !OperatorMethod->isImplicitObjectMemberFunction())
105 OperatorMethod =
nullptr;
106 unsigned ArgIndex = 0;
107 unsigned ArgOffset = OperatorMethod ? 1 : 0;
109 ParmVarDecl *Parm =
nullptr;
110 if (ArgIndex >= ArgOffset) {
111 unsigned ParmIndex = ArgIndex - ArgOffset;
112 if (ParmIndex < Callee->getNumParams())
113 Parm =
Callee->getParamDecl(ParmIndex);
114 }
else if (OperatorMethod->isConst()) {
118 if (mutatesGuardian(Arg, Parm))
125 bool VisitCXXMemberCallExpr(CXXMemberCallExpr *MCE)
override {
126 if (GuardianIsRawPtrOrRef)
130 if (ObjType.isConstQualified())
133 if (
auto *VarRef = dyn_cast<DeclRefExpr>(ThisArg)) {
141 bool mutatesGuardian(
const Expr *Arg,
const ParmVarDecl *ParmDecl) {
144 bool IsAddressOf =
false;
145 if (
auto *UO = dyn_cast<UnaryOperator>(Arg);
146 UO && UO->getOpcode() == UO_AddrOf) {
150 auto *VarRef = dyn_cast<DeclRefExpr>(Arg);
151 if (!VarRef || VarRef->getDecl() != Guardian)
153 if (GuardianIsRawPtrOrRef && !Guardian->getType()->isAnyPointerType())
156 if (!ArgType->isPointerType())
158 return !ArgType->getPointeeType().isConstQualified();
160 if (GuardianIsRawPtrOrRef) {
161 if (!ArgType->isReferenceType())
163 return !ArgType.getNonReferenceType().isConstQualified();
165 return !ArgType.getNonReferenceType().isConstQualified();
169bool isGuardedScopeEmbeddedInGuardianScope(
const VarDecl *Guarded,
170 const VarDecl *MaybeGuardian) {
172 assert(MaybeGuardian);
177 const CompoundStmt *guardiansClosestCompStmtAncestor =
nullptr;
182 !guardianAncestors.
empty();
187 for (
auto &guardianAncestor : guardianAncestors) {
188 if (
auto *CStmtParentAncestor = guardianAncestor.get<
CompoundStmt>()) {
189 guardiansClosestCompStmtAncestor = CStmtParentAncestor;
193 if (guardiansClosestCompStmtAncestor)
197 if (!guardiansClosestCompStmtAncestor)
204 !guardedVarAncestors.
empty();
209 for (
auto &guardedVarAncestor : guardedVarAncestors) {
210 if (
auto *CStmtAncestor = guardedVarAncestor.get<
CompoundStmt>()) {
211 if (!FirstCompondStmt) {
212 FirstCompondStmt = CStmtAncestor;
215 if (CStmtAncestor == guardiansClosestCompStmtAncestor) {
216 GuardianVisitor guardianVisitor(MaybeGuardian);
217 auto *GuardedScope =
const_cast<CompoundStmt *
>(FirstCompondStmt);
218 return guardianVisitor.TraverseCompoundStmt(GuardedScope);
230findObjCForCollectionStmtOfElement(
const VarDecl *
V) {
233 auto *DS = VarParent.get<
DeclStmt>();
234 if (!DS || !DS->isSingleDecl())
236 for (
auto &DeclStmtParent : Ctx.
getParents(*DS)) {
238 if (FCS && FCS->getElement() == DS)
245static const VarDecl *findAssignedVar(
const Expr *DestExpr) {
248 if (
auto *DRE = dyn_cast<DeclRefExpr>(DestExpr))
249 return dyn_cast_or_null<VarDecl>(DRE->getDecl());
250 if (
auto *UO = dyn_cast<UnaryOperator>(DestExpr);
251 UO && UO->getOpcode() == UO_Deref) {
252 DestExpr = UO->getSubExpr();
255 if (
auto *ASE = dyn_cast<ArraySubscriptExpr>(DestExpr)) {
256 DestExpr = ASE->getBase();
264class RawPtrRefLocalVarsChecker
265 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
267 EnsureFunctionAnalysis EFA;
270 mutable BugReporter *BR;
271 const std::unique_ptr<PtrRefSafetyModel> Model;
274 RawPtrRefLocalVarsChecker(
const char *description,
275 std::unique_ptr<PtrRefSafetyModel> Model)
276 : Bug(this, description,
"WebKit coding guidelines"),
277 Model(std::move(Model)) {}
279 std::optional<bool> isUnsafePtr(QualType
T)
const {
283 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
284 BugReporter &BRArg)
const {
291 const RawPtrRefLocalVarsChecker *Checker;
292 Decl *DeclWithIssue{
nullptr};
294 TrivialFunctionAnalysis TFA;
296 explicit LocalVisitor(
const RawPtrRefLocalVarsChecker *Checker)
299 ShouldVisitTemplateInstantiations =
true;
300 ShouldVisitImplicitCode =
false;
303 bool TraverseDecl(Decl *D)
override {
311 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
317 bool TraverseLambdaExpr(
LambdaExpr *L)
override {
320 return DynamicRecursiveASTVisitor::TraverseLambdaExpr(L);
330 for (
unsigned I = 0, N = L->
capture_size(); I != N; ++I) {
337 return TraverseDecl(FTD);
340 bool VisitTypedefDecl(TypedefDecl *TD)
override {
341 if (
auto *RTC = Checker->Model->retainTypeChecker())
342 RTC->visitTypedef(TD);
346 bool VisitVarDecl(VarDecl *
V)
override {
347 auto *
Init =
V->getInit();
348 if (
V->isLocalVarDecl())
349 Checker->visitVarDecl(
V,
V->getType(),
Init, DeclWithIssue);
353 bool VisitBinaryOperator(BinaryOperator *BO)
override {
355 if (Checker->Model->recognizesIndirectStores()) {
356 if (
auto *
V = findAssignedVar(BO->
getLHS()))
359 }
else if (
auto *VarRef = dyn_cast<DeclRefExpr>(BO->
getLHS())) {
360 if (
auto *
V = dyn_cast<VarDecl>(VarRef->getDecl()))
361 Checker->visitVarDecl(
V,
V->getType(), BO->
getRHS(),
368 bool TraverseIfStmt(IfStmt *IS)
override {
376 return TraverseStmt(Else);
381 return DynamicRecursiveASTVisitor::TraverseIfStmt(IS);
385 bool TraverseForStmt(ForStmt *FS)
override {
387 return DynamicRecursiveASTVisitor::TraverseForStmt(FS);
391 bool TraverseCXXForRangeStmt(CXXForRangeStmt *FRS)
override {
393 return DynamicRecursiveASTVisitor::TraverseCXXForRangeStmt(FRS);
397 bool TraverseWhileStmt(WhileStmt *WS)
override {
399 return DynamicRecursiveASTVisitor::TraverseWhileStmt(WS);
405 return DynamicRecursiveASTVisitor::TraverseCompoundStmt(CS);
409 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl)
override {
412 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
416 LocalVisitor visitor(
this);
417 if (
auto *RTC = Model->retainTypeChecker())
418 RTC->visitTranslationUnitDecl(TUD);
419 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
422 void visitVarDecl(
const VarDecl *
V, QualType SinkType,
const Expr *
Value,
423 const Decl *DeclWithIssue)
const {
424 if (shouldSkipVarDecl(
V))
427 if (
auto *DD = dyn_cast<DecompositionDecl>(
V)) {
428 const auto *InitList =
430 if (InitList && InitList->getNumInits() != DD->bindings().size())
433 unsigned BindingIndex = 0;
434 for (
auto *BD : DD->bindings()) {
435 const unsigned Index = BindingIndex++;
436 auto *Binding = BD->getBinding();
439 std::optional<bool> IsUncountedPtr = isUnsafePtr(Binding->getType());
440 if (!IsUncountedPtr || !*IsUncountedPtr)
443 const Expr *Origin =
nullptr;
444 if (Model->checksForInteriorDestruction()) {
445 const Expr *Source = InitList ? InitList->getInit(Index) :
Value;
446 if (isPtrOriginSafe(
V, Source, DeclWithIssue, Origin,
450 reportBug(
V,
V->getType(),
nullptr, BD, DeclWithIssue, Origin);
454 std::optional<bool> IsUncountedPtr = isUnsafePtr(SinkType);
455 if (IsUncountedPtr && *IsUncountedPtr) {
456 const Expr *Origin =
nullptr;
461 if (
auto *FCS = findObjCForCollectionStmtOfElement(
V))
462 Value = FCS->getCollection();
465 if (isPtrOriginSafe(
V,
Value, DeclWithIssue, Origin, SinkType))
467 }
else if (Model->checksForInteriorDestruction())
469 reportBug(
V, SinkType,
Value,
nullptr, DeclWithIssue, Origin);
473 bool isPtrOriginSafe(
const VarDecl *
V,
const Expr *
Value,
474 const Decl *DeclWithIssue,
const Expr *&Origin,
475 QualType SinkType = QualType())
const {
478 Model->checksForInteriorDestruction(),
479 [&](
const clang::CXXRecordDecl *
Record) {
480 return Model->isSafePtr(Record);
482 [&](
const clang::QualType
Type) { return Model->isSafePtrType(Type); },
483 [&](
const clang::Decl *D) {
484 return Model->isSafeDecl(D, BR->getSourceManager());
486 [&](
const clang::Expr *InitArgOrigin,
bool IsSafe,
487 bool OriginDependsOnFullExpressionTemporary,
488 bool PtrIsLifetimeBoundToOrigin) {
493 if (!OriginDependsOnFullExpressionTemporary)
496 Origin = InitArgOrigin;
512 if (EFA.isACallToEnsureFn(InitArgOrigin))
515 if (Model->isSafeExpr(InitArgOrigin, PtrIsLifetimeBoundToOrigin,
519 if (!Model->checksForInteriorDestruction() &&
520 hasGuardian(
V, InitArgOrigin, DeclWithIssue))
524 Origin = InitArgOrigin;
529 bool hasGuardian(
const VarDecl *
V,
const Expr *InitArgOrigin,
530 const Decl *DeclWithIssue)
const {
531 auto *Ref = dyn_cast<DeclRefExpr>(InitArgOrigin);
535 auto *MaybeGuardian = dyn_cast_or_null<VarDecl>(Ref->getFoundDecl());
544 if (
auto *FCS = findObjCForCollectionStmtOfElement(
V);
547 GuardianVisitor Visitor(MaybeGuardian,
true);
548 return Visitor.TraverseStmt(
const_cast<Stmt *
>(FCS->getBody()));
551 if (!GuardianType.
isNull()) {
554 (Model->isSafePtr(
Record) ||
555 isRefcountedStringsHack(MaybeGuardian))) {
558 if (
auto *FCS = findObjCForCollectionStmtOfElement(
V)) {
559 GuardianVisitor Visitor(MaybeGuardian);
560 return Visitor.TraverseStmt(
const_cast<Stmt *
>(FCS->getBody()));
562 if (isGuardedScopeEmbeddedInGuardianScope(
V, MaybeGuardian))
569 bool IsRawPtrOrRef = isUnsafePtr(GuardianType).value_or(
false);
570 GuardianVisitor Visitor{MaybeGuardian, IsRawPtrOrRef};
571 if (
auto *FD = dyn_cast<FunctionDecl>(DeclWithIssue))
572 return Visitor.TraverseStmt(FD->getBody());
573 if (
auto *MD = dyn_cast<ObjCMethodDecl>(DeclWithIssue))
574 return Visitor.TraverseStmt(MD->getBody());
580 bool shouldSkipVarDecl(
const VarDecl *
V)
const {
584 if (
V->isInitCapture())
591 const Expr *Origin)
const {
594 llvm::raw_svector_ostream Os(Buf);
600 Model->describeHazard(Os, Origin, SinkType);
604 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
607 Report->setDeclWithIssue(DeclWithIssue);
610 if (
V->hasLocalStorage())
611 Os <<
"Local variable ";
612 else if (
V->isStaticLocal())
613 Os <<
"Static local variable ";
614 else if (
V->hasGlobalStorage())
615 Os <<
"Global variable ";
623 Model->describeHazard(Os, Origin, SinkType);
626 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
627 Report->addRange(
V->getSourceRange());
628 Report->setDeclWithIssue(DeclWithIssue);
634class UncountedLocalVarsChecker final :
public RawPtrRefLocalVarsChecker {
636 UncountedLocalVarsChecker()
637 : RawPtrRefLocalVarsChecker(
"Uncounted raw pointer or reference not "
638 "provably backed by ref-counted variable",
642class UncheckedLocalVarsChecker final :
public RawPtrRefLocalVarsChecker {
644 UncheckedLocalVarsChecker()
645 : RawPtrRefLocalVarsChecker(
"Unchecked raw pointer or reference not "
646 "provably backed by checked variable",
650class UnretainedLocalVarsChecker final :
public RawPtrRefLocalVarsChecker {
652 UnretainedLocalVarsChecker()
653 : RawPtrRefLocalVarsChecker(
"Unretained raw pointer or reference not "
654 "provably backed by a RetainPtr",
658class UnborrowedLocalVarsChecker final :
public RawPtrRefLocalVarsChecker {
660 UnborrowedLocalVarsChecker()
661 : RawPtrRefLocalVarsChecker(
"Unborrowed raw pointer or reference not "
662 "provably backed by a Borrow",
668void ento::registerUncountedLocalVarsChecker(
CheckerManager &Mgr) {
672bool ento::shouldRegisterUncountedLocalVarsChecker(
const CheckerManager &) {
676void ento::registerUncheckedLocalVarsChecker(
CheckerManager &Mgr) {
680bool ento::shouldRegisterUncheckedLocalVarsChecker(
const CheckerManager &) {
684void ento::registerUnretainedLocalVarsChecker(
CheckerManager &Mgr) {
688bool ento::shouldRegisterUnretainedLocalVarsChecker(
const CheckerManager &) {
692void ento::registerUnborrowedLocalVarsChecker(
CheckerManager &Mgr) {
696bool ento::shouldRegisterUnborrowedLocalVarsChecker(
const CheckerManager &) {
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
llvm::MachO::Record Record
Defines the clang::SourceLocation class and associated facilities.
Defines the SourceManager interface.
Defines the Objective-C statement AST node classes.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
static bool isAssignmentOp(Opcode Opc)
A binding in a decomposition declaration.
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
CXXMethodDecl * getMethodDecl() const
Retrieve the declaration of the called method.
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
QualType getObjectType() const
Retrieve the type of the object argument.
FunctionTemplateDecl * getDependentLambdaCallOperator() const
Retrieve the dependent lambda call operator of the closure type if this is a templated closure type.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
CompoundStmt - This represents a group of statements like { stmt stmt }.
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Decl - This represents one declaration (or definition), e.g.
ASTContext & getASTContext() const LLVM_READONLY
bool isTemplated() const
Determine whether this declaration is a templated entity (whether it is.
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
virtual bool TraverseDecl(MaybeConst< Decl > *D)
This represents one expression.
Expr * IgnoreParenCasts() LLVM_READONLY
Skip past any parentheses and casts which might surround this expression until reaching a fixed point...
VarDecl * getConditionVariable()
Retrieve the variable declared in this "if" statement, if any.
capture_iterator capture_begin() const
Retrieve an iterator pointing to the first lambda capture.
unsigned capture_size() const
Determine the number of captures in this lambda.
capture_init_iterator capture_init_begin()
Retrieve the first initialization argument for this lambda expression (which initializes the first ca...
CXXRecordDecl * getLambdaClass() const
Retrieve the class that corresponds to the lambda.
Represents Objective-C's collection statement.
A (possibly-)qualified type.
bool isNull() const
Return true if this QualType doesn't point to a type yet.
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Encodes a location in the source.
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
Stmt - This represents one statement.
bool isTrivial(const Decl *D) const
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
bool isAnyPointerType() const
Represents a variable declaration or definition.
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
const SourceManager & getSourceManager()
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
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.
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.
Top level wrappers for InstallAPI frontend operations.
bool isa(CodeGen::Address addr)
std::unique_ptr< PtrRefSafetyModel > makeBorrowSafetyModel()
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
bool isPtrConversion(const FunctionDecl *F)
std::unique_ptr< PtrRefSafetyModel > makeCheckedPtrSafetyModel()
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
nullptr
This class represents a compute construct, representing a 'Kind' of ‘parallel’, 'serial',...
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...
const FunctionProtoType * T
bool isSmartPtrClass(const std::string &Name)
std::optional< bool > isGetterOfSafePtr(const CXXMethodDecl *M)
std::string safeGetName(const T *ASTNode)
bool isNullPtr(const clang::Expr *E)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
std::unique_ptr< PtrRefSafetyModel > makeRefPtrSafetyModel()
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, bool FollowLifetimeBound, 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, bool, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
std::unique_ptr< PtrRefSafetyModel > makeRetainPtrSafetyModel()
bool isConstOwnerPtrMemberExpr(const clang::Expr *E)