Skip to content

Commit b5b6dce

Browse files
authored
[C++ Safe Buffers][BoundsSafety] Cherrypicks for 143986251 & 145705060 (#10156)
* [-Wunsafe-buffer-usage][BoundsSafety] Extend safe patterns of arguments to counted_by parameters (#9971) Add support of - overloaded subscript operator of hardened view/containers, and - calls to const member functions of 0 arguments Now it can match patterns like: ``` std::span<char> D, S; std::array<std::span<char>, 10> A; memcpy(D.first(S.size()).data(), S.data(), S.size()); memcpy(D.first(A[x].size()).data(), A[x].data(), A[x].size()); ``` (rdar://143986251) * [Safe Buffers][BoundsSafety] Fix a bug in the interop analysis that can cause infinite loops 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) (cherry picked from commit 31f72f7)
1 parent 2b92dd1 commit b5b6dce

File tree

2 files changed

+263
-69
lines changed

2 files changed

+263
-69
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 128 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -412,66 +412,55 @@ getDependentValuesFromCall(const CountAttributedType *CAT,
412412
return {std::move(Values)};
413413
}
414414

415-
// Checks if Self and Other are the same member bases. This supports only very
416-
// simple forms of member bases.
417-
bool isSameMemberBase(const Expr *Self, const Expr *Other) {
418-
for (;;) {
419-
if (Self == Other)
420-
return true;
421-
422-
const auto *SelfICE = dyn_cast<ImplicitCastExpr>(Self);
423-
const auto *OtherICE = dyn_cast<ImplicitCastExpr>(Other);
424-
if (SelfICE && OtherICE &&
425-
SelfICE->getCastKind() == OtherICE->getCastKind() &&
426-
(SelfICE->getCastKind() == CK_LValueToRValue ||
427-
SelfICE->getCastKind() == CK_UncheckedDerivedToBase)) {
428-
Self = SelfICE->getSubExpr();
429-
Other = OtherICE->getSubExpr();
430-
}
431-
432-
const auto *SelfDRE = dyn_cast<DeclRefExpr>(Self);
433-
const auto *OtherDRE = dyn_cast<DeclRefExpr>(Other);
434-
if (SelfDRE && OtherDRE)
435-
return SelfDRE->getDecl() == OtherDRE->getDecl();
436-
437-
if (isa<CXXThisExpr>(Self) && isa<CXXThisExpr>(Other)) {
438-
// `Self` and `Other` should be evaluated at the same state so `this` must
439-
// mean the same thing for both:
440-
return true;
441-
}
442-
443-
const auto *SelfME = dyn_cast<MemberExpr>(Self);
444-
const auto *OtherME = dyn_cast<MemberExpr>(Other);
445-
if (!SelfME || !OtherME ||
446-
SelfME->getMemberDecl() != OtherME->getMemberDecl()) {
447-
return false;
448-
}
449-
450-
Self = SelfME->getBase();
451-
Other = OtherME->getBase();
452-
}
453-
}
454-
455415
// Impl of `isCompatibleWithCountExpr`. See `isCompatibleWithCountExpr` for
456-
// document.
416+
// high-level document.
417+
//
418+
// This visitor compares two expressions in a tiny subset of the language,
419+
// including DRE of VarDecls, constants, binary operators, subscripts,
420+
// dereferences, member accesses, and member function calls.
421+
//
422+
// - For constants, they are literal constants and expressions have
423+
// compile-time constant values.
424+
// - For a supported dereference expression, it can be either of the forms '*e'
425+
// or '*&e', where 'e' is a supported expression.
426+
// - For a subscript expression, it can be either an array subscript or
427+
// overloaded subscript operator.
428+
// - For a member function call, it must be a call to a 'const' (non-static)
429+
// member function with zero argument. This is to ensure side-effect free.
430+
// Other kinds of function calls are not supported, so an expression of the
431+
// form `f(...)` is not supported.
457432
struct CompatibleCountExprVisitor
458-
: public ConstStmtVisitor<CompatibleCountExprVisitor, bool, const Expr *> {
433+
: public ConstStmtVisitor<CompatibleCountExprVisitor, bool, const Expr *,
434+
bool> {
435+
// The third 'bool' type parameter for each visit method indicates whether a
436+
// parameter has been substituted with its argument expression in the
437+
// expression AST branch being visited. Since the argument expression may
438+
// contain DREs referencing to the same parameter Decl, the analysis may hit
439+
// an infinite loop of not knowing whether the substitution has happened. A
440+
// typical example that could introduce infinite loop without this knowledge
441+
// is shown below.
442+
// ```
443+
// void f(int * __counted_by(n) p, size_t n) {
444+
// f(p, n);
445+
// }
446+
// ```
459447
using BaseVisitor =
460-
ConstStmtVisitor<CompatibleCountExprVisitor, bool, const Expr *>;
448+
ConstStmtVisitor<CompatibleCountExprVisitor, bool, const Expr *, bool>;
461449

462450
const Expr *MemberBase;
463451
const DependentValuesTy *DependentValues;
464452
ASTContext &Ctx;
465453

466454
// If `Deref` has the form `*&e`, return `e`; otherwise return nullptr.
467-
const Expr *trySimplifyDerefAddressof(const UnaryOperator *Deref) {
455+
const Expr *trySimplifyDerefAddressof(const UnaryOperator *Deref,
456+
bool hasBeenSubstituted) {
468457
const Expr *DerefOperand = Deref->getSubExpr()->IgnoreParenImpCasts();
469458

470459
if (const auto *UO = dyn_cast<UnaryOperator>(DerefOperand))
471460
if (UO->getOpcode() == UO_AddrOf)
472461
return UO->getSubExpr();
473462
if (const auto *DRE = dyn_cast<DeclRefExpr>(DerefOperand)) {
474-
if (!DependentValues)
463+
if (!DependentValues || hasBeenSubstituted)
475464
return nullptr;
476465

477466
auto I = DependentValues->find(DRE->getDecl());
@@ -489,18 +478,22 @@ struct CompatibleCountExprVisitor
489478
ASTContext &Ctx)
490479
: MemberBase(MemberBase), DependentValues(DependentValues), Ctx(Ctx) {}
491480

492-
bool VisitStmt(const Stmt *S, const Expr *E) { return false; }
481+
bool VisitStmt(const Stmt *S, const Expr *E, bool hasBeenSubstituted) {
482+
return false;
483+
}
493484

494-
bool VisitImplicitCastExpr(const ImplicitCastExpr *SelfICE,
495-
const Expr *Other) {
496-
return Visit(SelfICE->getSubExpr(), Other);
485+
bool VisitImplicitCastExpr(const ImplicitCastExpr *SelfICE, const Expr *Other,
486+
bool hasBeenSubstituted) {
487+
return Visit(SelfICE->getSubExpr(), Other, hasBeenSubstituted);
497488
}
498489

499-
bool VisitParenExpr(const ParenExpr *SelfPE, const Expr *Other) {
500-
return Visit(SelfPE->getSubExpr(), Other);
490+
bool VisitParenExpr(const ParenExpr *SelfPE, const Expr *Other,
491+
bool hasBeenSubstituted) {
492+
return Visit(SelfPE->getSubExpr(), Other, hasBeenSubstituted);
501493
}
502494

503-
bool VisitIntegerLiteral(const IntegerLiteral *SelfIL, const Expr *Other) {
495+
bool VisitIntegerLiteral(const IntegerLiteral *SelfIL, const Expr *Other,
496+
bool hasBeenSubstituted) {
504497
if (const auto *IntLit =
505498
dyn_cast<IntegerLiteral>(Other->IgnoreParenImpCasts())) {
506499
return SelfIL == IntLit ||
@@ -510,7 +503,8 @@ struct CompatibleCountExprVisitor
510503
}
511504

512505
bool VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Self,
513-
const Expr *Other) {
506+
const Expr *Other,
507+
bool hasBeenSubstituted) {
514508
// If `Self` is a `sizeof` expression, try to evaluate and compare the two
515509
// expressions as constants:
516510
if (Self->getKind() == UnaryExprOrTypeTrait::UETT_SizeOf) {
@@ -527,13 +521,19 @@ struct CompatibleCountExprVisitor
527521
return false;
528522
}
529523

530-
bool VisitDeclRefExpr(const DeclRefExpr *SelfDRE, const Expr *Other) {
524+
bool VisitCXXThisExpr(const CXXThisExpr *SelfThis, const Expr *Other,
525+
bool hasBeenSubstituted) {
526+
return isa<CXXThisExpr>(Other->IgnoreParenImpCasts());
527+
}
528+
529+
bool VisitDeclRefExpr(const DeclRefExpr *SelfDRE, const Expr *Other,
530+
bool hasBeenSubstituted) {
531531
const ValueDecl *SelfVD = SelfDRE->getDecl();
532532

533-
if (DependentValues) {
533+
if (DependentValues && !hasBeenSubstituted) {
534534
const auto It = DependentValues->find(SelfVD);
535535
if (It != DependentValues->end())
536-
return Visit(It->second, Other);
536+
return Visit(It->second, Other, true);
537537
}
538538

539539
const auto *O = Other->IgnoreParenImpCasts();
@@ -548,49 +548,110 @@ struct CompatibleCountExprVisitor
548548
const auto *OtherME = dyn_cast<MemberExpr>(O);
549549
if (MemberBase && OtherME) {
550550
return OtherME->getMemberDecl() == SelfVD &&
551-
isSameMemberBase(OtherME->getBase(), MemberBase);
551+
Visit(OtherME->getBase(), MemberBase, hasBeenSubstituted);
552552
}
553553

554554
return false;
555555
}
556556

557-
bool VisitMemberExpr(const MemberExpr *Self, const Expr *Other) {
557+
bool VisitMemberExpr(const MemberExpr *Self, const Expr *Other,
558+
bool hasBeenSubstituted) {
558559
// Even though we don't support member expression in counted-by, actual
559560
// arguments can be member expressions.
560561
if (Self == Other)
561562
return true;
562563
if (const auto *DRE = dyn_cast<DeclRefExpr>(Other->IgnoreParenImpCasts()))
563564
return MemberBase && Self->getMemberDecl() == DRE->getDecl() &&
564-
isSameMemberBase(Self->getBase(), MemberBase);
565+
Visit(Self->getBase(), MemberBase, hasBeenSubstituted);
566+
if (const auto *OtherME =
567+
dyn_cast<MemberExpr>(Other->IgnoreParenImpCasts())) {
568+
return Self->getMemberDecl() == OtherME->getMemberDecl() &&
569+
Visit(Self->getBase(), OtherME->getBase(), hasBeenSubstituted);
570+
}
565571
return false;
566572
}
567573

568-
bool VisitUnaryOperator(const UnaryOperator *SelfUO, const Expr *Other) {
574+
bool VisitUnaryOperator(const UnaryOperator *SelfUO, const Expr *Other,
575+
bool hasBeenSubstituted) {
569576
if (SelfUO->getOpcode() != UO_Deref)
570577
return false; // We don't support any other unary operator
571578

572579
if (const auto *OtherUO =
573580
dyn_cast<UnaryOperator>(Other->IgnoreParenImpCasts())) {
574581
if (SelfUO->getOpcode() == OtherUO->getOpcode())
575-
return Visit(SelfUO->getSubExpr(), OtherUO->getSubExpr());
582+
return Visit(SelfUO->getSubExpr(), OtherUO->getSubExpr(),
583+
hasBeenSubstituted);
576584
}
577585
// If `Other` is not a dereference expression, try to simplify `SelfUO`:
578-
if (const auto *SimplifiedSelf = trySimplifyDerefAddressof(SelfUO)) {
579-
return Visit(SimplifiedSelf, Other);
586+
if (const auto *SimplifiedSelf =
587+
trySimplifyDerefAddressof(SelfUO, hasBeenSubstituted)) {
588+
return Visit(SimplifiedSelf, Other, hasBeenSubstituted);
580589
}
581590
return false;
582591
}
583592

584-
bool VisitBinaryOperator(const BinaryOperator *SelfBO, const Expr *Other) {
593+
bool VisitBinaryOperator(const BinaryOperator *SelfBO, const Expr *Other,
594+
bool hasBeenSubstituted) {
585595
const auto *OtherBO =
586596
dyn_cast<BinaryOperator>(Other->IgnoreParenImpCasts());
587597
if (OtherBO && OtherBO->getOpcode() == SelfBO->getOpcode()) {
588-
return Visit(SelfBO->getLHS(), OtherBO->getLHS()) &&
589-
Visit(SelfBO->getRHS(), OtherBO->getRHS());
598+
return Visit(SelfBO->getLHS(), OtherBO->getLHS(), hasBeenSubstituted) &&
599+
Visit(SelfBO->getRHS(), OtherBO->getRHS(), hasBeenSubstituted);
590600
}
591601

592602
return false;
593603
}
604+
605+
// Support any overloaded operator[] so long as it is a const method.
606+
bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *SelfOpCall,
607+
const Expr *Other, bool hasBeenSubstituted) {
608+
if (SelfOpCall->getOperator() != OverloadedOperatorKind::OO_Subscript)
609+
return false;
610+
611+
const auto *MD = dyn_cast<CXXMethodDecl>(SelfOpCall->getCalleeDecl());
612+
613+
if (!MD || !MD->isConst())
614+
return false;
615+
if (const auto *OtherOpCall =
616+
dyn_cast<CXXOperatorCallExpr>(Other->IgnoreParenImpCasts()))
617+
if (SelfOpCall->getOperator() == OtherOpCall->getOperator()) {
618+
return Visit(SelfOpCall->getArg(0), OtherOpCall->getArg(0),
619+
hasBeenSubstituted) &&
620+
Visit(SelfOpCall->getArg(1), OtherOpCall->getArg(1),
621+
hasBeenSubstituted);
622+
}
623+
return false;
624+
}
625+
626+
// Support array/pointer subscript. Even though these operators are generally
627+
// considered unsafe, they can be safely used on constant arrays with
628+
// known-safe literal indexes.
629+
bool VisitArraySubscriptExpr(const ArraySubscriptExpr *SelfAS,
630+
const Expr *Other, bool hasBeenSubstituted) {
631+
if (const auto *OtherAS =
632+
dyn_cast<ArraySubscriptExpr>(Other->IgnoreParenImpCasts()))
633+
return Visit(SelfAS->getLHS(), OtherAS->getLHS(), hasBeenSubstituted) &&
634+
Visit(SelfAS->getRHS(), OtherAS->getRHS(), hasBeenSubstituted);
635+
return false;
636+
}
637+
638+
// Support non-static member call:
639+
bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *SelfCall,
640+
const Expr *Other, bool hasBeenSubstituted) {
641+
const CXXMethodDecl *MD = SelfCall->getMethodDecl();
642+
643+
// The callee member function must be a const function with no parameter:
644+
if (MD->isConst() && MD->param_empty()) {
645+
if (auto *OtherCall =
646+
dyn_cast<CXXMemberCallExpr>(Other->IgnoreParenImpCasts())) {
647+
return OtherCall->getMethodDecl() == MD &&
648+
Visit(SelfCall->getImplicitObjectArgument(),
649+
OtherCall->getImplicitObjectArgument(),
650+
hasBeenSubstituted);
651+
}
652+
}
653+
return false;
654+
}
594655
};
595656

596657
// TL'DR:
@@ -632,7 +693,7 @@ bool isCompatibleWithCountExpr(const Expr *E, const Expr *ExpectedCountExpr,
632693
const DependentValuesTy *DependentValues,
633694
ASTContext &Ctx) {
634695
CompatibleCountExprVisitor Visitor(MemberBase, DependentValues, Ctx);
635-
return Visitor.Visit(ExpectedCountExpr, E);
696+
return Visitor.Visit(ExpectedCountExpr, E, /* hasBeenSubstituted*/ false);
636697
}
637698

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

0 commit comments

Comments
 (0)