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)
- 2sizeof(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