24#include "llvm/Support/SaveAndRestore.h"
32class RawPtrRefCallArgsChecker
33 :
public Checker<check::ASTDecl<TranslationUnitDecl>> {
36 TrivialFunctionAnalysis TFA;
37 EnsureFunctionAnalysis EFA;
40 mutable BugReporter *BR;
41 const std::unique_ptr<PtrRefSafetyModel> Model;
44 RawPtrRefCallArgsChecker(
const char *description,
45 std::unique_ptr<PtrRefSafetyModel> Model)
46 : Bug(this, description,
"WebKit coding guidelines"),
47 Model(std::move(Model)) {}
49 void checkASTDecl(
const TranslationUnitDecl *TUD, AnalysisManager &MGR,
50 BugReporter &BRArg)
const {
57 const RawPtrRefCallArgsChecker *Checker;
58 Decl *DeclWithIssue{
nullptr};
60 explicit LocalVisitor(
const RawPtrRefCallArgsChecker *Checker)
63 ShouldVisitTemplateInstantiations =
true;
64 ShouldVisitImplicitCode =
false;
67 bool TraverseClassTemplateDecl(ClassTemplateDecl *Decl)
override {
70 return DynamicRecursiveASTVisitor::TraverseClassTemplateDecl(Decl);
73 bool TraverseDecl(Decl *D)
override {
81 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
87 bool TraverseLambdaExpr(
LambdaExpr *L)
override {
90 return DynamicRecursiveASTVisitor::TraverseLambdaExpr(L);
98 for (
unsigned I = 0, N = L->
capture_size(); I != N; ++I) {
105 return TraverseDecl(FTD);
108 bool VisitCallExpr(CallExpr *CE)
override {
109 Checker->visitCallExpr(CE, DeclWithIssue);
113 bool VisitCXXConstructExpr(CXXConstructExpr *CE)
override {
114 Checker->visitConstructExpr(CE, DeclWithIssue);
118 bool VisitTypedefDecl(TypedefDecl *TD)
override {
119 if (
auto *RTC = Checker->Model->retainTypeChecker())
120 RTC->visitTypedef(TD);
124 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr)
override {
125 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
130 LocalVisitor visitor(
this);
131 if (
auto *RTC = Model->retainTypeChecker())
132 RTC->visitTranslationUnitDecl(TUD);
133 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
136 template <
typename CallOrConstruct>
137 void visitCallOrConstructExpr(
const CallOrConstruct *CE,
138 const FunctionDecl *F,
const Decl *D)
const {
141 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
142 checkThisArg(F, MemberCallExpr, D);
148 auto *Receiver = CE->getArg(0);
149 checkThisArg(F, Receiver, Receiver->getType(), D);
154 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
158 checkArg(F, CE->getArg(ArgIdx), (*P)->getType(), *P, D);
160 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
161 auto *Arg = CE->getArg(ArgIdx);
162 checkArg(F, Arg, Arg->getType(),
nullptr, D);
167 void visitCallExpr(
const CallExpr *CE,
const Decl *D)
const {
169 if (shouldSkipCall(CE, Callee))
173 visitCallOrConstructExpr(CE, Callee, D);
175 if (
auto *FnType =
Decl->getFunctionType()) {
176 if (
auto *ProtoType = dyn_cast<FunctionProtoType>(FnType)) {
177 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
178 checkThisArg(
nullptr, MemberCallExpr, D);
180 for (
auto PT = ProtoType->param_type_begin();
181 PT < ProtoType->param_type_end() && ArgIdx < CE->getNumArgs();
183 checkArg(
nullptr, CE->
getArg(ArgIdx), *PT,
nullptr, D);
185 auto *Arg = CE->
getArg(ArgIdx);
186 checkArg(
nullptr, Arg, Arg->getType(),
nullptr, D);
193 void visitConstructExpr(
const CXXConstructExpr *CE,
const Decl *D)
const {
201 void visitObjCMessageExpr(
const ObjCMessageExpr *E,
const Decl *D)
const {
202 if (BR->getSourceManager().isInSystemHeader(E->
getExprLoc()))
206 std::optional<bool> IsUnsafe = Model->isUnsafePtr(E->
getReceiverType());
207 const Expr *Origin =
nullptr;
208 if (IsUnsafe && *IsUnsafe &&
212 reportBugOnReceiver(E->
getMethodDecl(), Receiver, D, Origin);
221 for (
unsigned i = 0; i < ArgCount; ++i) {
223 bool hasParam = i < MethodDecl->param_size();
224 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
225 auto ArgType = Arg->getType();
226 std::optional<bool> IsUnsafe = Model->isUnsafePtr(ArgType);
227 if (!IsUnsafe || !(*IsUnsafe))
229 const Expr *Origin =
nullptr;
230 if (isPtrOriginSafe(Arg, &Origin, ArgType))
232 reportBug(MethodDecl, Arg, Param, D, Origin);
236 static bool isRefCountingOperation(
const CXXMethodDecl *MD) {
240 return name ==
"ref" ||
name ==
"deref" ||
241 name ==
"incrementCheckedPtrCount" ||
242 name ==
"decrementCheckedPtrCount";
245 void checkThisArg(
const NamedDecl *Callee,
246 const CXXMemberCallExpr *MemberCallExpr,
247 const Decl *DeclWithIssue)
const {
254 void checkThisArg(
const NamedDecl *Callee,
const Expr *Receiver,
255 QualType ReceiverType,
const Decl *DeclWithIssue)
const {
259 QualType ParamType = BR->getContext().getLValueReferenceType(
261 std::optional<bool> IsUnsafe = Model->isUnsafePtr(ParamType);
262 if (!IsUnsafe || !*IsUnsafe)
265 const Expr *Origin =
nullptr;
266 if (isPtrOriginSafe(Receiver, &Origin, ParamType))
269 reportBugOnThis(Callee, Receiver, DeclWithIssue, Origin);
272 void checkArg(
const NamedDecl *Callee,
const Expr *Arg, QualType ParamType,
273 const ParmVarDecl *Param,
const Decl *DeclWithIssue)
const {
274 std::optional<bool> IsUncounted = Model->isUnsafePtr(ParamType);
275 if (!IsUncounted || !(*IsUncounted))
278 if (
auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
279 Arg = DefaultArg->getExpr();
281 const Expr *Origin =
nullptr;
282 if (isPtrOriginSafe(Arg, &Origin, ParamType))
285 reportBug(Callee, Arg, Param, DeclWithIssue, Origin);
288 bool isPtrOriginSafe(
const Expr *Arg,
const Expr **Origin =
nullptr,
289 QualType SinkType = QualType())
const {
292 Model->checksForInteriorDestruction(),
293 [&](
const clang::CXXRecordDecl *
Record) {
294 return Model->isSafePtr(Record);
296 [&](
const clang::QualType
T) { return Model->isSafePtrType(T); },
297 [&](
const clang::Decl *D) {
298 return Model->isSafeDecl(D, BR->getSourceManager());
302 [&](
const clang::Expr *ArgOrigin,
bool IsSafe,
304 bool PtrIsLifetimeBoundToOrigin) {
307 if (isNullPtr(ArgOrigin))
309 if (isa<IntegerLiteral>(ArgOrigin)) {
318 if (!Model->checksForInteriorDestruction() &&
321 if (EFA.isACallToEnsureFn(ArgOrigin)) {
322 auto *MCE = dyn_cast<CXXMemberCallExpr>(ArgOrigin);
324 if (isPtrOriginSafe(MCE->getImplicitObjectArgument()))
327 if (Model->isSafeExpr(ArgOrigin, PtrIsLifetimeBoundToOrigin, SinkType,
330 if (Origin && !*Origin)
336 template <
typename CallOrConstruct>
337 bool shouldSkipCall(
const CallOrConstruct *CE,
348 if (CE->getNumArgs() == 0)
353 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
355 if (MemberOp->getOperator() ==
357 auto *callee = MemberOp->getDirectCallee();
358 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
360 if (Model->isSafePtr(classDecl))
365 if (MemberOp->isAssignmentOp())
372 if (isMethodOnWTFContainerType(Callee))
375 auto overloadedOperatorType =
Callee->getOverloadedOperator();
376 if (overloadedOperatorType == OO_EqualEqual ||
377 overloadedOperatorType == OO_ExclaimEqual ||
378 overloadedOperatorType == OO_LessEqual ||
379 overloadedOperatorType == OO_GreaterEqual ||
380 overloadedOperatorType == OO_Spaceship ||
381 overloadedOperatorType == OO_AmpAmp ||
382 overloadedOperatorType == OO_PipePipe)
389 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
390 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
392 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
393 name ==
"equalIgnoringASCIICaseCommon" ||
394 name ==
"equalIgnoringNullity" || name ==
"toString")
403 auto *ClassDecl =
Decl->getParent();
407 auto *NsDecl = ClassDecl->getParent();
413 StringRef ClsName = ClsNameStr;
416 return NamespaceName ==
"WTF" &&
417 (MethodName ==
"find" || MethodName ==
"findIf" ||
418 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
419 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
420 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
421 MethodName ==
"containsIf" ||
422 MethodName ==
"containsIgnoringASCIICase" ||
423 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
424 MethodName ==
"startsWithIgnoringASCIICase" ||
425 MethodName ==
"endsWithIgnoringASCIICase" ||
426 MethodName ==
"substring") &&
427 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
428 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
429 ClsName.ends_with(
"String"));
434 const Expr *Origin)
const {
438 llvm::raw_svector_ostream Os(Buf);
441 Os <<
"Function argument";
443 if (!paramName.empty() || Callee)
445 if (!paramName.empty()) {
450 if (!paramName.empty())
455 if (!paramName.empty() || Callee)
458 Model->describeHazard(Os, Origin, CallArg->
getType());
462 usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc()
466 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
468 Report->setDeclWithIssue(DeclWithIssue);
472 void reportBugOnThis(
const NamedDecl *Callee,
const Expr *CallArg,
473 const Decl *DeclWithIssue,
const Expr *Origin)
const {
479 llvm::raw_svector_ostream Os(Buf);
480 Os <<
"Function argument";
482 Os <<
" (parameter 'this'";
488 printHazardOrPointerTo(Os, CallArg, Origin);
491 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
493 Report->setDeclWithIssue(DeclWithIssue);
497 void reportBugOnReceiver(
const NamedDecl *Callee,
const Expr *CallArg,
498 const Decl *DeclWithIssue,
499 const Expr *Origin)
const {
505 llvm::raw_svector_ostream Os(Buf);
514 printHazardOrPointerTo(Os, CallArg, Origin);
517 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
519 Report->setDeclWithIssue(DeclWithIssue);
523 void printHazardOrPointerTo(llvm::raw_svector_ostream &Os,
524 const Expr *CallArg,
const Expr *Origin)
const {
525 if (Model->checksForInteriorDestruction()) {
526 Model->describeHazard(Os, Origin, CallArg->
getType());
531 Os <<
"raw pointer to " << Model->typeName() <<
" ";
537 llvm::raw_svector_ostream ArgOs(Buf);
540 StringRef ArgCode = ArgOs.str();
541 if (ArgCode.contains(
'\n'))
543 ArgCode = ArgCode.take_front(50);
544 if (ArgCode.size() == 50)
545 Os <<
" '" << ArgCode <<
"...'";
547 Os <<
" '" << ArgCode <<
"'";
551class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
553 UncountedCallArgsChecker()
554 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
555 "pointer/reference parameter",
559class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
561 UncheckedCallArgsChecker()
562 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
563 "pointer/reference parameter",
567class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
569 UnretainedCallArgsChecker()
570 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
571 "pointer/reference parameter",
575class UnborrowedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
577 UnborrowedCallArgsChecker()
578 : RawPtrRefCallArgsChecker(
"Unborrowed call argument for a raw "
579 "pointer/reference parameter",
589bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
597bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
601void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
605bool ento::shouldRegisterUnretainedCallArgsChecker(
const CheckerManager &) {
609void ento::registerUnborrowedCallArgsChecker(
CheckerManager &Mgr) {
613bool ento::shouldRegisterUnborrowedCallArgsChecker(
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.
Defines the SourceManager interface.
static void printArgument(const TemplateArgument &A, const PrintingPolicy &PP, llvm::raw_ostream &OS, bool IncludeType)
const clang::PrintingPolicy & getPrintingPolicy() const
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.
Represents a C++ struct/union/class.
FunctionTemplateDecl * getDependentLambdaCallOperator() const
Retrieve the dependent lambda call operator of the closure type if this is a templated closure type.
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.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Decl - This represents one declaration (or definition), e.g.
bool isTemplated() const
Determine whether this declaration is a templated entity (whether it is.
virtual bool TraverseDecl(MaybeConst< Decl > *D)
This represents one expression.
SourceLocation getExprLoc() const LLVM_READONLY
getExprLoc - Return the preferred location for the arrow when diagnosing a problem with a generic exp...
Represents a function declaration or definition.
param_iterator param_begin()
capture_iterator capture_begin() const
Retrieve an iterator pointing to the first lambda capture.
unsigned capture_size() const
Determine the number of captures in this lambda.
capture_init_iterator capture_init_begin()
Retrieve the first initialization argument for this lambda expression (which initializes the first ca...
CXXRecordDecl * getLambdaClass() const
Retrieve the class that corresponds to the lambda.
This represents a decl that may have a name.
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.
Represents a parameter to a function.
QualType getCanonicalType() const
Encodes a location in the source.
bool isInSystemHeader(SourceLocation Loc) const
Returns if a SourceLocation is in a system header.
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...
bool isTrivial(const Decl *D) const
const SourceManager & getSourceManager()
ASTContext & getContext()
virtual void emitReport(std::unique_ptr< BugReport > R)
Add the given report to the set of reports tracked by BugReporter.
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.
Top level wrappers for InstallAPI frontend operations.
bool isCtorOfSafePtr(const clang::FunctionDecl *F)
bool isTrivialBuiltinFunction(const FunctionDecl *F)
bool isa(CodeGen::Address addr)
std::unique_ptr< PtrRefSafetyModel > makeBorrowSafetyModel()
if(T->getSizeExpr()) TRY_TO(TraverseStmt(const_cast< Expr * >(T -> getSizeExpr())))
bool isPtrConversion(const FunctionDecl *F)
std::unique_ptr< PtrRefSafetyModel > makeCheckedPtrSafetyModel()
void printQuotedQualifiedName(llvm::raw_ostream &Os, const NamedDeclDerivedT &D)
const FunctionProtoType * T
bool isSmartPtrClass(const std::string &Name)
bool originOutlivesCall(const Expr *E)
For E referring to a ref-countable/-counted pointer/reference we return whether the pointee outlives ...
void printTypeName(llvm::raw_ostream &Os, const QualType QT)
std::string safeGetName(const T *ASTNode)
DynamicRecursiveASTVisitorBase< false > DynamicRecursiveASTVisitor
std::unique_ptr< PtrRefSafetyModel > makeRefPtrSafetyModel()
bool tryToFindPtrOrigin(const Expr *E, bool StopAtFirstRefCountedObj, bool FollowLifetimeBound, 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, bool, bool)> callback)
This function de-facto defines a set of transformations that we consider safe (in heuristical sense).
bool isAllocInit(const Expr *E, const Expr **InnerExpr)
std::unique_ptr< PtrRefSafetyModel > makeRetainPtrSafetyModel()