Skip to content

[IRGen] ObjC metadata and block PAC signing for arm64e#88114

Draft
oskarwirga wants to merge 4 commits intoswiftlang:mainfrom
oskarwirga:arm64e/objc-metadata-pac
Draft

[IRGen] ObjC metadata and block PAC signing for arm64e#88114
oskarwirga wants to merge 4 commits intoswiftlang:mainfrom
oskarwirga:arm64e/objc-metadata-pac

Conversation

@oskarwirga
Copy link
Copy Markdown

Summary

  • Sign isa, superclass, and class_ro pointers in class/metaclass metadata with DA key and ABI-mandated discriminators (0x6AE1, 0xB5AB, 0x61F8) to match Clang's CGObjCMac.cpp behavior on arm64e.
  • Authenticate isa pointers when loading heap object metadata for vtable dispatch. For the ObjC isa encoding path, bypass swift_isaMask (which can't strip PAC bits) and use object_getClass() instead.
  • Guard protocol method list pointer signing: only sign for class/category metadata, not protocol metadata. The ObjC runtime's fixupProtocol uses plain ldr on protocol method lists, while fixupMethodList authenticates class method lists with autda.
  • Sign block isa pointers with ObjCIsaPointers schema, matching Clang's EmitPointerAuthObjCISA. Without this, objc_msgSend's isa authentication fails on Swift @convention(block) closures.

Discriminator values come from Apple's objc-ptrauth.h. addSignedPointer with an empty schema falls back to unsigned add, so non-arm64e targets are unaffected.

Test plan

  • test/IRGen/ptrauth-objc-class-metadata.swift — verifies isa/superclass/class_ro signing in class metadata
  • test/IRGen/ptrauth-heap-object-isa.sil — verifies isa authentication on heap object metadata load
  • test/IRGen/ptrauth-protocol-method-list-guard.swift — verifies protocol method lists are NOT signed
  • test/IRGen/ptrauth-block-isa-signing.sil — verifies block isa pointer signing

@swift-ci Please smoke test

Sign isa, superclass, and class_ro pointers in class/metaclass metadata
using ObjCIsaPointers (DA, disc 0x6AE1), ObjCSuperPointers (DA, disc
0xB5AB), and ObjCClassROPointers (DA, disc 0) schemas respectively.

On arm64e, the ObjC runtime expects these pointers to be signed. Without
signing, loading a Swift class that inherits from NSObject (or any ObjC
class) causes a ptrauth failure when the runtime tries to authenticate
the isa or superclass pointer during class realization.

Key changes:
- Change ObjCClassStructTy layout to use OpaquePtrTy for the class_ro
  field (matching Clang's ClassnfABITy) to enable addSignedPointer.
- Sign metaclass isa, class isa (metaclass pointer), superclass, and
  class_ro pointers with appropriate ptrauth schemas.
- Move type descriptor signing to the singleton metadata access call
  site to avoid double-signing when the runtime passes pre-signed
  descriptors to generic instantiation functions.
- Add isProtocolRequirementDescriptor() and isValueWitnessTable()
  predicates to LinkEntity for use by later patches.

addSignedPointer with an empty schema falls back to an unsigned add,
so non-arm64e targets are completely unaffected.
On arm64e, the ObjC runtime signs isa pointers stored in object headers
using the DA key with address diversity and discriminator 0x6AE1. When
Swift loads the isa for vtable dispatch (IsaEncoding::Pointer path), the
raw load returns a PAC-signed pointer that must be authenticated before
use as a metadata base address.

Use the PointerAuthInfo/emitPointerAuthAuth abstraction layer to
authenticate the isa pointer against the ObjCIsaPointers schema,
matching the pattern used in GenFunc.cpp for block isa signing.

For the ObjC isa encoding path, bypass the swift_isaMask inline masking
approach when the ObjCIsaPointers schema is active and use
object_getClass() instead. The mask-based extraction doesn't handle
PAC bits, producing a corrupted metadata pointer that crashes on
subsequent access.
Only sign method list pointers for class and category metadata, not
protocol metadata. The ObjC runtime's fixupProtocolMethodList uses
plain ldr (no auth) on protocol method lists, while fixupMethodList
authenticates class method lists with autda.

Add a !isBuildingProtocol() guard to the method list pointer signing
code path. Without this guard, Swift-emitted @objc protocol metadata
has signed method list pointers that the runtime attempts to
dereference without authentication, causing EXC_BAD_ACCESS during
protocol fixup at image load time.

Also switch the metaclass metadata builder in GenClass.cpp from using
ConstantStruct::get to ConstantInitBuilder, which enables proper
ptrauth signing of isa, superclass, and class_ro pointers in the
metaclass struct on arm64e (matching the GenMeta.cpp approach).
Swift IRGen's emitBlockHeader stored __NSConcreteStackBlock as the
block's isa without ptrauth signing. On arm64e, objc_msgSend
authenticates all isa pointers with autda(DA, blend(addr, 0x6AE1)).
An unsigned isa causes EXC_BAD_ACCESS (code=261) -- the canonical
PAC authentication failure.

Sign the isa using the ObjCIsaPointers schema (DA key, disc 0x6AE1,
address-diversified), matching Clang's EmitPointerAuthObjCISA behavior
in CGObjC.cpp. This fixes crashes when Swift @convention(block)
closures with captures are passed to ObjC APIs that subsequently send
messages to the block.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant