47#if defined(INFIX_OS_WINDOWS)
57#if defined(INFIX_OS_MACOS)
63#if defined(INFIX_ENV_POSIX) && !defined(INFIX_OS_WINDOWS)
64#if !defined(MAP_ANON) && defined(MAP_ANONYMOUS)
65#define MAP_ANON MAP_ANONYMOUS
71#if defined(INFIX_OS_MACOS)
86typedef const struct __CFString * CFStringRef;
87typedef const void * CFTypeRef;
88typedef struct __SecTask * SecTaskRef;
89typedef struct __CFError * CFErrorRef;
90#define kCFStringEncodingUTF8 0x08000100
94 void (*CFRelease)(CFTypeRef);
95 bool (*CFBooleanGetValue)(CFTypeRef boolean);
96 CFStringRef (*CFStringCreateWithCString)(CFTypeRef allocator,
const char * cStr, uint32_t encoding);
97 CFTypeRef kCFAllocatorDefault;
98 SecTaskRef (*SecTaskCreateFromSelf)(CFTypeRef allocator);
99 CFTypeRef (*SecTaskCopyValueForEntitlement)(SecTaskRef task, CFStringRef entitlement, CFErrorRef * error);
109static void initialize_macos_apis(
void) {
111 void * cf = dlopen(
"/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY);
112 void * sec = dlopen(
"/System/Library/Frameworks/Security.framework/Security", RTLD_LAZY);
115 INFIX_DEBUG_PRINTF(
"Warning: Could not dlopen macOS frameworks. JIT security features will be degraded.");
121 memset(&g_macos_apis, 0,
sizeof(g_macos_apis));
125 g_macos_apis.CFRelease = dlsym(cf,
"CFRelease");
126 g_macos_apis.CFBooleanGetValue = dlsym(cf,
"CFBooleanGetValue");
127 g_macos_apis.CFStringCreateWithCString = dlsym(cf,
"CFStringCreateWithCString");
128 void ** pAlloc = (
void **)dlsym(cf,
"kCFAllocatorDefault");
130 g_macos_apis.kCFAllocatorDefault = *pAlloc;
132 g_macos_apis.SecTaskCreateFromSelf = dlsym(sec,
"SecTaskCreateFromSelf");
133 g_macos_apis.SecTaskCopyValueForEntitlement = dlsym(sec,
"SecTaskCopyValueForEntitlement");
144static bool has_jit_entitlement(
void) {
146 static pthread_once_t init_once = PTHREAD_ONCE_INIT;
147 pthread_once(&init_once, initialize_macos_apis);
149 if (!g_macos_apis.SecTaskCopyValueForEntitlement || !g_macos_apis.CFStringCreateWithCString)
154 SecTaskRef task = g_macos_apis.SecTaskCreateFromSelf(g_macos_apis.kCFAllocatorDefault);
158 CFStringRef key = g_macos_apis.CFStringCreateWithCString(
159 g_macos_apis.kCFAllocatorDefault,
"com.apple.security.cs.allow-jit", kCFStringEncodingUTF8);
160 CFTypeRef value =
nullptr;
163 value = g_macos_apis.SecTaskCopyValueForEntitlement(task, key,
nullptr);
164 g_macos_apis.CFRelease(key);
166 g_macos_apis.CFRelease(task);
170 if (g_macos_apis.CFBooleanGetValue && g_macos_apis.CFBooleanGetValue(value))
172 g_macos_apis.CFRelease(value);
180#if !defined(INFIX_OS_WINDOWS) && !defined(INFIX_OS_MACOS) && !defined(INFIX_OS_ANDROID) && !defined(INFIX_OS_OPENBSD)
197 uint64_t random_val = 0;
201 int rand_fd = open(
"/dev/urandom", O_RDONLY);
205 ssize_t bytes_read = read(rand_fd, &random_val,
sizeof(random_val));
207 if (bytes_read !=
sizeof(random_val))
210 snprintf(shm_name,
sizeof(shm_name),
"/infix-jit-%d-%llx", getpid(), (
unsigned long long)random_val);
213 int fd = shm_open(shm_name, O_RDWR | O_CREAT | O_EXCL, 0600);
217 shm_unlink(shm_name);
235#if defined(INFIX_OS_WINDOWS)
244#if defined(INFIX_OS_WINDOWS)
246 void * code = VirtualAlloc(
nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
252#elif defined(INFIX_OS_MACOS) || defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
254 void * code = MAP_FAILED;
256 int flags = MAP_PRIVATE | MAP_ANON;
257#if defined(INFIX_OS_MACOS)
259 static bool g_use_secure_jit_path =
false;
260 static bool g_checked_jit_support =
false;
261 if (!g_checked_jit_support) {
262 g_use_secure_jit_path = has_jit_entitlement();
264 g_use_secure_jit_path ?
"yes" :
"no",
265 g_use_secure_jit_path ?
"secure (MAP_JIT)" :
"legacy (mprotect)");
266 g_checked_jit_support =
true;
269 if (g_use_secure_jit_path)
272 code = mmap(
nullptr, size, PROT_READ | PROT_WRITE, flags, -1, 0);
275 if (code == MAP_FAILED) {
276 int fd = open(
"/dev/zero", O_RDWR);
278 code = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
282 if (code == MAP_FAILED)
292 if (ftruncate(exec.
shm_fd, size) != 0) {
297 exec.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, exec.
shm_fd, 0);
299 exec.
rx_ptr = mmap(
nullptr, size, PROT_READ | PROT_EXEC, MAP_SHARED, exec.
shm_fd, 0);
302 if (exec.
rw_ptr == MAP_FAILED || exec.
rx_ptr == MAP_FAILED) {
303 if (exec.
rw_ptr != MAP_FAILED)
304 munmap(exec.
rw_ptr, size);
305 if (exec.
rx_ptr != MAP_FAILED)
306 munmap(exec.
rx_ptr, size);
308 return (
infix_executable_t){.rx_ptr =
nullptr, .rw_ptr =
nullptr, .size = 0, .shm_fd = -1};
333#if defined(INFIX_OS_WINDOWS)
336 if (!VirtualProtect(exec.
rw_ptr, exec.
size, PAGE_NOACCESS, &(DWORD){0}))
337 INFIX_DEBUG_PRINTF(
"WARNING: VirtualProtect failed to set PAGE_NOACCESS guard page.");
338 VirtualFree(exec.
rw_ptr, 0, MEM_RELEASE);
340#elif defined(INFIX_OS_MACOS)
344#if INFIX_MACOS_SECURE_JIT_AVAILABLE
346 static bool g_use_secure_jit_path =
false;
347 if (g_use_secure_jit_path)
348 pthread_jit_write_protect_np(
true);
354#elif defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
396#if defined(INFIX_ARCH_AARCH64)
399 FlushInstructionCache(GetCurrentProcess(), exec.
rw_ptr, exec.
size);
402 __builtin___clear_cache((
char *)exec.
rw_ptr, (
char *)exec.
rw_ptr + exec.
size);
407#if defined(INFIX_OS_WINDOWS)
409 result = VirtualProtect(exec.
rw_ptr, exec.
size, PAGE_EXECUTE_READ, &(DWORD){0});
410#elif defined(INFIX_OS_MACOS)
411#if INFIX_MACOS_SECURE_JIT_AVAILABLE
412 static bool g_use_secure_jit_path =
false;
413 if (g_use_secure_jit_path) {
414 pthread_jit_write_protect_np(
false);
424 result = (mprotect(exec.
rw_ptr, exec.
size, PROT_READ | PROT_EXEC) == 0);
425#elif defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
427 result = (mprotect(exec.
rw_ptr, exec.
size, PROT_READ | PROT_EXEC) == 0);
455#if defined(INFIX_OS_WINDOWS)
456 prot.
rw_ptr = VirtualAlloc(
nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
459 prot.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
461 int fd = open(
"/dev/zero", O_RDWR);
465 prot.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
469 if (prot.
rw_ptr == MAP_FAILED)
485#if defined(INFIX_OS_WINDOWS)
486 VirtualFree(prot.
rw_ptr, 0, MEM_RELEASE);
507#if defined(INFIX_OS_WINDOWS)
508 result = VirtualProtect(prot.
rw_ptr, prot.
size, PAGE_READONLY, &(DWORD){0});
510 result = (mprotect(prot.
rw_ptr, prot.
size, PROT_READ) == 0);
542 "Dispatching reverse call. Context: %p, User Fn: %p", (
void *)context, context->
user_callback_fn);
557 cif_func(return_value_ptr, args_array);
564 handler(context, return_value_ptr, args_array);
#define c23_nodiscard
A compatibility macro for the C23 [[nodiscard]] attribute.
Definition compat_c23.h:113
void infix_protected_free(infix_protected_t prot)
Frees a block of protected memory.
Definition executor.c:482
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:389
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:329
c23_nodiscard infix_protected_t infix_protected_alloc(size_t size)
Allocates a block of standard memory for later protection.
Definition executor.c:451
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:234
c23_nodiscard bool infix_protected_make_readonly(infix_protected_t prot)
Makes a block of memory read-only for security hardening.
Definition executor.c:503
static int shm_open_anonymous()
Definition executor.c:195
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:540
void(* infix_cif_func)(void *, void **)
A function pointer type for a bound forward trampoline.
Definition infix.h:371
size_t size
Definition infix.h:214
void(* infix_closure_handler_fn)(infix_context_t *, void *, void **)
A function pointer type for a generic closure handler.
Definition infix.h:384
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:288
#define infix_memset
A macro that can be defined to override the default memset function.
Definition infix.h:340
Internal data structures, function prototypes, and constants.
Internal representation of an executable memory block for JIT code.
Definition infix_internals.h:60
size_t size
Definition infix_internals.h:68
void * rw_ptr
Definition infix_internals.h:67
void * rx_ptr
Definition infix_internals.h:66
int shm_fd
Definition infix_internals.h:64
Internal representation of a memory block that will be made read-only.
Definition infix_internals.h:80
size_t size
Definition infix_internals.h:82
void * rw_ptr
Definition infix_internals.h:81
Internal definition of a reverse trampoline (callback/closure) handle.
Definition infix_internals.h:121
infix_type * return_type
Definition infix_internals.h:125
void * user_callback_fn
Definition infix_internals.h:130
infix_forward_t * cached_forward_trampoline
Definition infix_internals.h:135
A header for conditionally compiled debugging utilities.
#define INFIX_DEBUG_PRINTF(...)
Definition utility.h:106