Another library load fails with hardened runtime

Dear Apple folks,


we develop a C++ application that depends on another 3rd party

library which is installed under /Library/frameworks.


This library is optionally loaded dynamically at runtime by a call to

dlopen().


So far, we have signed our application successfully with our

Developer ID Application certificate.

For notarization, we have enabled the hardened runtime option in our

application signature process.

With hardened runtime enabled, this library loading does not work

anymore on MacOS 10.14 and 10.15.


Following the Apple documentation and several developer forum threads

I have already checked the following constraints:


* The application is signed using a secure timestamp.

* The application is signed without using the option 'deep'.

* The 3rd party lib is signed by it's vendor with their Developer ID

Application certificate.

* The lib's deployment target is MacOS 10.9 or higher

(LC_VERSION_MIN_MACOSX).

* The lib's installation name is fixed under /Library/Frameworks.

(LC_ID_DYLIB, calling otool -l on the lib, is this correct?).

* As the lib uses another certificate than the application, the

application enables the disable-library-validation entitlement.

This has been checked via codesign -d --entitlements :- on the app.

* Enabling all hardened runtime related entitlements for test purposes

does not help.


To be clear again:

* A signed application without option 'hardened runtime' loads the lib

successfully.

* A signed application with option 'hardened runtime' enabled and all

related entitlements enabled fails loading the lib.


I do not see any relevant error messages so far:

* If the application is executed from a terminal, it does not produce

any error output.

* The system log does not produce related messages (or I do not see them).

* I do not see any dyld related messages (Where should I find them?).

* codesign --verify --verbose=4 APP_BUNDLE says 'valid on disk'.

* spctl --verbose?4 --assess --type execute APP_BUNDLE says 'accepted'.


So, how can I gather more information from the system about why it

forbids to load the lib?

Do you have any other ideas for me?


Thanks,

-Markus

Accepted Reply

OK, got it.


Solution:


Additionally to the constraints listed above, set an absolute path in the

field LC_LOAD_DYLIB of the wrapper lib.


Thanks,

Markus

Replies

how can I gather more information from the system about why it forbids to load the lib?

In your app, call

dlerror
after the failure to what it thinks the problem is. Then add a log point [1] immediately after the failure. Then reproduce the problem and look in the system log immediately before your log point. In many cases the kernel or
syspolicyd
will log info about why something failed.

Share and Enjoy

Quinn “The Eskimo!”
Apple Developer Relations, Developer Technical Support, Core OS/Hardware

let myEmail = "eskimo" + "1" + "@apple.com"

[1] If you’re already using the system log, use that. Otherwise it’s fine to use

NSLog
because this is temporary code for a test.

Ok, I am getting deeper into this ...


New info:

* dlerror() tells me 'image not found'.

* I still do not see any related entries within system log.

Especially syspolicyd keeps calm.

Please see the attached log.


I have currently 2 directions into which I investigate:

1. Heterogenous IDE and toolchain setups for different modules

2. Some dynamic link time resultion problem.

Perhaps MacOS searches for dylibs more restrictive with hardened

runtime enabled.


1. IDE and toolchain

Historically, we compile our application and some of it's libs using different

toolchains.

* The application itself is recompiled frequently using MacOS 10.14 and

XCode 11.

* Some of our wrapper libs are not compiled together with our

application. These binaries have been compiled using older versions

of MacOS and XCode (still need to investigate the exact versions).

For the one wrapper dylib that fails loading the 3rd party lib from

/Libraries/Frameworks, otool -l shows

LC_VERSION_MIN_MACOSX Version 10.7, SDK 10.12

This wrapper lib gets deployed within our bundle.

Therefore it also gets signed with our Developer ID Application

certificate.


Finally our bundle and it's dependancies look like this:


* BUNDLE/Contents/MacOS/APPLICATION

LC_VERSION_MIN_MACOSX Version 10.10, SDK 10.15

Signed with -o runtime + entitlements, using our certificate


* BUNDLE/Contents/Framework/WRAPPER_DYLIB

LC_VERSION_MIN_MACOSX Version 10.7, SDK 10.12

Signed, using our certificate


* /Libraries/Frameworks/FRAMEWORK_NAME.framework/Versions/A/3RD_PARTY_DYLIB

LC_VERSION_MIN_MACOSX Version 10.9, SDK 10.12

Signed, using 3rd party certificate


Q: In a chain like this, do all the bundle's internal modules need an

LC_VERSION_MIN_MACOSX >= 10.9 or only the lib to be loaded from

outside of the bundle?


Q: We need hardened runtime and entitlements only for the application,

not for the libraries, is this correct?



2. Dynamic link time resultion


The other thing I see is that the wrapper lib contains a field

LC_LOAD_DYLIB with an incomplete (and therefore relative?) path:

"FRAMEWORK_NAME.framework/Versions/A/3RD_PARTY_LIB_NAME"

No @rpath or similar prefix variables, but also no absolute path.


Q: Running with hardened runtime and loading libs from

/Libraries/Frameworks, do we need complete and absolute paths in

LC_LOAD_DYLIB?


Thanks,

-Markus

OK, got it.


Solution:


Additionally to the constraints listed above, set an absolute path in the

field LC_LOAD_DYLIB of the wrapper lib.


Thanks,

Markus