21#include "llvm/Support/SaveAndRestore.h"
29class RawPtrRefCallArgsChecker
30 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
33 TrivialFunctionAnalysis TFA;
34 EnsureFunctionAnalysis EFA;
37 mutable BugReporter *BR;
38 mutable std::optional<RetainTypeChecker> RTC;
41 RawPtrRefCallArgsChecker(
const char *description)
42 : Bug(this, description,
"WebKit coding guidelines") {}
44 virtual std::optional<bool> isUnsafeType(QualType)
const = 0;
45 virtual std::optional<bool>
isUnsafePtr(QualType)
const = 0;
47 virtual bool isSafePtrType(
const QualType
type)
const = 0;
48 virtual bool isSafeExpr(
const Expr *)
const {
return false; }
49 virtual bool isSafeDecl(
const Decl *)
const {
return false; }
50 virtual const char *ptrKind()
const = 0;
52 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
53 BugReporter &BRArg)
const {
60 const RawPtrRefCallArgsChecker *Checker;
61 Decl *DeclWithIssue{
nullptr};
63 explicit LocalVisitor(
const RawPtrRefCallArgsChecker *Checker)
66 ShouldVisitTemplateInstantiations =
true;
67 ShouldVisitImplicitCode =
false;
70 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl)
override {
73 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
76 bool TraverseDecl(Decl *D)
override {
77 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
83 bool VisitCallExpr(CallExpr *CE)
override {
84 Checker->visitCallExpr(CE, DeclWithIssue);
88 bool VisitTypedefDecl(TypedefDecl *TD)
override {
90 Checker->RTC->visitTypedef(TD);
94 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr)
override {
95 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
100 LocalVisitor visitor(
this);
102 RTC->visitTranslationUnitDecl(TUD);
103 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
106 void visitCallExpr(
const CallExpr *CE,
const Decl *D)
const {
107 if (shouldSkipCall(CE))
116 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
117 if (
auto *MD = MemberCallExpr->getMethodDecl()) {
119 if (name ==
"ref" || name ==
"deref")
121 if (name ==
"incrementCheckedPtrCount" ||
122 name ==
"decrementCheckedPtrCount")
125 auto *E = MemberCallExpr->getImplicitObjectArgument();
126 QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();
127 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
128 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E))
129 reportBugOnThis(E, D);
132 for (
auto P = F->param_begin();
137 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
142 QualType ArgType = (*P)->getType();
144 std::optional<bool> IsUncounted =
isUnsafePtr(ArgType);
145 if (!IsUncounted || !(*IsUncounted))
148 const auto *Arg = CE->
getArg(ArgIdx);
150 if (
auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
151 Arg = defaultArg->getExpr();
153 if (isPtrOriginSafe(Arg))
156 reportBug(Arg, *P, D);
159 const auto *Arg = CE->
getArg(ArgIdx);
160 auto ArgType = Arg->getType();
161 std::optional<bool> IsUncounted =
isUnsafePtr(ArgType);
162 if (!IsUncounted || !(*IsUncounted))
165 if (
auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
166 Arg = defaultArg->getExpr();
168 if (isPtrOriginSafe(Arg))
171 reportBug(Arg,
nullptr, D);
176 void visitObjCMessageExpr(
const ObjCMessageExpr *E,
const Decl *D)
const {
177 if (BR->getSourceManager().isInSystemHeader(E->
getExprLoc()))
183 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
184 if (
auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {
185 auto InnerSelector = InnerMsg->getSelector();
186 if (InnerSelector.getNameForSlot(0) ==
"alloc" &&
187 Selector.getNameForSlot(0).starts_with(
"init"))
190 reportBugOnReceiver(Receiver, D);
199 for (
unsigned i = 0; i < ArgCount; ++i) {
201 bool hasParam = i < MethodDecl->param_size();
202 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
203 auto ArgType = Arg->getType();
204 std::optional<bool> IsUnsafe =
isUnsafePtr(ArgType);
205 if (!IsUnsafe || !(*IsUnsafe))
207 if (isPtrOriginSafe(Arg))
209 reportBug(Arg, Param, D);
213 bool isPtrOriginSafe(
const Expr *Arg)
const {
217 [&](
const clang::QualType
T) {
return isSafePtrType(
T); },
218 [&](
const clang::Decl *D) {
return isSafeDecl(D); },
219 [&](
const clang::Expr *ArgOrigin,
bool IsSafe) {
235 if (EFA.isACallToEnsureFn(ArgOrigin))
237 if (isSafeExpr(ArgOrigin))
243 bool shouldSkipCall(
const CallExpr *CE)
const {
246 if (BR->getSourceManager().isInSystemHeader(CE->
getExprLoc()))
249 if (Callee && TFA.isTrivial(Callee) && !
Callee->isVirtualAsWritten())
260 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
262 if (MemberOp->getOperator() ==
264 auto *callee = MemberOp->getDirectCallee();
265 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
266 if (
const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
272 if (MemberOp->isAssignmentOp())
279 if (isMethodOnWTFContainerType(Callee))
282 auto overloadedOperatorType =
Callee->getOverloadedOperator();
283 if (overloadedOperatorType == OO_EqualEqual ||
284 overloadedOperatorType == OO_ExclaimEqual ||
285 overloadedOperatorType == OO_LessEqual ||
286 overloadedOperatorType == OO_GreaterEqual ||
287 overloadedOperatorType == OO_Spaceship ||
288 overloadedOperatorType == OO_AmpAmp ||
289 overloadedOperatorType == OO_PipePipe)
296 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
297 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
299 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
300 name ==
"equalIgnoringASCIICaseCommon" ||
301 name ==
"equalIgnoringNullity" || name ==
"toString")
307 bool isMethodOnWTFContainerType(
const FunctionDecl *Decl)
const {
310 auto *ClassDecl =
Decl->getParent();
314 auto *NsDecl = ClassDecl->getParent();
320 StringRef ClsName = ClsNameStr;
323 return NamespaceName ==
"WTF" &&
324 (MethodName ==
"find" || MethodName ==
"findIf" ||
325 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
326 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
327 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
328 MethodName ==
"containsIf" ||
329 MethodName ==
"containsIgnoringASCIICase" ||
330 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
331 MethodName ==
"startsWithIgnoringASCIICase" ||
332 MethodName ==
"endsWithIgnoringASCIICase" ||
333 MethodName ==
"substring") &&
334 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
335 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
336 ClsName.ends_with(
"String"));
339 void reportBug(
const Expr *CallArg,
const ParmVarDecl *Param,
340 const Decl *DeclWithIssue)
const {
343 SmallString<100> Buf;
344 llvm::raw_svector_ostream Os(Buf);
347 Os <<
"Call argument";
348 if (!paramName.empty()) {
349 Os <<
" for parameter ";
352 Os <<
" is " << ptrKind() <<
" and unsafe.";
355 const SourceLocation SrcLocToReport =
359 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
360 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
362 Report->setDeclWithIssue(DeclWithIssue);
363 BR->emitReport(std::move(
Report));
366 void reportBugOnThis(
const Expr *CallArg,
const Decl *DeclWithIssue)
const {
371 SmallString<100> Buf;
372 llvm::raw_svector_ostream Os(Buf);
373 Os <<
"Call argument for 'this' parameter is " << ptrKind();
374 Os <<
" and unsafe.";
376 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
377 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
379 Report->setDeclWithIssue(DeclWithIssue);
380 BR->emitReport(std::move(
Report));
383 void reportBugOnReceiver(
const Expr *CallArg,
384 const Decl *DeclWithIssue)
const {
389 SmallString<100> Buf;
390 llvm::raw_svector_ostream Os(Buf);
391 Os <<
"Receiver is " << ptrKind() <<
" and unsafe.";
393 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
394 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
396 Report->setDeclWithIssue(DeclWithIssue);
397 BR->emitReport(std::move(
Report));
401class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
403 UncountedCallArgsChecker()
404 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
405 "pointer/reference parameter") {}
407 std::optional<bool> isUnsafeType(QualType QT)
const final {
411 std::optional<bool>
isUnsafePtr(QualType QT)
const final {
419 bool isSafePtrType(
const QualType
type)
const final {
423 const char *ptrKind() const final {
return "uncounted"; }
426class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
428 UncheckedCallArgsChecker()
429 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
430 "pointer/reference parameter") {}
432 std::optional<bool> isUnsafeType(QualType QT)
const final {
436 std::optional<bool>
isUnsafePtr(QualType QT)
const final {
444 bool isSafePtrType(
const QualType
type)
const final {
448 bool isSafeExpr(
const Expr *E)
const final {
452 const char *ptrKind() const final {
return "unchecked"; }
455class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
457 UnretainedCallArgsChecker()
458 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
459 "pointer/reference parameter") {
460 RTC = RetainTypeChecker();
463 std::optional<bool> isUnsafeType(QualType QT)
const final {
464 return RTC->isUnretained(QT);
467 std::optional<bool>
isUnsafePtr(QualType QT)
const final {
468 return RTC->isUnretained(QT);
475 bool isSafePtrType(
const QualType
type)
const final {
479 bool isSafeExpr(
const Expr *E)
const final {
484 bool isSafeDecl(
const Decl *D)
const final {
489 const char *ptrKind() const final {
return "unretained"; }
498bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
506bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
510void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
514bool ento::shouldRegisterUnretainedCallArgsChecker(
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.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
unsigned getNumArgs() const
getNumArgs - Return the number of actual arguments to this call.
SourceLocation getLocation() const
virtual bool TraverseDecl(MaybeConst< Decl > *D)
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Expr * getInstanceReceiver()
Returns the object expression (receiver) for an instance message, or null for a message that is not a...
Selector getSelector() const
const ObjCMethodDecl * getMethodDecl() const
QualType getReceiverType() const
Retrieve the receiver type to which this message is being directed.
unsigned getNumArgs() const
Return the number of actual arguments in this message, not counting the receiver.
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
SourceLocation getBegin() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
const SourceManager & getSourceManager()
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.
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
bool isCocoaObjectRef(QualType T)
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 isCtorOfSafePtr(const clang::FunctionDecl *F)
bool isTrivialBuiltinFunction(const FunctionDecl *F)
bool isa(CodeGen::Address addr)
bool isExprToGetCheckedPtrCapableMember(const clang::Expr *E)
bool isPtrConversion(const FunctionDecl *F)
std::optional< bool > isUnchecked(const QualType T)
bool isRefOrCheckedPtrType(const clang::QualType T)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isRetainPtrOrOSPtrType(const clang::QualType T)
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).
std::optional< bool > isUnsafePtr(const QualType T, bool IsArcEnabled)
bool isASafeCallArg(const Expr *E)
For E referring to a ref-countable/-counted pointer/reference we return whether it's a safe call argu...
const FunctionProtoType * T
bool isSmartPtrClass(const std::string &Name)
bool isRefCounted(const CXXRecordDecl *R)
bool isRetainPtrOrOSPtr(const std::string &Name)
std::optional< bool > isUncountedPtr(const QualType T)
bool isSafePtr(clang::CXXRecordDecl *Decl)
std::string safeGetName(const T *ASTNode)
bool isNullPtr(const clang::Expr *E)
bool isCheckedPtr(const std::string &Name)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
std::optional< bool > isUncounted(const QualType T)
std::optional< bool > isUncheckedPtr(const QualType T)