diff --git a/clang/lib/Analysis/UnsafeBufferUsage.cpp b/clang/lib/Analysis/UnsafeBufferUsage.cpp index a644adb9cc37d..25951ea9c7418 100644 --- a/clang/lib/Analysis/UnsafeBufferUsage.cpp +++ b/clang/lib/Analysis/UnsafeBufferUsage.cpp @@ -465,69 +465,111 @@ 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) { + bool hasSelfBeenSubstituted, + bool hasOtherBeenSubstituted) { + Other = trySubstituteAndSimplify(Other, hasOtherBeenSubstituted, + DependentValuesOther); if (const auto *IntLit = dyn_cast(Other->IgnoreParenImpCasts())) { return SelfIL == IntLit || @@ -538,7 +580,10 @@ struct CompatibleCountExprVisitor 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) { @@ -556,81 +601,102 @@ struct CompatibleCountExprVisitor } 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 +704,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 +713,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 +730,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,7 +758,7 @@ struct CompatibleCountExprVisitor return OtherCall->getMethodDecl() == MD && Visit(SelfCall->getImplicitObjectArgument(), OtherCall->getImplicitObjectArgument(), - hasBeenSubstituted); + hasSelfBeenSubstituted, hasOtherBeenSubstituted); } } return false; @@ -689,45 +766,38 @@ struct CompatibleCountExprVisitor }; // 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 @@ -990,6 +1060,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(); @@ -1006,6 +1080,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; @@ -1031,8 +1110,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 @@ -1303,12 +1385,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 ff12cace65bf9..75f265292030f 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);