Skip to content

Commit 7cd35e2

Browse files
authored
[Sema] Check that return and argument types of a virtual member function (#993)
* [Sema] Check that return and argument types of a virtual member function are complete if the address of the member function is taken If they aren't complete, reject the code. rdar://problem/60888064 * Address review comments Explain why the types must be complete when pointer authentication is enabled. Check that virual function pointers aren't diagnosed in unevaluated contexts. Change the variable and diagnostic names to improve indentation. * Check that a pointer to a virtual function pointer with a dependent noexcept clause isn't diagnosed Test virtual functions of templated classes.
1 parent 1a73006 commit 7cd35e2

File tree

3 files changed

+83
-1
lines changed

3 files changed

+83
-1
lines changed

Diff for: clang/include/clang/Basic/DiagnosticSemaKinds.td

+5
Original file line numberDiff line numberDiff line change
@@ -776,6 +776,11 @@ def err_ptrauth_string_not_literal : Error<
776776
def err_ptrauth_type_disc_variably_modified : Error<
777777
"cannot pass variably-modified type %0 to "
778778
"'__builtin_ptrauth_type_discriminator'">;
779+
def note_ptrauth_virtual_function_pointer_incomplete_arg_ret :
780+
Note<"cannot take an address of a virtual member function if its return or "
781+
"argument types are incomplete">;
782+
def note_ptrauth_virtual_function_incomplete_arg_ret_type :
783+
Note<"%0 is incomplete">;
779784

780785
// __ptrauth qualifier
781786
def err_ptrauth_qualifier_return : Error<

Diff for: clang/lib/Sema/SemaExpr.cpp

+33
Original file line numberDiff line numberDiff line change
@@ -12759,6 +12759,39 @@ QualType Sema::CheckAddressOfOperand(ExprResult &OrigOp, SourceLocation OpLoc) {
1275912759

1276012760
QualType MPTy = Context.getMemberPointerType(
1276112761
op->getType(), Context.getTypeDeclType(MD->getParent()).getTypePtr());
12762+
12763+
if (getLangOpts().PointerAuthCalls && MD->isVirtual() &&
12764+
!isUnevaluatedContext() && !MPTy->isDependentType()) {
12765+
// When pointer authentication is enabled, argument and return types of
12766+
// vitual member functions must be complete. This is because vitrual
12767+
// member function pointers are implemented using virtual dispatch
12768+
// thunks and the thunks cannot be emitted if the argument or return
12769+
// types are incomplete.
12770+
auto ReturnOrParamTypeIsIncomplete = [&](QualType T,
12771+
SourceLocation DeclRefLoc,
12772+
SourceLocation RetArgTypeLoc) {
12773+
if (RequireCompleteType(DeclRefLoc, T, diag::err_incomplete_type)) {
12774+
Diag(DeclRefLoc,
12775+
diag::note_ptrauth_virtual_function_pointer_incomplete_arg_ret);
12776+
Diag(RetArgTypeLoc,
12777+
diag::note_ptrauth_virtual_function_incomplete_arg_ret_type)
12778+
<< T;
12779+
return true;
12780+
}
12781+
return false;
12782+
};
12783+
QualType RetTy = MD->getReturnType();
12784+
bool IsIncomplete =
12785+
!RetTy->isVoidType() &&
12786+
ReturnOrParamTypeIsIncomplete(
12787+
RetTy, OpLoc, MD->getReturnTypeSourceRange().getBegin());
12788+
for (auto *PVD : MD->parameters())
12789+
IsIncomplete |= ReturnOrParamTypeIsIncomplete(PVD->getType(), OpLoc,
12790+
PVD->getBeginLoc());
12791+
if (IsIncomplete)
12792+
return QualType();
12793+
}
12794+
1276212795
// Under the MS ABI, lock down the inheritance model now.
1276312796
if (Context.getTargetInfo().getCXXABI().isMicrosoft())
1276412797
(void)isCompleteType(OpLoc, MPTy);

Diff for: clang/test/SemaCXX/ptrauth.cpp

+45-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,41 @@
1-
// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++17 -fsyntax-only -verify -fptrauth-intrinsics %s
1+
// RUN: %clang_cc1 -triple arm64-apple-ios -std=c++17 -fsyntax-only -verify -fptrauth-intrinsics -fptrauth-calls %s
2+
3+
struct Incomplete0; // expected-note 3 {{forward declaration of 'Incomplete0'}}
4+
5+
template <class T>
6+
struct Incomplete1; // expected-note {{template is declared here}}
7+
8+
struct Complete0 {
9+
};
10+
11+
template <class T>
12+
struct Complete1 {
13+
};
214

315
struct S {
416
virtual int foo();
17+
virtual Incomplete0 virtual0(); // expected-note 2 {{'Incomplete0' is incomplete}}
18+
virtual void virtual1(Incomplete1<int>); // expected-note {{'Incomplete1<int>' is incomplete}}
19+
virtual Complete0 virtual2();
20+
virtual Complete1<int> virtual3();
21+
Incomplete0 nonvirtual0();
22+
template <class T>
23+
void m0() {
24+
(void)&S::virtual0; // expected-error {{incomplete type 'Incomplete0'}} expected-note {{cannot take an address of a virtual}}
25+
}
26+
};
27+
28+
template <bool T>
29+
struct S2 {
30+
virtual Incomplete0 virtual0() noexcept(T); // expected-note {{'Incomplete0' is incomplete}}
31+
32+
void m0() {
33+
(void)&S2<T>::virtual0;
34+
}
35+
36+
void m1() {
37+
(void)&S2<T>::virtual0; // expected-error {{incomplete type 'Incomplete0'}} expected-note {{cannot take an address of a virtual}}
38+
}
539
};
640

741
template <class T>
@@ -31,3 +65,13 @@ void test_builtin_ptrauth_type_discriminator(unsigned s) {
3165
__builtin_ptrauth_type_discriminator(&t); // expected-error {{expected a type}}
3266
__builtin_ptrauth_type_discriminator(decltype(vmarray)); // expected-error {{cannot pass variably-modified type 'decltype(vmarray)'}}
3367
}
68+
69+
void test_incomplete_virtual_member_function_return_arg_type() {
70+
(void)&S::virtual0; // expected-error {{incomplete type 'Incomplete0}} expected-note {{cannot take an address of a virtual member function}}
71+
(void)&S::virtual1; // expected-error {{implicit instantiation of undefined template 'Incomplete1<int>'}} expected-note {{cannot take an address of a virtual member function}}
72+
(void)&S::virtual2;
73+
(void)&S::virtual3;
74+
(void)&S::nonvirtual0;
75+
int s = sizeof(&S::virtual0);
76+
S2<true>().m1(); // expected-note {{in instantiation of}}
77+
}

0 commit comments

Comments
 (0)