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);
220#elif defined(INFIX_OS_MACOS) || defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
222 void * code = MAP_FAILED;
224 int flags = MAP_PRIVATE | MAP_ANON;
225#if defined(INFIX_OS_MACOS)
227 static bool g_use_secure_jit_path =
false;
228 static bool g_checked_jit_support =
false;
229 if (!g_checked_jit_support) {
230 g_use_secure_jit_path = has_jit_entitlement();
232 g_use_secure_jit_path ?
"yes" :
"no",
233 g_use_secure_jit_path ?
"secure (MAP_JIT)" :
"legacy (mprotect)");
234 g_checked_jit_support =
true;
237 if (g_use_secure_jit_path)
240 code = mmap(
nullptr, size, PROT_READ | PROT_WRITE, flags, -1, 0);
242 if (code == MAP_FAILED) {
243 int fd = open(
"/dev/zero", O_RDWR);
245 code = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
249 if (code == MAP_FAILED)
258 if (ftruncate(exec.
shm_fd, size) != 0) {
263 exec.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_SHARED, exec.
shm_fd, 0);
265 exec.
rx_ptr = mmap(
nullptr, size, PROT_READ | PROT_EXEC, MAP_SHARED, exec.
shm_fd, 0);
267 if (exec.
rw_ptr == MAP_FAILED || exec.
rx_ptr == MAP_FAILED) {
268 if (exec.
rw_ptr != MAP_FAILED)
269 munmap(exec.
rw_ptr, size);
270 if (exec.
rx_ptr != MAP_FAILED)
271 munmap(exec.
rx_ptr, size);
273 return (
infix_executable_t){.rx_ptr =
nullptr, .rw_ptr =
nullptr, .size = 0, .shm_fd = -1};
295#if defined(INFIX_OS_WINDOWS)
298 if (!VirtualProtect(exec.
rw_ptr, exec.
size, PAGE_NOACCESS, &(DWORD){0}))
299 INFIX_DEBUG_PRINTF(
"WARNING: VirtualProtect failed to set PAGE_NOACCESS guard page.");
300 VirtualFree(exec.
rw_ptr, 0, MEM_RELEASE);
302#elif defined(INFIX_OS_MACOS)
306#if INFIX_MACOS_SECURE_JIT_AVAILABLE
308 static bool g_use_secure_jit_path =
false;
309 if (g_use_secure_jit_path)
310 pthread_jit_write_protect_np(
true);
316#elif defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
356#if defined(INFIX_ARCH_AARCH64)
359 FlushInstructionCache(GetCurrentProcess(), exec.
rw_ptr, exec.
size);
362 __builtin___clear_cache((
char *)exec.
rw_ptr, (
char *)exec.
rw_ptr + exec.
size);
366#if defined(INFIX_OS_WINDOWS)
368 result = VirtualProtect(exec.
rw_ptr, exec.
size, PAGE_EXECUTE_READ, &(DWORD){0});
369#elif defined(INFIX_OS_MACOS)
370#if INFIX_MACOS_SECURE_JIT_AVAILABLE
371 static bool g_use_secure_jit_path =
false;
372 if (g_use_secure_jit_path) {
373 pthread_jit_write_protect_np(
false);
383 result = (mprotect(exec.
rw_ptr, exec.
size, PROT_READ | PROT_EXEC) == 0);
384#elif defined(INFIX_OS_ANDROID) || defined(INFIX_OS_OPENBSD) || defined(INFIX_OS_DRAGONFLY)
386 result = (mprotect(exec.
rw_ptr, exec.
size, PROT_READ | PROT_EXEC) == 0);
411#if defined(INFIX_OS_WINDOWS)
412 prot.
rw_ptr = VirtualAlloc(
nullptr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
415 prot.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
417 int fd = open(
"/dev/zero", O_RDWR);
421 prot.
rw_ptr = mmap(
nullptr, size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
425 if (prot.
rw_ptr == MAP_FAILED)
440#if defined(INFIX_OS_WINDOWS)
441 VirtualFree(prot.
rw_ptr, 0, MEM_RELEASE);
461#if defined(INFIX_OS_WINDOWS)
462 result = VirtualProtect(prot.
rw_ptr, prot.
size, PAGE_READONLY, &(DWORD){0});
464 result = (mprotect(prot.
rw_ptr, prot.
size, PROT_READ) == 0);
494 "Dispatching reverse call. Context: %p, User Fn: %p", (
void *)context, context->
user_callback_fn);
507 cif_func(return_value_ptr, args_array);
514 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:437
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:350
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:292
c23_nodiscard infix_protected_t infix_protected_alloc(size_t size)
Allocates a block of standard memory for later protection.
Definition executor.c:407
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:457
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:492
void(* infix_cif_func)(void *, void **)
A function pointer type for a bound forward trampoline.
Definition infix.h:336
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:348
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:278
#define infix_memset
A macro that can be defined to override the default memset function.
Definition infix.h:309
Internal data structures, function prototypes, and constants.
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