1 | //=======- ASTUtils.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 "PtrTypesSemantics.h" |
11 | #include "clang/AST/Attr.h" |
12 | #include "clang/AST/Decl.h" |
13 | #include "clang/AST/DeclCXX.h" |
14 | #include "clang/AST/ExprCXX.h" |
15 | #include "clang/AST/ExprObjC.h" |
16 | #include "clang/AST/StmtVisitor.h" |
17 | #include <optional> |
18 | |
19 | namespace clang { |
20 | |
21 | bool isSafePtr(clang::CXXRecordDecl *Decl) { |
22 | return isRefCounted(Class: Decl) || isCheckedPtr(Class: Decl); |
23 | } |
24 | |
25 | bool tryToFindPtrOrigin( |
26 | const Expr *E, bool StopAtFirstRefCountedObj, |
27 | std::function<bool(const clang::CXXRecordDecl *)> isSafePtr, |
28 | std::function<bool(const clang::QualType)> isSafePtrType, |
29 | std::function<bool(const clang::Expr *, bool)> callback) { |
30 | while (E) { |
31 | if (auto *DRE = dyn_cast<DeclRefExpr>(Val: E)) { |
32 | if (auto *VD = dyn_cast_or_null<VarDecl>(Val: DRE->getDecl())) { |
33 | auto QT = VD->getType(); |
34 | if (VD->hasGlobalStorage() && QT.isConstQualified()) { |
35 | return callback(E, true); |
36 | } |
37 | } |
38 | } |
39 | if (auto *tempExpr = dyn_cast<MaterializeTemporaryExpr>(Val: E)) { |
40 | E = tempExpr->getSubExpr(); |
41 | continue; |
42 | } |
43 | if (auto *tempExpr = dyn_cast<CXXBindTemporaryExpr>(Val: E)) { |
44 | E = tempExpr->getSubExpr(); |
45 | continue; |
46 | } |
47 | if (auto *tempExpr = dyn_cast<CXXConstructExpr>(Val: E)) { |
48 | if (auto *C = tempExpr->getConstructor()) { |
49 | if (auto *Class = C->getParent(); Class && isSafePtr(Class)) |
50 | return callback(E, true); |
51 | break; |
52 | } |
53 | } |
54 | if (auto *TempExpr = dyn_cast<CXXUnresolvedConstructExpr>(Val: E)) { |
55 | if (isSafePtrType(TempExpr->getTypeAsWritten())) |
56 | return callback(TempExpr, true); |
57 | } |
58 | if (auto *POE = dyn_cast<PseudoObjectExpr>(Val: E)) { |
59 | if (auto *RF = POE->getResultExpr()) { |
60 | E = RF; |
61 | continue; |
62 | } |
63 | } |
64 | if (auto *tempExpr = dyn_cast<ParenExpr>(Val: E)) { |
65 | E = tempExpr->getSubExpr(); |
66 | continue; |
67 | } |
68 | if (auto *OpaqueValue = dyn_cast<OpaqueValueExpr>(Val: E)) { |
69 | E = OpaqueValue->getSourceExpr(); |
70 | continue; |
71 | } |
72 | if (auto *Expr = dyn_cast<ConditionalOperator>(Val: E)) { |
73 | return tryToFindPtrOrigin(E: Expr->getTrueExpr(), StopAtFirstRefCountedObj, |
74 | isSafePtr, isSafePtrType, callback) && |
75 | tryToFindPtrOrigin(E: Expr->getFalseExpr(), StopAtFirstRefCountedObj, |
76 | isSafePtr, isSafePtrType, callback); |
77 | } |
78 | if (auto *cast = dyn_cast<CastExpr>(Val: E)) { |
79 | if (StopAtFirstRefCountedObj) { |
80 | if (auto *ConversionFunc = |
81 | dyn_cast_or_null<FunctionDecl>(Val: cast->getConversionFunction())) { |
82 | if (isCtorOfSafePtr(F: ConversionFunc)) |
83 | return callback(E, true); |
84 | } |
85 | if (isa<CXXFunctionalCastExpr>(Val: E) && isSafePtrType(cast->getType())) |
86 | return callback(E, true); |
87 | } |
88 | // FIXME: This can give false "origin" that would lead to false negatives |
89 | // in checkers. See https://reviews.llvm.org/D37023 for reference. |
90 | E = cast->getSubExpr(); |
91 | continue; |
92 | } |
93 | if (auto *call = dyn_cast<CallExpr>(Val: E)) { |
94 | if (auto *memberCall = dyn_cast<CXXMemberCallExpr>(Val: call)) { |
95 | if (auto *decl = memberCall->getMethodDecl()) { |
96 | std::optional<bool> IsGetterOfRefCt = isGetterOfSafePtr(Method: decl); |
97 | if (IsGetterOfRefCt && *IsGetterOfRefCt) { |
98 | E = memberCall->getImplicitObjectArgument(); |
99 | if (StopAtFirstRefCountedObj) { |
100 | return callback(E, true); |
101 | } |
102 | continue; |
103 | } |
104 | } |
105 | } |
106 | |
107 | if (auto *operatorCall = dyn_cast<CXXOperatorCallExpr>(Val: E)) { |
108 | if (auto *Callee = operatorCall->getDirectCallee()) { |
109 | auto ClsName = safeGetName(ASTNode: Callee->getParent()); |
110 | if (isRefType(Name: ClsName) || isCheckedPtr(Name: ClsName) || |
111 | isRetainPtr(Name: ClsName) || ClsName == "unique_ptr" || |
112 | ClsName == "UniqueRef" || ClsName == "WeakPtr" || |
113 | ClsName == "WeakRef" ) { |
114 | if (operatorCall->getNumArgs() == 1) { |
115 | E = operatorCall->getArg(Arg: 0); |
116 | continue; |
117 | } |
118 | } |
119 | } |
120 | } |
121 | |
122 | if (call->isCallToStdMove() && call->getNumArgs() == 1) { |
123 | E = call->getArg(Arg: 0)->IgnoreParenCasts(); |
124 | continue; |
125 | } |
126 | |
127 | if (auto *callee = call->getDirectCallee()) { |
128 | if (isCtorOfSafePtr(F: callee)) { |
129 | if (StopAtFirstRefCountedObj) |
130 | return callback(E, true); |
131 | |
132 | E = call->getArg(Arg: 0); |
133 | continue; |
134 | } |
135 | |
136 | if (isSafePtrType(callee->getReturnType())) |
137 | return callback(E, true); |
138 | |
139 | if (isSingleton(F: callee)) |
140 | return callback(E, true); |
141 | |
142 | if (callee->isInStdNamespace() && safeGetName(ASTNode: callee) == "forward" ) { |
143 | E = call->getArg(Arg: 0); |
144 | continue; |
145 | } |
146 | |
147 | if (isPtrConversion(F: callee)) { |
148 | E = call->getArg(Arg: 0); |
149 | continue; |
150 | } |
151 | |
152 | auto Name = safeGetName(ASTNode: callee); |
153 | if (Name == "__builtin___CFStringMakeConstantString" || |
154 | Name == "NSClassFromString" ) |
155 | return callback(E, true); |
156 | } |
157 | } |
158 | if (auto *ObjCMsgExpr = dyn_cast<ObjCMessageExpr>(Val: E)) { |
159 | if (auto *Method = ObjCMsgExpr->getMethodDecl()) { |
160 | if (isSafePtrType(Method->getReturnType())) |
161 | return callback(E, true); |
162 | } |
163 | auto Selector = ObjCMsgExpr->getSelector(); |
164 | auto NameForFirstSlot = Selector.getNameForSlot(argIndex: 0); |
165 | if ((NameForFirstSlot == "class" || NameForFirstSlot == "superclass" ) && |
166 | !Selector.getNumArgs()) |
167 | return callback(E, true); |
168 | } |
169 | if (auto *ObjCDict = dyn_cast<ObjCDictionaryLiteral>(Val: E)) |
170 | return callback(ObjCDict, true); |
171 | if (auto *ObjCArray = dyn_cast<ObjCArrayLiteral>(Val: E)) |
172 | return callback(ObjCArray, true); |
173 | if (auto *ObjCStr = dyn_cast<ObjCStringLiteral>(Val: E)) |
174 | return callback(ObjCStr, true); |
175 | if (auto *unaryOp = dyn_cast<UnaryOperator>(Val: E)) { |
176 | // FIXME: Currently accepts ANY unary operator. Is it OK? |
177 | E = unaryOp->getSubExpr(); |
178 | continue; |
179 | } |
180 | |
181 | break; |
182 | } |
183 | // Some other expression. |
184 | return callback(E, false); |
185 | } |
186 | |
187 | bool isASafeCallArg(const Expr *E) { |
188 | assert(E); |
189 | if (auto *Ref = dyn_cast<DeclRefExpr>(Val: E)) { |
190 | auto *FoundDecl = Ref->getFoundDecl(); |
191 | if (auto *D = dyn_cast_or_null<VarDecl>(Val: FoundDecl)) { |
192 | if (isa<ParmVarDecl>(Val: D) || D->isLocalVarDecl()) |
193 | return true; |
194 | if (auto *ImplicitP = dyn_cast<ImplicitParamDecl>(Val: D)) { |
195 | auto Kind = ImplicitP->getParameterKind(); |
196 | if (Kind == ImplicitParamKind::ObjCSelf || |
197 | Kind == ImplicitParamKind::ObjCCmd || |
198 | Kind == ImplicitParamKind::CXXThis || |
199 | Kind == ImplicitParamKind::CXXVTT) |
200 | return true; |
201 | } |
202 | } else if (auto *BD = dyn_cast_or_null<BindingDecl>(Val: FoundDecl)) { |
203 | VarDecl *VD = BD->getHoldingVar(); |
204 | if (VD && (isa<ParmVarDecl>(Val: VD) || VD->isLocalVarDecl())) |
205 | return true; |
206 | } |
207 | } |
208 | if (isConstOwnerPtrMemberExpr(E)) |
209 | return true; |
210 | |
211 | // TODO: checker for method calls on non-refcounted objects |
212 | return isa<CXXThisExpr>(Val: E); |
213 | } |
214 | |
215 | bool isConstOwnerPtrMemberExpr(const clang::Expr *E) { |
216 | if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Val: E)) { |
217 | if (auto *Callee = MCE->getDirectCallee()) { |
218 | auto Name = safeGetName(ASTNode: Callee); |
219 | if (Name == "get" || Name == "ptr" ) |
220 | E = MCE->getImplicitObjectArgument(); |
221 | if (isa<CXXConversionDecl>(Val: Callee)) |
222 | E = MCE->getImplicitObjectArgument(); |
223 | } |
224 | } else if (auto *OCE = dyn_cast<CXXOperatorCallExpr>(Val: E)) { |
225 | if (OCE->getOperator() == OO_Star && OCE->getNumArgs() == 1) |
226 | E = OCE->getArg(Arg: 0); |
227 | } |
228 | const ValueDecl *D = nullptr; |
229 | if (auto *ME = dyn_cast<MemberExpr>(Val: E)) |
230 | D = ME->getMemberDecl(); |
231 | else if (auto *IVR = dyn_cast<ObjCIvarRefExpr>(Val: E)) |
232 | D = IVR->getDecl(); |
233 | if (!D) |
234 | return false; |
235 | auto T = D->getType(); |
236 | return isOwnerPtrType(T) && T.isConstQualified(); |
237 | } |
238 | |
239 | bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E) { |
240 | auto *ME = dyn_cast<MemberExpr>(Val: E); |
241 | if (!ME) |
242 | return false; |
243 | auto *Base = ME->getBase(); |
244 | if (!Base) |
245 | return false; |
246 | if (!isa<CXXThisExpr>(Val: Base->IgnoreParenCasts())) |
247 | return false; |
248 | auto *D = ME->getMemberDecl(); |
249 | if (!D) |
250 | return false; |
251 | auto T = D->getType(); |
252 | auto *CXXRD = T->getAsCXXRecordDecl(); |
253 | if (!CXXRD) |
254 | return false; |
255 | auto result = isCheckedPtrCapable(Class: CXXRD); |
256 | return result && *result; |
257 | } |
258 | |
259 | class EnsureFunctionVisitor |
260 | : public ConstStmtVisitor<EnsureFunctionVisitor, bool> { |
261 | public: |
262 | bool VisitStmt(const Stmt *S) { |
263 | for (const Stmt *Child : S->children()) { |
264 | if (Child && !Visit(S: Child)) |
265 | return false; |
266 | } |
267 | return true; |
268 | } |
269 | |
270 | bool VisitReturnStmt(const ReturnStmt *RS) { |
271 | if (auto *RV = RS->getRetValue()) { |
272 | RV = RV->IgnoreParenCasts(); |
273 | if (isa<CXXNullPtrLiteralExpr>(Val: RV)) |
274 | return true; |
275 | return isConstOwnerPtrMemberExpr(E: RV); |
276 | } |
277 | return false; |
278 | } |
279 | }; |
280 | |
281 | bool EnsureFunctionAnalysis::isACallToEnsureFn(const clang::Expr *E) const { |
282 | auto *MCE = dyn_cast<CXXMemberCallExpr>(Val: E); |
283 | if (!MCE) |
284 | return false; |
285 | auto *Callee = MCE->getDirectCallee(); |
286 | if (!Callee) |
287 | return false; |
288 | auto *Body = Callee->getBody(); |
289 | if (!Body || Callee->isVirtualAsWritten()) |
290 | return false; |
291 | auto [CacheIt, IsNew] = Cache.insert(KV: std::make_pair(x&: Callee, y: false)); |
292 | if (IsNew) |
293 | CacheIt->second = EnsureFunctionVisitor().Visit(S: Body); |
294 | return CacheIt->second; |
295 | } |
296 | |
297 | } // namespace clang |
298 | |