Skip to content

Commit

Permalink
[clang] Ensure pointers passed to runtime support functions are corre…
Browse files Browse the repository at this point in the history
…ctly signed (#98276)

Updates codegen for global destructors and raising exceptions to ensure
that the function pointers being passed are signed using the correct
schema.

Notably this requires that CodeGenFunction::createAtExitStub to return
an opaque Constant* rather than a Function* as the value being emitted
is no longer necessarily a raw function pointer depending on the
configured ABI.

Co-Authored-By: Akira Hatanaka <[email protected]>
Co-Authored-By: John McCall <[email protected]>
  • Loading branch information
3 people authored Jul 17, 2024
1 parent d08527e commit 07f8a65
Show file tree
Hide file tree
Showing 5 changed files with 82 additions and 6 deletions.
12 changes: 9 additions & 3 deletions clang/lib/CodeGen/CGDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ void CodeGenFunction::EmitCXXGlobalVarDeclInit(const VarDecl &D,

/// Create a stub function, suitable for being passed to atexit,
/// which passes the given address to the given destructor function.
llvm::Function *CodeGenFunction::createAtExitStub(const VarDecl &VD,
llvm::Constant *CodeGenFunction::createAtExitStub(const VarDecl &VD,
llvm::FunctionCallee dtor,
llvm::Constant *addr) {
// Get the destructor function type, void(*)(void).
Expand Down Expand Up @@ -264,7 +264,12 @@ llvm::Function *CodeGenFunction::createAtExitStub(const VarDecl &VD,

CGF.FinishFunction();

return fn;
// Get a proper function pointer.
FunctionProtoType::ExtProtoInfo EPI(getContext().getDefaultCallingConvention(
/*IsVariadic=*/false, /*IsCXXMethod=*/false));
QualType fnType = getContext().getFunctionType(getContext().VoidTy,
{getContext().VoidPtrTy}, EPI);
return CGM.getFunctionPointer(fn, fnType);
}

/// Create a stub function, suitable for being passed to __pt_atexit_np,
Expand Down Expand Up @@ -333,7 +338,8 @@ void CodeGenFunction::registerGlobalDtorWithLLVM(const VarDecl &VD,
llvm::FunctionCallee Dtor,
llvm::Constant *Addr) {
// Create a function which calls the destructor.
llvm::Function *dtorStub = createAtExitStub(VD, Dtor, Addr);
llvm::Function *dtorStub =
cast<llvm::Function>(createAtExitStub(VD, Dtor, Addr));
CGM.AddGlobalDtor(dtorStub);
}

Expand Down
2 changes: 1 addition & 1 deletion clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -4864,7 +4864,7 @@ class CodeGenFunction : public CodeGenTypeCache {
void EmitCXXGlobalVarDeclInit(const VarDecl &D, llvm::GlobalVariable *GV,
bool PerformInit);

llvm::Function *createAtExitStub(const VarDecl &VD, llvm::FunctionCallee Dtor,
llvm::Constant *createAtExitStub(const VarDecl &VD, llvm::FunctionCallee Dtor,
llvm::Constant *Addr);

llvm::Function *createTLSAtExitStub(const VarDecl &VD,
Expand Down
21 changes: 19 additions & 2 deletions clang/lib/CodeGen/ItaniumCXXABI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1321,8 +1321,16 @@ void ItaniumCXXABI::emitThrow(CodeGenFunction &CGF, const CXXThrowExpr *E) {
if (const RecordType *RecordTy = ThrowType->getAs<RecordType>()) {
CXXRecordDecl *Record = cast<CXXRecordDecl>(RecordTy->getDecl());
if (!Record->hasTrivialDestructor()) {
// __cxa_throw is declared to take its destructor as void (*)(void *). We
// must match that if function pointers can be authenticated with a
// discriminator based on their type.
const ASTContext &Ctx = getContext();
QualType DtorTy = Ctx.getFunctionType(Ctx.VoidTy, {Ctx.VoidPtrTy},
FunctionProtoType::ExtProtoInfo());

CXXDestructorDecl *DtorD = Record->getDestructor();
Dtor = CGM.getAddrOfCXXStructor(GlobalDecl(DtorD, Dtor_Complete));
Dtor = CGM.getFunctionPointer(Dtor, DtorTy);
}
}
if (!Dtor) Dtor = llvm::Constant::getNullValue(CGM.Int8PtrTy);
Expand Down Expand Up @@ -2699,14 +2707,22 @@ static void emitGlobalDtorWithCXAAtExit(CodeGenFunction &CGF,
if (llvm::Function *fn = dyn_cast<llvm::Function>(atexit.getCallee()))
fn->setDoesNotThrow();

const auto &Context = CGF.CGM.getContext();
FunctionProtoType::ExtProtoInfo EPI(Context.getDefaultCallingConvention(
/*IsVariadic=*/false, /*IsCXXMethod=*/false));
QualType fnType =
Context.getFunctionType(Context.VoidTy, {Context.VoidPtrTy}, EPI);
llvm::Constant *dtorCallee = cast<llvm::Constant>(dtor.getCallee());
dtorCallee = CGF.CGM.getFunctionPointer(dtorCallee, fnType);

if (!addr)
// addr is null when we are trying to register a dtor annotated with
// __attribute__((destructor)) in a constructor function. Using null here is
// okay because this argument is just passed back to the destructor
// function.
addr = llvm::Constant::getNullValue(CGF.Int8PtrTy);

llvm::Value *args[] = {dtor.getCallee(), addr, handle};
llvm::Value *args[] = {dtorCallee, addr, handle};
CGF.EmitNounwindRuntimeCall(atexit, args);
}

Expand Down Expand Up @@ -4907,7 +4923,8 @@ void XLCXXABI::registerGlobalDtor(CodeGenFunction &CGF, const VarDecl &D,
}

// Create __dtor function for the var decl.
llvm::Function *DtorStub = CGF.createAtExitStub(D, Dtor, Addr);
llvm::Function *DtorStub =
cast<llvm::Function>(CGF.createAtExitStub(D, Dtor, Addr));

// Register above __dtor with atexit().
CGF.registerGlobalDtorWithAtExit(DtorStub);
Expand Down
24 changes: 24 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-static-destructors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \
// RUN: | FileCheck %s --check-prefix=CXAATEXIT

// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -emit-llvm -std=c++11 %s -o - \
// RUN: -fno-use-cxa-atexit \
// RUN: | FileCheck %s --check-prefix=ATEXIT

class Foo {
public:
~Foo() {
}
};

Foo global;

// CXAATEXIT: define internal void @__cxx_global_var_init()
// CXAATEXIT: call i32 @__cxa_atexit(ptr ptrauth (ptr @_ZN3FooD1Ev, i32 0), ptr @global, ptr @__dso_handle)


// ATEXIT: define internal void @__cxx_global_var_init()
// ATEXIT: %{{.*}} = call i32 @atexit(ptr ptrauth (ptr @__dtor_global, i32 0))

// ATEXIT: define internal void @__dtor_global() {{.*}} section "__TEXT,__StaticInit,regular,pure_instructions" {
// ATEXIT: %{{.*}} = call ptr @_ZN3FooD1Ev(ptr @global)
29 changes: 29 additions & 0 deletions clang/test/CodeGenCXX/ptrauth-throw.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// RUN: %clang_cc1 -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECK
// RUN: %clang_cc1 -fptrauth-function-pointer-type-discrimination -triple arm64-apple-ios -fptrauth-calls -fcxx-exceptions -emit-llvm %s -o - | FileCheck %s --check-prefix=CHECKDISC

class Foo {
public:
~Foo() {
}
};

// CHECK-LABEL: define void @_Z1fv()
// CHECK: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTI3Foo, ptr ptrauth (ptr @_ZN3FooD1Ev, i32 0))

// CHECKDISC-LABEL: define void @_Z1fv()
// CHECKDISC: call void @__cxa_throw(ptr %{{.*}}, ptr @_ZTI3Foo, ptr ptrauth (ptr @_ZN3FooD1Ev, i32 0, i64 10942))

void f() {
throw Foo();
}

// __cxa_throw is defined to take its destructor as "void (*)(void *)" in the ABI.
// CHECK-LABEL: define void @__cxa_throw({{.*}})
// CHECK: call void {{%.*}}(ptr noundef {{%.*}}) [ "ptrauth"(i32 0, i64 0) ]

// CHECKDISC-LABEL: define void @__cxa_throw({{.*}})
// CHECKDISC: call void {{%.*}}(ptr noundef {{%.*}}) [ "ptrauth"(i32 0, i64 10942) ]

extern "C" void __cxa_throw(void *exception, void *, void (*dtor)(void *)) {
dtor(exception);
}

0 comments on commit 07f8a65

Please sign in to comment.