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 {
80 llvm::SaveAndRestore SavedDecl(DeclWithIssue);
86 bool TraverseLambdaExpr(
LambdaExpr *L)
override {
89 return DynamicRecursiveASTVisitor::TraverseLambdaExpr(L);
97 for (
unsigned I = 0, N = L->
capture_size(); I != N; ++I) {
104 return TraverseDecl(FTD);
107 bool VisitCallExpr(CallExpr *CE)
override {
108 Checker->visitCallExpr(CE, DeclWithIssue);
112 bool VisitCXXConstructExpr(CXXConstructExpr *CE)
override {
113 Checker->visitConstructExpr(CE, DeclWithIssue);
117 bool VisitTypedefDecl(TypedefDecl *TD)
override {
118 if (
auto *RTC = Checker->Model->retainTypeChecker())
119 RTC->visitTypedef(TD);
123 bool VisitObjCMessageExpr(ObjCMessageExpr *ObjCMsgExpr)
override {
124 Checker->visitObjCMessageExpr(ObjCMsgExpr, DeclWithIssue);
129 LocalVisitor visitor(
this);
130 if (
auto *RTC = Model->retainTypeChecker())
131 RTC->visitTranslationUnitDecl(TUD);
132 visitor.TraverseDecl(
const_cast<TranslationUnitDecl *
>(TUD));
135 template <
typename CallOrConstruct>
136 void visitCallOrConstructExpr(
const CallOrConstruct *CE,
137 const FunctionDecl *F,
const Decl *D)
const {
140 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE)) {
141 checkThisArg(F, MemberCallExpr, D);
147 auto *Receiver = CE->getArg(0);
148 checkThisArg(F, Receiver, Receiver->getType(), D);
153 P < F->param_end() && ArgIdx < CE->getNumArgs(); ++P, ++ArgIdx) {
157 checkArg(F, CE->getArg(ArgIdx), (*P)->getType(), *P, D);
159 for (; ArgIdx < CE->getNumArgs(); ++ArgIdx) {
160 auto *Arg = CE->getArg(ArgIdx);
161 checkArg(F, Arg, Arg->getType(),
nullptr, D);
166 void visitCallExpr(
const CallExpr *CE,
const Decl *D)
const {
168 if (shouldSkipCall(CE, Callee))
172 visitCallOrConstructExpr(CE, Callee, D);
174 if (
auto *FnType =
Decl->getFunctionType()) {
175 if (
auto *ProtoType = dyn_cast<FunctionProtoType>(FnType)) {
176 if (
auto *MemberCallExpr = dyn_cast<CXXMemberCallExpr>(CE))
177 checkThisArg(
nullptr, MemberCallExpr, D);
179 for (
auto PT = ProtoType->param_type_begin();
180 PT < ProtoType->param_type_end() && ArgIdx < CE->getNumArgs();
182 checkArg(
nullptr, CE->
getArg(ArgIdx), *PT,
nullptr, D);
184 auto *Arg = CE->
getArg(ArgIdx);
185 checkArg(
nullptr, Arg, Arg->getType(),
nullptr, D);
192 void visitConstructExpr(
const CXXConstructExpr *CE,
const Decl *D)
const {
200 void visitObjCMessageExpr(
const ObjCMessageExpr *E,
const Decl *D)
const {
201 if (BR->getSourceManager().isInSystemHeader(E->
getExprLoc()))
205 std::optional<bool> IsUnsafe = Model->isUnsafePtr(E->
getReceiverType());
206 const Expr *Origin =
nullptr;
207 if (IsUnsafe && *IsUnsafe && !isPtrOriginSafe(Receiver, &Origin)) {
210 reportBugOnReceiver(E->
getMethodDecl(), Receiver, D, Origin);
219 for (
unsigned i = 0; i < ArgCount; ++i) {
221 bool hasParam = i < MethodDecl->param_size();
222 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
223 auto ArgType = Arg->getType();
224 std::optional<bool> IsUnsafe = Model->isUnsafePtr(ArgType);
225 if (!IsUnsafe || !(*IsUnsafe))
227 const Expr *Origin =
nullptr;
228 if (isPtrOriginSafe(Arg, &Origin))
230 reportBug(MethodDecl, Arg, Param, D, Origin);
234 static bool isRefCountingOperation(
const CXXMethodDecl *MD) {
238 return name ==
"ref" ||
name ==
"deref" ||
239 name ==
"incrementCheckedPtrCount" ||
240 name ==
"decrementCheckedPtrCount";
243 void checkThisArg(
const NamedDecl *Callee,
244 const CXXMemberCallExpr *MemberCallExpr,
245 const Decl *DeclWithIssue)
const {
252 void checkThisArg(
const NamedDecl *Callee,
const Expr *Receiver,
253 QualType ReceiverType,
const Decl *DeclWithIssue)
const {
257 QualType ParamType = BR->getContext().getLValueReferenceType(
259 std::optional<bool> IsUnsafe = Model->isUnsafePtr(ParamType);
260 if (!IsUnsafe || !*IsUnsafe)
263 const Expr *Origin =
nullptr;
264 if (isPtrOriginSafe(Receiver, &Origin))
267 reportBugOnThis(Callee, Receiver, DeclWithIssue, Origin);
270 void checkArg(
const NamedDecl *Callee,
const Expr *Arg, QualType ParamType,
271 const ParmVarDecl *Param,
const Decl *DeclWithIssue)
const {
272 std::optional<bool> IsUncounted = Model->isUnsafePtr(ParamType);
273 if (!IsUncounted || !(*IsUncounted))
276 if (
auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
277 Arg = DefaultArg->getExpr();
279 const Expr *Origin =
nullptr;
280 if (isPtrOriginSafe(Arg, &Origin))
283 reportBug(Callee, Arg, Param, DeclWithIssue, Origin);
286 bool isPtrOriginSafe(
const Expr *Arg,
const Expr **Origin =
nullptr)
const {
289 Model->checksForInteriorDestruction(),
290 [&](
const clang::CXXRecordDecl *
Record) {
291 return Model->isSafePtr(Record);
293 [&](
const clang::QualType
T) { return Model->isSafePtrType(T); },
294 [&](
const clang::Decl *D) {
295 return Model->isSafeDecl(D, BR->getSourceManager());
299 [&](
const clang::Expr *ArgOrigin,
bool IsSafe,
301 bool PtrIsLifetimeBoundToOrigin) {
304 if (isNullPtr(ArgOrigin))
306 if (isa<IntegerLiteral>(ArgOrigin)) {
315 if (!Model->checksForInteriorDestruction() &&
318 if (EFA.isACallToEnsureFn(ArgOrigin)) {
319 auto *MCE = dyn_cast<CXXMemberCallExpr>(ArgOrigin);
321 if (isPtrOriginSafe(MCE->getImplicitObjectArgument()))
324 if (Model->isSafeExpr(ArgOrigin, PtrIsLifetimeBoundToOrigin))
326 if (Origin && !*Origin)
332 template <
typename CallOrConstruct>
333 bool shouldSkipCall(
const CallOrConstruct *CE,
344 if (CE->getNumArgs() == 0)
349 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
351 if (MemberOp->getOperator() ==
353 auto *callee = MemberOp->getDirectCallee();
354 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
356 if (Model->isSafePtr(classDecl))
361 if (MemberOp->isAssignmentOp())
368 if (isMethodOnWTFContainerType(Callee))
371 auto overloadedOperatorType =
Callee->getOverloadedOperator();
372 if (overloadedOperatorType == OO_EqualEqual ||
373 overloadedOperatorType == OO_ExclaimEqual ||
374 overloadedOperatorType == OO_LessEqual ||
375 overloadedOperatorType == OO_GreaterEqual ||
376 overloadedOperatorType == OO_Spaceship ||
377 overloadedOperatorType == OO_AmpAmp ||
378 overloadedOperatorType == OO_PipePipe)
385 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
386 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
388 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
389 name ==
"equalIgnoringASCIICaseCommon" ||
390 name ==
"equalIgnoringNullity" || name ==
"toString")
399 auto *ClassDecl =
Decl->getParent();
403 auto *NsDecl = ClassDecl->getParent();
409 StringRef ClsName = ClsNameStr;
412 return NamespaceName ==
"WTF" &&
413 (MethodName ==
"find" || MethodName ==
"findIf" ||
414 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
415 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
416 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
417 MethodName ==
"containsIf" ||
418 MethodName ==
"containsIgnoringASCIICase" ||
419 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
420 MethodName ==
"startsWithIgnoringASCIICase" ||
421 MethodName ==
"endsWithIgnoringASCIICase" ||
422 MethodName ==
"substring") &&
423 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
424 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
425 ClsName.ends_with(
"String"));
430 const Expr *Origin)
const {
434 llvm::raw_svector_ostream Os(Buf);
437 Os <<
"Function argument";
439 if (!paramName.empty() || Callee)
441 if (!paramName.empty()) {
446 if (!paramName.empty())
451 if (!paramName.empty() || Callee)
454 Model->describeHazard(Os, Origin, CallArg->
getType());
458 usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc()
462 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
464 Report->setDeclWithIssue(DeclWithIssue);
468 void reportBugOnThis(
const NamedDecl *Callee,
const Expr *CallArg,
469 const Decl *DeclWithIssue,
const Expr *Origin)
const {
475 llvm::raw_svector_ostream Os(Buf);
476 Os <<
"Function argument";
478 Os <<
" (parameter 'this'";
484 printHazardOrPointerTo(Os, CallArg, Origin);
487 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
489 Report->setDeclWithIssue(DeclWithIssue);
493 void reportBugOnReceiver(
const NamedDecl *Callee,
const Expr *CallArg,
494 const Decl *DeclWithIssue,
495 const Expr *Origin)
const {
501 llvm::raw_svector_ostream Os(Buf);
510 printHazardOrPointerTo(Os, CallArg, Origin);
513 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
515 Report->setDeclWithIssue(DeclWithIssue);
519 void printHazardOrPointerTo(llvm::raw_svector_ostream &Os,
520 const Expr *CallArg,
const Expr *Origin)
const {
521 if (Model->checksForInteriorDestruction()) {
522 Model->describeHazard(Os, Origin, CallArg->
getType());
527 Os <<
"raw pointer to " << Model->typeName() <<
" ";
533 llvm::raw_svector_ostream ArgOs(Buf);
536 StringRef ArgCode = ArgOs.str();
537 if (ArgCode.contains(
'\n'))
539 ArgCode = ArgCode.take_front(50);
540 if (ArgCode.size() == 50)
541 Os <<
" '" << ArgCode <<
"...'";
543 Os <<
" '" << ArgCode <<
"'";
547class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
549 UncountedCallArgsChecker()
550 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
551 "pointer/reference parameter",
555class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
557 UncheckedCallArgsChecker()
558 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
559 "pointer/reference parameter",
563class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
565 UnretainedCallArgsChecker()
566 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
567 "pointer/reference parameter",
571class UnborrowedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
573 UnborrowedCallArgsChecker()
574 : RawPtrRefCallArgsChecker(
"Loan on a CanBorrow object not guarded by "
585bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
593bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
597void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
601bool ento::shouldRegisterUnretainedCallArgsChecker(
const CheckerManager &) {
605void ento::registerUnborrowedCallArgsChecker(
CheckerManager &Mgr) {
609bool 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.
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 Stmt **OffendingStmt=nullptr) 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()