Skip to content

Commit 37317a2

Browse files
committed
Fix zend_alloc aligned allocation on Windows - cherrypick phpGH-9721
1 parent b494186 commit 37317a2

File tree

1 file changed

+114
-79
lines changed

1 file changed

+114
-79
lines changed

Zend/zend_alloc.c

Lines changed: 114 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -416,50 +416,77 @@ stderr_last_error(char *msg)
416416
/* OS Allocation */
417417
/*****************/
418418

419-
#ifndef HAVE_MREMAP
420-
static void *zend_mm_mmap_fixed(void *addr, size_t size)
419+
static void zend_mm_munmap(void *addr, size_t size)
421420
{
422421
#ifdef _WIN32
423-
return VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
422+
MEMORY_BASIC_INFORMATION mbi;
423+
if (VirtualQuery(addr, &mbi, sizeof(mbi)) == 0) {
424+
#if ZEND_MM_ERROR
425+
stderr_last_error("VirtualQuery() failed");
426+
#endif
427+
}
428+
addr = mbi.AllocationBase;
429+
430+
if (VirtualFree(addr, 0, MEM_RELEASE) == 0) {
431+
#if ZEND_MM_ERROR
432+
stderr_last_error("VirtualFree() failed");
433+
#endif
434+
}
424435
#else
425-
int flags = MAP_PRIVATE | MAP_ANON;
426-
#if defined(MAP_EXCL)
427-
flags |= MAP_FIXED | MAP_EXCL;
436+
if (munmap(addr, size) != 0) {
437+
#if ZEND_MM_ERROR
438+
fprintf(stderr, "\nmunmap() failed: [%d] %s\n", errno, strerror(errno));
428439
#endif
429-
/* MAP_FIXED leads to discarding of the old mapping, so it can't be used. */
430-
void *ptr = mmap(addr, size, PROT_READ | PROT_WRITE, flags /*| MAP_POPULATE | MAP_HUGETLB*/, ZEND_MM_FD, 0);
440+
}
441+
#endif
442+
}
431443

432-
if (ptr == MAP_FAILED) {
433-
#if ZEND_MM_ERROR && !defined(MAP_EXCL)
434-
fprintf(stderr, "\nmmap() failed: [%d] %s\n", errno, strerror(errno));
444+
#ifdef _WIN32
445+
static void *zend_mm_mmap_win_reserve(size_t size)
446+
{
447+
void *ptr = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE);
448+
if (ptr == NULL) {
449+
#if ZEND_MM_ERROR
450+
stderr_last_error("VirtualAlloc() reserve failed");
435451
#endif
436452
return NULL;
437-
} else if (ptr != addr) {
438-
if (munmap(ptr, size) != 0) {
453+
}
454+
return ptr;
455+
}
456+
457+
#ifndef _WIN64
458+
static void *zend_mm_mmap_win_reserve_fixed(void *addr, size_t size)
459+
{
460+
void *ptr = VirtualAlloc(addr, size, MEM_RESERVE, PAGE_READWRITE);
461+
if (ptr == NULL) {
462+
/** ERROR_INVALID_ADDRESS is expected when addr - addr+size is not free */
463+
if (GetLastError() != ERROR_INVALID_ADDRESS) {
439464
#if ZEND_MM_ERROR
440-
fprintf(stderr, "\nmunmap() failed: [%d] %s\n", errno, strerror(errno));
465+
stderr_last_error("VirtualAlloc() reserve fixed failed");
441466
#endif
442467
}
468+
SetLastError(0);
443469
return NULL;
444470
}
445471
return ptr;
446-
#endif
447472
}
448473
#endif
449474

450-
static void *zend_mm_mmap(size_t size)
475+
static void *zend_mm_mmap_win_commit(void *addr, size_t size)
451476
{
452-
#ifdef _WIN32
453-
void *ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
454-
477+
void *ptr = VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE);
455478
if (ptr == NULL) {
456479
#if ZEND_MM_ERROR
457-
stderr_last_error("VirtualAlloc() failed");
480+
stderr_last_error("VirtualAlloc() commit failed");
458481
#endif
459482
return NULL;
460483
}
484+
ZEND_ASSERT(ptr == addr);
461485
return ptr;
462-
#else
486+
}
487+
#else /* not Windows */
488+
static void *zend_mm_mmap(size_t size)
489+
{
463490
void *ptr;
464491

465492
#ifdef MAP_HUGETLB
@@ -472,33 +499,39 @@ static void *zend_mm_mmap(size_t size)
472499
#endif
473500

474501
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, ZEND_MM_FD, 0);
475-
476502
if (ptr == MAP_FAILED) {
477503
#if ZEND_MM_ERROR
478504
fprintf(stderr, "\nmmap() failed: [%d] %s\n", errno, strerror(errno));
479505
#endif
480506
return NULL;
481507
}
482508
return ptr;
483-
#endif
484509
}
510+
#endif
485511

