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
9#include "RawPtrRefSafetyModel.h"
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"
17#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
18#include "clang/Basic/SourceManager.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(T: QT);
29 }
30 std::optional<bool> isUnsafePtr(QualType QT, bool) const override {
31 return isUncountedPtr(T: QT.getCanonicalType());
32 }
33 bool isSafePtr(const CXXRecordDecl *Record) const override {
34 return isRefCounted(Class: Record) || isCheckedPtr(Class: Record);
35 }
36 bool isSafePtrType(QualType T) const override {
37 return isRefOrCheckedPtrType(T);
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(T: QT);
49 }
50 std::optional<bool> isUnsafePtr(QualType QT, bool) const override {
51 return isUncheckedPtr(T: QT.getCanonicalType());
52 }
53 bool isSafePtr(const CXXRecordDecl *Record) const override {
54 return isRefCounted(Class: Record) || isCheckedPtr(Class: Record);
55 }
56 bool isSafePtrType(QualType T) const override {
57 return isRefOrCheckedPtrType(T);
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 {
63 return isExprToGetCheckedPtrCapableMember(E);
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: IgnoreARC);
77 }
78 bool isSafePtr(const CXXRecordDecl *Record) const override {
79 return isRetainPtrOrOSPtr(Class: Record);
80 }
81 bool isSafePtrType(QualType T) const override {
82 return isRetainPtrOrOSPtrType(T);
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 {
88 return ento::cocoa::isCocoaObjectRef(T: E->getType()) &&
89 isa<ObjCMessageExpr>(Val: E);
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(Loc: 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>(Val: VarType)) {
99 Os << typeName() << " ";
100 if (auto *Decl = RTC.getCanonicalDecl(SinkType)) {
101 printQuotedQualifiedName(Os, D: Decl);
102 } else {
103 const auto *Typedef = VarType->getAs<TypedefType>();
104 assert(Typedef);
105 printQuotedQualifiedName(Os, D: 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(Class: 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>(Val: Stripped))
132 return DeclRef->getDecl();
133 if (auto *Member = dyn_cast<MemberExpr>(Val: 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>(Val: Stripped)) {
144 OverloadedOperatorKind Kind = Op->getOperator();
145 if ((Kind != OO_Star && Kind != OO_Arrow) || Op->getNumArgs() != 1)
146 return nullptr;
147 if (!pointeeType(T: Op->getType()).isConstQualified())
148 return nullptr;
149 return constDeclaredObject(E: Op->getArg(Arg: 0));
150 }
151
152 const ValueDecl *Decl = declaredObject(E: Stripped);
153 if (!isa_and_nonnull<VarDecl>(Val: Decl) && !isa_and_nonnull<FieldDecl>(Val: 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(A: Pointee, B: CanBorrowType);
167
168 auto *Record = ViewType->getAsCXXRecordDecl();
169 if (!Record)
170 return false;
171 if (isBorrow(Class: Record))
172 return isSameRecord(A: borrowedType(T: ViewType), B: CanBorrowType);
173 Record = Record->getDefinition();
174 if (!Record)
175 return true;
176
177 return llvm::any_of(Range: Record->fields(),
178 P: [&](const FieldDecl *Field) {
179 return mayHoldPointerTo(ViewType: Field->getType(),
180 CanBorrowType);
181 }) ||
182 llvm::any_of(Range: Record->bases(), P: [&](const CXXBaseSpecifier &Base) {
183 return mayHoldPointerTo(ViewType: Base.getType(), CanBorrowType);
184 });
185}
186
187class BorrowSafetyModel : public PtrRefSafetyModel {
188public:
189 std::optional<bool> isUnsafeType(QualType QT) const override {
190 return isView(T: QT);
191 }
192 std::optional<bool> isUnsafePtr(QualType QT, bool) const override {
193 return isView(T: QT);
194 }
195 bool isSafePtr(const CXXRecordDecl *Record) const override {
196 return isBorrow(Class: 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(T: 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(T: OriginType))
213 return !SinkMayEscape;
214
215 if (!PtrIsLifetimeBoundToOrigin)
216 return true;
217
218 if (const ValueDecl *ConstObject = constDeclaredObject(E: Origin)) {
219 auto *ConstVar = dyn_cast<VarDecl>(Val: ConstObject);
220 if (!SinkMayEscape || (ConstVar && ConstVar->hasGlobalStorage()))
221 return true;
222 }
223
224 if (Origin->isPRValue() &&
225 isCanBorrowType(T: Origin->getType()).value_or(u: false) &&
226 !SinkType.isNull() && !mayHoldPointerTo(ViewType: SinkType, CanBorrowType: Origin->getType()))
227 return true;
228
229 auto *Record = OriginType->getAsCXXRecordDecl();
230 if (!Record)
231 return true;
232
233 auto Borrowable = isBorrowable(Class: 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(T: SinkType);
244 if (!SinkObject.isNull() && isBorrowType(T: SinkObject)) {
245 Os << "Borrow that does not travel with the lambda";
246 return;
247 }
248
249 Os << "loan on ";
250 QualType OriginType = Origin ? pointeeType(T: 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(T: OriginType))
255 OriginType = borrowedType(T: OriginType);
256
257 if (!OriginType.isNull() && OriginType->getAsRecordDecl()) {
258 Os << "CanBorrow type ";
259 printTypeName(Os, QT: 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(QT: 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}
293