From 364c64a201c123e06b2ca65b453cdbf99a5cddcb Mon Sep 17 00:00:00 2001 From: LoongT4o Date: Wed, 17 May 2023 19:31:31 -0700 Subject: [PATCH] Fix the JIT buffer relocation failure at the corner case MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avoid missing possible candidates due to the large address range of the free segment. Eg,  48000000-49400000 r-xs 08000000 00:0f 39322841               segment1 7ffff2ec8000-7ffff2f49000 rw-p 00000000 00:00 0              segment2 7ffff6fae000-7ffff735c000 r-xp 00200000 08:02 11538515       /usr/local/sbin/php-fpm original code will miss the opportunity between [7ffff2ec** - 7ffff2ec8000]. Fix issue #11265. Signed-off-by: Long, Tao Signed-off-by: Wang, Xue --- ext/opcache/shared_alloc_mmap.c | 167 +++++++++++++++++--------------- 1 file changed, 87 insertions(+), 80 deletions(-) diff --git a/ext/opcache/shared_alloc_mmap.c b/ext/opcache/shared_alloc_mmap.c index 1414ef96149d0..c384a34901802 100644 --- a/ext/opcache/shared_alloc_mmap.c +++ b/ext/opcache/shared_alloc_mmap.c @@ -58,96 +58,103 @@ static void *find_prefered_mmap_base(size_t requested_size) #if defined(__linux__) FILE *f; char buffer[MAXPATHLEN]; - + f = fopen("/proc/self/maps", "r"); if (!f) { return MAP_FAILED; } - while (fgets(buffer, MAXPATHLEN, f) && sscanf(buffer, "%lx-%lx", &start, &end) == 2) { - if ((uintptr_t)execute_ex >= start) { - /* the current segment lays before PHP .text segment or PHP .text segment itself */ - if (last_free_addr + requested_size <= start) { - last_candidate = last_free_addr; - } - if ((uintptr_t)execute_ex < end) { - /* the current segment is PHP .text segment itself */ - if (last_candidate != (uintptr_t)MAP_FAILED) { - if (end - last_candidate < UINT32_MAX) { - /* we have found a big enough hole before the text segment */ - break; - } - last_candidate = (uintptr_t)MAP_FAILED; + while (fgets(buffer, MAXPATHLEN, f) && sscanf(buffer, "%lx-%lx", &start, &end) == 2) { + if ((uintptr_t)execute_ex >= start) { + /* the current segment lays before PHP .text segment or PHP .text segment itself */ + /*Search for candidates at the end of the free segment near the .text segment + to prevent candidates from being missed due to large hole*/ + if (last_free_addr + requested_size <= start) { + last_candidate = ZEND_MM_ALIGNED_SIZE_EX(start - requested_size, huge_page_size); + if (last_candidate + requested_size > start) { + last_candidate -= huge_page_size; } - text_start = start; - } - } else { - /* the current segment lays after PHP .text segment */ - if (last_free_addr + requested_size - text_start > UINT32_MAX) { - /* the current segment and the following segments lay too far from PHP .text segment */ - break; - } - if (last_free_addr + requested_size <= start) { - last_candidate = last_free_addr; - break; - } - } - last_free_addr = ZEND_MM_ALIGNED_SIZE_EX(end, huge_page_size); - + } + if ((uintptr_t)execute_ex < end) { + /* the current segment is PHP .text segment itself */ + if (last_candidate != (uintptr_t)MAP_FAILED) { + if (end - last_candidate < UINT32_MAX) { + /* we have found a big enough hole before the .text segment */ + break; + } + last_candidate = (uintptr_t)MAP_FAILED; + } + text_start = start; + } + } else { + /* the current segment lays after PHP .text segment */ + if (last_free_addr + requested_size - text_start > UINT32_MAX) { + /* the current segment and the following segments lay too far from PHP .text segment */ + break; + } + if (last_free_addr + requested_size <= start) { + last_candidate = last_free_addr; + break; + } + } + last_free_addr = ZEND_MM_ALIGNED_SIZE_EX(end, huge_page_size); } fclose(f); #elif defined(__FreeBSD__) - size_t s = 0; - int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; - if (sysctl(mib, 4, NULL, &s, NULL, 0) == 0) { - s = s * 4 / 3; - void *addr = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); - if (addr != MAP_FAILED) { - if (sysctl(mib, 4, addr, &s, NULL, 0) == 0) { - start = (uintptr_t)addr; - end = start + s; - while (start < end) { - struct kinfo_vmentry *entry = (struct kinfo_vmentry *)start; - size_t sz = entry->kve_structsize; - if (sz == 0) { - break; - } - uintptr_t e_start = entry->kve_start; - uintptr_t e_end = entry->kve_end; - if ((uintptr_t)execute_ex >= e_start) { - /* the current segment lays before PHP .text segment or PHP .text segment itself */ - if (last_free_addr + requested_size <= e_start) { - last_candidate = last_free_addr; - } - if ((uintptr_t)execute_ex < e_end) { - /* the current segment is PHP .text segment itself */ - if (last_candidate != (uintptr_t)MAP_FAILED) { - if (e_end - last_candidate < UINT32_MAX) { - /* we have found a big enough hole before the text segment */ - break; - } - last_candidate = (uintptr_t)MAP_FAILED; + size_t s = 0; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_VMMAP, getpid()}; + if (sysctl(mib, 4, NULL, &s, NULL, 0) == 0) { + s = s * 4 / 3; + void *addr = mmap(NULL, s, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0); + if (addr != MAP_FAILED) { + if (sysctl(mib, 4, addr, &s, NULL, 0) == 0) { + start = (uintptr_t)addr; + end = start + s; + while (start < end) { + struct kinfo_vmentry *entry = (struct kinfo_vmentry *)start; + size_t sz = entry->kve_structsize; + if (sz == 0) { + break; + } + uintptr_t e_start = entry->kve_start; + uintptr_t e_end = entry->kve_end; + if ((uintptr_t)execute_ex >= e_start) { + /* the current segment lays before PHP .text segment or PHP .text segment itself */ + if (last_free_addr + requested_size <= e_start) { + last_candidate = ZEND_MM_ALIGNED_SIZE_EX(e_start - requested_size, huge_page_size); + if (last_candidate + requested_size > e_start) { + last_candidate -= huge_page_size; } - text_start = e_start; - } - } else { - /* the current segment lays after PHP .text segment */ - if (last_free_addr + requested_size - text_start > UINT32_MAX) { - /* the current segment and the following segments lay too far from PHP .text segment */ - break; - } - if (last_free_addr + requested_size <= e_start) { - last_candidate = last_free_addr; - break; - } - } - last_free_addr = ZEND_MM_ALIGNED_SIZE_EX(e_end, huge_page_size); - start += sz; - } - } - munmap(addr, s); - } - } + } + if ((uintptr_t)execute_ex < e_end) { + /* the current segment is PHP .text segment itself */ + if (last_candidate != (uintptr_t)MAP_FAILED) { + if (e_end - last_candidate < UINT32_MAX) { + /* we have found a big enough hole before the text segment */ + break; + } + last_candidate = (uintptr_t)MAP_FAILED; + } + text_start = e_start; + } + } else { + /* the current segment lays after PHP .text segment */ + if (last_free_addr + requested_size - text_start > UINT32_MAX) { + /* the current segment and the following segments lay too far from PHP .text segment */ + break; + } + if (last_free_addr + requested_size <= e_start) { + last_candidate = last_free_addr; + break; + } + } + last_free_addr = ZEND_MM_ALIGNED_SIZE_EX(e_end, huge_page_size); + start += sz; + } + } + munmap(addr, s); + } + } #endif return (void*)last_candidate;