23#include "llvm/Support/SaveAndRestore.h"
31class RawPtrRefCallArgsChecker
32 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
35 TrivialFunctionAnalysis TFA;
36 EnsureFunctionAnalysis EFA;
39 mutable BugReporter *BR;
40 const std::unique_ptr<PtrRefSafetyModel> Model;
43 RawPtrRefCallArgsChecker(
const char *description,
44 std::unique_ptr<PtrRefSafetyModel> Model)
45 : Bug(this, description,
"WebKit coding guidelines"),
46 Model(std::move(Model)) {}
48 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
49 BugReporter &BRArg)
const {
56 const RawPtrRefCallArgsChecker *Checker;
57 Decl *DeclWithIssue{
nullptr};
59 explicit LocalVisitor(
const RawPtrRefCallArgsChecker *Checker)
62 ShouldVisitTemplateInstantiations =
true;
63 ShouldVisitImplicitCode =
false;
66 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl)
override {
69 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
72 bool TraverseDecl(Decl *D)
override {
73 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
79 bool VisitCallExpr(CallExpr *CE)
override {
80 Checker->visitCallExpr(CE, DeclWithIssue);
84 bool VisitCXXConstructExpr(CXXConstructExpr *CE)
override {
85 Checker->visitConstructExpr(CE, DeclWithIssue);
89 bool VisitTypedefDecl(TypedefDecl *TD)
override {
90 if (
auto *RTC = Checker->Model->retainTypeChecker())
91 RTC->visitTypedef(TD);
95 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr)
override {
96 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
101 LocalVisitor visitor(
this);
102 if (
auto *RTC = Model->retainTypeChecker())
103 RTC->visitTranslationUnitDecl(TUD);
104 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
107 template <
typename CallOrConstrcut>
108 void visitCallOrConstructExpr(
const CallOrConstrcut *CE,
109 const FunctionDecl *F,
const Decl *D)
const {
116 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
117 checkThisArg(F, MemberCallExpr, D);
120 auto *Arg = CE->getArg(0);
121 QualType ArgType = Arg->getType().getCanonicalType();
122 std::optional<bool> IsUnsafe = Model->isUnsafeType(ArgType);
123 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Arg))
124 reportBugOnThis(F, Arg, D);
128 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
132 checkArg(F, CE->getArg(ArgIdx), (*P)->getType(), *P, D);
134 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
135 auto *Arg = CE->getArg(ArgIdx);
136 checkArg(F, Arg, Arg->getType(),
nullptr, D);
141 void visitCallExpr(
const CallExpr *CE,
const Decl *D)
const {
143 if (shouldSkipCall(CE, Callee))
147 visitCallOrConstructExpr(CE, Callee, D);
149 if (
auto *FnType =
Decl->getFunctionType()) {
150 if (
auto *ProtoType = dyn_cast<FunctionProtoType>(FnType)) {
151 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
152 checkThisArg(
nullptr, MemberCallExpr, D);
154 for (
auto PT = ProtoType->param_type_begin();
155 PT < ProtoType->param_type_end() && ArgIdx < CE->getNumArgs();
157 checkArg(
nullptr, CE->
getArg(ArgIdx), *PT,
nullptr, D);
159 auto *Arg = CE->
getArg(ArgIdx);
160 checkArg(
nullptr, Arg, Arg->getType(),
nullptr, D);
167 void visitConstructExpr(
const CXXConstructExpr *CE,
const Decl *D)
const {
175 void visitObjCMessageExpr(
const ObjCMessageExpr *E,
const Decl *D)
const {
176 if (BR->getSourceManager().isInSystemHeader(E->
getExprLoc()))
180 std::optional<bool> IsUnsafe = Model->isUnsafePtr(E->
getReceiverType());
181 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver)) {
185 if (SelectorName ==
"isEqual" || SelectorName ==
"isEqualToString")
196 for (
unsigned i = 0; i < ArgCount; ++i) {
198 bool hasParam = i < MethodDecl->param_size();
199 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
200 auto ArgType = Arg->getType();
201 std::optional<bool> IsUnsafe = Model->isUnsafePtr(ArgType);
202 if (!IsUnsafe || !(*IsUnsafe))
204 if (isPtrOriginSafe(Arg))
206 reportBug(MethodDecl, Arg, Param, D);
210 void checkThisArg(
const NamedDecl *Callee,
211 const CXXMemberCallExpr *MemberCallExpr,
212 const Decl *DeclWithIssue)
const {
215 if (name ==
"ref" || name ==
"deref")
217 if (name ==
"incrementCheckedPtrCount" ||
218 name ==
"decrementCheckedPtrCount")
223 std::optional<bool> IsUnsafe = Model->isUnsafeType(ArgType);
224 if (!IsUnsafe || !*IsUnsafe)
227 if (isPtrOriginSafe(ThisExpr))
230 reportBugOnThis(Callee, ThisExpr, DeclWithIssue);
233 void checkArg(
const NamedDecl *Callee,
const Expr *Arg, QualType ParamType,
234 const ParmVarDecl *Param,
const Decl *DeclWithIssue)
const {
235 std::optional<bool> IsUncounted = Model->isUnsafePtr(ParamType);
236 if (!IsUncounted || !(*IsUncounted))
239 if (
auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
240 Arg = DefaultArg->getExpr();
242 if (isPtrOriginSafe(Arg))
245 reportBug(Callee, Arg, Param, DeclWithIssue);
248 bool isPtrOriginSafe(
const Expr *Arg)
const {
251 [&](
const clang::CXXRecordDecl *
Record) {
252 return Model->isSafePtr(
Record);
254 [&](
const clang::QualType
T) {
return Model->isSafePtrType(
T); },
255 [&](
const clang::Decl *D) {
256 return Model->isSafeDecl(D, BR->getSourceManager());
258 [&](
const clang::Expr *ArgOrigin,
bool IsSafe) {
274 if (EFA.isACallToEnsureFn(ArgOrigin)) {
275 auto *MCE = dyn_cast<CXXMemberCallExpr>(ArgOrigin);
277 if (isPtrOriginSafe(MCE->getImplicitObjectArgument()))
280 if (Model->isSafeExpr(ArgOrigin))
286 template <
typename CallOrConstruct>
287 bool shouldSkipCall(
const CallOrConstruct *CE,
288 const FunctionDecl *Callee)
const {
289 if (BR->getSourceManager().isInSystemHeader(CE->getExprLoc()))
292 if (Callee && TFA.isTrivial(Callee))
298 if (CE->getNumArgs() == 0)
303 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
305 if (MemberOp->getOperator() ==
307 auto *callee = MemberOp->getDirectCallee();
308 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
309 if (
const CXXRecordDecl *classDecl = calleeDecl->getParent()) {
310 if (Model->isSafePtr(classDecl))
315 if (MemberOp->isAssignmentOp())
322 if (isMethodOnWTFContainerType(Callee))
325 auto overloadedOperatorType =
Callee->getOverloadedOperator();
326 if (overloadedOperatorType == OO_EqualEqual ||
327 overloadedOperatorType == OO_ExclaimEqual ||
328 overloadedOperatorType == OO_LessEqual ||
329 overloadedOperatorType == OO_GreaterEqual ||
330 overloadedOperatorType == OO_Spaceship ||
331 overloadedOperatorType == OO_AmpAmp ||
332 overloadedOperatorType == OO_PipePipe)
339 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
340 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
342 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
343 name ==
"equalIgnoringASCIICaseCommon" ||
344 name ==
"equalIgnoringNullity" || name ==
"toString")
350 bool isMethodOnWTFContainerType(
const FunctionDecl *Decl)
const {
353 auto *ClassDecl =
Decl->getParent();
357 auto *NsDecl = ClassDecl->getParent();
363 StringRef ClsName = ClsNameStr;
366 return NamespaceName ==
"WTF" &&
367 (MethodName ==
"find" || MethodName ==
"findIf" ||
368 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
369 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
370 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
371 MethodName ==
"containsIf" ||
372 MethodName ==
"containsIgnoringASCIICase" ||
373 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
374 MethodName ==
"startsWithIgnoringASCIICase" ||
375 MethodName ==
"endsWithIgnoringASCIICase" ||
376 MethodName ==
"substring") &&
377 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
378 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
379 ClsName.ends_with(
"String"));
382 void reportBug(
const NamedDecl *Callee,
const Expr *CallArg,
383 const ParmVarDecl *Param,
const Decl *DeclWithIssue)
const {
386 SmallString<100> Buf;
387 llvm::raw_svector_ostream Os(Buf);
390 Os <<
"Function argument";
392 if (!paramName.empty() || Callee)
394 if (!paramName.empty()) {
399 if (!paramName.empty())
404 if (!paramName.empty() || Callee)
409 if (printPointer(Os, ArgType) == PrintDeclKind::Pointer) {
410 auto *RTC = Model->retainTypeChecker();
412 if (
auto *Decl = RTC->getCanonicalDecl(CallArg->
getType())) {
425 const SourceLocation SrcLocToReport =
429 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
430 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
432 Report->setDeclWithIssue(DeclWithIssue);
433 BR->emitReport(std::move(
Report));
436 void reportBugOnThis(
const NamedDecl *Callee,
const Expr *CallArg,
437 const Decl *DeclWithIssue)
const {
442 SmallString<100> Buf;
443 llvm::raw_svector_ostream Os(Buf);
444 Os <<
"Function argument";
446 Os <<
" (parameter 'this'";
451 Os <<
") is a raw pointer to " << Model->typeName() <<
" ";
454 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
455 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
457 Report->setDeclWithIssue(DeclWithIssue);
458 BR->emitReport(std::move(
Report));
461 void reportBugOnReceiver(
const NamedDecl *Callee,
const Expr *CallArg,
462 const Decl *DeclWithIssue)
const {
467 SmallString<100> Buf;
468 llvm::raw_svector_ostream Os(Buf);
476 Os <<
" is a raw pointer to " << Model->typeName() <<
" ";
479 PathDiagnosticLocation BSLoc(SrcLocToReport, BR->getSourceManager());
480 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
482 Report->setDeclWithIssue(DeclWithIssue);
483 BR->emitReport(std::move(
Report));
486 void printArgument(llvm::raw_svector_ostream &Os,
const Expr *Arg)
const {
487 SmallString<100> Buf;
488 llvm::raw_svector_ostream ArgOs(Buf);
490 BR->getContext().getPrintingPolicy());
491 StringRef ArgCode = ArgOs.str();
492 if (ArgCode.contains(
'\n'))
494 ArgCode = ArgCode.take_front(50);
495 if (ArgCode.size() == 50)
496 Os <<
" '" << ArgCode <<
"...'";
498 Os <<
" '" << ArgCode <<
"'";
501 enum class PrintDeclKind { Pointee,
Pointer };
502 PrintDeclKind printPointer(llvm::raw_svector_ostream &Os,
503 const Type *
T)
const {
507 Os << Model->typeName() <<
" ";
508 return PrintDeclKind::Pointer;
512 Os <<
"raw " << (IsPtr ?
"pointer" :
"reference") <<
" to "
513 << Model->typeName();
514 return PrintDeclKind::Pointee;
518class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
520 UncountedCallArgsChecker()
521 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
522 "pointer/reference parameter",
526class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
528 UncheckedCallArgsChecker()
529 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
530 "pointer/reference parameter",
534class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
536 UnretainedCallArgsChecker()
537 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
538 "pointer/reference parameter",
548bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
556bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
560void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
564bool 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.
static void printArgument(const TemplateArgument &A, const PrintingPolicy &PP, llvm::raw_ostream &OS, bool IncludeType)
CXXConstructorDecl * getConstructor() const
Get the constructor that this expression will (ultimately) call.
CXXMethodDecl * getMethodDecl() const
Retrieve the declaration of the called method.
Expr * getImplicitObjectArgument() const
Retrieve the implicit object argument for the member call.
QualType getObjectType() const
Retrieve the type of the object argument.
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...
param_iterator param_begin()
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.
const Type * getTypePtr() const
Retrieves a pointer to the underlying (unqualified) type.
QualType getCanonicalType() const
StringRef getNameForSlot(unsigned argIndex) const
Retrieve the name at a given position in the selector.
SourceLocation getBegin() const
void printPretty(raw_ostream &OS, PrinterHelper *Helper, const PrintingPolicy &Policy, unsigned Indentation=0, StringRef NewlineSymbol="\n", const ASTContext *Context=nullptr) const
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
const T * getAs() const
Member-template getAs<specific type>'.
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
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.
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 isPtrConversion(const FunctionDecl *F)
std::unique_ptr< PtrRefSafetyModel > makeCheckedPtrSafetyModel()
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
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)
@ Type
The name was classified as a type.
void printTypeName(llvm::raw_ostream &Os, const QualType QT)
std::string safeGetName(const T *ASTNode)
bool isNullPtr(const clang::Expr *E)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
std::unique_ptr< PtrRefSafetyModel > makeRefPtrSafetyModel()
bool isAllocInit(const Expr *E, const Expr **InnerExpr)
std::unique_ptr< PtrRefSafetyModel > makeRetainPtrSafetyModel()