Skip to content

Commit 49f126f

Browse files
authored
[-Wunsafe-buffer-usage][BoundsSafety] Extend safe patterns of arguments to counted_by parameters (#9971)
* [-Wunsafe-buffer-usage][BoundsSafety] Extend safe patterns of arguments to counted_by parameters 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)
1 parent 15074d3 commit 49f126f

File tree

2 files changed

+201
-45
lines changed

2 files changed

+201
-45
lines changed

clang/lib/Analysis/UnsafeBufferUsage.cpp

Lines changed: 75 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -408,48 +408,23 @@ getDependentValuesFromCall(const CountAttributedType *CAT,
408408
return {std::move(Values)};
409409
}
410410

411-
// Checks if Self and Other are the same member bases. This supports only very
412-
// simple forms of member bases.
413-
bool isSameMemberBase(const Expr *Self, const Expr *Other) {
414-
for (;;) {
415-
if (Self == Other)
416-
return true;
417-
418-
const auto *SelfICE = dyn_cast<ImplicitCastExpr>(Self);
419-
const auto *OtherICE = dyn_cast<ImplicitCastExpr>(Other);
420-
if (SelfICE && OtherICE &&
421-
SelfICE->getCastKind() == OtherICE->getCastKind() &&
422-
(SelfICE->getCastKind() == CK_LValueToRValue ||
423-
SelfICE->getCastKind() == CK_UncheckedDerivedToBase)) {
424-
Self = SelfICE->getSubExpr();
425-
Other = OtherICE->getSubExpr();
426-
}
427-
428-
const auto *SelfDRE = dyn_cast<DeclRefExpr>(Self);
429-
const auto *OtherDRE = dyn_cast<DeclRefExpr>(Other);
430-
if (SelfDRE && OtherDRE)
431-
return SelfDRE->getDecl() == OtherDRE->getDecl();
432-
433-
if (isa<CXXThisExpr>(Self) && isa<CXXThisExpr>(Other)) {
434-
// `Self` and `Other` should be evaluated at the same state so `this` must
435-
// mean the same thing for both:
436-
return true;
437-
}
438-
439-
const auto *SelfME = dyn_cast<MemberExpr>(Self);
440-
const auto *OtherME = dyn_cast<MemberExpr>(Other);
441-
if (!SelfME || !OtherME ||
442-
SelfME->getMemberDecl() != OtherME->getMemberDecl()) {
443-
return false;
444-
}
445-
446-
Self = SelfME->getBase();
447-
Other = OtherME->getBase();
448-
}
449-
}
450-
451411
// Impl of `isCompatibleWithCountExpr`. See `isCompatibleWithCountExpr` for
452-
// document.
412+
// high-level document.
413+
//
414+
// This visitor compares two expressions in a tiny subset of the language,
415+
// including DRE of VarDecls, constants, binary operators, subscripts,
416+
// dereferences, member accesses, and member function calls.
417+
//
418+
// - For constants, they are literal constants and expressions have
419+
// compile-time constant values.
420+
// - For a supported dereference expression, it can be either of the forms '*e'
421+
// or '*&e', where 'e' is a supported expression.
422+
// - For a subscript expression, it can be either an array subscript or
423+
// overloaded subscript operator.
424+
// - For a member function call, it must be a call to a 'const' (non-static)
425+
// member function with zero argument. This is to ensure side-effect free.
426+
// Other kinds of function calls are not supported, so an expression of the
427+
// form `f(...)` is not supported.
453428
struct CompatibleCountExprVisitor
454429
: public ConstStmtVisitor<CompatibleCountExprVisitor, bool, const Expr *> {
455430
using BaseVisitor =
@@ -523,6 +498,10 @@ struct CompatibleCountExprVisitor
523498
return false;
524499
}
525500

