PTHREAD_JIT_WRITE_PROTECT_NP(3) Library Functions Manual PTHREAD_JIT_WRITE_PROTECT_NP(3)

pthread_jit_write_protect_supported_np, pthread_jit_write_protect_np, pthread_jit_write_with_callback_np, pthread_jit_write_freeze_callbacks_npthread JIT region write protection settings

#include <pthread.h>

int
pthread_jit_write_protect_supported_np(void);

void
pthread_jit_write_protect_np(int enabled);

typedef int (*pthread_jit_write_callback_t)(void *);
int
pthread_jit_write_with_callback_np(pthread_jit_write_callback_t callback, void *ctx);

void
pthread_jit_write_freeze_callbacks_np(void);

The () function returns whether per-thread write protection on the MAP_JIT region is supported on this platform.

On platforms where () is true, MAP_JIT regions are never writeable and executable simultaneously. When write protection is enabled for the thread, writes by the thread to the MAP_JIT region are denied and the MAP_JIT region is executable. When write protection is disabled for the thread, writes by the thread to the MAP_JIT region are allowed and the MAP_JIT region is not executable.

The () function sets whether MAP_JIT region write protection is enabled for this thread. Pass a non-zero value for the enabled parameter to enable thread JIT region write protection and allow execution. Pass a zero value for the enabled parameter to disable thread JIT write protection and deny execution.

The () function disables write protection, calls the supplied callback with ctx to write to the JIT region after validating its input for safety according to application logic, and then re-enables write protection. That is, it's roughly equivalent to:

pthread_jit_write_protect_np(false);
int rc = callback(ctx);
pthread_jit_write_protect_np(true);
return rc;

However, () enforces that the callback function must have been previously enumerated in a static list of allowed JIT write callbacks declared using the PTHREAD_JIT_WRITE_ALLOW_CALLBACKS_NP macro. Note that each image (i.e. main executable or framework/dylib) can specify its own list, and each image may specify at most one list. For example, an invocation of PTHREAD_JIT_WRITE_ALLOW_CALLBACKS_NP to allow a JIT write callback jit_write_cb() would be:

static void *jit_region;

int
jit_write_cb(void *ctx)
{
	size_t len = 0;
	void *insns = validate_instructions(ctx, &len);
	if (!insns) {
		__builtin_trap();
	}

	memcpy(jit_region, insns, len);
	return 0;
}

PTHREAD_JIT_WRITE_ALLOW_CALLBACKS_NP(jit_write_cb);

By default, JIT write callbacks are not allowed in frameworks/dylibs dynamically loaded at runtime via dlopen(3). However, applications that need JIT write callbacks in dynamically loaded code can take an additional entitlement (see ENTITLEMENTS) to allow them in any code loaded up until the () function is called.

On platforms where () is false, MAP_JIT regions are simultaenously writeable and executable. Calls to pthread_jit_write_protect_np() are no-ops on unsupported platforms. Calls to pthread_jit_write_with_callback_np() result in a direct call to callback on unsupported platforms.

If supported, the pthread_jit_write_protect_supported_np() function will return one. Otherwise the function will return zero.

pthread_jit_write_with_callback_np() returns the result returned by the callback function.

The purpose of per-thread JIT region write protection is to serve as a hardening measure against attacks that attempt to gain code execution by exploiting vulnerabilities to write unintended code into the JIT region.

Some attacks may attempt to defeat JIT write protection by inducing unexpected/unintended calls to pthread_jit_write_protect_np(), e.g. dynamically via dlsym(). To further harden against such attacks, the pthread_jit_write_with_callback_np() interface is intended to allow even tighter control over code permitted to write to the JIT region; in applications that adopt the com.apple.security.cs.jit-write-allowlist entitlement, only callbacks specifically enumerated in an invocation of PTHREAD_JIT_WRITE_ALLOW_CALLBACKS_NP can do so, and pthread_jit_write_protect_np() is disabled.

Applications and frameworks should prefer to use pthread_jit_write_with_callback_np() as a defense-in-depth hardening if relevant for their threat model.

Allowed callbacks should assume that their input is attacker-controlled and make an effort to validate that the instructions to be written should be permitted.

A Boolean value that indicates whether () should be disabled and pthread_jit_write_with_callback_np() must be used instead to write to the JIT region.
A Boolean value that indicates whether the runtime should wait for an explicit call to pthread_jit_write_freeze_callbacks_np() before "freezing" the set of allowed JIT write callbacks, to allow the application to dynamically load code containing JIT write callbacks that should be allowed.

mmap(2)

May 1, 2020 macOS 15.2