We are seeing a low-volume but persistent crash on iOS 26 that surfaces inside Foundation's base64 encoder, and we would like to see if and how we could prevent that crash
- (nonnull NSString *)pnd_UTF8Base64 {
NSData *encodedData = [self dataUsingEncoding:NSUTF8StringEncoding];
if (encodedData == nil || encodedData.length == 0) {
return @"";
}
NSData *safeData = [NSData dataWithBytes:encodedData.bytes length:encodedData.length];
return [safeData base64EncodedStringWithOptions:0];
}
Questions:
- Is our reading correct that _xzm_xzone_malloc_freelist_outlined indicates the freelist was ALREADY corrupted before this allocation (double-free / use-after-free / buffer overflow somewhere else), and that this base64 malloc is merely the first allocation to detect it (a 'canary'), not the cause?
- Did the iOS 26 xzone allocator add/strengthen freelist integrity checks such that latent corruption that previously went unnoticed now traps here?
- What is the recommended way to pin down the real corruptor in a shipping app where this reproduces only occasionally on end-user devices (MallocStackLogging, Guard Malloc / libgmalloc, MallocScribble, Address Sanitizer, MallocNanoZone settings)? Are any of these usable/meaningful against the xzone allocator?
- Is there any process-level or per-allocation API to validate heap integrity that is safe on the xzone allocator (e.g. malloc_zone_check behavior on iOS 26)?
Any guidance on interpreting this signature and isolating the root cause would be appreciated. Thanks.
1- Is our reading correct that _xzm_xzone_malloc_freelist_outlined indicates the freelist was ALREADY corrupted … ?
It’s hard to be 100% sure, but that’s very likely, yes.
2- Did the iOS 26 xzone allocator add/strengthen freelist integrity checks such that latent corruption that previously went unnoticed now traps here?
We regularly tweak the memory allocator to improve both performance and robustness, but there’s no guarantee you’re seeing this as the result of a memory allocator change. Memory corruption is the result of some part of the code relying on undefined behaviour, and that can manifest in a variety of different ways. So, this could be the result of a change in memory allocation patterns on iOS 26 that just happens to trigger this particular pathology.
3- What is the recommended way to pin down the real corruptor in a shipping app where this reproduces only occasionally on end-user devices (MallocStackLogging, Guard Malloc / libgmalloc, MallocScribble, Address Sanitizer, MallocNanoZone settings)? Are any of these usable/meaningful against the xzone allocator?
See this post about the standard memory debugging tools.
There are two broad categories of tools here:
- Development-time tools
- Deployment-time tools
The first includes things like zombies, ASan, and so on. They are things you turn on during development in the hope that you can make the problem more reproducible, and hence debuggable. In a case like this I recommend that you introduce them as part of your testing. That is, enable them for your tests — both unit tests and integration tests — and see if they turn up anything of interest. If not, set up a long-running soak test to see if you can trigger it that way.
The second category really only has one option: Memory Integrity Enforcement. On the plus side, that technology is super cool. I strongly recommend that you enable it. Start with enabling it in development and, if that doesn’t find the problem, ship a build with it enabled. As more and more users get MTE-capable hardware, it’ll become more and more effective at finding problems like this.
4- Is there any process-level or per-allocation API to validate heap integrity that is safe on the xzone allocator … ?
malloc_zone_check is API, so there’s nothing stopping you calling it. However, I doubt it’ll yield useful results, because of the trade-off between comprehensiveness and performance (if malloc_zone_check is comprehensive, you can’t call it often enough to be useful, and vice versa).
Share and Enjoy
—
Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"