Skip to content

Commit 56f70f5

Browse files
committed
Fix zend_alloc aligned allocation on Windows
1 parent db17c54 commit 56f70f5

File tree

1 file changed

+89
-45
lines changed

1 file changed

+89
-45
lines changed

Zend/zend_alloc.c

Lines changed: 89 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -419,6 +419,14 @@ stderr_last_error(char *msg)
419419
static void zend_mm_munmap(void *addr, size_t size)
420420
{
421421
#ifdef _WIN32
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+
422430
if (VirtualFree(addr, 0, MEM_RELEASE) == 0) {
423431
#if ZEND_MM_ERROR
424432
stderr_last_error("VirtualFree() failed");
@@ -433,19 +441,52 @@ static void zend_mm_munmap(void *addr, size_t size)
433441
#endif
434442
}
435443

436-
static void *zend_mm_mmap(size_t size)
437-
{
438444
#ifdef _WIN32
439-
void *ptr = VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
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");
451+
#endif
452+
return NULL;
453+
}
454+
return ptr;
455+
}
440456

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);
441461
if (ptr == NULL) {
462+
/** ERROR_INVALID_ADDRESS is expected when addr - addr+size is not free */
463+
if (GetLastError() != ERROR_INVALID_ADDRESS) {
442464
#if ZEND_MM_ERROR
443-
stderr_last_error("VirtualAlloc() failed");
465+
stderr_last_error("VirtualAlloc() reserve fixed failed");
444466
#endif
467+
}
468+
SetLastError(0);
445469
return NULL;
446470
}
447471
return ptr;
448-
#else
472+
}
473+
#endif
474+
475+
static void *zend_mm_mmap_win_commit(void *addr, size_t size)
476+
{
477+
void *ptr = VirtualAlloc(addr, size, MEM_COMMIT, PAGE_READWRITE);
478+
if (ptr == NULL) {
479+
#if ZEND_MM_ERROR
480+
stderr_last_error("VirtualAlloc() commit failed");
481+
#endif
482+
return NULL;
483+
}
484+
ZEND_ASSERT(ptr == addr);
485+
return ptr;
486+
}
487+
#else /* not Windows */
488+
static void *zend_mm_mmap(size_t size)
489+
{
449490
void *ptr;
450491

451492
#ifdef MAP_HUGETLB
@@ -458,43 +499,39 @@ static void *zend_mm_mmap(size_t size)
458499
#endif
459500

460501
ptr = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, ZEND_MM_FD, 0);
461-
462502
if (ptr == MAP_FAILED) {
463503
#if ZEND_MM_ERROR
464504
fprintf(stderr, "\nmmap() failed: [%d] %s\n", errno, strerror(errno));
465505
#endif
466506
return NULL;
467507
}
468508
return ptr;
469-
#endif
470509
}
510+
#endif
471511

