Using codesign to re-sign stripped executable fails, codesign_allocate is killed upon calling mmap to map in executable to be signed

Originator:mark
Number:rdar://FB8735191 Date Originated:2020-09-23
Status:Potential fix identified - in Xcode 12.2 Resolved:
Product:Xcode Product Version:12.2b1 12B5018i
Classification:Application Crash Reproducible:Always
 
As of macOS 11.0db6 20A5364e, all arm64 code must be signed. Starting with Xcode 12.0b4 12A8179i, ld64 adds an ad-hoc “linker-signed” code signature by default when linking arm64 code. More information at https://developer.apple.com/documentation/macos-release-notes/macos-big-sur-11-universal-apps-beta-release-notes#Code-Signing.

Some operations, like strip, modify executables such that the signature becomes invalid. In the past, on x86_64, such operations were normally done prior to signing, and since signatures were not universally required system-wide, this posed no problem. Now, these operations will invalidate even linker-signed code, causing it to be rejected by the system with SIGKILL in xnu/bsd/kern/kern_cs.c cs_invalid_page.

This situation is release-noted at https://developer.apple.com/documentation/xcode-release-notes/xcode-12_2-beta-release-notes#Apple-Clang-Compiler, which recommends running codesign --sign after running tools such as strip.

If an attempt to execute the code to be re-signed is made before it is re-signed, while its signature is invalid, code signing subsequently fails.

To demonstrate, I will build a simple no-op program (attached), strip it (invalidating its linker-signed code signature), attempt to execute it (fails, as designed), and then attempt to sign it.

mark@arm-and-hammer zsh% cat t_noop.c
int main(int argc, char* argv[]) { return 0; }
mark@arm-and-hammer zsh% clang -Wall -Werror t_noop.c -o t_noop
mark@arm-and-hammer zsh% ./t_noop
mark@arm-and-hammer zsh% strip t_noop
/Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip: warning: changes being made to the file will invalidate the code signature in: /Users/mark/t_noop
mark@arm-and-hammer zsh% ./t_noop
zsh: killed     ./t_noop
mark@arm-and-hammer zsh% codesign --sign=- t_noop
t_noop: the codesign_allocate helper tool cannot be found or used
mark@arm-and-hammer zsh% codesign --verify --verbose t_noop
t_noop: invalid signature (code or signature have been modified)
In architecture: arm64
mark@arm-and-hammer zsh% ./t_noop
zsh: killed     ./t_noop

If no attempt to execute t_noop is made in between the “strip” and “codesign --sign”, re-signing completes successfully.

The message “the codesign_allocate helper tool cannot be found or used” comes from codesign, indicating errSecCSHelperFailed, is being shown when codesign_allocate does not complete successfully but is instead killed by SIGKILL. This appears in the system log:

2020-09-23 20:34:18.105692-0400 0x7070     Default     0x0                  709
   0    ReportCrash: Parsing corpse data for pid 1843
2020-09-23 20:34:18.105787-0400 0x7070     Default     0x0                  709
   0    ReportCrash: Parsing corpse data for process codesign_allocat [pid 1843]
2020-09-23 20:34:18.105228-0400 0x7085     Default     0x0                  0
   0    kernel: CODE SIGNING: cs_invalid_page(0x10262c000): p=1843[codesign_allocat] final status 0x23004200, denying page sending SIGKILL
2020-09-23 20:34:18.105274-0400 0x7085     Default     0x0                  0
   0    kernel: CODE SIGNING: process 1843[codesign_allocat]: rejecting invalid page at address 0x10262c000 from offset 0x0 in file "…/t_noop" (cs_mtime:1600907652.371077488 == mtime:1600907652.371077488) (signed:1 validated:1 tainted:1 nx:0 wpmapped:0 dirty:0 depth:0)
