Correctly use ffi_closure_alloc(), fixing mmap permissions error

This restores the reverted-commit ed8634d:

    Author: Dan Winship <danw@gnome.org>
    Date:   Mon Apr 5 14:05:52 2010 -0400

    g_callable_info_prepare_closure: handle mmap permissions error

    When SELinux or similar is active, a process may not be able to mmap()
    a segment that is both writable and executable, which would causing
    g_callable_info_prepare_closure() to fail. Libffi has a workaround for
    this problem though (it maps the same non-anonymous region twice, once
    writable and once executable, and returns two separate pointers to
    it), so use that.

But with an added fix, when using ffi_closure_alloc(), we need to use
ffi_prep_closure_loc() so we can pass in the executable address of the
trampoline separately from the writable address, rather than the
deprecated ffi_prep_closure().

https://bugzilla.gnome.org/show_bug.cgi?id=615105
This commit is contained in:
Owen W. Taylor 2010-04-07 17:48:52 -04:00
parent 6545640515
commit 9d2fd90f0a

View File

@ -297,6 +297,11 @@ g_function_invoker_destroy (GIFunctionInvoker *invoker)
g_free (invoker->cif.arg_types); g_free (invoker->cif.arg_types);
} }
typedef struct {
ffi_closure ffi_closure;
gpointer writable_self;
} GIClosureWrapper;
/** /**
* g_callable_info_prepare_closure: * g_callable_info_prepare_closure:
* @callable_info: a callable info from a typelib * @callable_info: a callable info from a typelib
@ -306,11 +311,8 @@ g_function_invoker_destroy (GIFunctionInvoker *invoker)
* *
* Prepares a callback for ffi invocation. * Prepares a callback for ffi invocation.
* *
* Note: this function requires the heap to be executable, which
* might not function properly on systems with SELinux enabled.
*
* Return value: the ffi_closure or NULL on error. * Return value: the ffi_closure or NULL on error.
* The return value should be freed by calling g_callable_info_prepare_closure(). * The return value should be freed by calling g_callable_info_free_closure().
*/ */
ffi_closure * ffi_closure *
g_callable_info_prepare_closure (GICallableInfo *callable_info, g_callable_info_prepare_closure (GICallableInfo *callable_info,
@ -318,21 +320,21 @@ g_callable_info_prepare_closure (GICallableInfo *callable_info,
GIFFIClosureCallback callback, GIFFIClosureCallback callback,
gpointer user_data) gpointer user_data)
{ {
ffi_closure *closure; gpointer exec_ptr;
GIClosureWrapper *closure;
ffi_status status; ffi_status status;
g_return_val_if_fail (callable_info != NULL, FALSE); g_return_val_if_fail (callable_info != NULL, FALSE);
g_return_val_if_fail (cif != NULL, FALSE); g_return_val_if_fail (cif != NULL, FALSE);
g_return_val_if_fail (callback != NULL, FALSE); g_return_val_if_fail (callback != NULL, FALSE);
closure = mmap (NULL, sizeof (ffi_closure), closure = ffi_closure_alloc (sizeof (GIClosureWrapper), &exec_ptr);
PROT_EXEC | PROT_READ | PROT_WRITE,
MAP_ANON | MAP_PRIVATE, -1, sysconf (_SC_PAGE_SIZE));
if (!closure) if (!closure)
{ {
g_warning("mmap failed: %s\n", strerror(errno)); g_warning ("could not allocate closure\n");
return NULL; return NULL;
} }
closure->writable_self = closure;
status = ffi_prep_cif (cif, FFI_DEFAULT_ABI, status = ffi_prep_cif (cif, FFI_DEFAULT_ABI,
g_callable_info_get_n_args (callable_info), g_callable_info_get_n_args (callable_info),
@ -340,27 +342,23 @@ g_callable_info_prepare_closure (GICallableInfo *callable_info,
g_callable_info_get_ffi_arg_types (callable_info)); g_callable_info_get_ffi_arg_types (callable_info));
if (status != FFI_OK) if (status != FFI_OK)
{ {
g_warning("ffi_prep_cif failed: %d\n", status); g_warning ("ffi_prep_cif failed: %d\n", status);
munmap(closure, sizeof (closure)); ffi_closure_free (closure);
return NULL; return NULL;
} }
status = ffi_prep_closure (closure, cif, callback, user_data); status = ffi_prep_closure_loc (&closure->ffi_closure, cif, callback, user_data, exec_ptr);
if (status != FFI_OK) if (status != FFI_OK)
{ {
g_warning ("ffi_prep_closure failed: %d\n", status); g_warning ("ffi_prep_closure failed: %d\n", status);
munmap(closure, sizeof (closure)); ffi_closure_free (closure);
return NULL; return NULL;
} }
if (mprotect(closure, sizeof (closure), PROT_READ | PROT_EXEC) == -1) /* Return exec_ptr, which points to the same underlying memory as
{ * closure, but via an executable-non-writable mapping.
g_warning ("ffi_prep_closure failed: %s\n", strerror(errno)); */
munmap(closure, sizeof (closure)); return exec_ptr;
return NULL;
}
return closure;
} }
/** /**
@ -374,5 +372,7 @@ void
g_callable_info_free_closure (GICallableInfo *callable_info, g_callable_info_free_closure (GICallableInfo *callable_info,
ffi_closure *closure) ffi_closure *closure)
{ {
munmap(closure, sizeof (closure)); GIClosureWrapper *wrapper = (GIClosureWrapper *)closure;
ffi_closure_free (wrapper->writable_self);
} }