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 &&
211 reportBugOnReceiver(E->
getMethodDecl(), Receiver, D, Origin);
220 for (
unsigned i = 0; i < ArgCount; ++i) {
222 bool hasParam = i < MethodDecl->param_size();
223 auto *Param = hasParam ? MethodDecl->getParamDecl(i) :
nullptr;
224 auto ArgType = Arg->getType();
225 std::optional<bool> IsUnsafe = Model->isUnsafePtr(ArgType);
226 if (!IsUnsafe || !(*IsUnsafe))
228 const Expr *Origin =
nullptr;
229 if (isPtrOriginSafe(Arg, &Origin, ArgType))
231 reportBug(MethodDecl, Arg, Param, D, Origin);
235 static bool isRefCountingOperation(
const CXXMethodDecl *MD) {
239 return name ==
"ref" ||
name ==
"deref" ||
240 name ==
"incrementCheckedPtrCount" ||
241 name ==
"decrementCheckedPtrCount";
244 void checkThisArg(
const NamedDecl *Callee,
245 const CXXMemberCallExpr *MemberCallExpr,
246 const Decl *DeclWithIssue)
const {
253 void checkThisArg(
const NamedDecl *Callee,
const Expr *Receiver,
254 QualType ReceiverType,
const Decl *DeclWithIssue)
const {
258 QualType ParamType = BR->getContext().getLValueReferenceType(
260 std::optional<bool> IsUnsafe = Model->isUnsafePtr(ParamType);
261 if (!IsUnsafe || !*IsUnsafe)
264 const Expr *Origin =
nullptr;
265 if (isPtrOriginSafe(Receiver, &Origin, ParamType))
268 reportBugOnThis(Callee, Receiver, DeclWithIssue, Origin);
271 void checkArg(
const NamedDecl *Callee,
const Expr *Arg, QualType ParamType,
272 const ParmVarDecl *Param,
const Decl *DeclWithIssue)
const {
273 std::optional<bool> IsUncounted = Model->isUnsafePtr(ParamType);
274 if (!IsUncounted || !(*IsUncounted))
277 if (
auto *DefaultArg = dyn_cast<CXXDefaultArgExpr>(Arg))
278 Arg = DefaultArg->getExpr();
280 const Expr *Origin =
nullptr;
281 if (isPtrOriginSafe(Arg, &Origin, ParamType))
284 reportBug(Callee, Arg, Param, DeclWithIssue, Origin);
287 bool isPtrOriginSafe(
const Expr *Arg,
const Expr **Origin =
nullptr,
288 QualType SinkType = QualType())
const {
291 Model->checksForInteriorDestruction(),
292 [&](
const clang::CXXRecordDecl *
Record) {
293 return Model->isSafePtr(Record);
295 [&](
const clang::QualType
T) { return Model->isSafePtrType(T); },
296 [&](
const clang::Decl *D) {
297 return Model->isSafeDecl(D, BR->getSourceManager());
301 [&](
const clang::Expr *ArgOrigin,
bool IsSafe,
303 bool PtrIsLifetimeBoundToOrigin) {
306 if (isNullPtr(ArgOrigin))
308 if (isa<IntegerLiteral>(ArgOrigin)) {
317 if (!Model->checksForInteriorDestruction() &&
320 if (EFA.isACallToEnsureFn(ArgOrigin)) {
321 auto *MCE = dyn_cast<CXXMemberCallExpr>(ArgOrigin);
323 if (isPtrOriginSafe(MCE->getImplicitObjectArgument()))
326 if (Model->isSafeExpr(ArgOrigin, PtrIsLifetimeBoundToOrigin, SinkType,
329 if (Origin && !*Origin)
335 template <
typename CallOrConstruct>
336 bool shouldSkipCall(
const CallOrConstruct *CE,
347 if (CE->getNumArgs() == 0)
352 if (
auto *MemberOp = dyn_cast<CXXOperatorCallExpr>(CE)) {
354 if (MemberOp->getOperator() ==
356 auto *callee = MemberOp->getDirectCallee();
357 if (
auto *calleeDecl = dyn_cast<CXXMethodDecl>(callee)) {
359 if (Model->isSafePtr(classDecl))
364 if (MemberOp->isAssignmentOp())
371 if (isMethodOnWTFContainerType(Callee))
374 auto overloadedOperatorType =
Callee->getOverloadedOperator();
375 if (overloadedOperatorType == OO_EqualEqual ||
376 overloadedOperatorType == OO_ExclaimEqual ||
377 overloadedOperatorType == OO_LessEqual ||
378 overloadedOperatorType == OO_GreaterEqual ||
379 overloadedOperatorType == OO_Spaceship ||
380 overloadedOperatorType == OO_AmpAmp ||
381 overloadedOperatorType == OO_PipePipe)
388 if (name ==
"adoptRef" || name ==
"getPtr" || name ==
"WeakPtr" ||
389 name ==
"is" || name ==
"equal" || name ==
"hash" || name ==
"isType" ||
391 name ==
"CFEqual" || name ==
"equalIgnoringASCIICase" ||
392 name ==
"equalIgnoringASCIICaseCommon" ||
393 name ==
"equalIgnoringNullity" || name ==
"toString")
402 auto *ClassDecl =
Decl->getParent();
406 auto *NsDecl = ClassDecl->getParent();
412 StringRef ClsName = ClsNameStr;
415 return NamespaceName ==
"WTF" &&
416 (MethodName ==
"find" || MethodName ==
"findIf" ||
417 MethodName ==
"reverseFind" || MethodName ==
"reverseFindIf" ||
418 MethodName ==
"findIgnoringASCIICase" || MethodName ==
"get" ||
419 MethodName ==
"inlineGet" || MethodName ==
"contains" ||
420 MethodName ==
"containsIf" ||
421 MethodName ==
"containsIgnoringASCIICase" ||
422 MethodName ==
"startsWith" || MethodName ==
"endsWith" ||
423 MethodName ==
"startsWithIgnoringASCIICase" ||
424 MethodName ==
"endsWithIgnoringASCIICase" ||
425 MethodName ==
"substring") &&
426 (ClsName.ends_with(
"Vector") || ClsName.ends_with(
"Set") ||
427 ClsName.ends_with(
"Map") || ClsName ==
"StringImpl" ||
428 ClsName.ends_with(
"String"));
433 const Expr *Origin)
const {
437 llvm::raw_svector_ostream Os(Buf);
440 Os <<
"Function argument";
442 if (!paramName.empty() || Callee)
444 if (!paramName.empty()) {
449 if (!paramName.empty())
454 if (!paramName.empty() || Callee)
457 Model->describeHazard(Os, Origin, CallArg->
getType());
461 usesDefaultArgValue ? Param->getDefaultArg()->getExprLoc()
465 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
467 Report->setDeclWithIssue(DeclWithIssue);
471 void reportBugOnThis(
const NamedDecl *Callee,
const Expr *CallArg,
472 const Decl *DeclWithIssue,
const Expr *Origin)
const {
478 llvm::raw_svector_ostream Os(Buf);
479 Os <<
"Function argument";
481 Os <<
" (parameter 'this'";
487 printHazardOrPointerTo(Os, CallArg, Origin);
490 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
492 Report->setDeclWithIssue(DeclWithIssue);
496 void reportBugOnReceiver(
const NamedDecl *Callee,
const Expr *CallArg,
497 const Decl *DeclWithIssue,
498 const Expr *Origin)
const {
504 llvm::raw_svector_ostream Os(Buf);
513 printHazardOrPointerTo(Os, CallArg, Origin);
516 auto Report = std::make_unique<BasicBugReport>(Bug, Os.str(), BSLoc);
518 Report->setDeclWithIssue(DeclWithIssue);
522 void printHazardOrPointerTo(llvm::raw_svector_ostream &Os,
523 const Expr *CallArg,
const Expr *Origin)
const {
524 if (Model->checksForInteriorDestruction()) {
525 Model->describeHazard(Os, Origin, CallArg->
getType());
530 Os <<
"raw pointer to " << Model->typeName() <<
" ";
536 llvm::raw_svector_ostream ArgOs(Buf);
539 StringRef ArgCode = ArgOs.str();
540 if (ArgCode.contains(
'\n'))
542 ArgCode = ArgCode.take_front(50);
543 if (ArgCode.size() == 50)
544 Os <<
" '" << ArgCode <<
"...'";
546 Os <<
" '" << ArgCode <<
"'";
550class UncountedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
552 UncountedCallArgsChecker()
553 : RawPtrRefCallArgsChecker(
"Uncounted call argument for a raw "
554 "pointer/reference parameter",
558class UncheckedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
560 UncheckedCallArgsChecker()
561 : RawPtrRefCallArgsChecker(
"Unchecked call argument for a raw "
562 "pointer/reference parameter",
566class UnretainedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
568 UnretainedCallArgsChecker()
569 : RawPtrRefCallArgsChecker(
"Unretained call argument for a raw "
570 "pointer/reference parameter",
574class UnborrowedCallArgsChecker final :
public RawPtrRefCallArgsChecker {
576 UnborrowedCallArgsChecker()
577 : RawPtrRefCallArgsChecker(
"Unborrowed call argument for a raw "
578 "pointer/reference parameter",
588bool ento::shouldRegisterUncountedCallArgsChecker(
const CheckerManager &) {
596bool ento::shouldRegisterUncheckedCallArgsChecker(
const CheckerManager &) {
600void ento::registerUnretainedCallArgsChecker(
CheckerManager &Mgr) {
604bool ento::shouldRegisterUnretainedCallArgsChecker(
const CheckerManager &) {
608void ento::registerUnborrowedCallArgsChecker(
CheckerManager &Mgr) {
612bool 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
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()