From ffc05e4826c6b695c233ec13bb8c402faef171e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Piotr=20Ha=C5=82as?= Date: Thu, 3 Sep 2026 14:44:29 +0200 Subject: [PATCH] ext/opcache: keep huge page remap inside the reserved range create_segments() reserves requested_size bytes with MAP_32BIT, frees them, rounds the address up to the 2 MB huge page boundary, and then MAP_FIXED-maps requested_size bytes at the new address. The address goes up but the size stays the same, so the mapping ends up to 2 MB above the memory we reserved, and MAP_FIXED discards what is mapped there. If huge pages are available the remap succeeds and replaces that memory. If they are not, mmap() fails, but the kernel has already removed it and leaves a hole (mm/vma.c, vms_abort_munmap_vmas). On a normal host there is usually nothing above the reservation, so this is not visible. Under Rosetta 2 MAP_32BIT is not honored, the reservation lands directly below libc, and the overshoot unmaps its first pages: php-fpm then dies with SIGSEGV shortly after start. Reserve one extra huge page, so the aligned range always stays inside the reservation. zend_mm_chunk_alloc_int() already does this for 2 MB aligned chunks. --- NEWS | 2 ++ ext/opcache/shared_alloc_mmap.c | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 84f5597d9780..13b3286c7b67 100644 --- a/NEWS +++ b/NEWS @@ -59,6 +59,8 @@ PHP NEWS (Ilia Alshanetsky) - Opcache: + . Fixed a crash when the huge page SHM remap discarded mappings outside the + reserved address range. (Piotr Hałas) . Fixed opcache.protect_memory race under ZTS. (realFlowControl) . Fixed bug GH-23288 (Crash on restart when opcache.interned_strings_buffer is overridden in an individual FPM pool). (David Carlier) diff --git a/ext/opcache/shared_alloc_mmap.c b/ext/opcache/shared_alloc_mmap.c index 18c7532478f4..c46b6d8b4aea 100644 --- a/ext/opcache/shared_alloc_mmap.c +++ b/ext/opcache/shared_alloc_mmap.c @@ -245,9 +245,9 @@ static int create_segments(size_t requested_size, zend_shared_segment ***shared_ /* to got HUGE PAGES in low 32-bit address we have to reserve address space and then remap it using MAP_HUGETLB */ - p = mmap(NULL, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_32BIT, fd, 0); + p = mmap(NULL, requested_size + huge_page_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_32BIT, fd, 0); if (p != MAP_FAILED) { - munmap(p, requested_size); + munmap(p, requested_size + huge_page_size); p = (void*)(ZEND_MM_ALIGNED_SIZE_EX((ptrdiff_t)p, huge_page_size)); p = mmap(p, requested_size, flags, MAP_SHARED|MAP_ANONYMOUS|MAP_32BIT|MAP_HUGETLB|MAP_FIXED, -1, 0); if (p != MAP_FAILED) {