472512
#ifndef HAVE_MREMAP
513+
#ifndef _WIN32
473514
static void *zend_mm_mmap_fixed(void *addr, size_t size)
474515
{
475-
#ifdef _WIN32
476-
return VirtualAlloc(addr, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
477-
#else
478516
int flags = MAP_PRIVATE | MAP_ANON;
479517
#if defined(MAP_EXCL)
480518
flags |= MAP_FIXED | MAP_EXCL;
481519
#endif
482520
/* MAP_FIXED leads to discarding of the old mapping, so it can't be used. */
483521
void *ptr = mmap(addr, size, PROT_READ | PROT_WRITE, flags /*| MAP_POPULATE | MAP_HUGETLB*/, ZEND_MM_FD, 0);
484-
485522
if (ptr == MAP_FAILED) {
486523
#if ZEND_MM_ERROR && !defined(MAP_EXCL)
487-
fprintf(stderr, "\nmmap() failed: [%d] %s\n", errno, strerror(errno));
524+
fprintf(stderr, "\nmmap() fixed failed: [%d] %s\n", errno, strerror(errno));
488525
#endif
489526
return NULL;
490527
} else if (ptr != addr) {
491528
zend_mm_munmap(ptr, size);
492529
return NULL;
493530
}
494531
return ptr;
495-
#endif
496532
}
497533
#endif
534+
#endif
498535

499536
/***********/
500537
/* Bitmask */
@@ -660,28 +697,47 @@ static zend_always_inline int zend_mm_bitset_is_free_range(zend_mm_bitset *bitse
660697

661698
static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment)
662699
{
663-
void *ptr = zend_mm_mmap(size);
664-
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);
665707
if (ptr == NULL) {
666708
return NULL;
667-
} else if (ZEND_MM_ALIGNED_OFFSET(ptr, alignment) != 0) {
668-
size_t offset;
709+
}
669710

670-
/* chunk has to be aligned */
671-
zend_mm_munmap(ptr, size);
672-
ptr = zend_mm_mmap(size + alignment - REAL_PAGE_SIZE);
673-
#ifdef _WIN32
674-
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
675-
zend_mm_munmap(ptr, size + alignment - REAL_PAGE_SIZE);
676-
ptr = zend_mm_mmap_fixed((void*)((char*)ptr + (alignment - offset)), size);
677-
offset = ZEND_MM_ALIGNED_OFFSET(ptr, alignment);
678-
if (offset != 0) {
679-
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) {
680721
return NULL;
681722
}
682-
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);
683730
#else
684-
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);
685741
if (offset != 0) {
686742
offset = alignment - offset;
687743
zend_mm_munmap(ptr, offset);
@@ -691,7 +747,6 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment)
691747
if (alignment > REAL_PAGE_SIZE) {
692748
zend_mm_munmap((char*)ptr + size, alignment - REAL_PAGE_SIZE);
693749
}
694-
#endif
695750
}
696751

697752
#ifdef MADV_HUGEPAGE
@@ -701,6 +756,7 @@ static void *zend_mm_chunk_alloc_int(size_t size, size_t alignment)
701756
#endif
702757

703758
return ptr;
759+
#endif
704760
}
705761

706762
static void *zend_mm_chunk_alloc(zend_mm_heap *heap, size_t size, size_t alignment)
@@ -1838,11 +1894,7 @@ static zend_mm_heap *zend_mm_init(void)
18381894

18391895
if (UNEXPECTED(chunk == NULL)) {
18401896
#if ZEND_MM_ERROR
1841-
#ifdef _WIN32
1842-
stderr_last_error("Can't initialize heap");
1843-
#else
1844-
fprintf(stderr, "\nCan't initialize heap: [%d] %s\n", errno, strerror(errno));
1845-
#endif
1897+
fprintf(stderr, "Can't initialize heap\n");
18461898
#endif
18471899
return NULL;
18481900
}
@@ -2969,11 +3021,7 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
29693021
chunk = (zend_mm_chunk*)handlers->chunk_alloc(&tmp_storage, ZEND_MM_CHUNK_SIZE, ZEND_MM_CHUNK_SIZE);
29703022
if (UNEXPECTED(chunk == NULL)) {
29713023
#if ZEND_MM_ERROR
2972-
#ifdef _WIN32
2973-
stderr_last_error("Can't initialize heap");
2974-
#else
2975-
fprintf(stderr, "\nCan't initialize heap: [%d] %s\n", errno, strerror(errno));
2976-
#endif
3024+
fprintf(stderr, "Can't initialize heap\n");
29773025
#endif
29783026
return NULL;
29793027
}
@@ -3016,11 +3064,7 @@ ZEND_API zend_mm_heap *zend_mm_startup_ex(const zend_mm_handlers *handlers, void
30163064
if (!storage) {
30173065
handlers->chunk_free(&tmp_storage, chunk, ZEND_MM_CHUNK_SIZE);
30183066
#if ZEND_MM_ERROR
3019-
#ifdef _WIN32
3020-
stderr_last_error("Can't initialize heap");
3021-
#else
3022-
fprintf(stderr, "\nCan't initialize heap: [%d] %s\n", errno, strerror(errno));
3023-
#endif
3067+
fprintf(stderr, "Can't initialize heap\n");
30243068
#endif
30253069
return NULL;
30263070
}

0 commit comments

Comments
 (0)