Skip to content

Commit e0eb8f0

Browse files
offsetofcor3ntin
andauthored
[clang] Fix static_cast bypassing access control (#132285)
Fix access and ambiguity checks not being performed when converting to an rvalue reference to a base class type with `static_cast`. Fixes #121429 --------- Co-authored-by: Corentin Jabot <corentinjabot@gmail.com>
1 parent 799270a commit e0eb8f0

File tree

3 files changed

+80
-15
lines changed

3 files changed

+80
-15
lines changed

clang/docs/ReleaseNotes.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -895,6 +895,7 @@ Bug Fixes to C++ Support
895895
- Fixed a Clang regression in C++20 mode where unresolved dependent call expressions were created inside non-dependent contexts (#GH122892)
896896
- Clang now emits the ``-Wunused-variable`` warning when some structured bindings are unused
897897
and the ``[[maybe_unused]]`` attribute is not applied. (#GH125810)
898+
- Fixed ``static_cast`` not performing access or ambiguity checks when converting to an rvalue reference to a base class. (#GH121429)
898899
- Declarations using class template argument deduction with redundant
899900
parentheses around the declarator are no longer rejected. (#GH39811)
900901
- Fixed a crash caused by invalid declarations of ``std::initializer_list``. (#GH132256)

clang/lib/Sema/SemaCast.cpp

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ static void DiagnoseCastQual(Sema &Self, const ExprResult &SrcExpr,
263263
// %2: Destination Type
264264
static TryCastResult TryLValueToRValueCast(Sema &Self, Expr *SrcExpr,
265265
QualType DestType, bool CStyle,
266-
CastKind &Kind,
266+
SourceRange OpRange, CastKind &Kind,
267267
CXXCastPath &BasePath,
268268
unsigned &msg);
269269
static TryCastResult
@@ -1425,8 +1425,8 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr,
14251425
// C++11 [expr.static.cast]p3:
14261426
// A glvalue of type "cv1 T1" can be cast to type "rvalue reference to cv2
14271427
// T2" if "cv2 T2" is reference-compatible with "cv1 T1".
1428-
tcr = TryLValueToRValueCast(Self, SrcExpr.get(), DestType, CStyle, Kind,
1429-
BasePath, msg);
1428+
tcr = TryLValueToRValueCast(Self, SrcExpr.get(), DestType, CStyle, OpRange,
1429+
Kind, BasePath, msg);
14301430
if (tcr != TC_NotApplicable)
14311431
return tcr;
14321432

@@ -1602,8 +1602,8 @@ static TryCastResult TryStaticCast(Sema &Self, ExprResult &SrcExpr,
16021602
/// Tests whether a conversion according to N2844 is valid.
16031603
TryCastResult TryLValueToRValueCast(Sema &Self, Expr *SrcExpr,
16041604
QualType DestType, bool CStyle,
1605-
CastKind &Kind, CXXCastPath &BasePath,
1606-
unsigned &msg) {
1605+
SourceRange OpRange, CastKind &Kind,
1606+
CXXCastPath &BasePath, unsigned &msg) {
16071607
// C++11 [expr.static.cast]p3:
16081608
// A glvalue of type "cv1 T1" can be cast to type "rvalue reference to
16091609
// cv2 T2" if "cv2 T2" is reference-compatible with "cv1 T1".
@@ -1616,7 +1616,6 @@ TryCastResult TryLValueToRValueCast(Sema &Self, Expr *SrcExpr,
16161616

16171617
// Because we try the reference downcast before this function, from now on
16181618
// this is the only cast possibility, so we issue an error if we fail now.
1619-
// FIXME: Should allow casting away constness if CStyle.
16201619
QualType FromType = SrcExpr->getType();
16211620
QualType ToType = R->getPointeeType();
16221621
if (CStyle) {
@@ -1640,13 +1639,12 @@ TryCastResult TryLValueToRValueCast(Sema &Self, Expr *SrcExpr,
16401639

16411640
if (RefConv & Sema::ReferenceConversions::DerivedToBase) {
16421641
Kind = CK_DerivedToBase;
1643-
CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
1644-
/*DetectVirtual=*/true);
1645-
if (!Self.IsDerivedFrom(SrcExpr->getBeginLoc(), SrcExpr->getType(),
1646-
R->getPointeeType(), Paths))
1647-
return TC_NotApplicable;
1648-
1649-
Self.BuildBasePathArray(Paths, BasePath);
1642+
if (Self.CheckDerivedToBaseConversion(FromType, ToType,
1643+
SrcExpr->getBeginLoc(), OpRange,
1644+
&BasePath, CStyle)) {
1645+
msg = 0;
1646+
return TC_Failed;
1647+
}
16501648
} else
16511649
Kind = CK_NoOp;
16521650

clang/test/CXX/expr/expr.post/expr.static.cast/p3-0x.cpp

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
1-
// RUN: %clang_cc1 -std=c++11 -fsyntax-only -verify %s
2-
// expected-no-diagnostics
1+
// RUN: %clang_cc1 -std=c++14 -Wno-unused-value -verify %s
32

43
// A glvalue of type "cv1 T1" can be cast to type "rvalue reference to
54
// cv2 T2" if "cv2 T2" is reference-compatible with "cv1 T1" (8.5.3).
@@ -23,3 +22,70 @@ void test(A &a, B &b) {
2322
const A &&ar10 = static_cast<const A&&>(xvalue<A>());
2423
const A &&ar11 = static_cast<const A&&>(xvalue<B>());
2524
}
25+
26+
namespace GH121429 {
27+
28+
struct C : private A { // expected-note 4 {{declared private here}}
29+
C&& that();
30+
31+
void f() {
32+
static_cast<A&&>(*this);
33+
static_cast<const A&&>(*this);
34+
35+
static_cast<A&&>(that());
36+
static_cast<const A&&>(that());
37+
}
38+
};
39+
C c;
40+
const C cc;
41+
42+
void f() {
43+
static_cast<A&&>(c); // expected-error {{cannot cast 'C' to its private base class 'A'}}
44+
static_cast<A&&>(c.that()); // expected-error {{cannot cast 'C' to its private base class 'A'}}
45+
46+
static_cast<const A&&>(c); // expected-error {{cannot cast 'C' to its private base class 'const A'}}
47+
static_cast<const A&&>(c.that()); // expected-error {{cannot cast 'C' to its private base class 'const A'}}
48+
}
49+
50+
constexpr bool g() {
51+
(A&&)c;
52+
(A&&)(C&&)c;
53+
(A&&)cc;
54+
(A&&)(const C&&)c;
55+
(const A&&)c;
56+
(const A&&)(C&&)c;
57+
(const A&&)cc;
58+
(const A&&)(const C&&)c;
59+
return true;
60+
}
61+
static_assert(g(), "");
62+
63+
struct D : A, B { // expected-warning {{direct base 'A' is inaccessible due to ambiguity}}
64+
D&& rv();
65+
};
66+
D d;
67+
68+
void h(const D cd) {
69+
static_cast<A&&>(d); // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
70+
static_cast<A&&>(d.rv()); // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
71+
72+
static_cast<const A&&>(d); // expected-error {{ambiguous conversion from derived class 'D' to base class 'const A'}}
73+
static_cast<const A&&>(d.rv()); // expected-error {{ambiguous conversion from derived class 'D' to base class 'const A'}}
74+
75+
(A&&)d; // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
76+
(A&&)(D&&)d; // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
77+
(A&&)cd; // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
78+
(A&&)(const D&&)d; // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
79+
(const A&&)d; // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
80+
(const A&&)(D&&)d; // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
81+
(const A&&)cd; // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
82+
(const A&&)(const D&&)d; // expected-error {{ambiguous conversion from derived class 'D' to base class 'A'}}
83+
}
84+
85+
template<class T, class U>
86+
auto s(U u = {}) -> decltype(static_cast<T&&>(u)); // expected-note 2 {{substitution failure}}
87+
88+
int i = s<A, C>(); // expected-error {{no matching function}}
89+
int j = s<A, D>(); // expected-error {{no matching function}}
90+
91+
}

0 commit comments

Comments
 (0)