Creating function pointers from closures results in setExecutable runtime error on macOS 12+
Summary
Running a signed binary on macOS 12+ that creates a FunPtr
from a closure results in a runtime error that mentions setExecutable
.
According to https://www.securemac.com/news/arm-macs-faq, this will also be a problem in unsigned binaries when running on Apple Silicon(ARM).
Steps to reproduce
-
Create two files,
Main.hs
andbar.c
:-- Main.hs module Main where import Foreign.Ptr (FunPtr) foreign import ccall "bar" bar :: Callback -> Int main :: IO () main = do cb <- createFooCallback (foo 2) let res = bar cb print res type CallbackType = Int -> Int type Callback = FunPtr CallbackType foo :: Int -> Int -> Int foo = (+) foreign import ccall "wrapper" createFooCallback :: CallbackType -> IO Callback
// bar.c extern int (*fun_ptr)(int); int bar(int (*f)(int)) { return (*f)(3); }
-
Compile them:
ghc --make -main-is Main Main.hs bar.c
-
Sign the binary:
sudo codesign -f --options runtime -s DEVELOPER_ID Main
- The program runs as expected if this step is omitted.
- This step requires a developer signing certificate.
-
Run the signed binary:
./Main
Expected behavior
Expected:
$ ./Main
5
Actual:
$ ./Main
Main: internal error: setExecutable: failed to protect 0x0x42001fc000
(GHC version 8.10.4 for x86_64_apple_darwin)
Please report this as a GHC bug: https://www.haskell.org/ghc/reportabug
fish: Job 1, './Main' terminated by signal SIGABRT (Abort)
Extra notes
I believe #18376 (closed) is the root cause, and I created this new issue to draw attention to a use-case that triggers the bug. Hoping that it might be fixed :-)
We managed to work around the issue by allowing unsigned executable memory when signing: https://developer.apple.com/documentation/bundleresources/entitlements/com_apple_security_cs_allow-unsigned-executable-memory However, that is not an ideal solution.
Environment
- GHC version used: 8.10.4 and 8.10.7
Optional:
- Operating System: macOS 12.1
- System Architecture: Intel