From 3c1920fd1dcbc1ea29122f3ddb5c21dc3b835dc4 Mon Sep 17 00:00:00 2001 From: Ilija Tovilo Date: Tue, 12 Dec 2023 15:17:37 +0100 Subject: [PATCH] Fix SELinux mprotect execheap error due to mem adjacent to heap It seems SELinux has a bug where memory directly adjacent to the heap is interpreted as heap memory. Dodge this issue by leaving some space between the heap and memory suggested by find_prefered_mmap_base. See GH-12932 See https://bugzilla.kernel.org/show_bug.cgi?id=218258 --- ext/opcache/shared_alloc_mmap.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/ext/opcache/shared_alloc_mmap.c b/ext/opcache/shared_alloc_mmap.c index 9b0e99608a34d..33727d0da656c 100644 --- a/ext/opcache/shared_alloc_mmap.c +++ b/ext/opcache/shared_alloc_mmap.c @@ -62,6 +62,18 @@ static void *find_prefered_mmap_base(size_t requested_size) } while (fgets(buffer, MAXPATHLEN, f) && sscanf(buffer, "%lx-%lx", &start, &end) == 2) { + /* Don't place the segment directly before or after the heap segment. Due to an selinux bug, + * a segment directly preceding or following the heap is interpreted as heap memory, which + * will result in an execheap violation for the JIT. + * See https://bugzilla.kernel.org/show_bug.cgi?id=218258. */ + bool heap_segment = strstr(buffer, "[heap]") != NULL; + if (heap_segment) { + uintptr_t start_base = start & ~(huge_page_size - 1); + if (last_free_addr + requested_size >= start_base) { + last_free_addr = ZEND_MM_ALIGNED_SIZE_EX(end + huge_page_size, huge_page_size); + continue; + } + } 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) { @@ -90,7 +102,9 @@ static void *find_prefered_mmap_base(size_t requested_size) } } last_free_addr = ZEND_MM_ALIGNED_SIZE_EX(end, huge_page_size); - + if (heap_segment) { + last_free_addr += huge_page_size; + } } fclose(f); #elif defined(__FreeBSD__)