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()))
182 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
185 reportBugOnReceiver(Receiver, D);
194 for (
unsigned i = 0; i < ArgCount; ++i) {
196 bool hasParam = i < MethodDecl->param_size();
197 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
198 auto ArgType = Arg->getType();
199 std::optional<bool> IsUnsafe = isUnsafePtr(ArgType);
200 if (!IsUnsafe || !(*IsUnsafe))
202 if (isPtrOriginSafe(Arg))
204 reportBug(Arg, Param, D);
208 bool isPtrOriginSafe(
const Expr *Arg)
const {
212 [&](
const clang::QualType
T) {
return isSafePtrType(
T); },
213 [&](
const clang::Decl *D) {
return isSafeDecl(D); },
214 [&](
const clang::Expr *ArgOrigin,
bool IsSafe) {
230 if (EFA.isACallToEnsureFn(ArgOrigin))
232 if (isSafeExpr(ArgOrigin))
238 bool shouldSkipCall(
const CallExpr *CE)
const {
241 if (BR->getSourceManager().isInSystemHeader(CE->
getExprLoc()))
244 if (Callee && TFA.isTrivial(Callee) && !
Callee->isVirtualAsWritten())
255 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
257 if (MemberOp->getOperator() ==
259 auto *callee = MemberOp->getDirectCallee();
260 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
261 if (
const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
267 if (MemberOp->isAssignmentOp())
274 if (isMethodOnWTFContainerType(Callee))
277 auto overloadedOperatorType =
Callee->getOverloadedOperator();
278 if (overloadedOperatorType == OO_EqualEqual ||
279 overloadedOperatorType == OO_ExclaimEqual ||
280 overloadedOperatorType == OO_LessEqual ||
281 overloadedOperatorType == OO_GreaterEqual ||
282 overloadedOperatorType == OO_Spaceship ||
283 overloadedOperatorType == OO_AmpAmp ||
284 overloadedOperatorType == OO_PipePipe)
291 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
292 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
294 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
295 name ==
"equalIgnoringASCIICaseCommon" ||
296 name ==
"equalIgnoringNullity" || name ==
"toString")
302 bool isMethodOnWTFContainerType(
const FunctionDecl *Decl)
const {
305 auto *ClassDecl =
Decl->getParent();
309 auto *NsDecl = ClassDecl->getParent();
315 StringRef ClsName = ClsNameStr;
318 return NamespaceName ==
"WTF" &&
319 (MethodName ==
"find" || MethodName ==
"findIf" ||
320 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
321 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
322 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
323 MethodName ==
"containsIf" ||
324 MethodName ==
"containsIgnoringASCIICase" ||
325 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
326 MethodName ==
"startsWithIgnoringASCIICase" ||
327 MethodName ==
"endsWithIgnoringASCIICase" ||
328 MethodName ==
"substring") &&
329 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
330 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
331 ClsName.ends_with(
"String"));
334 void reportBug(
const Expr *CallArg,
const ParmVarDecl *Param,
335 const Decl *DeclWithIssue)
const {
338 SmallString<100> Buf;
339 llvm::raw_svector_ostream Os(Buf);
342 Os <<
"Call argument";
343 if (!paramName.empty()) {
344 Os <<
" for parameter ";
347 Os <<
" is " << ptrKind() <<
" and unsafe.";
350 const SourceLocation SrcLocToReport =
354 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
355 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
357 Report->setDeclWithIssue(DeclWithIssue);
358 BR->emitReport(std::move(
Report));
361 void reportBugOnThis(
const Expr *CallArg,
const Decl *DeclWithIssue)
const {
366 SmallString<100> Buf;
367 llvm::raw_svector_ostream Os(Buf);
368 Os <<
"Call argument for 'this' parameter is " << ptrKind();
369 Os <<
" and unsafe.";
371 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
372 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
374 Report->setDeclWithIssue(DeclWithIssue);
375 BR->emitReport(std::move(
Report));
378 void reportBugOnReceiver(
const Expr *CallArg,
379 const Decl *DeclWithIssue)
const {
384 SmallString<100> Buf;
385 llvm::raw_svector_ostream Os(Buf);
386 Os <<
"Receiver is " << ptrKind() <<
" and unsafe.";
388 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
389 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
391 Report->setDeclWithIssue(DeclWithIssue);
392 BR->emitReport(std::move(
Report));
396class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
398 UncountedCallArgsChecker()
399 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
400 "pointer/reference parameter") {}
402 std::optional<bool> isUnsafeType(QualType QT)
const final {
406 std::optional<bool> isUnsafePtr(QualType QT)
const final {
414 bool isSafePtrType(
const QualType
type)
const final {
418 const char *ptrKind() const final {
return "uncounted"; }
421class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
423 UncheckedCallArgsChecker()
424 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
425 "pointer/reference parameter") {}
427 std::optional<bool> isUnsafeType(QualType QT)
const final {
431 std::optional<bool> isUnsafePtr(QualType QT)
const final {
439 bool isSafePtrType(
const QualType
type)
const final {
443 bool isSafeExpr(
const Expr *E)
const final {
447 const char *ptrKind() const final {
return "unchecked"; }
450class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
452 UnretainedCallArgsChecker()
453 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
454 "pointer/reference parameter") {
455 RTC = RetainTypeChecker();
458 std::optional<bool> isUnsafeType(QualType QT)
const final {
459 return RTC->isUnretained(QT);
462 std::optional<bool> isUnsafePtr(QualType QT)
const final {
463 return RTC->isUnretained(QT);
470 bool isSafePtrType(
const QualType
type)
const final {
474 bool isSafeExpr(
const Expr *E)
const final {
479 bool isSafeDecl(
const Decl *D)
const final {
484 const char *ptrKind() const final {
return "unretained"; }
493bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
501bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
505void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
509bool 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...
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).
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
bool isAllocInit(const Expr *E, const Expr **InnerExpr)
std::optional< bool > isUncounted(const QualType T)
std::optional< bool > isUncheckedPtr(const QualType T)