On July 30, 2026, I reported two defects in com.apple.CodeSigningHelper.xpc, an XPC service inside Security.framework. An application with no entitlements, no TCC grant and no administrator rights asked the helper for its own bundle's Info.plist and received the contents of a file it had just been refused open(2) on. The same request works for /private/etc/sudoers, /private/etc/master.passwd and a path behind Full Disk Access.
The helper runs as root, holds com.apple.private.tcc.allow for kTCCServiceSystemPolicyAllFiles, and runs under a sandbox profile of (deny default) plus an unqualified (allow file-read*). It performs no check of the calling process. The binary imports none of xpc_connection_get_audit_token, xpc_dictionary_get_audit_token, xpc_connection_get_pid or xpc_copy_entitlement_for_token, and it contains no Objective-C, so the import table is the whole story.
The symlink the header says to refuse
BundleDiskRep::loadRegularFile follows a symlink at <bundle>/Contents/Info.plist. checkPlainFile does detect the symlink, but it routes the result into recordStrictError, which only strict validation consults. The shipping SDK header states the intent that the code does not enforce:
CSCommon.h
errSecCSRegularFile = -67015
/* the main executable or Info.plist must be a regular file (no symlinks, etc.) */
bundlediskrep.cpp line 303 says the function "makes sure that it is a regular file and not a symlink". On the non-strict path, it does not.
The hash compared against the wrong slot
The caller must supply a hash of the component it is asking for. copyComponent passes the slot constant to getSlot() without negating it, so the caller-supplied hash is compared against an ordinary code-page hash instead of the Info.plist special slot. cdInfoSlot is 1, and getSlot() performs no internal negation. codedirectory.h states that the hash array "covers elements in the range [-nSpecialSlots .. nCodeSlots-1]. Non-negative indices denote pages of the main executable. Negative indices indicate 'special' hashes."
The correct sibling is in the same file and the same class. In Security-61901.120.67, SecStaticCode::component() negates:
codeDirectory()->validateSlot(CFDataGetBytePtr(data), CFDataGetLength(data), -slot, false)
while SecStaticCode::copyComponent() does not:
const void *slotHash = cd->getSlot(slot, false);
if (cd->hashSize != CFDataGetLength(hash) ||
0 != memcmp(slotHash, CFDataGetBytePtr(hash), cd->hashSize)) {
The consequence is that SLOT[-1], the SHA-256 of the sealed Info.plist and the value SecCodePriv.h documents as "the native slot hash for the slot requested", is rejected by the shipping helper. SLOT[+1] is accepted, and it is printable from a non-running binary with codesign -d --verbose=6. The hash was meant to tie the request to a sealed plist. Instead it ties the request to a page of the caller's own executable, which the caller can read.
Putting the two together
A caller names its own pid, points its own Contents/Info.plist at any absolute path, supplies a hash read from its own binary, and receives the file's bytes. The same SPI called in-process is correct: SecCodeCopyComponent(code, 1, NULL) at uid 501 on the identical bundle with the identical symlink returns NULL for a file the process cannot read. The symlink alone grants nothing. The helper's privilege is the difference.
The proof is an unentitled, ad-hoc signed app with a bundle identifier that has never been granted anything. It creates both files it reads, with a per-run nonce so the returned bytes cannot be pre-staged, and restores its own Info.plist byte-for-byte before exiting. It draws no conclusion and prints no verdict. Verbatim from one run on macOS Tahoe 26.6 (25G72), uid 501, no TCC grant, SIP enabled:
readable fixture, bytes returned by helper vs stat ....... 62 / 62
readable fixture, returned bytes carry this run's nonce .. yes
mode-000 fixture, this process open(2) ................... refused
mode-000 fixture, bytes returned by helper vs stat ....... 62 / 62
mode-000 fixture, returned bytes carry this run's nonce .. yes
request with zeroed infohash ............................. no data returned
request with slot -1 digest (SHA-256 of sealed Info.plist) no data returned
The third and fourth lines are the report. The process was refused open(2) on that file and holds its full contents. The last two lines are the negative controls: a zeroed hash and the correct slot -1 digest both return nothing, so the helper is checking something, and what it checks is the wrong thing.
Measured separately with a command-line harness of the same mechanism, each target denied to the same process in the same run and returned byte-complete against stat:
/private/etc/sudoers 56 / 56
/private/etc/master.passwd 10004 / 10004
a Full-Disk-Access-gated path 1709 / 1709
60 of 60 successes across three targets and twenty iterations. The helper writes no log entry for a successful read.
What it does not do
It is read only. The wire interface returns bytes and writes nothing, so there is no path from this to modifying the TCC database. The App Sandbox blocks it: a sandboxed app reaches the service and gets a reply, but cannot unlink its own Contents/Info.plist, so the symlink cannot be planted from inside a container. A possible out-of-bounds memcmp when the positive slot is absent was identified and deliberately not triggered, to avoid crashing a root service.
The impact is that an application with no entitlements, no TCC grant and no administrator rights reads the contents of any file readable by root, including files protected by POSIX mode and files behind Full Disk Access. One launch, no further user interaction, no prompt, no log record.
Report status
I reported this under OE1106920714159 on July 30, 2026. Apple reproduced it and addressed it in iOS 27 and iPadOS 27 and macOS Golden Gate 27, and said that my credit had missed the initial advisory publication and would be added. Apple's Security Research portal now records the report under CVE-2026-84508. As of October 6, 2026 that entry has not yet appeared in the published security content for those releases, and the CVE record is not yet public at NVD or MITRE. This page will be updated with the advisory link when it is.
A privileged helper that validates the thing it was handed, but not the process that handed it, is a confused deputy with a checksum.