62#if defined(INFIX_OS_WINDOWS)
72#if defined(INFIX_OS_MACOS)
78#if defined(INFIX_ENV_POSIX) && !defined(INFIX_OS_WINDOWS)
79#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
80#define MAP_ANON MAP_ANONYMOUS
85#if defined(INFIX_OS_MACOS)
88typedef const struct __CFString * CFStringRef;
89typedef const void * CFTypeRef;
90typedef struct __SecTask * SecTaskRef;
91typedef struct __CFError * CFErrorRef;
92#define kCFStringEncodingUTF8 0x08000100
96 void (*CFRelease)(CFTypeRef);
97 bool (*CFBooleanGetValue)(CFTypeRef boolean);
98 CFStringRef (*CFStringCreateWithCString)(CFTypeRef allocator,
const char * cStr, uint32_t encoding);
99 CFTypeRef kCFAllocatorDefault;
100 SecTaskRef (*SecTaskCreateFromSelf)(CFTypeRef allocator);
101 CFTypeRef (*SecTaskCopyValueForEntitlement)(SecTaskRef task, CFStringRef entitlement, CFErrorRef * error);
112static void initialize_macos_apis(
void) {
114 void * cf = dlopen(
"/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY);
115 void * sec = dlopen(
"/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY);
119 INFIX_DEBUG_PRINTF(
"Warning: Could not dlopen macOS frameworks. JIT security features will be degraded.");
125 memset(&g_macos_apis, 0,
sizeof(g_macos_apis));
130 g_macos_apis.CFRelease = dlsym(cf,
"CFRelease");
131 g_macos_apis.CFBooleanGetValue = dlsym(cf,
"CFBooleanGetValue");
132 g_macos_apis.CFStringCreateWithCString = dlsym(cf,
"CFStringCreateWithCString");
133 void ** pAlloc = (
void **)dlsym(cf,
"kCFAllocatorDefault");
135 g_macos_apis.kCFAllocatorDefault = *pAlloc;
137 g_macos_apis.SecTaskCreateFromSelf = dlsym(sec,
"SecTaskCreateFromSelf");
138 g_macos_apis.SecTaskCopyValueForEntitlement = dlsym(sec,
"SecTaskCopyValueForEntitlement");
165static bool has_jit_entitlement(
void) {
167 static pthread_once_t init_once = PTHREAD_ONCE_INIT;
168 pthread_once(&init_once, initialize_macos_apis);
171 if (!g_macos_apis.SecTaskCopyValueForEntitlement || !g_macos_apis.CFStringCreateWithCString)
176 SecTaskRef task = g_macos_apis.SecTaskCreateFromSelf(g_macos_apis.kCFAllocatorDefault);
181 CFStringRef key = g_macos_apis.CFStringCreateWithCString(
182 g_macos_apis.kCFAllocatorDefault,
"com.apple.security.cs.allow-jit", kCFStringEncodingUTF8);
183 CFTypeRef value = NULL;
185 value = g_macos_apis.SecTaskCopyValueForEntitlement(task, key, NULL);
186 g_macos_apis.CFRelease(key);
188 g_macos_apis.CFRelease(task);
192 if (g_macos_apis.CFBooleanGetValue && g_macos_apis.CFBooleanGetValue(value))
194 g_macos_apis.CFRelease(value);
200#if !defined(INFIX_OS_WINDOWS) && !defined(INFIX_OS_MACOS) && !defined(INFIX_OS_ANDROID) && !defined(INFIX_OS_OPENBSD)
226 uint64_t random_val = 0;
230 int rand_fd = open(
"/dev/urandom", O_RDONLY);
234 ssize_t bytes_read = read(rand_fd, &random_val,
sizeof(random_val));
236 if (bytes_read !=
sizeof(random_val))
239 snprintf(shm_name,
sizeof(shm_name),
"/infix-jit-%d-%llx", getpid(), (
unsigned long long)random_val);
244 int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0600);
246 shm_unlink(shm_name);
287#if defined(INFIX_OS_WINDOWS)
296#if defined(INFIX_OS_WINDOWS)
297 void * code = VirtualAlloc(
nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
303#elif defined(INFIX_OS_MACOS) || defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
304 void * code = MAP_FAILED;
306 int flags = MAP_PRIVATE | MAP_ANON;
307#if defined(INFIX_OS_MACOS)
308 static bool g_use_secure_jit_path =
false;
309 static bool g_checked_jit_support =
false;
310 if (!g_checked_jit_support) {
311 g_use_secure_jit_path = has_jit_entitlement();
313 g_use_secure_jit_path ?
"yes" :
"no",
314 g_use_secure_jit_path ?
"secure (MAP_JIT)" :
"legacy/insecure (mprotect)");
315 g_checked_jit_support =
true;
319 if (g_use_secure_jit_path)
322 code = mmap(
nullptr, size, PROT_READ | PROT_WRITE, flags, -1, 0);
324 if (code == MAP_FAILED) {
325 int fd = open(
"/dev/zero", O_RDWR);
327 code = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
331 if (code == MAP_FAILED)
340 if (ftruncate(exec.
shm_fd, size) != 0) {
344 exec.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, exec.
shm_fd, 0);
345 exec.
rx_ptr = mmap(
nullptr, size, PROT_READ | PROT_EXEC, MAP_SHARED, exec.
shm_fd, 0);
346 if (exec.
rw_ptr == MAP_FAILED || exec.
rx_ptr == MAP_FAILED) {
347 if (exec.
rw_ptr != MAP_FAILED)
348 munmap(exec.
rw_ptr, size);
349 if (exec.
rx_ptr != MAP_FAILED)
350 munmap(exec.
rx_ptr, size);
352 return (
infix_executable_t){.rx_ptr =
nullptr, .rw_ptr =
nullptr, .size = 0, .shm_fd = -1};
392#if defined(INFIX_OS_WINDOWS)
395 if (!VirtualProtect(exec.
rw_ptr, exec.
size, PAGE_NOACCESS, &(DWORD){0}))
398 VirtualFree(exec.
rw_ptr, 0, MEM_RELEASE);
400#elif defined(INFIX_OS_MACOS)
402#if INFIX_MACOS_SECURE_JIT_AVAILABLE
404 static bool g_use_secure_jit_path =
false;
405 if (g_use_secure_jit_path)
408 pthread_jit_write_protect_np(
true);
413#elif defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
457#if defined(INFIX_ARCH_AARCH64)
459 FlushInstructionCache(GetCurrentProcess(), exec.
rw_ptr, exec.
size);
461 __builtin___clear_cache((
char *)exec.
rw_ptr, (
char *)exec.
rw_ptr + exec.
size);
466#if defined(INFIX_OS_WINDOWS)
467 result = VirtualProtect(exec.
rw_ptr, exec.
size, PAGE_EXECUTE_READ, &(DWORD){0});
468#elif defined(INFIX_OS_MACOS)
469#if INFIX_MACOS_SECURE_JIT_AVAILABLE
470 static bool g_use_secure_jit_path =
false;
471 if (g_use_secure_jit_path) {
472 pthread_jit_write_protect_np(
false);
477 result = (mprotect(exec.
rw_ptr, exec.
size, PROT_READ | PROT_EXEC) == 0);
478#elif defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
479 result = (mprotect(exec.
rw_ptr, exec.
size, PROT_READ | PROT_EXEC) == 0);
511#if defined(INFIX_OS_WINDOWS)
512 prot.
rw_ptr = VirtualAlloc(
nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
515 prot.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
517 int fd = open(
"/dev/zero", O_RDWR);
521 prot.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
525 if (prot.
rw_ptr == MAP_FAILED)
549#if defined(INFIX_OS_WINDOWS)
550 VirtualFree(prot.
rw_ptr, 0, MEM_RELEASE);
581#if defined(INFIX_OS_WINDOWS)
582 result = VirtualProtect(prot.
rw_ptr, prot.
size, PAGE_READONLY, &(DWORD){0});
585 result = (mprotect(prot.
rw_ptr, prot.
size, PROT_READ) == 0);
626 cif_func(return_value_ptr, args_array);
632 handler(context, return_value_ptr, args_array);
#define c23_nodiscard
Definition compat_c23.h:93
void infix_protected_free(infix_protected_t prot)
Frees a block of protected data memory.
Definition executor.c:546
c23_nodiscard bool infix_executable_make_executable(infix_executable_t exec)
Makes a JIT memory region readable and executable (and non-writable).
Definition executor.c:453
void infix_executable_free(infix_executable_t exec)
Frees executable memory, creating a guard page to prevent use-after-free.
Definition executor.c:388
c23_nodiscard infix_protected_t infix_protected_alloc(size_t size)
Allocates a page-aligned block of data memory.
Definition executor.c:507
c23_nodiscard infix_executable_t infix_executable_alloc(size_t size)
Allocates a page-aligned block of W^X-compliant executable memory.
Definition executor.c:286
c23_nodiscard bool infix_protected_make_readonly(infix_protected_t prot)
Hardens a block of protected data memory to be read-only.
Definition executor.c:577
static int shm_open_anonymous()
Definition executor.c:224
void infix_internal_dispatch_callback_fn_impl(infix_reverse_t *context, void *return_value_ptr, void **args_array)
The high-level C dispatcher function called by reverse trampoline stubs.
Definition executor.c:610
c23_nodiscard infix_cif_func infix_forward_get_code(infix_forward_t *)
Retrieves the executable code pointer from a bound forward trampoline.
Definition trampoline.c:227
#define infix_memset
A macro for setting a block of memory to a specific value.
Definition infix.h:300
void(* infix_closure_handler_fn)(infix_context_t *context, void *return_value, void **args)
The signature for a generic "closure" handler.
Definition infix.h:346
void(* infix_cif_func)(void *, void **)
The signature for a "bound" forward-call trampoline.
Definition infix.h:335
Declarations for internal-only functions, types, and constants.
Definition infix_internals.h:42
size_t size
The total size of the allocated memory region in bytes.
Definition infix_internals.h:50
void * rw_ptr
Pointer with Read+Write permissions (where code is written).
Definition infix_internals.h:49
void * rx_ptr
Pointer with Read+Execute permissions (the callable address).
Definition infix_internals.h:48
int shm_fd
File descriptor for the shared memory object on POSIX systems that use dual-mapping.
Definition infix_internals.h:46
Definition infix_internals.h:60
size_t size
The size of the allocated memory region in bytes.
Definition infix_internals.h:62
void * rw_ptr
A pointer to the read-write data memory.
Definition infix_internals.h:61
Definition infix_internals.h:102
infix_type * return_type
The infix_type of the callback's return value.
Definition infix_internals.h:106
void * user_callback_fn
Pointer to the user's actual C callback handler function.
Definition infix_internals.h:111
infix_forward_t * cached_forward_trampoline
A pre-compiled forward trampoline for calling a type-safe user C callback.
Definition infix_internals.h:123
size_t size
The total size of the type in bytes, per sizeof.
Definition infix.h:163
A header for conditionally compiled debugging utilities.
#define INFIX_DEBUG_PRINTF(...)
Definition utility.h:100