This is a recap of the Q&A from the Meet with Apple activity Fortify your app: Essential strategies to strengthen security. If you attended the event and asked questions, thank you for coming and participating! If you weren’t able to join us live we hope this recap is useful.
Memory Integrity Enforcement (MTE)
What is Memory Integrity Enforcement and which devices support it?
Memory Integrity Enforcement is supported on A19, A19 Pro, M5, M5 Pro, and M5 Max chips, which power iPhone 17e, the new MacBook Air (M5), and the new MacBook Pro (M5 Pro or M5 Max). Starting in the 26.4 OS versions, applications that enable MTE (checked-allocations) as part of Enhanced Security will also run with MTE enabled in the simulator when running on macOS hardware that supports MTE.
How can I use Memory Integrity Enforcement with third-party SDKs?
Third-party SDKs linked into your app will generally use the system allocator automatically and benefit from Memory Integrity Enforcement automatically. If there are memory corruption bugs in those SDKs that Memory Integrity Enforcement features like MTE detect and turn into crashes, you'll want to work with the developers of those SDKs to have them fix the underlying bugs. You could use MTE soft mode to avoid having those memory corruptions crash your app while you wait for fixes from the developers, at the cost of the relative reduction in security that entails.
Why does my app crash on launch with MTE enabled, with tags showing as 0?
Tag-check violations where the ltag (logical tag) is 0 and the atag (actual tag) is non-zero can be caused by code patterns that strip the high bits that the ltag is stored in and fail to restore them before use. Additionally, arm64 binaries produced by older versions of clang may have issues where the tag is incorrectly stripped from the pointer. Recompiling the binary with a recent compiler should remediate the issue.
Can I use Memory Integrity Enforcement with older Swift versions?
Yes, Memory Integrity Enforcement can be used with any Swift version.
Pointer Authentication (PAC)
How does Pointer Authentication work and why is it opt-in?
PAC is an opt-in feature because although adopting PAC is frequently as easy as turning on the compiler flag, some software is not trivially compatible. For example, while it mostly works in arm64 to memcpy a C++ object, this is invalid and generates fatal exceptions in arm64e. Additionally, PAC is a compile time change as it requires different instructions throughout the program.
Pointer authentication makes it more difficult to create a pointer (from an integer) or to modify an existing pointer. This complements technologies such as MTE (which can catch many bound and lifetime errors) and typed allocation (which mitigates the effects of memory re-use).
Where are the cryptographic keys for Pointer Authentication stored?
The keys used for generating PAC signatures are stored in the CPU itself as specified by the ARM architecture. These keys are ephemeral and can change across process launches and boots, depending on which PAC key is used. The signatures are, however, stored in the upper bits of the pointer itself.
How does Pointer Authentication work with Objective-C method swizzling?
When you use the functions provided by the ObjC runtime, they ensure that any necessary pointer signing is correctly handled.
What deployment targets and OS versions support Pointer Authentication?
PAC is tied to the arm64e architecture. arm64e is first supported in iOS 17.4, and generally supported starting with iOS 26 and macOS 26. Universal binaries can be built for arm64e + arm64, and arm64 will be used when arm64e isn't supported. When building the universal binary, both architectures can be compiled for an older deployment target, but keep in mind that arm64e will only be used on newer iOS.
How do I enable Pointer Authentication in modular apps?
arm64e is indeed required, and every target that contributes binary code that's linked or dynamically loaded into an app does need to have arm64e added as an architecture. When enabling the Enhanced Security capability, Xcode adds the ENABLE_POINTER_AUTHENTICATION build setting (that adds arm64e) as needed, but you may need to add that separately as well.
Bounds Safety and Annotations
How do bounds safety checks work in Clang?
With -fbounds-safety enabled Clang will emit bounds checks wherever pointers are dereferenced or reassigned (exception: assigning to __bidi_indexable does not trigger a bounds check, since __bidi_indexable can track the fact that the pointer is out of bounds and defer the bounds check). If the bounds check fails the program will jump to an instruction that traps the process. Clang uses a combination of static analysis and runtime checks to enforce that pointer bounds are respected.
How can I work with libraries that don't have bounds annotations?
Forging safe pointers at the boundary (using __unsafe_forge_single etc.) is the recommended approach when interoperating with libraries that do not have bounds annotations, when you want to be explicit about the fact that you're interacting with unsafe code. This makes it easy to grep for "unsafe" in your code base when doing a security audit.
If you are confident that the API adheres to a bounds safe interface but simply lacks the annotations, you can redeclare the signature in your local header with added bounds annotations, like this:
//--- system_header.h
bar_t * /* implicitly __unsafe_indexable */ foo();
//--- project_header.h
#include <ptrcheck.h>
#include <system_header.h>
bar_t * __single foo();
How can I safely pass Swift data to C/C++ functions?
This is a great question! Automatically generated wrapper functions that safely unwrap Span types and pass along the pointer to C/C++ is a feature available since Xcode 26 when the experimental feature SafeInteropWrappers is enabled. This requires annotating std::span<T> parameters with __noescape, or pointer parameters with both __noescape and __counted_by/__sized_by, directly in the header or using API notes. Note that this is only safe if Swift can accurately track the lifetime of the unwrapped pointer, which is why the Span wrapper is not generated without the __noescape annotation.
Since this is an experimental feature with ongoing development, questions and feedback on the Swift forums are extra welcome to help us shape and stabilize this feature!
Continued in next post...