启用Runtime加固签名后Mac App Bundle无法访问USB设备
Background
We've built a native cross-platform app that accesses USB card terminals via a closed-source library. The app is written in C/C++ with Gtk3 as the UI framework (no Xcode used), and depends on libraries like LIBMICROHTTPD, GNUTLS, JSONCPP. We also link against LIBASAN to address undefined behavior issues with macOS/CLANG.
The app runs fine as an App Bundle, and works normally when signed without the --options=runtime flag. However, enabling this flag (required for notarization) causes the app to start up but hang/loop during USB access.
Our Signing Workflow
# This first command is critical for notarization but causes the issue codesign -f -s "macosnewbie" carrier.app/Contents/MacOS/* --timestamp --options=runtime codesign -f -s "macosnewbie" carrier.app/Contents/libs/lib* --timestamp codesign -f -s "macosnewbie" carrier.app/Contents/libs/gdk-pixbuf-2.0/2.10.0/loaders/libpixbufloader-* --timestamp codesign -f -s "macosnewbie" carrier.app/Contents/Resources/* --timestamp codesign -f -s "macosnewbie" --options=runtime --entitlements entitlements.plist carrier.app --deep
Removing --options=runtime from the first command resolves the issue, but we can't skip it for notarization. We've also completed compression, notarization, and stapling:
xcrun altool --notarize-app --primary-bundle-id "19" -f carrier.zip -u "me@ourcompancy.de" xcrun stapler staple carrier.app
We can provide the contents of Info.plist, entitlements.plist, and the App Bundle directory structure, but can't create a minimal reproducible example due to the closed-source USB library. We're looking for troubleshooting directions.
Troubleshooting Directions
1. Audit Entitlements for USB Access Permissions
Runtime hardening (the runtime option) enforces stricter sandboxing rules even if you don't explicitly enable the sandbox. Double-check your entitlements.plist to ensure it includes the necessary permissions for USB access:
- Add the
com.apple.security.device.usbentitlement (you can specify allowed USB devices via their vendor/product IDs if needed for tighter restrictions) - Verify that any sandbox exemptions required by your closed-source library are included. For example, if the library uses low-level I/O operations, you might need
com.apple.security.sandbox.externalbsdor other niche exemptions.
2. Debug the Hang with System Logs & Instruments
- Monitor system logs in real-time while the app hangs: run
log stream --predicate 'process == "carrier"'in Terminal, or useConsole.appfiltered to your app. Look for sandbox denial messages, code signing errors, or USB subsystem logs (like entries fromIOUSBHostorIOUSBDevice). - Use Instruments to profile the stuck app: attach to the running process and check for blocked threads. Focus on calls to the closed-source USB library—runtime hardening might be blocking the library from accessing a critical resource, leading to an infinite loop or unresponsive I/O.
3. Check LIBASAN Compatibility with Runtime Hardening
LIBASAN (AddressSanitizer) can sometimes clash with macOS's runtime hardening features. Try temporarily disabling LIBASAN in your build to see if that resolves the hang. If it does, you have two paths forward:
- Build a dedicated release version without LIBASAN (since notarized apps are typically production builds, this is often acceptable)
- Investigate compiler flag adjustments or additional entitlements that might allow LIBASAN to work alongside runtime hardening.
4. Standardize Code Signing Flags Across All Components
Runtime hardening requires consistent signing flags across all executable code in the bundle. Try adding --options=runtime to your library signing commands as well, not just the main binary:
codesign -f -s "macosnewbie" carrier.app/Contents/libs/lib* --timestamp --options=runtime
Also, avoid over-reliance on --deep—it can cause inconsistent signing. Instead, sign each component explicitly with matching flags, then sign the bundle itself as the final step.
5. Isolate the Issue with a Minimal Test App
Create a stripped-down test app that only initializes and uses the closed-source USB library, then sign it with --options=runtime. If this reproduces the hang, it confirms the issue lies in the library's interaction with runtime hardening, rather than your full app's complex setup. This internal test can help you narrow down whether the problem is related to library loading, resource access, or something else.
6. Verify Dynamic Library Loading
Runtime hardening can alter how dynamic libraries are loaded and validated. Use otool -L on your main binary and the closed-source library to confirm all dependencies are correctly referenced and signed. Also, check if the library tries to load any plugins or additional binaries that aren't properly signed with runtime hardening flags.
内容的提问来源于stack exchange,提问作者Peter