[…]
2020-09-23 20:34:18.722958-0400 0x7070     Default     0x0                  709
   0    ReportCrash: (CrashReporterSupport) Saved crash report for codesign_allocate[1843] version 0.3 to codesign_allocate_2020-09-23-203418_arm-and-hammer.crash

/Library/Logs/DiagnosticReports/codesign_allocate_2020-09-23-203418_arm-and-hammer.crash shows, excerpted (the full version is attached):

Process:               codesign_allocate [1843]
Path:                  /Applications/Xcode-beta.app/Contents/Developer/Toolchain
s/XcodeDefault.xctoolchain/usr/bin/codesign_allocate
Identifier:            codesign_allocate
Version:               0.3
Code Type:             ARM-64 (Native)
Parent Process:        codesign [1842]
[…]
Exception Type:        EXC_BAD_ACCESS (Code Signature Invalid)
Exception Codes:       0x0000000000000032, 0x000000010262c000
Exception Note:        EXC_CORPSE_NOTIFY

Termination Reason:    Namespace CODESIGNING, Code 0x2
[…]
VM Regions Near 0x10262c000:
    MALLOC metadata               102628000-10262c000        [   16K] r--/rwx SM=PRV  
--> mapped file                   10262c000-102634000        [   32K] rw-/rwx SM=COW  Object_id=f2c581cf
    __TEXT                        102794000-102810000        [  496K] r-x/r-x SM=COW  /usr/lib/dyld

Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0   codesign_allocate                   0x00000001025ab7a8 0x1025a0000 + 47016
1   codesign_allocate                   0x00000001025ab790 0x1025a0000 + 46992
2   codesign_allocate                   0x00000001025a9b84 0x1025a0000 + 39812
3   codesign_allocate                   0x00000001025a7f24 0x1025a0000 + 32548
4   codesign_allocate                   0x00000001025a3818 0x1025a0000 + 14360
5   libdyld.dylib                       0x0000000182641418 start + 4
[…]
Binary Images:
       0x1025a0000 -        0x1025c3fff +codesign_allocate (0.3) <D627D9D9-DE31-3721-B195-9FB789BFF85C> /Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/codesign_allocate

This is happening in codesign_allocate after it has called mmap to map in the executable to be re-signed, upon first access to a page that the system has already decided is invalid for code signing purposes.

Any program that attempts to access an executable with a code signature broken in this way will be affected. I am attaching a test program, t_mmap, that innocuously opens a file as data, memory-maps it, and attempts to read from it via that mapping.

mark@arm-and-hammer zsh% clang -Wall -Werror t_mmap.c -o t_mmap
mark@arm-and-hammer zsh% clang -Wall -Werror t_noop.c -o t_noop
mark@arm-and-hammer zsh% ./t_mmap t_noop
mark@arm-and-hammer zsh% strip t_noop
/Applications/Xcode-beta.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/strip: warning: changes being made to the file will invalidate the code signature in: /Users/mark/t_noop
mark@arm-and-hammer zsh% ./t_noop
zsh: killed     ./t_noop
mark@arm-and-hammer zsh% ./t_mmap t_noop
zsh: killed     ./t_mmap t_noop

This access should never have been forbidden. Code signing “kill” semantics are being applied inappropriately to “code” that is not actually being treated as code but as data.

The treatment as code is inconsistent, as it requires that the system have (recently?) treated the file with the broken code signature as code and registered the problem via cs_invalid_page. If no attempt to execute the page in question had been made, the attempt to access it via memory mapping will succeed, as expected.

I have reproduced this on macOS 11.0db8 20A5374i on arm64. I have also reproduced it on macOS 10.15.6 19G2021 on x86_64, but only when the program calling mmap has “kill” semantics enabled, requested with codesign --sign --options=kill. (“runtime” includes “kill” semantics and works as well; “hard” also causes the program to crash, but with SIGBUS instead of SIGKILL, making it potentially recoverable via user-space fault handler, but regardless, it’s inappropriate to deny access when it is not being treated as code.) The “codesign” test case, which fails on arm64, does not fail on x86_64 because codesign_allocate has none of these code signing flags:

mark@sweet16 zsh% codesign --display --verbose --arch=x86_64 $(xcrun --find codesign_allocate) 2>&1 | grep flags=
CodeDirectory v=20200 size=1481 flags=0x0(none) hashes=41+2 location=embedded

On arm64, the code signature flags are irrelevant for these purposes, as the system always enforces “kill” semantics.

Workaround: the code signature is attached to the vnode, so force a new vnode such as by, making a copy of the file.

mark@arm-and-hammer zsh% codesign --sign=- t_noop
t_noop: the codesign_allocate helper tool cannot be found or used
mark@arm-and-hammer zsh% cp t_noop t_noop.new
mark@arm-and-hammer zsh% mv t_noop.new t_noop
mark@arm-and-hammer zsh% codesign --sign=- t_noop
mark@arm-and-hammer zsh% codesign --verify --verbose t_noop
t_noop: valid on disk
t_noop: satisfies its Designated Requirement
mark@arm-and-hammer zsh% ./t_noop

Or, in my case, I delegated responsibility for the “strip” to ld64 with -s:

mark@arm-and-hammer zsh% clang -Wall -Werror t_noop.c -o t_noop -Wl,-s
ld: warning: option -s is obsolete and being ignored
mark@arm-and-hammer zsh% codesign --verify --verbose t_noop
t_noop: valid on disk
t_noop: satisfies its Designated Requirement
mark@arm-and-hammer zsh% ./t_noop

(ld64 claims that -s is obsolete and ignored, but it’s just not true.)

In this test, the OS (including xnu and the codesign tool) is 11.0db8 20A5374i, and Xcode (including clang, ld64, and codesign_allocate) is 12.2b1 12B5018i.

Comments

t_noop.c.gz.base64

H4sIAB5icl8CA8vMK1HITczM0wAxEovSk3UUkjMSi7RA7LLoWE2FaoWi1JLSojwFA2uFWi4AVxOmuC 8AAAA=

t_mmap.c.gz.base64

H4sIAMvua18CA4RQBa7bQBAVg0/xys5XWPCZmZnB2qyxstfWQpmu0ev1JN0JQyE4HnhUq4GnTESoXL M0tb+BlLmE9rKMFVWOSt6tHedFInhq/AALdqcaLw01Qi50OtpS2k+EnuzlE600aY31PqqapRSTXaXZ GKQRiYWgnmPpkLFEuFQwGfEyeMzkFNXv7h9L+OwASQiXZni2iGanBYSFtDeha5ECKct4bhSLgjm8Ul gomI6XHsTzcgem/liab9/IQBspsHmze+Ftre4eXJ5t0uCr4wA8F0p3yekei53jxuM8jUlf6NtmXgTC pYUyjr2zjeOjg1tCb4u0Cwuo9xQGUrrDVFYjHT8v9TmVloZrUEJQrT4KPVusMl6rVuk/iO3lIUgCUK 2q0p5KPgX2uDF0/GHiOkkDJAo6z6EylqZDSMZanvH0FDJWFAFZz2zlHl0eHJQxoCjj5Oz4wjvbXN3A l059fbZ7YdEPV0+8k7Pdq1V6IDv1flA9yMX2EunZ3PiHSSIe88jTXAU2pP/l096bdAWORUx1ZNBMSS Z8l5fmgVoNb43SUDl0zDQS/evHTwWRaxhhVOA7QM+GERRJB2Y4lP+posMRS7+HnDKDQ52dXYODrblq uQCRF75a6gMAAA==


Please note: Reports posted here will not necessarily be seen by Apple. All problems should be submitted at bugreport.apple.com before they are posted here. Please only post information for Radars that you have filed yourself, and please do not include Apple confidential information in your posts. Thank you!