501+
bool VisitCXXThisExpr(const CXXThisExpr *SelfThis, const Expr *Other) {
502+
return isa<CXXThisExpr>(Other->IgnoreParenImpCasts());
503+
}
504+
526505
bool VisitDeclRefExpr(const DeclRefExpr *SelfDRE, const Expr *Other) {
527506
const ValueDecl *SelfVD = SelfDRE->getDecl();
528507

@@ -544,7 +523,7 @@ struct CompatibleCountExprVisitor
544523
const auto *OtherME = dyn_cast<MemberExpr>(O);
545524
if (MemberBase && OtherME) {
546525
return OtherME->getMemberDecl() == SelfVD &&
547-
isSameMemberBase(OtherME->getBase(), MemberBase);
526+
Visit(OtherME->getBase(), MemberBase);
548527
}
549528

550529
return false;
@@ -557,7 +536,12 @@ struct CompatibleCountExprVisitor
557536
return true;
558537
if (const auto *DRE = dyn_cast<DeclRefExpr>(Other->IgnoreParenImpCasts()))
559538
return MemberBase && Self->getMemberDecl() == DRE->getDecl() &&
560-
isSameMemberBase(Self->getBase(), MemberBase);
539+
Visit(Self->getBase(), MemberBase);
540+
if (const auto *OtherME =
541+
dyn_cast<MemberExpr>(Other->IgnoreParenImpCasts())) {
542+
return Self->getMemberDecl() == OtherME->getMemberDecl() &&
543+
Visit(Self->getBase(), OtherME->getBase());
544+
}
561545
return false;
562546
}
563547

@@ -587,6 +571,54 @@ struct CompatibleCountExprVisitor
587571

588572
return false;
589573
}
574+
575+
// Support any overloaded operator[] so long as it is a const method.
576+
bool VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *SelfOpCall,
577+
const Expr *Other) {
578+
if (SelfOpCall->getOperator() != OverloadedOperatorKind::OO_Subscript)
579+
return false;
580+
581+
const auto *MD = dyn_cast<CXXMethodDecl>(SelfOpCall->getCalleeDecl());
582+
583+
if (!MD || !MD->isConst())
584+
return false;
585+
if (const auto *OtherOpCall =
586+
dyn_cast<CXXOperatorCallExpr>(Other->IgnoreParenImpCasts()))
587+
if (SelfOpCall->getOperator() == OtherOpCall->getOperator()) {
588+
return Visit(SelfOpCall->getArg(0), OtherOpCall->getArg(0)) &&
589+
Visit(SelfOpCall->getArg(1), OtherOpCall->getArg(1));
590+
}
591+
return false;
592+
}
593+
594+
// Support array/pointer subscript. Even though these operators are generally
595+
// considered unsafe, they can be safely used on constant arrays with
596+
// known-safe literal indexes.
597+
bool VisitArraySubscriptExpr(const ArraySubscriptExpr *SelfAS,
598+
const Expr *Other) {
599+
if (const auto *OtherAS =
600+
dyn_cast<ArraySubscriptExpr>(Other->IgnoreParenImpCasts()))
601+
return Visit(SelfAS->getLHS(), OtherAS->getLHS()) &&
602+
Visit(SelfAS->getRHS(), OtherAS->getRHS());
603+
return false;
604+
}
605+
606+
// Support non-static member call:
607+
bool VisitCXXMemberCallExpr(const CXXMemberCallExpr *SelfCall,
608+
const Expr *Other) {
609+
const CXXMethodDecl *MD = SelfCall->getMethodDecl();
610+
611+
// The callee member function must be a const function with no parameter:
612+
if (MD->isConst() && MD->param_empty()) {
613+
if (auto *OtherCall =
614+
dyn_cast<CXXMemberCallExpr>(Other->IgnoreParenImpCasts())) {
615+
return OtherCall->getMethodDecl() == MD &&
616+
Visit(SelfCall->getImplicitObjectArgument(),
617+
OtherCall->getImplicitObjectArgument());
618+
}
619+
}
620+
return false;
621+
}
590622
};
591623

592624
// TL'DR:

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

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
#include <stddef.h>
55

