Difficulty enabling Apple's MIE
14 January 2026
I previously blogged a demo in which I activated Apple's new hardware memory tagging feature, part of Memory Integrity Enforcement. This could accurately detect at runtime when I had overflowed a buffer or caused a use-after-free. Very neat stuff. I emphasised that you had to go to the Scheme settings and turn on Hardware Memory Tagging under Diagnostics to see this work.
At first I didn't think too much about this Scheme step but I'm beginning to suspect there's a problem. I found Apple's video demonstrating MIE. This features a test app with a deliberate use-after-free bug. In the video they run it three times:
- Default project: UAF is exploited (presumably simulated) and the app keeps running normally.
- Add Enhanced Security and ensure memory tagging is enabled: the tag mismatch is detected and the app crashes messily in the debugger with
EXC_ARM_MTE_TAG_FAULT. - Enable Hardware Memory Tagging under Scheme Diagnostics: additional debugging information is included and Xcode reports a nice clean error that's easier to fix.
The trouble is, while I reliably catch bugs where they occur with (3) I cannot for the life of me get it to fail in configuration (2). Certainly I can make the app crash, but only through the usual effects of memory corruption rather than because MTE detected something wrong with the tags. I've resorted to trying rather extreme manipulation to get it to fail:
#define PTR_TAG(p) ((unsigned)(((uintptr_t)(p) >> 56) & 0xF))
// Guarantee a tag mismatch by modifying p2's tag
unsigned old_tag = PTR_TAG(p2);
unsigned new_tag = (old_tag + 1) % 16;
uintptr_t addr_only = (uintptr_t)p2 & 0x00FFFFFFFFFFFFFF;
void *p2_bad_tag = (void *)(addr_only | ((uintptr_t)new_tag << 56));
os_log(OS_LOG_DEFAULT, "Original tag: %u, Modified tag: %u\n",
PTR_TAG(p2),
PTR_TAG(p2_bad_tag));
os_log(OS_LOG_DEFAULT, "Attempting read with guaranteed bad tag...\n");
volatile char c2 = *(volatile char *)p2_bad_tag; // Should this crash?
os_log(OS_LOG_DEFAULT, "Read succeeded! Value: %d\n", c2);
This shows output like the following. It demonstrates that tags are being assigned but it simply doesn't care if you dereference through a pointer with the wrong tag.
Original tag: 14, Modified tag: 15 Attempting read with guaranteed bad tag... Read succeeded! Value: 31
Still unsure whether this was a case of PEBKAC, I posted on the developer forums. Just today I received a reply from Quinn, who gave an intriguing response that doesn't really explain anything yet. He said that hard mode is not supported in third party apps yet (like mine), but this is not documented anywhere, and he's going to find out why. "Hard mode" is the opposite of "soft mode", a setting you can enable to turn any tag mismatches into logged errors rather than crashing your app. This is intended to let you find any problems in your code before you enable strict enforcement.
It is implied in his message that soft mode should be working however I have never got that to do anything either. As I explained on the forum, I haven't been able to get it to emit any logs or crash reports—it appears just as dead as when I have it in hard mode.
This is all quite odd and fascinating. Why wouldn't they support hard mode when the entire point of the feature is that the app gets terminated when corruption occurs? Why isn't soft mode working for me? Given that Quinn didn't immediately point out any flaws I'm assuming my test scenario is legitimate. I look forward to learning more. If you're reading this and you have any insights (or speculation), please do drop me an email.
Serious Computer Business Blog by Thomas Karpiniec
Posts RSS, Atom