44#if defined(INFIX_OS_WINDOWS)
53#if defined(INFIX_OS_MACOS)
58#if defined(INFIX_ENV_POSIX) && !defined(INFIX_OS_WINDOWS)
59#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
60#define MAP_ANON MAP_ANONYMOUS
64#if defined(INFIX_OS_MACOS)
79typedef const struct __CFString * CFStringRef;
80typedef const void * CFTypeRef;
81typedef struct __SecTask * SecTaskRef;
82typedef struct __CFError * CFErrorRef;
83#define kCFStringEncodingUTF8 0x08000100
86 void (*CFRelease)(CFTypeRef);
87 bool (*CFBooleanGetValue)(CFTypeRef boolean);
88 CFStringRef (*CFStringCreateWithCString)(CFTypeRef allocator,
const char * cStr, uint32_t encoding);
89 CFTypeRef kCFAllocatorDefault;
90 SecTaskRef (*SecTaskCreateFromSelf)(CFTypeRef allocator);
91 CFTypeRef (*SecTaskCopyValueForEntitlement)(SecTaskRef task, CFStringRef entitlement, CFErrorRef * error);
100static void initialize_macos_apis(
void) {
102 void * cf = dlopen(
"/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY);
103 void * sec = dlopen(
"/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY);
105 INFIX_DEBUG_PRINTF(
"Warning: Could not dlopen macOS frameworks. JIT security features will be degraded.");
110 memset(&g_macos_apis, 0,
sizeof(g_macos_apis));
113 g_macos_apis.CFRelease = dlsym(cf,
"CFRelease");
114 g_macos_apis.CFBooleanGetValue = dlsym(cf,
"CFBooleanGetValue");
115 g_macos_apis.CFStringCreateWithCString = dlsym(cf,
"CFStringCreateWithCString");
116 void ** pAlloc = (
void **)dlsym(cf,
"kCFAllocatorDefault");
118 g_macos_apis.kCFAllocatorDefault = *pAlloc;
119 g_macos_apis.SecTaskCreateFromSelf = dlsym(sec,
"SecTaskCreateFromSelf");
120 g_macos_apis.SecTaskCopyValueForEntitlement = dlsym(sec,
"SecTaskCopyValueForEntitlement");
129static bool has_jit_entitlement(
void) {
131 static pthread_once_t init_once = PTHREAD_ONCE_INIT;
132 pthread_once(&init_once, initialize_macos_apis);
133 if (!g_macos_apis.SecTaskCopyValueForEntitlement || !g_macos_apis.CFStringCreateWithCString)
136 SecTaskRef task = g_macos_apis.SecTaskCreateFromSelf(g_macos_apis.kCFAllocatorDefault);
139 CFStringRef key = g_macos_apis.CFStringCreateWithCString(
140 g_macos_apis.kCFAllocatorDefault,
"com.apple.security.cs.allow-jit", kCFStringEncodingUTF8);
141 CFTypeRef value =
nullptr;
144 value = g_macos_apis.SecTaskCopyValueForEntitlement(task, key,
nullptr);
145 g_macos_apis.CFRelease(key);
147 g_macos_apis.CFRelease(task);
150 if (g_macos_apis.CFBooleanGetValue && g_macos_apis.CFBooleanGetValue(value))
152 g_macos_apis.CFRelease(value);
158#if !defined(INFIX_OS_WINDOWS) && !defined(INFIX_OS_MACOS) && !defined(INFIX_OS_ANDROID) && !defined(INFIX_OS_OPENBSD)
175 uint64_t random_val = 0;
178 int rand_fd = open(
"/dev/urandom", O_RDONLY);
181 ssize_t bytes_read = read(rand_fd, &random_val,
sizeof(random_val));
183 if (bytes_read !=
sizeof(random_val))
185 snprintf(shm_name,
sizeof(shm_name),
"/infix-jit-%d-%llx", getpid(), (
unsigned long long)random_val);
187 int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0600);
191 shm_unlink(shm_name);
206#if defined(INFIX_OS_WINDOWS)
213#if defined(INFIX_OS_WINDOWS)
215 void * code = VirtualAlloc(
nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
216 if (code ==
nullptr) {
223#elif defined(INFIX_OS_MACOS) || defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
225 void * code = MAP_FAILED;
227 int flags = MAP_PRIVATE | MAP_ANON;
228#if defined(INFIX_OS_MACOS)
230 static bool g_use_secure_jit_path =
false;
231 static bool g_checked_jit_support =
false;
232 if (!g_checked_jit_support) {
233 g_use_secure_jit_path = has_jit_entitlement();
235 g_use_secure_jit_path ?
"yes" :
"no",
236 g_use_secure_jit_path ?
"secure (MAP_JIT)" :
"legacy (mprotect)");
237 g_checked_jit_support =
true;
240 if (g_use_secure_jit_path)
243 code = mmap(
nullptr, size, PROT_READ | PROT_WRITE, flags, -1, 0);
245 if (code == MAP_FAILED) {
246 int fd = open(
"/dev/zero", O_RDWR);
248 code = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
252 if (code == MAP_FAILED) {
266 if (ftruncate(exec.
shm_fd, size) != 0) {
274 exec.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, exec.
shm_fd, 0);
276 exec.
rx_ptr = mmap(
nullptr, size, PROT_READ | PROT_EXEC, MAP_SHARED, exec.
shm_fd, 0);
278 if (exec.
rw_ptr == MAP_FAILED || exec.
rx_ptr == MAP_FAILED) {
280 if (exec.
rw_ptr != MAP_FAILED)
281 munmap(exec.
rw_ptr, size);
282 if (exec.
rx_ptr != MAP_FAILED)
283 munmap(exec.
rx_ptr, size);
286 return (
infix_executable_t){.rx_ptr =
nullptr, .rw_ptr =
nullptr, .size = 0, .shm_fd = -1};
314#if defined(INFIX_OS_WINDOWS)
317 if (!VirtualProtect(exec.
rw_ptr, exec.
size, PAGE_NOACCESS, &(DWORD){0}))
318 INFIX_DEBUG_PRINTF(
"WARNING: VirtualProtect failed to set PAGE_NOACCESS guard page.");
319 VirtualFree(exec.
rw_ptr, 0, MEM_RELEASE);
321#elif defined(INFIX_OS_MACOS)
325#if INFIX_MACOS_SECURE_JIT_AVAILABLE
327 static bool g_use_secure_jit_path =
false;
328 if (g_use_secure_jit_path)
329 pthread_jit_write_protect_np(
true);
335#elif defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
375#if defined(INFIX_ARCH_AARCH64)
378 FlushInstructionCache(GetCurrentProcess(), exec.
rw_ptr, exec.
size);
381 __builtin___clear_cache((
char *)exec.
rw_ptr, (
char *)exec.
rw_ptr + exec.
size);
385#if defined(INFIX_OS_WINDOWS)
387 result = VirtualProtect(exec.
rw_ptr, exec.
size, PAGE_EXECUTE_READ, &(DWORD){0});
390#elif defined(INFIX_OS_MACOS)
391#if INFIX_MACOS_SECURE_JIT_AVAILABLE
392 static bool g_use_secure_jit_path =
false;
393 if (g_use_secure_jit_path) {
394 pthread_jit_write_protect_np(
false);
404 result = (mprotect(exec.
rw_ptr, exec.
size, PROT_READ | PROT_EXEC) == 0);
407#elif defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
409 result = (mprotect(exec.
rw_ptr, exec.
size, PROT_READ | PROT_EXEC) == 0);
436#if defined(INFIX_OS_WINDOWS)
437 prot.
rw_ptr = VirtualAlloc(
nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
442 prot.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
444 int fd = open(
"/dev/zero", O_RDWR);
448 prot.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
452 if (prot.
rw_ptr == MAP_FAILED) {
469#if defined(INFIX_OS_WINDOWS)
470 VirtualFree(prot.
rw_ptr, 0, MEM_RELEASE);
490#if defined(INFIX_OS_WINDOWS)
491 result = VirtualProtect(prot.
rw_ptr, prot.
size, PAGE_READONLY, &(DWORD){0});
495 result = (mprotect(prot.
rw_ptr, prot.
size, PROT_READ) == 0);
527 "Dispatching reverse call. Context: %p, User Fn: %p", (
void *)context, context->
user_callback_fn);
540 cif_func(return_value_ptr, args_array);
547 handler(context, return_value_ptr, args_array);
#define c23_nodiscard
A compatibility macro for the C23 [[nodiscard]] attribute.
Definition compat_c23.h:106
void infix_protected_free(infix_protected_t prot)
Frees a block of protected memory.
Definition executor.c:466
c23_nodiscard bool infix_executable_make_executable(infix_executable_t exec)
Makes a block of JIT memory executable, completing the W^X process.
Definition executor.c:369
void infix_executable_free(infix_executable_t exec)
Frees a block of executable memory and applies guard pages to prevent use-after-free.
Definition executor.c:311
c23_nodiscard infix_protected_t infix_protected_alloc(size_t size)
Allocates a block of standard memory for later protection.
Definition executor.c:432
c23_nodiscard infix_executable_t infix_executable_alloc(size_t size)
Allocates a block of executable memory using the platform's W^X strategy.
Definition executor.c:205
c23_nodiscard bool infix_protected_make_readonly(infix_protected_t prot)
Makes a block of memory read-only for security hardening.
Definition executor.c:486
static int shm_open_anonymous()
Definition executor.c:173
void infix_internal_dispatch_callback_fn_impl(infix_reverse_t *context, void *return_value_ptr, void **args_array)
The universal C entry point for all reverse call trampolines.
Definition executor.c:525
@ INFIX_CODE_PROTECTION_FAILURE
Definition infix.h:1235
@ INFIX_CODE_EXECUTABLE_MEMORY_FAILURE
Definition infix.h:1234
@ INFIX_CODE_OUT_OF_MEMORY
Definition infix.h:1233
@ INFIX_CATEGORY_ALLOCATION
Definition infix.h:1218
void(* infix_cif_func)(void *, void **)
A function pointer type for a bound forward trampoline.
Definition infix.h:338
size_t size
Definition infix.h:197
void(* infix_closure_handler_fn)(infix_context_t *, void *, void **)
A function pointer type for a generic closure handler.
Definition infix.h:350
c23_nodiscard infix_cif_func infix_forward_get_code(infix_forward_t *)
Gets the callable function pointer from a bound forward trampoline.
Definition trampoline.c:283
#define infix_memset
A macro that can be defined to override the default memset function.
Definition infix.h:311
Internal data structures, function prototypes, and constants.
void _infix_set_system_error(infix_error_category_t category, infix_error_code_t code, long system_code, const char *msg)
Sets the thread-local error state for a system-level error.
Definition error.c:242
Internal representation of an executable memory block for JIT code.
Definition infix_internals.h:56
size_t size
Definition infix_internals.h:64
void * rw_ptr
Definition infix_internals.h:63
void * rx_ptr
Definition infix_internals.h:62
int shm_fd
Definition infix_internals.h:60
Internal representation of a memory block that will be made read-only.
Definition infix_internals.h:75
size_t size
Definition infix_internals.h:77
void * rw_ptr
Definition infix_internals.h:76
Internal definition of a reverse trampoline (callback/closure) handle.
Definition infix_internals.h:114
infix_type * return_type
Definition infix_internals.h:118
void * user_callback_fn
Definition infix_internals.h:123
infix_forward_t * cached_forward_trampoline
Definition infix_internals.h:128
A header for conditionally compiled debugging utilities.
#define INFIX_DEBUG_PRINTF(...)
Definition utility.h:100