From 29bae29ae3468cae1a1783991f4f2a2b9a4a7781 Mon Sep 17 00:00:00 2001 From: Ziqing Luo Date: Tue, 9 Sep 2025 17:16:25 -0700 Subject: [PATCH] Reland "[C++ BoundsSafety] Fix false positives when pointer argument is a function call (#11046)" (#11358) When the pointer argument passing to a counted_by parameter is a function call returning a counted_by pointer, in order to check the compatibility with respect to their counted_by attributes, the compiler need to compare two expressions, which both have references to parameters that need substitutions. This commit is to generalize the compatibility checking to allow both comparands to be accompanied by substitution maps. rdar://155952016 (cherry picked from commit 46a1881d9e9e2bd609ee1055f37f9a0022c36f60) --- clang/lib/Analysis/UnsafeBufferUsage.cpp | 340 +++++++++++------- ...sage-count-attributed-pointer-argument.cpp | 45 ++- 2 files changed, 257 insertions(+), 128 deletions(-) diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index 1c532906cf3d9..3ee827d776390 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -465,172 +465,240 @@ getDependentValuesFromCall(const CountAttributedType *CAT, // form `f(...)` is not supported. struct CompatibleCountExprVisitor : public ConstStmtVisitor { - // The third 'bool' type parameter for each visit method indicates whether the - // current visiting expression is the result of the formal parameter to actual - // argument substitution. Since the argument expression may contain DREs - // referencing to back to those parameters (in cases of recursive calls), the - // analysis may hit an infinite loop if not knowing whether the substitution - // has happened. A typical example that could introduce infinite loop without - // this knowledge is shown below. + bool, bool> { + // The third and forth 'bool' type parameters for each visit method indicates + // whether the current visiting expression is the result of the formal + // parameter to actual argument substitution (for `Self` and `Other` resp.). + // Since the argument expression may contain DREs referencing to back to those + // parameters (in cases of recursive calls), the analysis may hit an infinite + // loop if not knowing whether the substitution has happened. A typical + // example that could introduce infinite loop without this knowledge is shown + // below. // ``` // void f(int * __counted_by(n) p, size_t n) { // f(p, n); // } // ``` - using BaseVisitor = - ConstStmtVisitor; + using BaseVisitor = ConstStmtVisitor; const Expr *MemberBase; - const DependentValuesTy *DependentValues; + const DependentValuesTy *DependentValuesSelf, *DependentValuesOther; ASTContext &Ctx; - // If `Deref` has the form `*&e`, return `e`; otherwise return nullptr. - const Expr *trySimplifyDerefAddressof(const UnaryOperator *Deref, - bool hasBeenSubstituted) { - const Expr *DerefOperand = Deref->getSubExpr()->IgnoreParenImpCasts(); - - if (const auto *UO = dyn_cast(DerefOperand)) - if (UO->getOpcode() == UO_AddrOf) - return UO->getSubExpr(); - if (const auto *DRE = dyn_cast(DerefOperand)) { - if (!DependentValues || hasBeenSubstituted) - return nullptr; - - auto I = DependentValues->find(DRE->getDecl()); + // Attempts to perform substitution on E and simplify the result, if the + // result has the form "*&e". + // + // This function will be repeatedly called on the "Other" Expr, because the + // kind of "Other" stays unknown during the traversal. + const Expr * + trySubstituteAndSimplify(const Expr *E, bool &hasBeenSubstituted, + const DependentValuesTy *DependentValues) const { + // Attempts to simplify `E`: if `E` has the form `*&e`, return `e`; + // return `E` without change otherwise: + auto trySimplifyDerefAddressof = + [](const Expr *E, + const DependentValuesTy + *DependentValues, // Deref may need subsitution + bool &hasBeenSubstituted) -> const Expr * { + const auto *Deref = dyn_cast(E->IgnoreParenImpCasts()); + + if (!Deref || Deref->getOpcode() != UO_Deref) + return E; + + const Expr *DerefOperand = Deref->getSubExpr()->IgnoreParenImpCasts(); + + if (const auto *UO = dyn_cast(DerefOperand)) + if (UO->getOpcode() == UO_AddrOf) + return UO->getSubExpr(); + if (const auto *DRE = dyn_cast(DerefOperand)) { + if (!DependentValues || hasBeenSubstituted) + return E; + + if (auto I = DependentValues->find(DRE->getDecl()); + I != DependentValues->end()) + if (const auto *UO = dyn_cast( + I->getSecond()->IgnoreParenImpCasts())) + if (UO->getOpcode() == UO_AddrOf) { + hasBeenSubstituted = true; + return UO->getSubExpr(); + } + } + return E; + }; - if (I != DependentValues->end()) - if (const auto *UO = dyn_cast(I->getSecond())) - if (UO->getOpcode() == UO_AddrOf) - return UO->getSubExpr(); + if (!hasBeenSubstituted && DependentValues) { + if (const auto *DRE = dyn_cast(E->IgnoreParenImpCasts())) { + if (auto It = DependentValues->find(DRE->getDecl()); + It != DependentValues->end()) { + hasBeenSubstituted = true; + return trySimplifyDerefAddressof(It->second, nullptr, + hasBeenSubstituted); + } + } } - return nullptr; + return trySimplifyDerefAddressof(E, DependentValues, hasBeenSubstituted); } - explicit CompatibleCountExprVisitor(const Expr *MemberBase, - const DependentValuesTy *DependentValues, - ASTContext &Ctx) - : MemberBase(MemberBase), DependentValues(DependentValues), Ctx(Ctx) {} + explicit CompatibleCountExprVisitor( + const Expr *MemberBase, const DependentValuesTy *DependentValuesSelf, + const DependentValuesTy *DependentValuesOther, ASTContext &Ctx) + : MemberBase(MemberBase), DependentValuesSelf(DependentValuesSelf), + DependentValuesOther(DependentValuesOther), Ctx(Ctx) {} - bool VisitStmt(const Stmt *S, const Expr *E, bool hasBeenSubstituted) { + bool VisitStmt(const Stmt *Self, const Expr *Other, + bool hasSelfBeenSubstituted, bool hasOtherBeenSubstituted) { return false; } bool VisitImplicitCastExpr(const ImplicitCastExpr *SelfICE, const Expr *Other, - bool hasBeenSubstituted) { - return Visit(SelfICE->getSubExpr(), Other, hasBeenSubstituted); + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + return Visit(SelfICE->getSubExpr(), Other, hasSelfBeenSubstituted, + hasOtherBeenSubstituted); } bool VisitParenExpr(const ParenExpr *SelfPE, const Expr *Other, - bool hasBeenSubstituted) { - return Visit(SelfPE->getSubExpr(), Other, hasBeenSubstituted); + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + return Visit(SelfPE->getSubExpr(), Other, hasSelfBeenSubstituted, + hasOtherBeenSubstituted); } bool VisitIntegerLiteral(const IntegerLiteral *SelfIL, const Expr *Other, - bool hasBeenSubstituted) { - if (const auto *IntLit = - dyn_cast(Other->IgnoreParenImpCasts())) { - return SelfIL == IntLit || - llvm::APInt::isSameValue(SelfIL->getValue(), IntLit->getValue()); - } + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); + + Expr::EvalResult SelfER, OtherER; + + // If both are constants: + if (SelfIL->EvaluateAsInt(SelfER, Ctx) && + Other->EvaluateAsInt(OtherER, Ctx)) + return llvm::APInt::isSameValue(SelfER.Val.getInt(), + OtherER.Val.getInt()); return false; } bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Self, const Expr *Other, - bool hasBeenSubstituted) { + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); // If `Self` is a `sizeof` expression, try to evaluate and compare the two // expressions as constants: if (Self->getKind() == UnaryExprOrTypeTrait::UETT_SizeOf) { - Expr::EvalResult ER; - - if (Self->EvaluateAsConstantExpr(ER, Ctx)) { - llvm::APInt SelfVal = ER.Val.getInt(); - - if (Other->getType()->isIntegerType()) - if (Other->EvaluateAsConstantExpr(ER, Ctx)) - return llvm::APInt::isSameValue(SelfVal, ER.Val.getInt()); - } + Expr::EvalResult SelfER, OtherER; + + // If both are constants: + if (Other->getType()->isIntegerType()) + if (Self->EvaluateAsInt(SelfER, Ctx) && + Other->EvaluateAsInt(OtherER, Ctx)) + return llvm::APInt::isSameValue(SelfER.Val.getInt(), + OtherER.Val.getInt()); } return false; } bool VisitCXXThisExpr(const CXXThisExpr *SelfThis, const Expr *Other, - bool hasBeenSubstituted) { + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); return isa(Other->IgnoreParenImpCasts()); } bool VisitDeclRefExpr(const DeclRefExpr *SelfDRE, const Expr *Other, - bool hasBeenSubstituted) { - const ValueDecl *SelfVD = SelfDRE->getDecl(); + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + const auto *SubstSelf = trySubstituteAndSimplify( + SelfDRE, hasSelfBeenSubstituted, DependentValuesSelf); - if (DependentValues && !hasBeenSubstituted) { - const auto It = DependentValues->find(SelfVD); - if (It != DependentValues->end()) - return Visit(It->second, Other, true); - } + if (SubstSelf != SelfDRE) + return Visit(SubstSelf, Other, hasSelfBeenSubstituted, + hasOtherBeenSubstituted); + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); const auto *O = Other->IgnoreParenImpCasts(); if (const auto *OtherDRE = dyn_cast(O)) { // Both SelfDRE and OtherDRE can be transformed from member expressions: - if (OtherDRE->getDecl() == SelfVD) + if (OtherDRE->getDecl() == SelfDRE->getDecl()) return true; return false; } const auto *OtherME = dyn_cast(O); if (MemberBase && OtherME) { - return OtherME->getMemberDecl() == SelfVD && - Visit(OtherME->getBase(), MemberBase, hasBeenSubstituted); + return OtherME->getMemberDecl() == SelfDRE->getDecl() && + Visit(OtherME->getBase(), MemberBase, hasSelfBeenSubstituted, + hasOtherBeenSubstituted); } return false; } bool VisitMemberExpr(const MemberExpr *Self, const Expr *Other, - bool hasBeenSubstituted) { + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); // Even though we don't support member expression in counted-by, actual // arguments can be member expressions. if (Self == Other) return true; if (const auto *DRE = dyn_cast(Other->IgnoreParenImpCasts())) return MemberBase && Self->getMemberDecl() == DRE->getDecl() && - Visit(Self->getBase(), MemberBase, hasBeenSubstituted); + Visit(Self->getBase(), MemberBase, hasSelfBeenSubstituted, + hasOtherBeenSubstituted); if (const auto *OtherME = dyn_cast(Other->IgnoreParenImpCasts())) { return Self->getMemberDecl() == OtherME->getMemberDecl() && - Visit(Self->getBase(), OtherME->getBase(), hasBeenSubstituted); + Visit(Self->getBase(), OtherME->getBase(), hasSelfBeenSubstituted, + hasOtherBeenSubstituted); } return false; } bool VisitUnaryOperator(const UnaryOperator *SelfUO, const Expr *Other, - bool hasBeenSubstituted) { + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { if (SelfUO->getOpcode() != UO_Deref) return false; // We don't support any other unary operator - + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); if (const auto *OtherUO = dyn_cast(Other->IgnoreParenImpCasts())) { if (SelfUO->getOpcode() == OtherUO->getOpcode()) return Visit(SelfUO->getSubExpr(), OtherUO->getSubExpr(), - hasBeenSubstituted); + hasSelfBeenSubstituted, hasOtherBeenSubstituted); } // If `Other` is not a dereference expression, try to simplify `SelfUO`: - if (const auto *SimplifiedSelf = - trySimplifyDerefAddressof(SelfUO, hasBeenSubstituted)) { - return Visit(SimplifiedSelf, Other, hasBeenSubstituted); - } + const auto *SimplifiedSelf = trySubstituteAndSimplify( + SelfUO, hasSelfBeenSubstituted, DependentValuesSelf); + + if (SimplifiedSelf != SelfUO) + return Visit(SimplifiedSelf, Other, hasSelfBeenSubstituted, + hasOtherBeenSubstituted); return false; } bool VisitBinaryOperator(const BinaryOperator *SelfBO, const Expr *Other, - bool hasBeenSubstituted) { + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); + const auto *OtherBO = dyn_cast(Other->IgnoreParenImpCasts()); if (OtherBO && OtherBO->getOpcode() == SelfBO->getOpcode()) { - return Visit(SelfBO->getLHS(), OtherBO->getLHS(), hasBeenSubstituted) && - Visit(SelfBO->getRHS(), OtherBO->getRHS(), hasBeenSubstituted); + return Visit(SelfBO->getLHS(), OtherBO->getLHS(), hasSelfBeenSubstituted, + hasOtherBeenSubstituted) && + Visit(SelfBO->getRHS(), OtherBO->getRHS(), hasSelfBeenSubstituted, + hasOtherBeenSubstituted); } return false; @@ -638,7 +706,8 @@ struct CompatibleCountExprVisitor // Support any overloaded operator[] so long as it is a const method. bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *SelfOpCall, - const Expr *Other, bool hasBeenSubstituted) { + const Expr *Other, bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { if (SelfOpCall->getOperator() != OverloadedOperatorKind::OO_Subscript) return false; @@ -646,13 +715,15 @@ struct CompatibleCountExprVisitor if (!MD || !MD->isConst()) return false; + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); if (const auto *OtherOpCall = dyn_cast(Other->IgnoreParenImpCasts())) if (SelfOpCall->getOperator() == OtherOpCall->getOperator()) { return Visit(SelfOpCall->getArg(0), OtherOpCall->getArg(0), - hasBeenSubstituted) && + hasSelfBeenSubstituted, hasOtherBeenSubstituted) && Visit(SelfOpCall->getArg(1), OtherOpCall->getArg(1), - hasBeenSubstituted); + hasSelfBeenSubstituted, hasOtherBeenSubstituted); } return false; } @@ -661,19 +732,27 @@ struct CompatibleCountExprVisitor // considered unsafe, they can be safely used on constant arrays with // known-safe literal indexes. bool VisitArraySubscriptExpr(const ArraySubscriptExpr *SelfAS, - const Expr *Other, bool hasBeenSubstituted) { + const Expr *Other, bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); if (const auto *OtherAS = dyn_cast(Other->IgnoreParenImpCasts())) - return Visit(SelfAS->getLHS(), OtherAS->getLHS(), hasBeenSubstituted) && - Visit(SelfAS->getRHS(), OtherAS->getRHS(), hasBeenSubstituted); + return Visit(SelfAS->getLHS(), OtherAS->getLHS(), hasSelfBeenSubstituted, + hasOtherBeenSubstituted) && + Visit(SelfAS->getRHS(), OtherAS->getRHS(), hasSelfBeenSubstituted, + hasOtherBeenSubstituted); return false; } // Support non-static member call: bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *SelfCall, - const Expr *Other, bool hasBeenSubstituted) { + const Expr *Other, bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { const CXXMethodDecl *MD = SelfCall->getMethodDecl(); + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); // The callee member function must be a const function with no parameter: if (MD->isConst() && MD->param_empty()) { if (auto *OtherCall = @@ -681,58 +760,53 @@ struct CompatibleCountExprVisitor return OtherCall->getMethodDecl() == MD && Visit(SelfCall->getImplicitObjectArgument(), OtherCall->getImplicitObjectArgument(), - hasBeenSubstituted); + hasSelfBeenSubstituted, hasOtherBeenSubstituted); } } return false; } bool VisitCXXDefaultArgExpr(const CXXDefaultArgExpr *SelfDAE, - const Expr *Other, bool hasBeenSubstituted) { - return Visit(SelfDAE->getExpr(), Other, hasBeenSubstituted); + const Expr *Other, bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + return Visit(SelfDAE->getExpr(), Other, hasSelfBeenSubstituted, + hasOtherBeenSubstituted); } }; // TL'DR: -// Checks if `E` is the same as `ExpectedCountExpr` modulo implicit casts and +// Checks if `Self` is the same as `Other` modulo implicit casts and // parens. // // Lengthy description: -// `E`: represents the actual count/size associated to a -// pointer. -// `ExpectedCountExpr`: represents the counted-by expression of a counted-by -// type attribute. +// `Self`: an expression, one of the comparands +// `Other`: an expression, the other comparands // `MemberBase`: represents "this" member base. A MemberExpr -// representing `this->f` in both `E` and -// `ExpectedCountExpr` may be transformed to a DRE +// representing `this->f` in either `Self` and +// `Other` may be transformed to a DRE // representing just `f`. Therefore we need to keep track -// of the base for them in that case. -// `DependentValues`: is a mapping from parameter DREs to actual argument -// expressions. It serves as a "state" where -// `ExpectedCountExpr` is "interpreted". -// -// This function then checks for a pointer with a known count `E` that `E` is -// equivalent to the count `ExpectedCountExpr` of a counted-by type attribute at -// the state `DependentValues`. -// -// For example, suppose there is a call to a function `foo(int *__counted_by(n) -// p, size_t n)`: -// -// foo(x, y+z); +// of the base for them in that case. The two comparands +// must share a member base if it exists. +// `DependentValuesSelf`: +// a mapping from parameters to arguments for the parameter +// references appearing in `Self`. +// `DependentValuesOther`: +// a mapping from parameters to arguments for the parameter +// references appearing in `Other`. // -// We check that the count associated to the pointer 'x' is same as the -// expected count expression 'n' with the mapping (state) '{n -> y+z}'. The -// count of 'x' is determined by pre-defined knowledge, e.g., if 'x' has the -// form of 'span.data()', its' count is 'span.size()', etc. At this point, we -// already know that `E` is the count of 'x'. So we just need to compare `E` -// to 'n' with 'n' being interpreted under '{n -> y+z}'. That is, this function -// will return true iff `E` is same as 'y+z'. -bool isCompatibleWithCountExpr(const Expr *E, const Expr *ExpectedCountExpr, - const Expr *MemberBase, - const DependentValuesTy *DependentValues, - ASTContext &Ctx) { - CompatibleCountExprVisitor Visitor(MemberBase, DependentValues, Ctx); - return Visitor.Visit(ExpectedCountExpr, E, /* hasBeenSubstituted*/ false); +// The comparison is on the two expressions `Self` and `Other` with the +// substitution maps `DependentValuesSelf` and `DependentValuesOther` being +// applied to them, respectively. +bool isCompatibleWithCountExpr( + ASTContext &Ctx, const Expr *Self, const Expr *Other, + const Expr *MemberBase = nullptr, + const DependentValuesTy *DependentValuesSelf = nullptr, + const DependentValuesTy *DependentValuesOther = nullptr) { + CompatibleCountExprVisitor Visitor(MemberBase, DependentValuesSelf, + DependentValuesOther, Ctx); + return Visitor.Visit(Self, Other, + /* hasSelfBeenSubstituted */ false, + /* hasOtherBeenSubstituted */ false); } // Returns true iff `C` is a C++ nclass method call to the function @@ -998,6 +1072,10 @@ static bool isCountAttributedPointerArgumentSafeImpl( // the actual count of the pointer inferred through patterns below: const Expr *ActualCount = nullptr; const Expr *MemberBase = nullptr; + // While `DependentValueMap` maps formal parameters to actual arguments of the + // call being checked, when the pointer argument is another call expression + // 'c', `DependentValueMapForActual` is the map for 'c': + std::optional DependentValueMapForActual = std::nullopt; if (const auto *ME = dyn_cast(PtrArgNoImp)) MemberBase = ME->getBase(); @@ -1014,6 +1092,11 @@ static bool isCountAttributedPointerArgumentSafeImpl( if (ArgCAT->isOrNull() == isOrNull && areBoundsAttributesCompatible) ActualCount = ArgCAT->getCountExpr(); + // In case `PtrArgNoImp` is a function call expression of counted_by type + // (i.e., return type is a CAT), create a map for the call: + if (auto *PtrArgCall = dyn_cast(PtrArgNoImp)) + DependentValueMapForActual = + getDependentValuesFromCall(ArgCAT, PtrArgCall); } else { // Form 6-7. const Expr *ArrArg = PtrArgNoImp; @@ -1039,8 +1122,11 @@ static bool isCountAttributedPointerArgumentSafeImpl( // In case (b), the actual count of the pointer must match the argument // passed to the parameter representing the count: CountedByExpr = CountArg; - return isCompatibleWithCountExpr(ActualCount, CountedByExpr, MemberBase, - DependentValueMap, Context); + return isCompatibleWithCountExpr( + Context, ActualCount, CountedByExpr, MemberBase, + (DependentValueMapForActual.has_value() ? &*DependentValueMapForActual + : nullptr), + DependentValueMap); } // Checks if the argument passed to a count-attributed pointer is safe. This @@ -1319,12 +1405,12 @@ static bool isPtrBufferSafe(const Expr *Ptr, const Expr *Size, auto ValuesOpt = getDependentValuesFromCall(CAT, Call); if (!ValuesOpt.has_value()) return false; - return isCompatibleWithCountExpr(Size, CAT->getCountExpr(), MemberBase, - &*ValuesOpt, Ctx); + return isCompatibleWithCountExpr(Ctx, Size, CAT->getCountExpr(), + MemberBase, nullptr, &*ValuesOpt); } - return isCompatibleWithCountExpr(Size, CAT->getCountExpr(), MemberBase, - /*DependentValues=*/nullptr, Ctx); + return isCompatibleWithCountExpr(Ctx, Size, CAT->getCountExpr(), + MemberBase); } /* TO_UPSTREAM(BoundsSafetyInterop) OFF */ return false; diff --git a/clang/test/SemaCXX/warn-unsafe-buffer-usage-count-attributed-pointer-argument.cpp b/clang/test/SemaCXX/warn-unsafe-buffer-usage-count-attributed-pointer-argument.cpp index 03504d32866c5..61bb010f62e0e 100644 --- a/clang/test/SemaCXX/warn-unsafe-buffer-usage-count-attributed-pointer-argument.cpp +++ b/clang/test/SemaCXX/warn-unsafe-buffer-usage-count-attributed-pointer-argument.cpp @@ -69,7 +69,7 @@ void cb_cchar(const char *__counted_by(len) s, size_t len); // expected-note@+1 3{{consider using 'std::span' and passing '.first(...).data()' to the parameter 's'}} void cb_cchar_42(const char *__counted_by(42) s); -// expected-note@+1 19{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'count' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}} +// expected-note@+1 31{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'count' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}} void cb_int(int *__counted_by(count) p, size_t count); // expected-note@+1 34{{consider using a safe container and passing '.data()' to the parameter 'p' and '.size()' to its dependent parameter 'count' or 'std::span' and passing '.first(...).data()' to the parameter 'p'}} @@ -647,6 +647,49 @@ namespace output_param_test { } // namespace output_param_test +namespace pointer_is_call_test { +int *__counted_by(len) fn_cb(size_t len); +int *__counted_by(42) fn_cb_const(); +int *__counted_by(a + b) fn_cb_plus(size_t a, size_t b); +int *__counted_by(*p) fn_cb_deref(size_t *p); + +struct T { + size_t size() const; + size_t n; +}; + +void test1(size_t n, size_t n2, size_t *p, T * t) { + cb_int(fn_cb(n), n); + //cb_int(fn_cb(*&n), n); + //cb_int(fn_cb(*&n), *&n); + cb_int(fn_cb(n), *&n); + cb_int(fn_cb(n), 42); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + cb_int(fn_cb(n2), n); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + cb_int(fn_cb(n), n + 1); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + + cb_int(fn_cb(t->size()), t->size()); + cb_int(fn_cb(t->n), t->n); + cb_int(fn_cb(t->size()), t->n); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + cb_int(fn_cb(t->n), t->size()); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + + cb_int(fn_cb_const(), 42); + cb_int(fn_cb_const(), 43); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + cb_int(fn_cb_const(), n); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + cb_int(fn_cb_const(), n + 1); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + + cb_int(fn_cb_plus(n, n2), n + n2); + cb_int(fn_cb_plus(n, n), n + n); + cb_int(fn_cb_plus(n, n2), n + n); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + cb_int(fn_cb_plus(n, n), n * 2); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + + cb_int(fn_cb_deref(p), *p); + cb_int(fn_cb_deref(&n), n); + cb_int(fn_cb_deref(&n), *&n); + cb_int(fn_cb_deref(p), n); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} + cb_int(fn_cb_deref(&n), *p); // expected-warning {{unsafe assignment to function parameter of count-attributed type}} +} +} // namespace pointer_is_call_test + static void previous_infinite_loop(int * __counted_by(n) p, size_t n) { previous_infinite_loop(p, n);