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;