clang 24.0.0git
RawPtrRefSafetyModel.cpp
Go to the documentation of this file.
1//=======- RawPtrRefSafetyModel.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
10#include "ASTUtils.h"
11#include "DiagOutputUtils.h"
12#include "clang/AST/Decl.h"
13#include "clang/AST/Expr.h"
14#include "clang/AST/ExprCXX.h"
15#include "clang/AST/ExprObjC.h"
16#include "clang/AST/Type.h"
19#include "llvm/ADT/STLExtras.h"
20
21using namespace clang;
22
23namespace {
24
25class RefCountedSafetyModel : public PtrRefSafetyModel {
26public:
27 std::optional<bool> isUnsafeType(QualType QT) const override {
28 return isUncounted(QT);
29 }
30 std::optional<bool> isUnsafePtr(QualType QT, bool) const override {
32 }
33 bool isSafePtr(const CXXRecordDecl *Record) const override {
35 }
36 bool isSafePtrType(QualType T) const override {
38 }
39 bool isPtrType(const std::string &Name) const override {
40 return isRefType(Name);
41 }
42 const char *typeName() const override { return "RefPtr-capable type"; }
43};
44
45class CheckedPtrSafetyModel : public PtrRefSafetyModel {
46public:
47 std::optional<bool> isUnsafeType(QualType QT) const override {
48 return isUnchecked(QT);
49 }
50 std::optional<bool> isUnsafePtr(QualType QT, bool) const override {
52 }
53 bool isSafePtr(const CXXRecordDecl *Record) const override {
55 }
56 bool isSafePtrType(QualType T) const override {
58 }
59 bool isPtrType(const std::string &Name) const override {
60 return isCheckedPtr(Name);
61 }
62 bool isSafeExpr(const Expr *E, bool, QualType, bool) const override {
64 }
65 const char *typeName() const override { return "CheckedPtr-capable type"; }
66};
67
68class RetainPtrSafetyModel : public PtrRefSafetyModel {
69 mutable RetainTypeChecker RTC;
70
71public:
72 std::optional<bool> isUnsafeType(QualType QT) const override {
73 return RTC.isUnretained(QT);
74 }
75 std::optional<bool> isUnsafePtr(QualType QT, bool IgnoreARC) const override {
76 return RTC.isUnretained(QT, IgnoreARC);
77 }
78 bool isSafePtr(const CXXRecordDecl *Record) const override {
80 }
81 bool isSafePtrType(QualType T) const override {
83 }
84 bool isPtrType(const std::string &Name) const override {
85 return isRetainPtrOrOSPtr(Name);
86 }
87 bool isSafeExpr(const Expr *E, bool, QualType, bool) const override {
90 }
91 bool isSafeDecl(const Decl *D, const SourceManager &SM) const override {
92 // Treat NS/CF globals in system header as immortal.
93 return SM.isInSystemHeader(D->getLocation());
94 }
95 void describeHazard(llvm::raw_ostream &Os, const Expr *Origin,
96 QualType SinkType) const override {
97 auto *VarType = SinkType.getTypePtr();
98 if (isa<TypedefType>(VarType)) {
99 Os << typeName() << " ";
100 if (auto *Decl = RTC.getCanonicalDecl(SinkType)) {
101 printQuotedQualifiedName(Os, Decl);
102 } else {
103 const auto *Typedef = VarType->getAs<TypedefType>();
104 assert(Typedef);
105 printQuotedQualifiedName(Os, Typedef->getDecl());
106 }
107 return;
108 }
109 PtrRefSafetyModel::describeHazard(Os, Origin, SinkType);
110 }
111 const char *typeName() const override { return "RetainPtr-capable type"; }
112 RetainTypeChecker *retainTypeChecker() const override { return &RTC; }
113};
114
115static std::optional<bool> isCanBorrowType(QualType T) {
116 auto *Record = T->getAsCXXRecordDecl();
117 if (!Record)
118 return false;
119 return isBorrowable(Record);
120}
121
122static bool isSameRecord(QualType A, QualType B) {
123 auto *RecordA = A->getAsCXXRecordDecl();
124 auto *RecordB = B->getAsCXXRecordDecl();
125 return RecordA && RecordB &&
126 RecordA->getCanonicalDecl() == RecordB->getCanonicalDecl();
127}
128
129static const ValueDecl *declaredObject(const Expr *E) {
130 const Expr *Stripped = E->IgnoreParenImpCasts();
131 if (auto *DeclRef = dyn_cast<DeclRefExpr>(Stripped))
132 return DeclRef->getDecl();
133 if (auto *Member = dyn_cast<MemberExpr>(Stripped))
134 return Member->getMemberDecl();
135 return nullptr;
136}
137
138// Returns the const-declared variable or member that E denotes, directly or
139// through a dereference that vends it as const, or nullptr.
140static const ValueDecl *constDeclaredObject(const Expr *E) {
141 const Expr *Stripped = E->IgnoreParenImpCasts();
142
143 if (auto *Op = dyn_cast<CXXOperatorCallExpr>(Stripped)) {
144 OverloadedOperatorKind Kind = Op->getOperator();
145 if ((Kind != OO_Star && Kind != OO_Arrow) || Op->getNumArgs() != 1)
146 return nullptr;
147 if (!pointeeType(Op->getType()).isConstQualified())
148 return nullptr;
149 return constDeclaredObject(Op->getArg(0));
150 }
151
152 const ValueDecl *Decl = declaredObject(Stripped);
153 if (!isa_and_nonnull<VarDecl>(Decl) && !isa_and_nonnull<FieldDecl>(Decl))
154 return nullptr;
155 QualType T = Decl->getType();
156 if (!T.isConstQualified() || T->isReferenceType() || T->isPointerType())
157 return nullptr;
158 return Decl;
159}
160
161static bool mayHoldPointerTo(QualType ViewType, QualType CanBorrowType) {
162 if (ViewType.isNull() || CanBorrowType.isNull())
163 return false;
164
165 if (QualType Pointee = ViewType->getPointeeType(); !Pointee.isNull())
166 return isSameRecord(Pointee, CanBorrowType);
167
168 auto *Record = ViewType->getAsCXXRecordDecl();
169 if (!Record)
170 return false;
171 if (isBorrow(Record))
172 return isSameRecord(borrowedType(ViewType), CanBorrowType);
173 Record = Record->getDefinition();
174 if (!Record)
175 return true;
176
177 return llvm::any_of(Record->fields(),
178 [&](const FieldDecl *Field) {
179 return mayHoldPointerTo(Field->getType(),
180 CanBorrowType);
181 }) ||
182 llvm::any_of(Record->bases(), [&](const CXXBaseSpecifier &Base) {
183 return mayHoldPointerTo(Base.getType(), CanBorrowType);
184 });
185}
186
187class BorrowSafetyModel : public PtrRefSafetyModel {
188public:
189 std::optional<bool> isUnsafeType(QualType QT) const override {
190 return isView(QT);
191 }
192 std::optional<bool> isUnsafePtr(QualType QT, bool) const override {
193 return isView(QT);
194 }
195 bool isSafePtr(const CXXRecordDecl *Record) const override {
196 return isBorrow(Record);
197 }
198 bool isSafePtrType(QualType T) const override { return isBorrowType(T); }
199 bool isPtrType(const std::string &Name) const override {
200 return isBorrow(Name);
201 }
202
203 bool isSafeExpr(const Expr *Origin, bool PtrIsLifetimeBoundToOrigin,
204 QualType SinkType, bool SinkMayEscape) const override {
205 QualType OriginType = pointeeType(Origin->getType());
206 if (OriginType.isNull())
207 return true;
208
209 // A Borrow or a non-global const object guards a loan only within its own
210 // scope, so neither vouches for a sink that may outlive that scope. This
211 // holds for a reference to the Borrow itself, not just a loan through it.
212 if (isBorrowType(OriginType))
213 return !SinkMayEscape;
214
215 if (!PtrIsLifetimeBoundToOrigin)
216 return true;
217
218 if (const ValueDecl *ConstObject = constDeclaredObject(Origin)) {
219 auto *ConstVar = dyn_cast<VarDecl>(ConstObject);
220 if (!SinkMayEscape || (ConstVar && ConstVar->hasGlobalStorage()))
221 return true;
222 }
223
224 if (Origin->isPRValue() &&
225 isCanBorrowType(Origin->getType()).value_or(false) &&
226 !SinkType.isNull() && !mayHoldPointerTo(SinkType, Origin->getType()))
227 return true;
228
229 auto *Record = OriginType->getAsCXXRecordDecl();
230 if (!Record)
231 return true;
232
233 auto Borrowable = isBorrowable(Record);
234 return !Borrowable || !*Borrowable;
235 }
236
237 bool checksForInteriorDestruction() const override { return true; }
238 bool recognizesIndirectStores() const override { return true; }
239 const char *typeName() const override { return "CanBorrow type"; }
240
241 void describeHazard(llvm::raw_ostream &Os, const Expr *Origin,
242 QualType SinkType) const override {
243 QualType SinkObject = pointeeType(SinkType);
244 if (!SinkObject.isNull() && isBorrowType(SinkObject)) {
245 Os << "Borrow that does not travel with the lambda";
246 return;
247 }
248
249 Os << "loan on ";
250 QualType OriginType = Origin ? pointeeType(Origin->getType()) : QualType();
251
252 // Name the borrowed type, not the Borrow<T> guard, when the loan was
253 // taken from a Borrow<T>.
254 if (!OriginType.isNull() && isBorrowType(OriginType))
255 OriginType = borrowedType(OriginType);
256
257 if (!OriginType.isNull() && OriginType->getAsRecordDecl()) {
258 Os << "CanBorrow type ";
259 printTypeName(Os, OriginType);
260 } else
261 Os << "a CanBorrow object";
262 Os << " that is not guarded by const or a Borrow";
263 }
264};
265
266} // namespace
267
268std::optional<bool> clang::isUnsafePtrForStorage(const PtrRefSafetyModel &Model,
269 QualType T, bool IgnoreARC) {
270 // A __strong / __weak Objective-C storage location is memory managed and
271 // thus safe. This exemption applies to variables/members/captures but not to
272 // call arguments, so it lives here rather than in the policy itself.
273 if (Model.retainTypeChecker() && T.hasStrongOrWeakObjCLifetime())
274 return false;
275 return Model.isUnsafePtr(T, IgnoreARC);
276}
277
278std::unique_ptr<PtrRefSafetyModel> clang::makeRefPtrSafetyModel() {
279 return std::make_unique<RefCountedSafetyModel>();
280}
281
282std::unique_ptr<PtrRefSafetyModel> clang::makeCheckedPtrSafetyModel() {
283 return std::make_unique<CheckedPtrSafetyModel>();
284}
285
286std::unique_ptr<PtrRefSafetyModel> clang::makeRetainPtrSafetyModel() {
287 return std::make_unique<RetainPtrSafetyModel>();
288}
289
290std::unique_ptr<PtrRefSafetyModel> clang::makeBorrowSafetyModel() {
291 return std::make_unique<BorrowSafetyModel>();
292}
Defines the clang::Expr interface and subclasses for C++ expressions.
llvm::MachO::Record Record
Definition MachO.h:31
Defines the SourceManager interface.
C Language Family Type Representation.
Represents a base class of a C++ class.
Definition DeclCXX.h:146
CXXRecordDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition DeclCXX.h:523
Decl - This represents one declaration (or definition), e.g.
Definition DeclBase.h:86
SourceLocation getLocation() const
Definition DeclBase.h:447
This represents one expression.
Definition Expr.h:113
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition Expr.cpp:3126
bool isPRValue() const
Definition Expr.h:286
QualType getType() const
Definition Expr.h:145
Represents a member of a struct/union/class.
Definition Decl.h:3299
Models one WebKit pointer-safety policy: ref-counted (RefPtr), checked (CheckedPtr),...
virtual void describeHazard(llvm::raw_ostream &Os, const Expr *, QualType SinkType) const
Prints a phrase describing why the reported value is unsafe, completing a sentence of the form "Local...
A (possibly-)qualified type.
Definition TypeBase.h:938
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition TypeBase.h:1005
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
Definition TypeBase.h:8446
QualType getCanonicalType() const
Definition TypeBase.h:8498
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition TypeBase.h:8519
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Definition Type.h:41
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:885
Represent the declaration of a variable (in which case it is an lvalue) a function (in which case it ...
Definition Decl.h:713
bool isCocoaObjectRef(QualType T)
constexpr bool isPtrType(PrimType T)
Definition PrimType.h:57
Top level wrappers for InstallAPI frontend operations.
OverloadedOperatorKind
Enumeration specifying the different kinds of C++ overloaded operators.
bool isa(CodeGen::Address addr)
Definition Address.h:330
std::unique_ptr< PtrRefSafetyModel > makeBorrowSafetyModel()
bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E)
Definition ASTUtils.cpp:555
QualType pointeeType(QualType T)
std::optional< bool > isUnchecked(const QualType T)
QualType borrowedType(QualType T)
bool isRefOrCheckedPtrType(const clang::QualType T)
bool isView(const clang::QualType T)
std::unique_ptr< PtrRefSafetyModel > makeCheckedPtrSafetyModel()
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isRetainPtrOrOSPtrType(const clang::QualType T)
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 isBorrow(const clang::CXXRecordDecl *R)
std::optional< bool > isBorrowable(const clang::CXXRecordDecl *R)
const FunctionProtoType * T
bool isRefCounted(const CXXRecordDecl *R)
bool isRetainPtrOrOSPtr(const std::string &Name)
bool isRefType(const std::string &Name)
std::optional< bool > isUncountedPtr(const QualType T)
bool isSafePtr(clang::CXXRecordDecl *Decl)
Definition ASTUtils.cpp:23
bool isCheckedPtr(const std::string &Name)
bool isBorrowType(const clang::QualType T)
std::unique_ptr< PtrRefSafetyModel > makeRefPtrSafetyModel()
std::unique_ptr< PtrRefSafetyModel > makeRetainPtrSafetyModel()
std::optional< bool > isUncounted(const QualType T)
std::optional< bool > isUncheckedPtr(const QualType T)