Code signing breaks conda environment

Hi,

I am trying to release a small application which bundles a conda environment and a python script. I am using Platypus to turn it into a .app, and I include all necessary resources (libraries, binaries etc) inside the Resources directory. My application works correctly before code signing, and is portable between machines (so I don't think it is the case that the conda environment is missing something). However, after signing, it crashes when it runs one of the programs within the conda environment.

I am first signing all .so, .dylib and all files in conda_env/bin as follows:

# Within the conda environment directory in Resources 
find bin -type f | xargs -n1 codesign -f -o runtime --timestamp --sign "Developer ID Application: Whatever (123456789)"

find . -name "*.dylib" -o -name "*.so" -type f | xargs -n1 codesign -f -o runtime --timestamp --sign "Developer ID Application: Whatever (123456789)"

I am then signing the .app itself

codesign -f -o runtime --timestamp --sign "Developer ID Application: Whatever (123456789)" my_app.app

Finally, I convert it into a .dmg (with appdmg) and sign that.

codesign -f --sign "Developer ID Application: Whatever (123456789)" --timestamp my_app.dmg

I submit to the notary service, which succeeds, and then I staple the ticket to the .dmg:

xcrun notarytool submit my_app.dmg --keychain-profile my_notarytool_keychain_id --wait

xcrun stapler staple my_app.dmg

spcl is happy with the signed .app and .dmg and accepts them both.

spctl -a -vv my_app.app

# my_app.app: accepted
# source=Notarized Developer ID
# origin=Whatever (123456789)

spctl -a -vv -t install my_app.dmg

# my_app.dmg: accepted
# source=Notarized Developer ID
# origin=Whatever (123456789)

I have a valid Developer Application ID. All good, right?

Except, during execution, the signed .app crashes. When I look in the Console, the error log always looks similar - something like:

Exception Type:        EXC_BAD_ACCESS (SIGKILL (Code Signature Invalid))
Termination Reason:    Namespace CODESIGNING, Code 2 Invalid Page

Thread 0 Crashed::  Dispatch queue: com.apple.main-thread
0   libsystem_platform.dylib      	       0x186e15848 sys_icache_invalidate + 40
1   libllvmlite.dylib             	       0x2a022f8e8 llvm::sys::Memory::protectMappedMemory(llvm::sys::MemoryBlock const&, unsigned int) + 384
2   libllvmlite.dylib             	       0x29d765528 LLVMPY_TryAllocateExecutableMemory + 92
3   libffi.8.dylib                	       0x103abc04c ffi_call_SYSV + 76
etc

I think all the .dylib, .so, and binaries are signed in my codesign scripts, except for the libsystem_platform.dylib mentioned in the first line of the log. Could this be the problem?

How can I find if I am not signing something that is being used? Are there other types of files that I should be signing that I am missing?

I've been trying to fix this for several days and I feel I have tried everything (constructing the conda env in different ways, signing in different ways, e.g. with/without --deep, with/without signing each type of library/binary) to no avail... Any help would be greatly appreciated!

All the best, George

Answered by DTS Engineer in 789867022

The issue here is that the notary service requires that you enable the hardened runtime and the hardened runtime enables a variety of security enhancements by default. One of those prevents you from generating executable code on the fly, and hence this crash.

It is possible to opt out of this enhanced security via entitlements. The three entitlements of interest here are:

  • com.apple.security.cs.allow-jit

  • com.apple.security.cs.allow-unsigned-executable-memory

  • com.apple.security.cs.disable-executable-page-protection

These are listed from most-to-least secure. The first one is best, but it requires that your code use the MAP_JIT technique. I’m not sure if your third-party tooling has been updated for that. If not, use the second.

The third is generally not needed. It only has an effect on Intel. On Apple silicon, it behaves the same as the second.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

The issue here is that the notary service requires that you enable the hardened runtime and the hardened runtime enables a variety of security enhancements by default. One of those prevents you from generating executable code on the fly, and hence this crash.

It is possible to opt out of this enhanced security via entitlements. The three entitlements of interest here are:

  • com.apple.security.cs.allow-jit

  • com.apple.security.cs.allow-unsigned-executable-memory

  • com.apple.security.cs.disable-executable-page-protection

These are listed from most-to-least secure. The first one is best, but it requires that your code use the MAP_JIT technique. I’m not sure if your third-party tooling has been updated for that. If not, use the second.

The third is generally not needed. It only has an effect on Intel. On Apple silicon, it behaves the same as the second.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Code signing breaks conda environment
 
 
Q