diff --git a/src/pcre2_config.c b/src/pcre2_config.c index 5ef103caf..a7184e538 100644 --- a/src/pcre2_config.c +++ b/src/pcre2_config.c @@ -145,7 +145,7 @@ switch (what) case PCRE2_CONFIG_JIT: #ifdef SUPPORT_JIT - *((uint32_t *)where) = 1; + *((uint32_t *)where) = !!PRIV(jit_supported)(); #else *((uint32_t *)where) = 0; #endif diff --git a/src/pcre2_internal.h b/src/pcre2_internal.h index 92dd3138d..b31c4d022 100644 --- a/src/pcre2_internal.h +++ b/src/pcre2_internal.h @@ -1992,6 +1992,7 @@ is available. */ #define _pcre2_jit_free PCRE2_SUFFIX(_pcre2_jit_free_) #define _pcre2_jit_get_size PCRE2_SUFFIX(_pcre2_jit_get_size_) #define _pcre2_jit_get_target PCRE2_SUFFIX(_pcre2_jit_get_target_) +#define _pcre2_jit_supported PCRE2_SUFFIX(_pcre2_jit_supported_) #define _pcre2_memctl_malloc PCRE2_SUFFIX(_pcre2_memctl_malloc_) #define _pcre2_ord2utf PCRE2_SUFFIX(_pcre2_ord2utf_) #define _pcre2_script_run PCRE2_SUFFIX(_pcre2_script_run_) @@ -2019,6 +2020,7 @@ extern void _pcre2_jit_free_rodata(void *, void *); extern void _pcre2_jit_free(void *, pcre2_memctl *); extern size_t _pcre2_jit_get_size(void *); const char * _pcre2_jit_get_target(void); +extern int _pcre2_jit_supported(void); extern void * _pcre2_memctl_malloc(size_t, pcre2_memctl *); extern unsigned int _pcre2_ord2utf(uint32_t, PCRE2_UCHAR *); extern BOOL _pcre2_script_run(PCRE2_SPTR, PCRE2_SPTR, BOOL); diff --git a/src/pcre2_jit_misc.c b/src/pcre2_jit_misc.c index bb6a5589c..780aa3c47 100644 --- a/src/pcre2_jit_misc.c +++ b/src/pcre2_jit_misc.c @@ -199,6 +199,21 @@ if (jit_stack != NULL) } +/************************************************* +* Test runtime JIT support * +*************************************************/ + +int +PRIV(jit_supported)(void) +{ +#ifndef SUPPORT_JIT +return 0; +#else /* SUPPORT_JIT */ +return sljit_get_runtime_support(); +#endif /* SUPPORT_JIT */ +} + + /************************************************* * Get target CPU type * *************************************************/ diff --git a/src/sljit/sljitConfigInternal.h b/src/sljit/sljitConfigInternal.h index cd3ce6973..0ad655827 100644 --- a/src/sljit/sljitConfigInternal.h +++ b/src/sljit/sljitConfigInternal.h @@ -633,6 +633,17 @@ SLJIT_API_FUNC_ATTRIBUTE sljit_sw sljit_exec_offset(void* ptr); #define SLJIT_EXEC_OFFSET(ptr) sljit_exec_offset(ptr) #else #define SLJIT_EXEC_OFFSET(ptr) 0 + +/* SELinux or grsecurity kernels may deny creating rwx mappings, so we need +to probe at runtime if JIT memory is supported. */ +#if defined __linux__ && \ + (!defined SLJIT_PROT_EXECUTABLE_ALLOCATOR || !SLJIT_PROT_EXECUTABLE_ALLOCATOR) && \ + (!defined SLJIT_WX_EXECUTABLE_ALLOCATOR || !SLJIT_WX_EXECUTABLE_ALLOCATOR) +SLJIT_API_FUNC_ATTRIBUTE int sljit_get_runtime_support(void); +#else +#define sljit_get_runtime_support() 1 +#endif + #endif #endif /* SLJIT_EXECUTABLE_ALLOCATOR */ diff --git a/src/sljit/sljitExecAllocator.c b/src/sljit/sljitExecAllocator.c index 92d940ddc..ed948259d 100644 --- a/src/sljit/sljitExecAllocator.c +++ b/src/sljit/sljitExecAllocator.c @@ -94,6 +94,61 @@ static SLJIT_INLINE void free_chunk(void *chunk, sljit_uw size) #else /* POSIX */ +#ifdef __linux__ +#include + +static SLJIT_INLINE int is_permission_error(int err) +{ + /* PaX uses EPERM, SELinux uses EACCES */ + return err == EPERM || err == EACCES; +} + +SLJIT_API_FUNC_ATTRIBUTE int sljit_get_runtime_support(void) +{ + int status = -1; + size_t size; + void *addr; + FILE *f; + + /* Try to get the status from /proc/self/status, looking for PaX flags. */ + f = fopen("/proc/self/status", "re"); + if (f) { + char *buf = NULL; + size_t len; + + while (getline(&buf, &len, f) != -1) { + if (strncmp(buf, "PaX:", 4)) + continue; + + /* Look for 'm', indicating PaX MPROTECT is disabled. */ + status = !!strchr(buf+4, 'm'); + break; + } + + fclose(f); + free(buf); + + if (status != -1) + return status; + } + + /* + * Try to create a temporary rwx mapping to probe for its support. If + * this fails, test 'errno' to ensure it failed because we were not + * allowed to create such a mapping and not because of some transient + * error. + */ + size = get_page_alignment() + 1; + addr = mmap(NULL, size, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, -1, 0); + if (addr == MAP_FAILED) + return is_permission_error(errno) ? 0 : -1; + + munmap(addr, size); + + return 1; +} +#endif + #if defined(__APPLE__) && defined(MAP_JIT) /* On macOS systems, returns MAP_JIT if it is defined _and_ we're running on a diff --git a/src/sljit/sljitProtExecAllocator.c b/src/sljit/sljitProtExecAllocator.c index 915411fbe..10641bcd7 100644 --- a/src/sljit/sljitProtExecAllocator.c +++ b/src/sljit/sljitProtExecAllocator.c @@ -107,6 +107,7 @@ static SLJIT_INLINE int create_tempfile(void) int fd; char tmp_name[256]; size_t tmp_name_len = 0; + size_t tmp_len; char *dir; struct stat st; #if defined(SLJIT_SINGLE_THREADED) && SLJIT_SINGLE_THREADED @@ -125,18 +126,22 @@ static SLJIT_INLINE int create_tempfile(void) dir = secure_getenv("TMPDIR"); if (dir) { - tmp_name_len = strlen(dir); - if (tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name)) { - if ((stat(dir, &st) == 0) && S_ISDIR(st.st_mode)) + tmp_len = strlen(dir); + if (tmp_len > 0 && tmp_len < sizeof(tmp_name)) { + if ((stat(dir, &st) == 0) && S_ISDIR(st.st_mode)) { strcpy(tmp_name, dir); + tmp_name_len = tmp_len; + } } } #ifdef P_tmpdir if (!tmp_name_len) { - tmp_name_len = strlen(P_tmpdir); - if (tmp_name_len > 0 && tmp_name_len < sizeof(tmp_name)) + tmp_len = strlen(P_tmpdir); + if (tmp_len > 0 && tmp_len < sizeof(tmp_name)) { strcpy(tmp_name, P_tmpdir); + tmp_name_len = tmp_len; + } } #endif if (!tmp_name_len) {