Skip to content

Commit e06cbc2

Browse files
authored
[Safe Buffers][BoundsSafety] Fix a bug in the interop analysis that can cause infinite loops (#10129)
The interop analysis substitutes formal parameters to arguments in AST visiting. Conceptually, the substitution should be done exactly once. But the previous implementation never checked if the substitution has happened. In cases where the argument contains DREs referring to the Decl of the corresponding formal parameter, the analysis enters an infinite loop. A typical example is the following. ``` void f(int * __counted_by(n) p, size_t n) { f(p, n); } ``` This commit fixes the issue. (rdar://145705060)
1 parent 438058e commit e06cbc2

File tree

2 files changed

+83
-36
lines changed

2 files changed

+83
-36
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 65 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -426,23 +426,37 @@ getDependentValuesFromCall(const CountAttributedType *CAT,
426426
// Other kinds of function calls are not supported, so an expression of the
427427
// form `f(...)` is not supported.
428428
struct CompatibleCountExprVisitor
429-
: public ConstStmtVisitor<CompatibleCountExprVisitor, bool, const Expr *> {
429+
: public ConstStmtVisitor<CompatibleCountExprVisitor, bool, const Expr *,
430+
bool> {
431+
// The third 'bool' type parameter for each visit method indicates whether the
432+
// current visiting expression is the result of the formal parameter to actual
433+
// argument substitution. Since the argument expression may contain DREs
434+
// referencing to back to those parameters (in cases of recursive calls), the
435+
// analysis may hit an infinite loop if not knowing whether the substitution
436+
// has happened. A typical example that could introduce infinite loop without
437+
// this knowledge is shown below.
438+
// ```
439+
// void f(int * __counted_by(n) p, size_t n) {
440+
// f(p, n);
441+
// }
442+
// ```
430443
using BaseVisitor =
431-
ConstStmtVisitor<CompatibleCountExprVisitor, bool, const Expr *>;
444+
ConstStmtVisitor<CompatibleCountExprVisitor, bool, const Expr *, bool>;
432445

433446
const Expr *MemberBase;
434447
const DependentValuesTy *DependentValues;
435448
ASTContext &Ctx;
436449

437450
// If `Deref` has the form `*&e`, return `e`; otherwise return nullptr.
438-
const Expr *trySimplifyDerefAddressof(const UnaryOperator *Deref) {
451+
const Expr *trySimplifyDerefAddressof(const UnaryOperator *Deref,
452+
bool hasBeenSubstituted) {
439453
const Expr *DerefOperand = Deref->getSubExpr()->IgnoreParenImpCasts();
440454

441455
if (const auto *UO = dyn_cast<UnaryOperator>(DerefOperand))
442456
if (UO->getOpcode() == UO_AddrOf)
443457
return UO->getSubExpr();
444458
if (const auto *DRE = dyn_cast<DeclRefExpr>(DerefOperand)) {
445-
if (!DependentValues)
459+
if (!DependentValues || hasBeenSubstituted)
446460
return nullptr;
447461

448462
auto I = DependentValues->find(DRE->getDecl());
@@ -460,18 +474,22 @@ struct CompatibleCountExprVisitor
460474
ASTContext &Ctx)
461475
: MemberBase(MemberBase), DependentValues(DependentValues), Ctx(Ctx) {}
462476

463-
bool VisitStmt(const Stmt *S, const Expr *E) { return false; }
477+
bool VisitStmt(const Stmt *S, const Expr *E, bool hasBeenSubstituted) {
478+
return false;
479+
}
464480

465-
bool VisitImplicitCastExpr(const ImplicitCastExpr *SelfICE,
466-
const Expr *Other) {
467-
return Visit(SelfICE->getSubExpr(), Other);
481+
bool VisitImplicitCastExpr(const ImplicitCastExpr *SelfICE, const Expr *Other,
482+
bool hasBeenSubstituted) {
483+
return Visit(SelfICE->getSubExpr(), Other, hasBeenSubstituted);
468484
}
469485

470-
bool VisitParenExpr(const ParenExpr *SelfPE, const Expr *Other) {
471-
return Visit(SelfPE->getSubExpr(), Other);
486+
bool VisitParenExpr(const ParenExpr *SelfPE, const Expr *Other,
487+
bool hasBeenSubstituted) {
488+
return Visit(SelfPE->getSubExpr(), Other, hasBeenSubstituted);
472489
}
473490

474-
bool VisitIntegerLiteral(const IntegerLiteral *SelfIL, const Expr *Other) {
491+
bool VisitIntegerLiteral(const IntegerLiteral *SelfIL, const Expr *Other,
492+
bool hasBeenSubstituted) {
475493
if (const auto *IntLit =
476494
dyn_cast<IntegerLiteral>(Other->IgnoreParenImpCasts())) {
477495
return SelfIL == IntLit ||
@@ -481,7 +499,8 @@ struct CompatibleCountExprVisitor
481499
}
482500

483501
bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Self,
484-
const Expr *Other) {
502+
const Expr *Other,
503+
bool hasBeenSubstituted) {
485504
// If `Self` is a `sizeof` expression, try to evaluate and compare the two
486505
// expressions as constants:
487506
if (Self->getKind() == UnaryExprOrTypeTrait::UETT_SizeOf) {
@@ -498,17 +517,19 @@ struct CompatibleCountExprVisitor
498517
return false;
499518
}
500519

501-
bool VisitCXXThisExpr(const CXXThisExpr *SelfThis, const Expr *Other) {
520+
bool VisitCXXThisExpr(const CXXThisExpr *SelfThis, const Expr *Other,
521+
bool hasBeenSubstituted) {
502522
return isa<CXXThisExpr>(Other->IgnoreParenImpCasts());
503523
}
504524

505-
bool VisitDeclRefExpr(const DeclRefExpr *SelfDRE, const Expr *Other) {
525+
bool VisitDeclRefExpr(const DeclRefExpr *SelfDRE, const Expr *Other,
526+
bool hasBeenSubstituted) {
506527
const ValueDecl *SelfVD = SelfDRE->getDecl();
507528

508-
if (DependentValues) {
529+
if (DependentValues && !hasBeenSubstituted) {
509530
const auto It = DependentValues->find(SelfVD);
510531
if (It != DependentValues->end())
511-
return Visit(It->second, Other);
532+
return Visit(It->second, Other, true);
512533
}
513534

514535
const auto *O = Other->IgnoreParenImpCasts();
@@ -523,58 +544,63 @@ struct CompatibleCountExprVisitor
523544
const auto *OtherME = dyn_cast<MemberExpr>(O);
524545
if (MemberBase && OtherME) {
525546
return OtherME->getMemberDecl() == SelfVD &&
526-
Visit(OtherME->getBase(), MemberBase);
547+
Visit(OtherME->getBase(), MemberBase, hasBeenSubstituted);
527548
}
528549

529550
return false;
530551
}
531552

532-
bool VisitMemberExpr(const MemberExpr *Self, const Expr *Other) {
553+
bool VisitMemberExpr(const MemberExpr *Self, const Expr *Other,
554+
bool hasBeenSubstituted) {
533555
// Even though we don't support member expression in counted-by, actual
534556
// arguments can be member expressions.
535557
if (Self == Other)
536558
return true;
537559
if (const auto *DRE = dyn_cast<DeclRefExpr>(Other->IgnoreParenImpCasts()))
538560
return MemberBase && Self->getMemberDecl() == DRE->getDecl() &&
539-
Visit(Self->getBase(), MemberBase);
561+
Visit(Self->getBase(), MemberBase, hasBeenSubstituted);
540562
if (const auto *OtherME =
541563
dyn_cast<MemberExpr>(Other->IgnoreParenImpCasts())) {
542564
return Self->getMemberDecl() == OtherME->getMemberDecl() &&
543-
Visit(Self->getBase(), OtherME->getBase());
565+
Visit(Self->getBase(), OtherME->getBase(), hasBeenSubstituted);
544566
}
545567
return false;
546568
}
547569

548-
bool VisitUnaryOperator(const UnaryOperator *SelfUO, const Expr *Other) {
570+
bool VisitUnaryOperator(const UnaryOperator *SelfUO, const Expr *Other,
571+
bool hasBeenSubstituted) {
549572
if (SelfUO->getOpcode() != UO_Deref)
550573
return false; // We don't support any other unary operator
551574

552575
if (const auto *OtherUO =
553576
dyn_cast<UnaryOperator>(Other->IgnoreParenImpCasts())) {
554577
if (SelfUO->getOpcode() == OtherUO->getOpcode())
555-
return Visit(SelfUO->getSubExpr(), OtherUO->getSubExpr());
578+
return Visit(SelfUO->getSubExpr(), OtherUO->getSubExpr(),
579+
hasBeenSubstituted);
556580
}
557581
// If `Other` is not a dereference expression, try to simplify `SelfUO`:
558-
if (const auto *SimplifiedSelf = trySimplifyDerefAddressof(SelfUO)) {
559-
return Visit(SimplifiedSelf, Other);
582+
if (const auto *SimplifiedSelf =
583+
trySimplifyDerefAddressof(SelfUO, hasBeenSubstituted)) {
584+
return Visit(SimplifiedSelf, Other, hasBeenSubstituted);
560585
}
561586
return false;
562587
}
563588

564-
bool VisitBinaryOperator(const BinaryOperator *SelfBO, const Expr *Other) {
589+
bool VisitBinaryOperator(const BinaryOperator *SelfBO, const Expr *Other,
590+
bool hasBeenSubstituted) {
565591
const auto *OtherBO =
566592
dyn_cast<BinaryOperator>(Other->IgnoreParenImpCasts());
567593
if (OtherBO && OtherBO->getOpcode() == SelfBO->getOpcode()) {
568-
return Visit(SelfBO->getLHS(), OtherBO->getLHS()) &&
569-
Visit(SelfBO->getRHS(), OtherBO->getRHS());
594+
return Visit(SelfBO->getLHS(), OtherBO->getLHS(), hasBeenSubstituted) &&
595+
Visit(SelfBO->getRHS(), OtherBO->getRHS(), hasBeenSubstituted);
570596
}
571597

572598
return false;
573599
}
574600

575601
// Support any overloaded operator[] so long as it is a const method.
576602
bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *SelfOpCall,
577-
const Expr *Other) {
603+
const Expr *Other, bool hasBeenSubstituted) {
578604
if (SelfOpCall->getOperator() != OverloadedOperatorKind::OO_Subscript)
579605
return false;
580606

@@ -585,8 +611,10 @@ struct CompatibleCountExprVisitor
585611
if (const auto *OtherOpCall =
586612
dyn_cast<CXXOperatorCallExpr>(Other->IgnoreParenImpCasts()))
587613
if (SelfOpCall->getOperator() == OtherOpCall->getOperator()) {
588-
return Visit(SelfOpCall->getArg(0), OtherOpCall->getArg(0)) &&
589-
Visit(SelfOpCall->getArg(1), OtherOpCall->getArg(1));
614+
return Visit(SelfOpCall->getArg(0), OtherOpCall->getArg(0),
615+
hasBeenSubstituted) &&
616+
Visit(SelfOpCall->getArg(1), OtherOpCall->getArg(1),
617+
hasBeenSubstituted);
590618
}
591619
return false;
592620
}
@@ -595,17 +623,17 @@ struct CompatibleCountExprVisitor
595623
// considered unsafe, they can be safely used on constant arrays with
596624
// known-safe literal indexes.
597625
bool VisitArraySubscriptExpr(const ArraySubscriptExpr *SelfAS,
598-
const Expr *Other) {
626+
const Expr *Other, bool hasBeenSubstituted) {
599627
if (const auto *OtherAS =
600628
dyn_cast<ArraySubscriptExpr>(Other->IgnoreParenImpCasts()))
601-
return Visit(SelfAS->getLHS(), OtherAS->getLHS()) &&
602-
Visit(SelfAS->getRHS(), OtherAS->getRHS());
629+
return Visit(SelfAS->getLHS(), OtherAS->getLHS(), hasBeenSubstituted) &&
630+
Visit(SelfAS->getRHS(), OtherAS->getRHS(), hasBeenSubstituted);
603631
return false;
604632
}
605633

606634
// Support non-static member call:
607635
bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *SelfCall,
608-
const Expr *Other) {
636+
const Expr *Other, bool hasBeenSubstituted) {
609637
const CXXMethodDecl *MD = SelfCall->getMethodDecl();
610638

611639
// The callee member function must be a const function with no parameter:
@@ -614,7 +642,8 @@ struct CompatibleCountExprVisitor
614642
dyn_cast<CXXMemberCallExpr>(Other->IgnoreParenImpCasts())) {
615643
return OtherCall->getMethodDecl() == MD &&
616644
Visit(SelfCall->getImplicitObjectArgument(),
617-
OtherCall->getImplicitObjectArgument());
645+
OtherCall->getImplicitObjectArgument(),
646+
hasBeenSubstituted);
618647
}
619648
}
620649
return false;
@@ -660,7 +689,7 @@ bool isCompatibleWithCountExpr(const Expr *E, const Expr *ExpectedCountExpr,
660689
const DependentValuesTy *DependentValues,
661690
ASTContext &Ctx) {
662691
CompatibleCountExprVisitor Visitor(MemberBase, DependentValues, Ctx);
663-
return Visitor.Visit(ExpectedCountExpr, E);
692+
return Visitor.Visit(ExpectedCountExpr, E, /* hasBeenSubstituted*/ false);
664693
}
665694

666695
// Returns if a pair of expressions contain method calls to .data()/.c_str() and

clang/test/SemaCXX/warn-unsafe-buffer-usage-count-attributed-pointer-argument.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -598,3 +598,21 @@ namespace output_param_test {
598598
};
599599

600600
} // namespace output_param_test
601+
602+
603+
static void previous_infinite_loop(int * __counted_by(n) p, size_t n) {
604+
previous_infinite_loop(p, n);
605+
}
606+
607+
static void previous_infinite_loop2(int * __counted_by(n + 10) p, size_t n) {
608+
previous_infinite_loop2(p, n);
609+
}
610+
611+
static void previous_infinite_loop3(int * __counted_by(n + n * m) p, size_t n,
612+
// expected-note@+1 {{consider using 'std::span' and passing '.first(...).data()' to the parameter 'q'}}
613+
int * __counted_by(m * m + o) q,
614+
// expected-note@+1 {{consider using a safe container and passing '.data()' to the parameter 'r' and '.size()' to its dependent parameter 'o' or 'std::span' and passing '.first(...).data()' to the parameter 'r'}}
615+
int * __counted_by(o) r, size_t m, size_t o) {
616+
previous_infinite_loop3(p, n, q, r, m, o);
617+
previous_infinite_loop3(p, n, q, r, m, o + 1); // expected-warning 2{{unsafe assignment to function parameter of count-attributed type}}
618+
}

0 commit comments

Comments
 (0)