Add a hard-coded FFI_EXEC_TRAMPOLINE_TABLE arm implementation.
This implements support for re-mapping a shared table of executable trampolines directly in front of a writable configuration page, working around PROT_WRITE restrictions for sandboxed applications on Apple's iOS. This implementation is for testing purposes; a proper allocator is still necessary, and ARM-specific code needs to be moved out of src/closures.c.
This commit is contained in:
@@ -297,10 +297,17 @@ ffi_prep_closure_loc (ffi_closure* closure,
|
|||||||
{
|
{
|
||||||
FFI_ASSERT (cif->abi == FFI_SYSV);
|
FFI_ASSERT (cif->abi == FFI_SYSV);
|
||||||
|
|
||||||
|
#if FFI_EXEC_TRAMPOLINE_TABLE
|
||||||
|
// XXX - hardcoded offset
|
||||||
|
void **config = (void **) (((uint8_t *) codeloc) - 4080);
|
||||||
|
config[0] = closure;
|
||||||
|
config[1] = ffi_closure_SYSV;
|
||||||
|
#else
|
||||||
FFI_INIT_TRAMPOLINE (&closure->tramp[0], \
|
FFI_INIT_TRAMPOLINE (&closure->tramp[0], \
|
||||||
&ffi_closure_SYSV, \
|
&ffi_closure_SYSV, \
|
||||||
codeloc);
|
codeloc);
|
||||||
|
#endif
|
||||||
|
|
||||||
closure->cif = cif;
|
closure->cif = cif;
|
||||||
closure->user_data = user_data;
|
closure->user_data = user_data;
|
||||||
closure->fun = fun;
|
closure->fun = fun;
|
||||||
|
|||||||
@@ -32,7 +32,7 @@
|
|||||||
#include <ffi.h>
|
#include <ffi.h>
|
||||||
#include <ffi_common.h>
|
#include <ffi_common.h>
|
||||||
|
|
||||||
#ifndef FFI_MMAP_EXEC_WRIT
|
#if !FFI_MMAP_EXEC_WRIT && !FFI_EXEC_TRAMPOLINE_TABLE
|
||||||
# if __gnu_linux__
|
# if __gnu_linux__
|
||||||
/* This macro indicates it may be forbidden to map anonymous memory
|
/* This macro indicates it may be forbidden to map anonymous memory
|
||||||
with both write and execute permission. Code compiled when this
|
with both write and execute permission. Code compiled when this
|
||||||
@@ -63,7 +63,89 @@
|
|||||||
|
|
||||||
#if FFI_CLOSURES
|
#if FFI_CLOSURES
|
||||||
|
|
||||||
# if FFI_MMAP_EXEC_WRIT
|
# if FFI_EXEC_TRAMPOLINE_TABLE
|
||||||
|
|
||||||
|
// XXX - non-thread-safe, non-portable implementation, intended for initial testing
|
||||||
|
#include <mach/mach.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
extern void *ffi_closure_trampoline_table;
|
||||||
|
|
||||||
|
static void *tramp_table = NULL;
|
||||||
|
static void **config_table = NULL;
|
||||||
|
static int tramp_table_free = 0;
|
||||||
|
|
||||||
|
void *
|
||||||
|
ffi_closure_alloc (size_t size, void **code)
|
||||||
|
{
|
||||||
|
if (!code)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
/* Allocate our config page and remap the trampoline table */
|
||||||
|
while (tramp_table == NULL) {
|
||||||
|
vm_address_t config_page = 0x0;
|
||||||
|
kern_return_t kt;
|
||||||
|
|
||||||
|
/* Try to allocate two pages */
|
||||||
|
kt = vm_allocate(mach_task_self(), &config_page, PAGE_SIZE*2, VM_FLAGS_ANYWHERE);
|
||||||
|
if (kt != KERN_SUCCESS) {
|
||||||
|
fprintf(stderr, "vm_allocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Now drop the second half of the allocation to make room for the trampoline table */
|
||||||
|
kt = vm_deallocate(mach_task_self(), config_page+PAGE_SIZE, PAGE_SIZE);
|
||||||
|
if (kt != KERN_SUCCESS) {
|
||||||
|
fprintf(stderr, "vm_deallocate() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Remap the trampoline table to directly follow the config page */
|
||||||
|
vm_address_t table_page = config_page+PAGE_SIZE;
|
||||||
|
vm_prot_t cur_prot;
|
||||||
|
vm_prot_t max_prot;
|
||||||
|
|
||||||
|
kt = vm_remap(mach_task_self(), &table_page, PAGE_SIZE, 0x0, FALSE, mach_task_self(), (vm_address_t) &ffi_closure_trampoline_table, FALSE, &cur_prot, &max_prot, VM_INHERIT_SHARE);
|
||||||
|
|
||||||
|
/* If we lost access to the destination trampoline page, drop our config allocation mapping and retry */
|
||||||
|
if (kt != KERN_SUCCESS) {
|
||||||
|
/* Log unexpected failures */
|
||||||
|
if (kt != KERN_NO_SPACE) {
|
||||||
|
fprintf(stderr, "vm_remap() failure: %d at %s:%d\n", kt, __FILE__, __LINE__);
|
||||||
|
}
|
||||||
|
|
||||||
|
vm_deallocate(mach_task_self(), config_page, PAGE_SIZE);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
tramp_table = (void *) table_page;
|
||||||
|
config_table = (void **) config_page;
|
||||||
|
config_table += 4; // XXX - there's a 16 byte offset into the config page on ARM
|
||||||
|
|
||||||
|
fprintf(stderr, "[DEBUG] Allocated config page at %p, trampoline page at %p, configs start at %p\n", (void *) config_page, (void *) table_page, config_table);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Check for permanent allocation failure */
|
||||||
|
if (tramp_table == NULL)
|
||||||
|
return *code = NULL;
|
||||||
|
|
||||||
|
*code = tramp_table + (3 * tramp_table_free);
|
||||||
|
tramp_table_free++;
|
||||||
|
|
||||||
|
void *closure = malloc(size);
|
||||||
|
|
||||||
|
return closure;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
ffi_closure_free (void *ptr)
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
//free (ptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
# elif FFI_MMAP_EXEC_WRIT /* !FFI_EXEC_TRAMPOLINE_TABLE */
|
||||||
|
|
||||||
#define USE_LOCKS 1
|
#define USE_LOCKS 1
|
||||||
#define USE_DL_PREFIX 1
|
#define USE_DL_PREFIX 1
|
||||||
|
|||||||
Reference in New Issue
Block a user