GHC's `allocateExec()` fails on libffi-3.4, corrupts memory.
Summary
GHC's allocateExec()
fails on libffi-3.4, corrupts memory.
The problem is observed on ghc
built against libffi-3.4-rc1
:
$ ghci
GHCi, version 8.10.5: https://www.haskell.org/ghc/ :? for help
ghc: freeHaskellFunctionPtr: not for me, guv! 0x7f0417a1efe8
ghc: freeHaskellFunctionPtr: not for me, guv! 0x7f0417a1efc8
Here ghc
tells us that freeHaskellFunctionPtr
sees corrupted
code memory where trampoline should be stored.
Details
That happens because ghc
has very strong assumptions about libffi
's
void * ret = ffi_closure_alloc (size_t size, void **code)
semantics:
-
code
has at leastsize
bytes of executable memory -
ret
data is not used by libffi
#if defined(linux_HOST_OS) || defined(netbsd_HOST_OS)
// On Linux we need to use libffi for allocating executable memory,
// because it knows how to work around the restrictions put in place
// by SELinux. The same goes for NetBSD where it is prohibited to
// mark a page mapping both writable and executable at the same time.
AdjustorWritable allocateExec (W_ bytes, AdjustorExecutable *exec_ret)
{
void **ret, **exec;
ACQUIRE_SM_LOCK;
ret = ffi_closure_alloc (sizeof(void *) + (size_t)bytes, (void**)&exec);
RELEASE_SM_LOCK;
if (ret == NULL) return ret;
/// breaks [1.] assumption: writes into libffi's space:
*ret = ret; // save the address of the writable mapping, for freeExec().
*exec_ret = exec + 1;
/// breaks [2.] assumption:
return (ret + 1);
}
Starting from https://github.com/libffi/libffi/commit/9ba559217bea0803263a9a9a0bafcf9203606f5b
libffi-3.4
neither of [1.]
or [2.]
is true:
-
code
now is a small trampoline of very specific form, user can't write arbitrary instructions there. -
ret
now keeps a pointer to the trampoline used later byffi_closure_free()
.
I think ghc needs to implement it's own form of allocateExec()
and should stop
relying on libffi
's internals to provide executable scratch space.
workaround
As a workaround https://github.com/libffi/libffi/pull/647 adds --disable-exec-static-tramp
build-time libffi flag to allow ghc
to limp along for a while.
Environment
- GHC version used:
8.10.5
andHEAD
, both have a problem.
Optional:
- Operating System: linux
- System Architecture: x86_64