From 4b6cd3958f26d870fd6ce4127968d7dada5bfcab 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 | 67 ++++++++++++++++--- 1 file changed, 56 insertions(+), 11 deletions(-) diff --git a/zend_abstract_interface/jit_utils/jit_blacklist.c b/zend_abstract_interface/jit_utils/jit_blacklist.c index 059c5bba2bf..f4212f2fa64 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,60 @@ 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 + +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) { + goto again; + } else { + // we don't know... asume true +#ifdef ZEND_DEBUG + abort(); +#else + return true; +#endif + } +} +#elif defined(__APPLE__) && PHP_VERSION_ID < 80400 +#include +static bool is_address_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; +} +#endif + void zai_jit_blacklist_function_inlining(zend_op_array *op_array) { #if PHP_VERSION_ID >= 80400 if (opcache_handle) { @@ -210,14 +257,12 @@ void zai_jit_blacklist_function_inlining(zend_op_array *op_array) { size_t offset = jit_extension->offset; -#ifndef _WIN32 +#if (defined(__linux__) || defined(__APPLE__)) && (defined(__x86_64__) || defined(__aarch64__)) // 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)) {