486-
static void zend_mm_munmap(void *addr, size_t size)
512+
#ifndef HAVE_MREMAP
513+
#ifndef _WIN32
514+
static void *zend_mm_mmap_fixed(void *addr, size_t size)
487515
{
488-
#ifdef _WIN32
489-
if (VirtualFree(addr, 0, MEM_RELEASE) == 0) {
490-
#if ZEND_MM_ERROR
491-
stderr_last_error("VirtualFree() failed");
516+
int flags = MAP_PRIVATE | MAP_ANON;
517+
#if defined(MAP_EXCL)
518+
flags |= MAP_FIXED | MAP_EXCL;
492519
#endif
493-
}
494-
#else
495-
if (munmap(addr, size) != 0) {
496-
#if ZEND_MM_ERROR
497-
fprintf(stderr, "\nmunmap() failed: [%d] %s\n", errno, strerror(errno));
520+
/* MAP_FIXED leads to discarding of the old mapping, so it can't be used. */
521+
void *ptr = mmap(addr, size, PROT_READ | PROT_WRITE, flags /*| MAP_POPULATE | MAP_HUGETLB*/, ZEND_MM_FD, 0);
522+
if (ptr == MAP_FAILED) {
523+
#if ZEND_MM_ERROR && !defined(MAP_EXCL)
524+
fprintf(stderr, "\nmmap() fixed failed: [%d] %s\n", errno, strerror(errno));
498525
#endif
526+
return NULL;
527+
} else if (ptr != addr) {
528+
zend_mm_munmap(ptr, size);
529+
return NULL;
499530
}
500-
#endif
531+
return ptr;
501532
}
533+
#endif
534+
#endif
502535

503536
/***********/
504537
/* Bitmask */
@@ -664,35 +697,47 @@ static zend_always_inline int zend_mm_bitset_is_free_range(zend_mm_bitset *bitse
664697

665698
static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment)
666699
{
667-
void *ptr = zend_mm_mmap(size);
668-
700+
#ifdef _WIN32
701+
void *ptr = NULL;
702+
size_t offset;
703+
#ifndef _WIN64
704+
/* 32-bit build has limited memory address space and partical deallocation is not supported,
705+
* try to reserve exact aligned size first, GH-9650 */
706+
ptr = zend_mm_mmap_win_reserve(size + alignment);
669707
if (ptr == NULL) {
670708
return NULL;
671-
} else if (ZEND_MM_ALIGNED_OFFSET(ptr, alignment) == 0) {
672-
#ifdef MADV_HUGEPAGE
673-
if (zend_mm_use_huge_pages) {
674-
madvise(ptr, size, MADV_HUGEPAGE);
675-
}
676-
#endif
677-
return ptr;
678-
} else {
679-
size_t offset;
709+
}
680710

681-
/* chunk has to be aligned */
682-
zend_mm_munmap(ptr, size);
683-
ptr = zend_mm_mmap(size + alignment - REAL_PAGE_SIZE);
684-
#ifdef _WIN32
685-
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
686-
zend_mm_munmap(ptr, size + alignment - REAL_PAGE_SIZE);
687-
ptr = zend_mm_mmap_fixed((void*)((char*)ptr + (alignment - offset)), size);
688-
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
689-
if (offset != 0) {
690-
zend_mm_munmap(ptr, size);
711+
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
712+
if (offset != 0) {
713+
offset = alignment - offset;
714+
}
715+
zend_mm_munmap(ptr, size + alignment);
716+
ptr = zend_mm_mmap_win_reserve_fixed((void*)((char*)ptr + offset), size);
717+
#endif
718+
if (ptr == NULL) {
719+
ptr = zend_mm_mmap_win_reserve(size + alignment);
720+
if (ptr == NULL) {
691721
return NULL;
692722
}
693-
return ptr;
723+
}
724+
725+
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
726+
if (offset != 0) {
727+
offset = alignment - offset;
728+
}
729+
return zend_mm_mmap_win_commit((void*)((char*)ptr + offset), size);
694730
#else
695-
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
731+
void *ptr = zend_mm_mmap(size);
732+
if (ptr == NULL) {
733+
return NULL;
734+
}
735+
736+
if (ZEND_MM_ALIGNED_OFFSET(ptr, alignment) != 0) {
737+
zend_mm_munmap(ptr, size);
738+
ptr = zend_mm_mmap(size + alignment - REAL_PAGE_SIZE);
739+
740+
size_t offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
696741
if (offset != 0) {
697742
offset = alignment - offset;
698743
zend_mm_munmap(ptr, offset);
@@ -702,14 +747,16 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment)
702747
if (alignment > REAL_PAGE_SIZE) {
703748
zend_mm_munmap((char*)ptr + size, alignment - REAL_PAGE_SIZE);
704749
}
705-
# ifdef MADV_HUGEPAGE
706-
if (zend_mm_use_huge_pages) {
707-
madvise(ptr, size, MADV_HUGEPAGE);
708-
}
709-
# endif
710-
#endif
711-
return ptr;
712750
}
751+
752+
#ifdef MADV_HUGEPAGE
753+
if (zend_mm_use_huge_pages) {
754+
madvise(ptr, size, MADV_HUGEPAGE);
755+
}
756+
#endif
757+
758+
return ptr;
759+
#endif
713760
}
714761

715762
static void *zend_mm_chunk_alloc(zend_mm_heap *heap, size_t size, size_t alignment)
@@ -1847,11 +1894,7 @@ static zend_mm_heap *zend_mm_init(void)
18471894

18481895
if (UNEXPECTED(chunk == NULL)) {
18491896
#if ZEND_MM_ERROR
1850-
#ifdef _WIN32
1851-
stderr_last_error("Can't initialize heap");
1852-
#else
1853-
fprintf(stderr, "\nCan't initialize heap: [%d] %s\n", errno, strerror(errno));
1854-
#endif
1897+
fprintf(stderr, "Can't initialize heap\n");
18551898
#endif
18561899
return NULL;
18571900
}
@@ -2978,11 +3021,7 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
29783021
chunk = (zend_mm_chunk*)handlers->chunk_alloc(&tmp_storage, ZEND_MM_CHUNK_SIZE, ZEND_MM_CHUNK_SIZE);
29793022
if (UNEXPECTED(chunk == NULL)) {
29803023
#if ZEND_MM_ERROR
2981-
#ifdef _WIN32
2982-
stderr_last_error("Can't initialize heap");
2983-
#else
2984-
fprintf(stderr, "\nCan't initialize heap: [%d] %s\n", errno, strerror(errno));
2985-
#endif
3024+
fprintf(stderr, "Can't initialize heap\n");
29863025
#endif
29873026
return NULL;
29883027
}
@@ -3025,11 +3064,7 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
30253064
if (!storage) {
30263065
handlers->chunk_free(&tmp_storage, chunk, ZEND_MM_CHUNK_SIZE);
30273066
#if ZEND_MM_ERROR
3028-
#ifdef _WIN32
3029-
stderr_last_error("Can't initialize heap");
3030-
#else
3031-
fprintf(stderr, "\nCan't initialize heap: [%d] %s\n", errno, strerror(errno));
3032-
#endif
3067+
fprintf(stderr, "Can't initialize heap\n");
30333068
#endif
30343069
return NULL;
30353070
}

0 commit comments

Comments
 (0)