Skip to content

Commit 485c80e

Browse files
authored
[PAC] Ignore noexcept on function type when computing discriminator of member function pointers (llvm#109056)
This fixes a bug where a member function pointer signed using a function type with noexcept as the discriminator was being authenticated using a function type without noexcept. Fixes llvm#106487.
1 parent cf6d79a commit 485c80e

File tree

2 files changed

+91
-0
lines changed

2 files changed

+91
-0
lines changed

clang/lib/AST/ASTContext.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3503,6 +3503,34 @@ uint16_t ASTContext::getPointerAuthTypeDiscriminator(QualType T) {
35033503
encodeTypeForFunctionPointerAuth(*this, Out, T);
35043504
} else {
35053505
T = T.getUnqualifiedType();
3506+
// Calls to member function pointers don't need to worry about
3507+
// language interop or the laxness of the C type compatibility rules.
3508+
// We just mangle the member pointer type directly, which is
3509+
// implicitly much stricter about type matching. However, we do
3510+
// strip any top-level exception specification before this mangling.
3511+
// C++23 requires calls to work when the function type is convertible
3512+
// to the pointer type by a function pointer conversion, which can
3513+
// change the exception specification. This does not technically
3514+
// require the exception specification to not affect representation,
3515+
// because the function pointer conversion is still always a direct
3516+
// value conversion and therefore an opportunity to resign the
3517+
// pointer. (This is in contrast to e.g. qualification conversions,
3518+
// which can be applied in nested pointer positions, effectively
3519+
// requiring qualified and unqualified representations to match.)
3520+
// However, it is pragmatic to ignore exception specifications
3521+
// because it allows a certain amount of `noexcept` mismatching
3522+
// to not become a visible ODR problem. This also leaves some
3523+
// room for the committee to add laxness to function pointer
3524+
// conversions in future standards.
3525+
if (auto *MPT = T->getAs<MemberPointerType>())
3526+
if (MPT->isMemberFunctionPointer()) {
3527+
QualType PointeeType = MPT->getPointeeType();
3528+
if (PointeeType->castAs<FunctionProtoType>()->getExceptionSpecType() !=
3529+
EST_None) {
3530+
QualType FT = getFunctionTypeWithExceptionSpec(PointeeType, EST_None);
3531+
T = getMemberPointerType(FT, MPT->getClass());
3532+
}
3533+
}
35063534
std::unique_ptr<MangleContext> MC(createMangleContext());
35073535
MC->mangleCanonicalTypeName(T, Out);
35083536
}

clang/test/CodeGenCXX/ptrauth-member-function-pointer.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN %s
2+
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,DARWIN,CXX17 %s
23
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,DARWIN %s
34
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT
45
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT
56
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 3 -o - %s | FileCheck %s -check-prefix=STACK-PROT
67

78
// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF %s
9+
// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++17 -O1 -disable-llvm-passes -o - %s | FileCheck -check-prefixes=CHECK,NODEBUG,ELF,CXX17 %s
810
// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -debug-info-kind=limited -o - %s | FileCheck -check-prefixes=CHECK,ELF %s
911
// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 1 -o - %s | FileCheck %s -check-prefix=STACK-PROT
1012
// RUN: %clang_cc1 -triple aarch64-linux-gnu -fptrauth-calls -fptrauth-intrinsics -emit-llvm -std=c++11 -O1 -disable-llvm-passes -stack-protector 2 -o - %s | FileCheck %s -check-prefix=STACK-PROT
@@ -20,6 +22,10 @@
2022
// CHECK: @__const._Z13testArrayInitv.c0 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base011nonvirtual0Ev, i32 0, i64 35591) to i64), i64 0 } }, align 8
2123
// CHECK: @__const._Z13testArrayInitv.c1 = private unnamed_addr constant %struct.Class0 { { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN5Base08virtual1Ev_vfpthunk_, i32 0, i64 35591) to i64), i64 0 } }, align 8
2224

25+
// CHECK: @_ZN22testNoexceptConversion6mfptr1E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[TYPEDISC3:.*]]) to i64), i64 0 },
26+
// CHECK: @_ZN22testNoexceptConversion6mfptr2E = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S16virtual_noexceptEv_vfpthunk_, i32 0, i64 [[TYPEDISC3]]) to i64), i64 0 },
27+
// CHECK: @_ZN22testNoexceptConversion15mfptr3_noexceptE = global { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[TYPEDISC3]]) to i64), i64 0 },
28+
2329
// CHECK: @_ZTV5Base0 = unnamed_addr constant { [5 x ptr] } { [5 x ptr] [ptr null, ptr @_ZTI5Base0,
2430
// CHECK-SAME: ptr ptrauth (ptr @_ZN5Base08virtual1Ev, i32 0, i64 55600, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 2)),
2531
// CHECK-SAME: ptr ptrauth (ptr @_ZN5Base08virtual3Ev, i32 0, i64 53007, ptr getelementptr inbounds ({ [5 x ptr] }, ptr @_ZTV5Base0, i32 0, i32 0, i32 3)),
@@ -77,6 +83,9 @@ struct Derived1 : Base0, Base1 {
7783
};
7884

7985
typedef void (Base0::*MethodTy0)();
86+
#if __cplusplus >= 201703L
87+
typedef void (Base0::*NoExceptMethodTy0)() noexcept;
88+
#endif
8089
typedef void (Base0::*VariadicMethodTy0)(int, ...);
8190
typedef void (Derived0::*MethodTy1)();
8291

@@ -293,6 +302,16 @@ void test1(Base0 *a0, MethodTy0 a1) {
293302
(a0->*a1)();
294303
}
295304

305+
// CXX17: define{{.*}} void @_Z14test1_noexceptP5Base0MS_DoFvvE(
306+
// CXX17: %[[V14:.*]] = phi ptr [ %{{.*}}, {{.*}} ], [ %{{.*}}, {{.*}} ]
307+
// CXX17: %[[V15:.*]] = phi i64 [ 0, {{.*}} ], [ [[TYPEDISC0]], {{.*}} ]
308+
// CXX17: call void %[[V14]](ptr noundef nonnull align {{[0-9]+}} dereferenceable(8) %{{.*}}) {{.*}}[ "ptrauth"(i32 0, i64 %[[V15]]) ]
309+
#if __cplusplus >= 201703L
310+
void test1_noexcept(Base0 *a0, NoExceptMethodTy0 a1) {
311+
(a0->*a1)();
312+
}
313+
#endif
314+
296315
// CHECK: define{{.*}} void @_Z15testConversion0M5Base0FvvEM8Derived0FvvE([2 x i64] %[[METHOD0_COERCE:.*]], [2 x i64] %[[METHOD1_COERCE:.*]])
297316
// CHECK: %[[METHOD0:.*]] = alloca { i64, i64 }, align 8
298317
// CHECK: %[[METHOD1:.*]] = alloca { i64, i64 }, align 8
@@ -438,3 +457,47 @@ void testArrayInit() {
438457
void testConvertNull() {
439458
VariadicMethodTy0 t = (VariadicMethodTy0)(MethodTy0{});
440459
}
460+
461+
namespace testNoexceptConversion {
462+
463+
// CHECK-LABEL: define internal void @__cxx_global_var_init()
464+
// CHECK: %[[V0:.*]] = load { i64, i64 }, ptr @_ZN22testNoexceptConversion15mfptr0_noexceptE, align 8
465+
// CHECK: store { i64, i64 } %[[V0]], ptr @_ZN22testNoexceptConversion6mfptr4E, align 8
466+
467+
// CHECK: define {{.*}}void @_ZN22testNoexceptConversion5test0Ev()
468+
// CHECK: %[[P0:.*]] = alloca { i64, i64 }, align 8
469+
// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S19nonvirtual_noexceptEv, i32 0, i64 [[TYPEDISC3]]) to i64), i64 0 }, ptr %[[P0]], align 8,
470+
471+
// CHECK: define {{.*}}void @_ZN22testNoexceptConversion5test1Ev()
472+
// CHECK: %[[P0:.*]] = alloca { i64, i64 }, align 8
473+
// CHECK: store { i64, i64 } { i64 ptrtoint (ptr ptrauth (ptr @_ZN22testNoexceptConversion1S16virtual_noexceptEv_vfpthunk_, i32 0, i64 [[TYPEDISC3]]) to i64), i64 0 }, ptr %[[P0]], align 8,
474+
475+
// CHECK: define {{.*}}void @_ZN22testNoexceptConversion5test2Ev()
476+
// CHECK: %[[P0:.*]] = alloca { i64, i64 }, align 8
477+
// CHECK: %[[V0:.*]] = load { i64, i64 }, ptr @_ZN22testNoexceptConversion15mfptr0_noexceptE, align 8
478+
// CHECK: store { i64, i64 } %[[V0]], ptr %[[P0]], align 8,
479+
480+
struct S {
481+
void nonvirtual_noexcept() noexcept;
482+
virtual void virtual_noexcept() noexcept;
483+
};
484+
485+
void (S::*mfptr0_noexcept)() noexcept;
486+
void (S::*mfptr1)() = &S::nonvirtual_noexcept;
487+
void (S::*mfptr2)() = &S::virtual_noexcept;
488+
void (S::*mfptr3_noexcept)() noexcept = &S::nonvirtual_noexcept;
489+
void (S::*mfptr4)() = mfptr0_noexcept;
490+
491+
void test0() {
492+
void (S::*p0)() = &S::nonvirtual_noexcept;
493+
}
494+
495+
void test1() {
496+
void (S::*p0)() = &S::virtual_noexcept;
497+
}
498+
499+
void test2() {
500+
void (S::*p0)() = mfptr0_noexcept;
501+
}
502+
503+
}

0 commit comments

Comments
 (0)