Skip to content

Commit a851a91

Browse files
committed
Fix zend_alloc aligned allocation on Windows - cherrypick phpGH-9721
1 parent a6972ab commit a851a91

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
@@ -415,50 +415,77 @@ stderr_last_error(char *msg)
415415
/* OS Allocation */
416416
/*****************/
417417

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

431-
if (ptr == MAP_FAILED) {
432-
#if ZEND_MM_ERROR && !defined(MAP_EXCL)
433-
fprintf(stderr, "\nmmap() failed: [%d] %s\n", errno, strerror(errno));
443+
#ifdef _WIN32
444+
static void *zend_mm_mmap_win_reserve(size_t size)
445+
{
446+
void *ptr = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE);
447+
if (ptr == NULL) {
448+
#if ZEND_MM_ERROR
449+
stderr_last_error("VirtualAlloc() reserve failed");
434450
#endif
435451
return NULL;
436-
} else if (ptr != addr) {
437-
if (munmap(ptr, size) != 0) {
452+
}
453+
return ptr;
454+
}
455+
456+
#ifndef _WIN64
457+
static void *zend_mm_mmap_win_reserve_fixed(void *addr, size_t size)
458+
{
459+
void *ptr = VirtualAlloc(addr, size, MEM_RESERVE, PAGE_READWRITE);
460+
if (ptr == NULL) {
461+
/** ERROR_INVALID_ADDRESS is expected when addr - addr+size is not free */
462+
if (GetLastError() != ERROR_INVALID_ADDRESS) {
438463
#if ZEND_MM_ERROR
439-
fprintf(stderr, "\nmunmap() failed: [%d] %s\n", errno, strerror(errno));
464+
stderr_last_error("VirtualAlloc() reserve fixed failed");
440465
#endif
441466
}
467+
SetLastError(0);
442468
return NULL;
443469
}
444470
return ptr;
445-
#endif
446471
}
447472
#endif
448473

449-
static void *zend_mm_mmap(size_t size)
474+
static void *zend_mm_mmap_win_commit(void *addr, size_t size)
450475
{
451-
#ifdef _WIN32
452-
void *ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
453-
476+
void *ptr = VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE);
454477
if (ptr == NULL) {
455478
#if ZEND_MM_ERROR
456-
stderr_last_error("VirtualAlloc() failed");
479+
stderr_last_error("VirtualAlloc() commit failed");
457480
#endif
458481
return NULL;
459482
}
483+
ZEND_ASSERT(ptr == addr);
460484
return ptr;
461-
#else
485+
}
486+
#else /* not Windows */
487+
static void *zend_mm_mmap(size_t size)
488+
{
462489
void *ptr;
463490

464491
#ifdef MAP_HUGETLB
@@ -471,33 +498,39 @@ static void *zend_mm_mmap(size_t size)
471498
#endif
472499

473500
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, ZEND_MM_FD, 0);
474-
475501
if (ptr == MAP_FAILED) {
476502
#if ZEND_MM_ERROR
477503
fprintf(stderr, "\nmmap() failed: [%d] %s\n", errno, strerror(errno));
478504
#endif
479505
return NULL;
480506
}
481507
return ptr;
482-
#endif
483508
}
509+
#endif
484510

