From 21dcf99f62553397ed1357bbf9e8122a7919ad8d Mon Sep 17 00:00:00 2001 From: Gustavo Lopes Date: Fri, 22 Nov 2024 12:14:16 +0000 Subject: [PATCH] Fix appsec tests in 8.1-8.3 The use of pipe() creates an extra file descriptor which makes the helper unable to find the correct pipe file descriptor. While this could perhaps be improved by telling the helper explicitly the id of the correct file descriptor, it's probably better to swap write() calls of reading invalid addresses with mincore(). --- .../jit_utils/jit_blacklist.c | 75 ++++++++++++++++--- 1 file changed, 63 insertions(+), 12 deletions(-) diff --git a/zend_abstract_interface/jit_utils/jit_blacklist.c b/zend_abstract_interface/jit_utils/jit_blacklist.c index 059c5bba2b..98cf32695c 100644 --- a/zend_abstract_interface/jit_utils/jit_blacklist.c +++ b/zend_abstract_interface/jit_utils/jit_blacklist.c @@ -88,10 +88,6 @@ typedef union _zend_op_trace_info { #define ZEND_OP_TRACE_INFO(opline, offset) \ ((zend_op_trace_info*)(((char*)opline) + offset)) - -#ifndef _WIN32 -static int dd_probe_pipes[2]; -#endif #endif #define ZEND_FUNC_INFO(op_array) \ @@ -107,9 +103,6 @@ static void zai_jit_find_opcache_handle(void *ext) { // opcache startup NULLs its handle. MINIT is executed before extension startup. void zai_jit_minit(void) { -#if PHP_VERSION_ID < 80400 && !defined(_WIN32) - pipe(dd_probe_pipes); -#endif zend_llist_apply(&zend_extensions, zai_jit_find_opcache_handle); } @@ -185,6 +178,68 @@ int zai_get_zend_func_rid(zend_op_array *op_array) { return zend_func_info_rid; } +#if defined(__linux__) && (defined(__x86_64__) || defined(__aarch64__)) && PHP_VERSION_ID < 80400 +static bool is_mapped(void *addr, size_t size) { + uintptr_t page_size = sysconf(_SC_PAGESIZE); + assert(size <= page_size); + uintptr_t page_addr = ((uintptr_t)addr & ~(page_size - 1)); + uintptr_t last_page_addr = ((uintptr_t)(addr + size - 1) & ~(page_size - 1)); + + unsigned char vec[2]; +#ifdef __x86_64__ +#define SYS_mincore 0x1B +#else // aarch64 +#define SYS_mincore 0xE8 +#endif + + int retries = 5; +again: + if (syscall(SYS_mincore, page_addr, (1 + (page_addr != last_page_addr)) * page_size, &vec) == 0) { + return true; + } else if (errno == EFAULT || errno == ENOMEM) { + return false; + } else if (errno == EAGAIN) { + if (retries-- > 0) { + goto again; + } + return true; + } else { + // we don't know... assume true +#ifdef ZEND_DEBUG + abort(); +#else + return true; +#endif + } +} +#elif defined(__APPLE__) && PHP_VERSION_ID < 80400 +#include +static bool is_mapped(void *addr, size_t size) { + mach_port_t task = mach_task_self(); + vm_address_t address = (vm_address_t)addr; + + while (address < (vm_address_t)addr + size) { + __auto_type a = address; + vm_size_t region_size; + vm_region_basic_info_data_64_t info; + kern_return_t kr = vm_region_64(task, &address, ®ion_size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, + &(mach_msg_type_number_t){VM_REGION_BASIC_INFO_COUNT_64}, &(memory_object_name_t){0}); + + if (kr != KERN_SUCCESS || !(info.protection & VM_PROT_READ)) { + return false; + } + + address += region_size; + } + + return true; +} +#else +static inline bool is_mapped(...) { + return true; +} +#endif + void zai_jit_blacklist_function_inlining(zend_op_array *op_array) { #if PHP_VERSION_ID >= 80400 if (opcache_handle) { @@ -210,15 +265,11 @@ void zai_jit_blacklist_function_inlining(zend_op_array *op_array) { size_t offset = jit_extension->offset; -#ifndef _WIN32 // check whether the op_trace_info is actually readable or EFAULTing // we can't trust opcache too much here... - char dummy_buf[sizeof(zend_op_trace_info)]; - if (write(dd_probe_pipes[1], ZEND_OP_TRACE_INFO(opline, offset), sizeof(zend_op_trace_info)) < 0) { + if (!is_mapped(ZEND_OP_TRACE_INFO(opline, offset), sizeof(zend_op_trace_info))) { return; } - read(dd_probe_pipes[0], dummy_buf, sizeof(zend_op_trace_info)); -#endif if (!(ZEND_OP_TRACE_INFO(opline, offset)->trace_flags & ZEND_JIT_TRACE_BLACKLISTED)) { bool is_protected_memory = false;