Zend memory manager possible left overflow in heap initialization

From: Date: Mon, 02 Nov 2015 08:35:30 +0000
Subject: Zend memory manager possible left overflow in heap initialization
Groups: php.internals 
Request: Send a blank email to internals+get-89030@lists.php.net to get a copy of this message
Hi, I'm into the process of reviewing the Zend memory manager, and I noticed something in the allocation of the heap. I'm working on PHP 5.6.14, but nothing that could affect it has changed in 5.6.15; I'm building on Debian squeeze, 32 bits. I'm referring to Zend/zend_alloc.c:zend_mm_init() The heap struct (zend_mm_heap) should be like this: ... -------------------------
| internal              |
-------------------------
| cached                |
-------------------------
| cache[n]              |
------------------------- ... n times... -------------------------
| free_buckets[2n]      |
------------------------- ... 2*n times... ------------------------- | large_free_buckets[n] | ------------------------- ... n stands for ZEND_MM_NUM_BUCKETS ZEND_MM_CACHE is set to 1, so cache and cached exist ZEND_DEBUG is set to 0 cache and free_buckets (and large_free_buckets, but this doesn't matter) are arrays containing the same type: pointers to zend_mm_free_block This line troubles me: p = ZEND_MM_SMALL_FREE_BUCKET(heap, 0); The corresponding macro is: #define ZEND_MM_SMALL_FREE_BUCKET(heap, index) \ (zend_mm_free_block*) ((char*)&heap->free_buckets[index * 2] + \ sizeof(zend_mm_free_block*) * 2 - \ sizeof(zend_mm_small_free_block)) sizeof(zend_mm_free_block*) is 4 bytes sizeof(zend_mm_small_free_block) is sizeof(zend_mm_block_info) + 2*sizeof(zend_mm_free_block*), i.e. 16 bytes Which means that the pointer returned by this macro is 2 behind the beginning of free_buckets (index=0), and pointing to the nearly end of the cache array (which is located before, hence "left" overflow). As it contains the same type, it doesn't break immediately, it will run smoothly, but it may lead to memory corruption later, when the last elements of the cache array will be accessed. Am I wrong? If I'm mistaking, and so this is the right behaviour, could anyone explain why does the pointer is shifted using the size of a structure, which does not contain only pointers but integers too? It could have made sense for me if it was containing only pointers, but it doesn't. En apparté, I haven't found where do the zend_mm_free_blocks are allocated. The heap is malloc-ated in the previous calling function (zend_mm_startup_ex), but there isn't any space for those, and right after the problematic macro, members are accessed: for (i = 0; i < ZEND_MM_NUM_BUCKETS; i++) { p->next_free_block = p; Does anyone have some hints to spare? It would be greatly appreciated. Thanks, Aurélien

« previous php.internals (#89030) next »