485-
static void zend_mm_munmap(void *addr, size_t size)
511+
#ifndef HAVE_MREMAP
512+
#ifndef _WIN32
513+
static void *zend_mm_mmap_fixed(void *addr, size_t size)
486514
{
487-
#ifdef _WIN32
488-
if (VirtualFree(addr, 0, MEM_RELEASE) == 0) {
489-
#if ZEND_MM_ERROR
490-
stderr_last_error("VirtualFree() failed");
515+
int flags = MAP_PRIVATE | MAP_ANON;
516+
#if defined(MAP_EXCL)
517+
flags |= MAP_FIXED | MAP_EXCL;
491518
#endif
492-
}
493-
#else
494-
if (munmap(addr, size) != 0) {
495-
#if ZEND_MM_ERROR
496-
fprintf(stderr, "\nmunmap() failed: [%d] %s\n", errno, strerror(errno));
519+
/* MAP_FIXED leads to discarding of the old mapping, so it can't be used. */
520+
void *ptr = mmap(addr, size, PROT_READ | PROT_WRITE, flags /*| MAP_POPULATE | MAP_HUGETLB*/, ZEND_MM_FD, 0);
521+
if (ptr == MAP_FAILED) {
522+
#if ZEND_MM_ERROR && !defined(MAP_EXCL)
523+
fprintf(stderr, "\nmmap() fixed failed: [%d] %s\n", errno, strerror(errno));
497524
#endif
525+
return NULL;
526+
} else if (ptr != addr) {
527+
zend_mm_munmap(ptr, size);
528+
return NULL;
498529
}
499-
#endif
530+
return ptr;
500531
}
532+
#endif
533+
#endif
501534

502535
/***********/
503536
/* Bitmask */
@@ -663,35 +696,47 @@ static zend_always_inline int zend_mm_bitset_is_free_range(zend_mm_bitset *bitse
663696

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

680-
/* chunk has to be aligned */
681-
zend_mm_munmap(ptr, size);
682-
ptr = zend_mm_mmap(size + alignment - REAL_PAGE_SIZE);
683-
#ifdef _WIN32
684-
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
685-
zend_mm_munmap(ptr, size + alignment - REAL_PAGE_SIZE);
686-
ptr = zend_mm_mmap_fixed((void*)((char*)ptr + (alignment - offset)), size);
687-
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
688-
if (offset != 0) {
689-
zend_mm_munmap(ptr, size);
710+
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
711+
if (offset != 0) {
712+
offset = alignment - offset;
713+
}
714+
zend_mm_munmap(ptr, size + alignment);
715+
ptr = zend_mm_mmap_win_reserve_fixed((void*)((char*)ptr + offset), size);
716+
#endif
717+
if (ptr == NULL) {
718+
ptr = zend_mm_mmap_win_reserve(size + alignment);
719+
if (ptr == NULL) {
690720
return NULL;
691721
}
692-
return ptr;
722+
}
723+
724+
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
725+
if (offset != 0) {
726+
offset = alignment - offset;
727+
}
728+
return zend_mm_mmap_win_commit((void*)((char*)ptr + offset), size);
693729
#else
694-
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
730+
void *ptr = zend_mm_mmap(size);
731+
if (ptr == NULL) {
732+
return NULL;
733+
}
734+
735+
if (ZEND_MM_ALIGNED_OFFSET(ptr, alignment) != 0) {
736+
zend_mm_munmap(ptr, size);
737+
ptr = zend_mm_mmap(size + alignment - REAL_PAGE_SIZE);
738+
739+
size_t offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
695740
if (offset != 0) {
696741
offset = alignment - offset;
697742
zend_mm_munmap(ptr, offset);
@@ -701,14 +746,16 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment)
701746
if (alignment > REAL_PAGE_SIZE) {
702747
zend_mm_munmap((char*)ptr + size, alignment - REAL_PAGE_SIZE);
703748
}
704-
# ifdef MADV_HUGEPAGE
705-
if (zend_mm_use_huge_pages) {
706-
madvise(ptr, size, MADV_HUGEPAGE);
707-
}
708-
# endif
709-
#endif
710-
return ptr;
711749
}
750+
751+
#ifdef MADV_HUGEPAGE
752+
if (zend_mm_use_huge_pages) {
753+
madvise(ptr, size, MADV_HUGEPAGE);
754+
}
755+
#endif
756+
757+
return ptr;
758+
#endif
712759
}
713760

714761
static void *zend_mm_chunk_alloc(zend_mm_heap *heap, size_t size, size_t alignment)
@@ -1846,11 +1893,7 @@ static zend_mm_heap *zend_mm_init(void)
18461893

18471894
if (UNEXPECTED(chunk == NULL)) {
18481895
#if ZEND_MM_ERROR
1849-
#ifdef _WIN32
1850-
stderr_last_error("Can't initialize heap");
1851-
#else
1852-
fprintf(stderr, "\nCan't initialize heap: [%d] %s\n", errno, strerror(errno));
1853-
#endif
1896+
fprintf(stderr, "Can't initialize heap\n");
18541897
#endif
18551898
return NULL;
18561899
}
@@ -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)