Skip to content

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:

  1. code has at least size bytes of executable memory
  2. 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:

  1. code now is a small trampoline of very specific form, user can't write arbitrary instructions there.
  2. ret now keeps a pointer to the trampoline used later by ffi_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 and HEAD, both have a problem.

Optional:

  • Operating System: linux
  • System Architecture: x86_64
Edited by Sergei Trofimovich
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information