21#include "llvm/Support/SaveAndRestore.h"
29class RawPtrRefCallArgsChecker
30 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
32 mutable BugReporter *BR;
34 TrivialFunctionAnalysis TFA;
35 EnsureFunctionAnalysis EFA;
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 const char *ptrKind()
const = 0;
51 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
52 BugReporter &BRArg)
const {
59 const RawPtrRefCallArgsChecker *Checker;
60 Decl *DeclWithIssue{
nullptr};
62 explicit LocalVisitor(
const RawPtrRefCallArgsChecker *Checker)
65 ShouldVisitTemplateInstantiations =
true;
66 ShouldVisitImplicitCode =
false;
69 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl)
override {
72 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
75 bool TraverseDecl(Decl *D)
override {
76 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
82 bool VisitCallExpr(CallExpr *CE)
override {
83 Checker->visitCallExpr(CE, DeclWithIssue);
87 bool VisitTypedefDecl(TypedefDecl *TD)
override {
89 Checker->RTC->visitTypedef(TD);
93 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr)
override {
94 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
99 LocalVisitor visitor(
this);
101 RTC->visitTranslationUnitDecl(TUD);
102 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
105 void visitCallExpr(
const CallExpr *CE,
const Decl *D)
const {
106 if (shouldSkipCall(CE))
115 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
116 if (
auto *MD = MemberCallExpr->getMethodDecl()) {
118 if (name ==
"ref" || name ==
"deref")
120 if (name ==
"incrementCheckedPtrCount" ||
121 name ==
"decrementCheckedPtrCount")
124 auto *E = MemberCallExpr->getImplicitObjectArgument();
125 QualType ArgType = MemberCallExpr->getObjectType().getCanonicalType();
126 std::optional<bool> IsUnsafe = isUnsafeType(ArgType);
127 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(E))
128 reportBugOnThis(E, D);
131 for (
auto P = F->param_begin();
136 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
141 QualType ArgType = (*P)->getType();
143 std::optional<bool> IsUncounted =
isUnsafePtr(ArgType);
144 if (!IsUncounted || !(*IsUncounted))
147 const auto *Arg = CE->
getArg(ArgIdx);
149 if (
auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
150 Arg = defaultArg->getExpr();
152 if (isPtrOriginSafe(Arg))
155 reportBug(Arg, *P, D);
158 const auto *Arg = CE->
getArg(ArgIdx);
159 auto ArgType = Arg->getType();
160 std::optional<bool> IsUncounted =
isUnsafePtr(ArgType);
161 if (!IsUncounted || !(*IsUncounted))
164 if (
auto *defaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
165 Arg = defaultArg->getExpr();
167 if (isPtrOriginSafe(Arg))
170 reportBug(Arg,
nullptr, D);
175 void visitObjCMessageExpr(
const ObjCMessageExpr *E,
const Decl *D)
const {
176 if (BR->getSourceManager().isInSystemHeader(E->
getExprLoc()))
182 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
183 if (
auto *InnerMsg = dyn_cast<ObjCMessageExpr>(Receiver)) {
184 auto InnerSelector = InnerMsg->getSelector();
185 if (InnerSelector.getNameForSlot(0) ==
"alloc" &&
186 Selector.getNameForSlot(0).starts_with(
"init"))
189 reportBugOnReceiver(Receiver, D);
198 for (
unsigned i = 0; i < ArgCount; ++i) {
200 bool hasParam = i < MethodDecl->param_size();
201 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
202 auto ArgType = Arg->getType();
203 std::optional<bool> IsUnsafe =
isUnsafePtr(ArgType);
204 if (!IsUnsafe || !(*IsUnsafe))
206 if (isPtrOriginSafe(Arg))
208 reportBug(Arg, Param, D);
212 bool isPtrOriginSafe(
const Expr *Arg)
const {
216 [&](
const clang::QualType
T) {
return isSafePtrType(
T); },
217 [&](
const clang::Expr *ArgOrigin,
bool IsSafe) {
233 if (EFA.isACallToEnsureFn(ArgOrigin))
235 if (isSafeExpr(ArgOrigin))
241 bool shouldSkipCall(
const CallExpr *CE)
const {
244 if (BR->getSourceManager().isInSystemHeader(CE->
getExprLoc()))
247 if (Callee && TFA.isTrivial(Callee) && !
Callee->isVirtualAsWritten())
258 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
260 if (MemberOp->getOperator() ==
262 auto *callee = MemberOp->getDirectCallee();
263 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
264 if (
const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
270 if (MemberOp->isAssignmentOp())
277 if (isMethodOnWTFContainerType(Callee))
280 auto overloadedOperatorType =
Callee->getOverloadedOperator();
281 if (overloadedOperatorType == OO_EqualEqual ||
282 overloadedOperatorType == OO_ExclaimEqual ||
283 overloadedOperatorType == OO_LessEqual ||
284 overloadedOperatorType == OO_GreaterEqual ||
285 overloadedOperatorType == OO_Spaceship ||
286 overloadedOperatorType == OO_AmpAmp ||
287 overloadedOperatorType == OO_PipePipe)
294 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
295 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
297 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
298 name ==
"equalIgnoringASCIICaseCommon" ||
299 name ==
"equalIgnoringNullity" || name ==
"toString")
305 bool isMethodOnWTFContainerType(
const FunctionDecl *Decl)
const {
308 auto *ClassDecl =
Decl->getParent();
312 auto *NsDecl = ClassDecl->getParent();
318 StringRef ClsName = ClsNameStr;
321 return NamespaceName ==
"WTF" &&
322 (MethodName ==
"find" || MethodName ==
"findIf" ||
323 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
324 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
325 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
326 MethodName ==
"containsIf" ||
327 MethodName ==
"containsIgnoringASCIICase" ||
328 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
329 MethodName ==
"startsWithIgnoringASCIICase" ||
330 MethodName ==
"endsWithIgnoringASCIICase" ||
331 MethodName ==
"substring") &&
332 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
333 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
334 ClsName.ends_with(
"String"));
337 void reportBug(
const Expr *CallArg,
const ParmVarDecl *Param,
338 const Decl *DeclWithIssue)
const {
341 SmallString<100> Buf;
342 llvm::raw_svector_ostream Os(Buf);
345 Os <<
"Call argument";
346 if (!paramName.empty()) {
347 Os <<
" for parameter ";
350 Os <<
" is " << ptrKind() <<
" and unsafe.";
353 const SourceLocation SrcLocToReport =
357 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
358 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
360 Report->setDeclWithIssue(DeclWithIssue);
361 BR->emitReport(std::move(
Report));
364 void reportBugOnThis(
const Expr *CallArg,
const Decl *DeclWithIssue)
const {
369 SmallString<100> Buf;
370 llvm::raw_svector_ostream Os(Buf);
371 Os <<
"Call argument for 'this' parameter is " << ptrKind();
372 Os <<
" and unsafe.";
374 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
375 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
377 Report->setDeclWithIssue(DeclWithIssue);
378 BR->emitReport(std::move(
Report));
381 void reportBugOnReceiver(
const Expr *CallArg,
382 const Decl *DeclWithIssue)
const {
387 SmallString<100> Buf;
388 llvm::raw_svector_ostream Os(Buf);
389 Os <<
"Receiver is " << ptrKind() <<
" and unsafe.";
391 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
392 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
394 Report->setDeclWithIssue(DeclWithIssue);
395 BR->emitReport(std::move(
Report));
399class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
401 UncountedCallArgsChecker()
402 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
403 "pointer/reference parameter") {}
405 std::optional<bool> isUnsafeType(QualType QT)
const final {
409 std::optional<bool>
isUnsafePtr(QualType QT)
const final {
417 bool isSafePtrType(
const QualType
type)
const final {
421 const char *ptrKind() const final {
return "uncounted"; }
424class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
426 UncheckedCallArgsChecker()
427 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
428 "pointer/reference parameter") {}
430 std::optional<bool> isUnsafeType(QualType QT)
const final {
434 std::optional<bool>
isUnsafePtr(QualType QT)
const final {
442 bool isSafePtrType(
const QualType
type)
const final {
446 bool isSafeExpr(
const Expr *E)
const final {
450 const char *ptrKind() const final {
return "unchecked"; }
453class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
455 UnretainedCallArgsChecker()
456 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
457 "pointer/reference parameter") {
458 RTC = RetainTypeChecker();
461 std::optional<bool> isUnsafeType(QualType QT)
const final {
462 return RTC->isUnretained(QT);
465 std::optional<bool>
isUnsafePtr(QualType QT)
const final {
466 return RTC->isUnretained(QT);
473 bool isSafePtrType(
const QualType
type)
const final {
477 bool isSafeExpr(
const Expr *E)
const final {
482 const char *ptrKind() const final {
return "unretained"; }
491bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
499bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
503void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
507bool 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.
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.
SourceLocation getBegin() const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
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 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::Expr *, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
bool isRefOrCheckedPtrType(const clang::QualType T)
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
bool isRetainPtrOrOSPtrType(const clang::QualType T)
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)