66
namespace std {
7-
87
template <typename T, size_t N>
98
struct array {
109
const T *data() const noexcept;
1110
T *data() noexcept;
1211
size_t size() const noexcept;
12+
const T &operator[](const size_t idx) const;
1313
};
1414

1515
template <typename CharT>
@@ -40,6 +40,7 @@ struct span {
4040
span<T> first(size_t count) const noexcept;
4141
span<T> last(size_t count) const noexcept;
4242
span<T> subspan(size_t offset, size_t count) const noexcept;
43+
const T &operator[](const size_t idx) const;
4344
};
4445

4546
template <typename T>
@@ -71,7 +72,7 @@ void cb_cchar_42(const char *__counted_by(42) s);
7172
// 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'}}
7273
void cb_int(int *__counted_by(count) p, size_t count);
7374

74-
// expected-note@+1 11{{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'}}
75+
// expected-note@+1 33{{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'}}
7576
void cb_cint(const int *__counted_by(count) p, size_t count);
7677

7778
// expected-note@+1 10{{consider using 'std::span' and passing '.first(...).data()' to the parameter 'p'}}
@@ -367,6 +368,129 @@ void single_variable() {
367368
sb_cvoid(&Arr, sizeof(decltype(Var))); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
368369
}
369370

371+
namespace test_subscript {
372+
void foo(std::span<int> S) {
373+
std::array<int, 10> A;
374+
375+
cb_cint(S.first(S[5]).data(), S[5]);
376+
cb_cint(S.first(A[5]).data(), A[5]);
377+
cb_cint(S.subspan(0, S[5]).data(), S[5]);
378+
cb_cint(S.subspan(0, A[5]).data(), A[5]);
379+
cb_cint(S.subspan(0, S[5]).data(), A[5]); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
380+
cb_cint(S.subspan(0, S[5]).data(), S[6]); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
381+
382+
unsigned x;
383+
384+
cb_cint(S.first(S[x]).data(), S[x]);
385+
cb_cint(S.first(A[x]).data(), A[x]);
386+
cb_cint(S.subspan(0, S[x]).data(), S[x]);
387+
cb_cint(S.subspan(0, A[x]).data(), A[x]);
388+
cb_cint(S.first(S[x + 1]).data(), S[x + 1]);
389+
cb_cint(S.subspan(0, A[x + 1]).data(), A[x + 1]);
390+
cb_cint(S.first(A[A[x]]).data(), A[A[x]]);
391+
cb_cint(S.first(A[A[x] + 1]).data(), A[A[x] + 1]);
392+
cb_cint(S.first(S[x + 1]).data(), S[x - 1]); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
393+
cb_cint(S.first(A[A[x]]).data(), A[A[x] + 1]); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
394+
395+
std::array<std::array<int, 10>, 10> A2d;
396+
397+
cb_cint(S.first(A2d[x][5]).data(), A2d[x][5]);
398+
cb_cint(S.first(A2d[x][5]).data(), A2d[5][x]); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
399+
}
400+
401+
struct T {
402+
std::array<int, 10> a;
403+
} t, *tp;
404+
405+
struct TT {
406+
struct T t;
407+
struct T * tp;
408+
} tt, *ttp;
409+
410+
void bar(std::span<int> S) {
411+
unsigned x;
412+
413+
cb_cint(S.first(t.a[x]).data(), t.a[x]);
414+
cb_cint(S.first(tp->a[x]).data(), tp->a[x]);
415+
cb_cint(S.first(tt.t.a[x]).data(), tt.t.a[x]);
416+
cb_cint(S.first(ttp->t.a[x]).data(), ttp->t.a[x]);
417+
cb_cint(S.first(tt.tp->a[x]).data(), tt.tp->a[x]);
418+
cb_cint(S.first(ttp->tp->a[x]).data(), ttp->tp->a[x]);
419+
cb_cint(S.first(t.a[x]).data(), tp->a[x]); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
420+
cb_cint(S.first(tt.t.a[x]).data(), tt.tp->a[x]); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
421+
cb_cint(S.first(ttp->t.a[x]).data(), ttp->tp->a[x]); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
422+
423+
int cArr[10];
424+
425+
cb_cint(S.first(cArr[5]).data(), cArr[5]);
426+
cb_cint(S.first(cArr[1]).data(), cArr[sizeof(char)]);
427+
cb_cint(S.first(cArr[5]).data(), cArr[6]); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
428+
cb_cint(S.first(cArr[5]).data(), cArr[sizeof(char)]);// expected-warning{{unsafe assignment to function parameter of count-attributed type}}
429+
430+
std::array<struct TT, 10> ttA;
431+
432+
cb_cint(S.first(ttA[4].t.a[x]).data(), ttA[4].t.a[x]);
433+
cb_cint(S.first(ttA[4].tp->a[x]).data(), ttA[4].tp->a[x]);
434+
cb_cint(S.first(ttA[3].t.a[x]).data(), ttA[4].t.a[x]);// expected-warning{{unsafe assignment to function parameter of count-attributed type}}
435+
cb_cint(S.first(ttA[3].tp->a[x]).data(), ttA[4].tp->a[x]);// expected-warning{{unsafe assignment to function parameter of count-attributed type}}
436+
}
437+
} //namespace test_subscript
438+
439+
namespace test_member_fun_call {
440+
struct T {
441+
unsigned size() {return 0;} // missing 'const'
442+
unsigned size(int x) const {return 0;} // has arguments
443+
unsigned supported_size() const {return 0;}
444+
} t, *tp;
445+
446+
void foo(std::span<int> S1, std::span<int> S2) {
447+
cb_cint(S1.first(S2.size()).data(), S2.size());
448+
cb_cint(S1.first(S2.size()).data(), S1.size()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
449+
450+
451+
cb_cint(S1.first(t.supported_size()).data(), t.supported_size());
452+
cb_cint(S1.first(tp->supported_size()).data(), tp->supported_size());
453+
cb_cint(S1.first(t.size()).data(), t.size()); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
454+
cb_cint(S1.first(t.size(0)).data(), t.size(0)); // expected-warning{{unsafe assignment to function parameter of count-attributed type}}
455+
cb_cint(S1.first(t.supported_size()).data(), tp->supported_size()); // expected-warning{{unsafe assignment to function parameter of count-attributed}}
456+
}
457+
void bar(std::span<int> S) {
458+
std::array<struct T, 10> Ts;
459+
std::array<struct T*, 10> Tps;
460+
unsigned x;
461+
462+
cb_cint(S.first(Ts[5].supported_size()).data(), Ts[5].supported_size());
463+
cb_cint(S.first(Ts[x].supported_size()).data(), Ts[x].supported_size());
464+
cb_cint(S.first(Tps[5]->supported_size()).data(), Tps[5]->supported_size());
465+
cb_cint(S.first(Ts[x].supported_size()).data(), Ts[5].supported_size()); // expected-warning{{unsafe assignment to function parameter of count-attributed}}
466+
cb_cint(S.first(Ts[x].supported_size()).data(), Tps[x]->supported_size());// expected-warning{{unsafe assignment to function parameter of count-attributed}}
467+
}
468+
469+
template<typename Ty, size_t N>
470+
struct GenT {
471+
unsigned size() {return 0;} // missing 'const'
472+
unsigned size(int x) const {return 0;} // has arguments
473+
static unsigned static_size() {return 0;} // static function is not supported
474+
unsigned supported_size() const {return 0;}
475+
476+
void test(std::span<int> S) {
477+
cb_cint(S.first(supported_size()).data(), supported_size());
478+
cb_cint(S.first(supported_size()).data(), size()); // expected-warning{{unsafe assignment to function parameter of count-attributed}}
479+
}
480+
};
481+
482+
void baz(std::span<int> S) {
483+
GenT<int, 5> T5;
484+
GenT<int, 10> T10;
485+
486+
cb_cint(S.first(T5.supported_size()).data(), T5.supported_size());
487+
cb_cint(S.first(T5.static_size()).data(), T5.static_size()); // expected-warning{{unsafe assignment to function parameter of count-attributed}}
488+
cb_cint(S.first(GenT<int, 5>::static_size()).data(), GenT<int, 5>::static_size()); // expected-warning{{unsafe assignment to function parameter of count-attributed}}
489+
cb_cint(S.first(T5.supported_size()).data(), T10.supported_size()); // expected-warning{{unsafe assignment to function parameter of count-attributed}}
490+
T5.test(S);
491+
}
492+
} // namespace test_member_fun_call
493+
370494
namespace test_member_access {
371495
class Base {
372496
public:

0 commit comments

Comments
 (0)