/* +----------------------------------------------------------------------+ | Zend Engine | +----------------------------------------------------------------------+ | Copyright (c) 1998-2001 Zend Technologies Ltd. (http://www.zend.com) | +----------------------------------------------------------------------+ | This source file is subject to version 0.92 of the Zend license, | | that is bundled with this package in the file LICENSE, and is | | available at through the world-wide-web at | | http://www.zend.com/license/0_92.txt. | | If you did not receive a copy of the Zend license and are unable to | | obtain it through the world-wide-web, please send a note to | | license@zend.com so we can mail you a copy immediately. | +----------------------------------------------------------------------+ | Authors: Andi Gutmans | | Zeev Suraski | +----------------------------------------------------------------------+ */ #include #include "zend.h" #include "zend_alloc.h" #include "zend_globals.h" #include "zend_fast_cache.h" #ifdef HAVE_SIGNAL_H # include #endif #ifdef HAVE_UNISTD_H # include #endif #ifndef ZTS ZEND_API zend_alloc_globals alloc_globals; #endif #define ZEND_DISABLE_MEMORY_CACHE 0 #if ZEND_DEBUG # define END_MAGIC_SIZE sizeof(long) # define END_ALIGNMENT(size) (((size)%PLATFORM_ALIGNMENT)?(PLATFORM_ALIGNMENT-((size)%PLATFORM_ALIGNMENT)):0) #else # define END_MAGIC_SIZE 0 # define END_ALIGNMENT(size) 0 #endif # if MEMORY_LIMIT # if ZEND_DEBUG #define CHECK_MEMORY_LIMIT(s, rs) _CHECK_MEMORY_LIMIT(s, rs, __zend_filename, __zend_lineno) # else #define CHECK_MEMORY_LIMIT(s, rs) _CHECK_MEMORY_LIMIT(s, rs, NULL,0) # endif #define _CHECK_MEMORY_LIMIT(s, rs, file, lineno) { AG(allocated_memory) += rs;\ if (AG(memory_limit)persistent && p==AG(head)) { \ AG(head) = p->pNext; \ } else if (p->persistent && p==AG(phead)) { \ AG(phead) = p->pNext; \ } else { \ p->pLast->pNext = p->pNext; \ } \ if (p->pNext) { \ p->pNext->pLast = p->pLast; \ } #define ADD_POINTER_TO_LIST(p) \ if (p->persistent) { \ p->pNext = AG(phead); \ if (AG(phead)) { \ AG(phead)->pLast = p; \ } \ AG(phead) = p; \ } else { \ p->pNext = AG(head); \ if (AG(head)) { \ AG(head)->pLast = p; \ } \ AG(head) = p; \ } \ p->pLast = (zend_mem_header *) NULL; #define DECLARE_CACHE_VARS \ unsigned int real_size; \ unsigned int cache_index; #define REAL_SIZE(size) ((size+7) & ~0x7) #define CALCULATE_REAL_SIZE_AND_CACHE_INDEX(size) \ real_size = REAL_SIZE(size); \ cache_index = real_size >> 3; #define SIZE real_size #define CACHE_INDEX cache_index ZEND_API void *_emalloc(size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { zend_mem_header *p; DECLARE_CACHE_VARS ALS_FETCH(); CALCULATE_REAL_SIZE_AND_CACHE_INDEX(size); if (!ZEND_DISABLE_MEMORY_CACHE && (CACHE_INDEX < MAX_CACHED_MEMORY) && (AG(cache_count)[CACHE_INDEX] > 0)) { p = AG(cache)[CACHE_INDEX][--AG(cache_count)[CACHE_INDEX]]; #if ZEND_DEBUG p->filename = __zend_filename; p->lineno = __zend_lineno; p->orig_filename = __zend_orig_filename; p->orig_lineno = __zend_orig_lineno; p->magic = MEM_BLOCK_START_MAGIC; p->reported = 0; /* Setting the thread id should not be necessary, because we fetched this block * from this thread's cache */ AG(cache_stats)[CACHE_INDEX][1]++; #endif p->persistent = 0; p->cached = 0; p->size = size; HANDLE_BLOCK_INTERRUPTIONS(); ADD_POINTER_TO_LIST(p); HANDLE_UNBLOCK_INTERRUPTIONS(); return (void *)((char *)p + sizeof(zend_mem_header) + PLATFORM_PADDING); } else { #if ZEND_DEBUG if (CACHE_INDEXpersistent = p->cached = 0; ADD_POINTER_TO_LIST(p); p->size = size; /* Save real size for correct cache output */ #if ZEND_DEBUG p->filename = __zend_filename; p->lineno = __zend_lineno; p->orig_filename = __zend_orig_filename; p->orig_lineno = __zend_orig_lineno; p->magic = MEM_BLOCK_START_MAGIC; p->reported = 0; # ifdef ZTS p->thread_id = tsrm_thread_id(); # endif *((long *)(((char *) p) + sizeof(zend_mem_header)+SIZE+PLATFORM_PADDING+END_ALIGNMENT(SIZE))) = MEM_BLOCK_END_MAGIC; #endif #if MEMORY_LIMIT CHECK_MEMORY_LIMIT(size, SIZE); #endif HANDLE_UNBLOCK_INTERRUPTIONS(); return (void *)((char *)p + sizeof(zend_mem_header) + PLATFORM_PADDING); } ZEND_API void _efree(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { zend_mem_header *p = (zend_mem_header *) ((char *)ptr - sizeof(zend_mem_header) - PLATFORM_PADDING); DECLARE_CACHE_VARS ALS_FETCH(); #if defined(ZTS) && ZEND_DEBUG if (p->thread_id != tsrm_thread_id()) { tsrm_error(TSRM_ERROR_LEVEL_ERROR, "Memory block allocated at %s:(%d) on thread %x freed at %s:(%d) on thread %x, ignoring", p->filename, p->lineno, p->thread_id, __zend_filename, __zend_lineno, tsrm_thread_id()); return; } #endif CALCULATE_REAL_SIZE_AND_CACHE_INDEX(p->size); #if ZEND_DEBUG if (!_mem_block_check(ptr, 1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC)) { return; } memset(ptr, 0x5a, SIZE); #endif if (!ZEND_DISABLE_MEMORY_CACHE && !p->persistent && (CACHE_INDEX < MAX_CACHED_MEMORY) && (AG(cache_count)[CACHE_INDEX] < MAX_CACHED_ENTRIES)) { AG(cache)[CACHE_INDEX][AG(cache_count)[CACHE_INDEX]++] = p; p->cached = 1; #if ZEND_DEBUG p->magic = MEM_BLOCK_CACHED_MAGIC; #endif HANDLE_BLOCK_INTERRUPTIONS(); REMOVE_POINTER_FROM_LIST(p); HANDLE_UNBLOCK_INTERRUPTIONS(); return; } HANDLE_BLOCK_INTERRUPTIONS(); REMOVE_POINTER_FROM_LIST(p); #if MEMORY_LIMIT AG(allocated_memory) -= SIZE; #endif free(p); HANDLE_UNBLOCK_INTERRUPTIONS(); } ZEND_API void *_ecalloc(size_t nmemb, size_t size ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { void *p; int final_size=size*nmemb; HANDLE_BLOCK_INTERRUPTIONS(); p = _emalloc(final_size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); if (!p) { HANDLE_UNBLOCK_INTERRUPTIONS(); return (void *) p; } memset(p,(int)NULL, final_size); HANDLE_UNBLOCK_INTERRUPTIONS(); return p; } ZEND_API void *_erealloc(void *ptr, size_t size, int allow_failure ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { zend_mem_header *p = (zend_mem_header *) ((char *)ptr-sizeof(zend_mem_header)-PLATFORM_PADDING); zend_mem_header *orig = p; DECLARE_CACHE_VARS ALS_FETCH(); if (!ptr) { return _emalloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } #if defined(ZTS) && ZEND_DEBUG if (p->thread_id != tsrm_thread_id()) { void *new_p; tsrm_error(TSRM_ERROR_LEVEL_ERROR, "Memory block allocated at %s:(%d) on thread %x reallocated at %s:(%d) on thread %x, duplicating", p->filename, p->lineno, p->thread_id, __zend_filename, __zend_lineno, tsrm_thread_id()); new_p = _emalloc(size ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); memcpy(new_p, ptr, p->size); return new_p; } #endif CALCULATE_REAL_SIZE_AND_CACHE_INDEX(size); HANDLE_BLOCK_INTERRUPTIONS(); REMOVE_POINTER_FROM_LIST(p); p = (zend_mem_header *) realloc(p,sizeof(zend_mem_header)+SIZE+PLATFORM_PADDING+END_ALIGNMENT(SIZE)+END_MAGIC_SIZE); if (!p) { if (!allow_failure) { fprintf(stderr,"FATAL: erealloc(): Unable to allocate %ld bytes\n", (long) size); #if ZEND_DEBUG && HAVE_KILL && HAVE_GETPID kill(getpid(), SIGSEGV); #else exit(1); #endif } ADD_POINTER_TO_LIST(orig); HANDLE_UNBLOCK_INTERRUPTIONS(); return (void *)NULL; } ADD_POINTER_TO_LIST(p); #if ZEND_DEBUG p->filename = __zend_filename; p->lineno = __zend_lineno; p->magic = MEM_BLOCK_START_MAGIC; *((long *)(((char *) p) + sizeof(zend_mem_header)+SIZE+PLATFORM_PADDING+END_ALIGNMENT(SIZE))) = MEM_BLOCK_END_MAGIC; #endif #if MEMORY_LIMIT CHECK_MEMORY_LIMIT(size - p->size, SIZE - REAL_SIZE(p->size)); #endif p->size = size; HANDLE_UNBLOCK_INTERRUPTIONS(); return (void *)((char *)p+sizeof(zend_mem_header)+PLATFORM_PADDING); } ZEND_API char *_estrdup(const char *s ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { int length; char *p; length = strlen(s)+1; HANDLE_BLOCK_INTERRUPTIONS(); p = (char *) _emalloc(length ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); if (!p) { HANDLE_UNBLOCK_INTERRUPTIONS(); return (char *)NULL; } HANDLE_UNBLOCK_INTERRUPTIONS(); memcpy(p,s,length); return p; } ZEND_API char *_estrndup(const char *s, uint length ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { char *p; HANDLE_BLOCK_INTERRUPTIONS(); p = (char *) _emalloc(length+1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); if (!p) { HANDLE_UNBLOCK_INTERRUPTIONS(); return (char *)NULL; } HANDLE_UNBLOCK_INTERRUPTIONS(); memcpy(p,s,length); p[length]=0; return p; } ZEND_API char *zend_strndup(const char *s, uint length) { char *p; p = (char *) malloc(length+1); if (!p) { return (char *)NULL; } if (length) { memcpy(p,s,length); } p[length]=0; return p; } ZEND_API int zend_set_memory_limit(unsigned int memory_limit) { #if MEMORY_LIMIT ALS_FETCH(); AG(memory_limit) = memory_limit; return SUCCESS; #else return FAILURE; #endif } ZEND_API void start_memory_manager(ALS_D) { #ifndef ZTS int i, j; void *cached_entries[MAX_CACHED_MEMORY][MAX_CACHED_ENTRIES]; #endif AG(phead) = AG(head) = NULL; #if MEMORY_LIMIT AG(memory_limit)=1<<30; /* rediculous limit, effectively no limit */ AG(allocated_memory)=0; AG(memory_exhausted)=0; #endif memset(AG(fast_cache_list_head), 0, sizeof(AG(fast_cache_list_head))); memset(AG(cache_count),0,sizeof(AG(cache_count))); #ifndef ZTS /* Initialize cache, to prevent fragmentation */ /* We can't do this in ZTS mode, because calling emalloc() from within start_memory_manager() * will yield an endless recursion calling to alloc_globals_ctor() */ for (i=1; inext; efree(fast_cache_list_entry); fast_cache_list_entry = next_fast_cache_list_entry; } AG(fast_cache_list_head)[fci] = NULL; } p=AG(head); t=AG(head); while (t) { if (!t->cached || clean_cache) { #if ZEND_DEBUG if (!t->cached && !t->reported) { zend_mem_header *iterator; int total_leak=0, total_leak_count=0; had_leaks=1; if (!silent) { zend_message_dispatcher(ZMSG_MEMORY_LEAK_DETECTED, t); } t->reported = 1; for (iterator=t->pNext; iterator; iterator=iterator->pNext) { if (!iterator->cached && iterator->filename==t->filename && iterator->lineno==t->lineno) { total_leak += iterator->size; total_leak_count++; iterator->reported = 1; } } if (!silent && total_leak_count>0) { zend_message_dispatcher(ZMSG_MEMORY_LEAK_REPEATED, (void *) (long) (total_leak_count)); } } #endif #if MEMORY_LIMIT AG(allocated_memory) -= t->size; #endif p = t->pNext; REMOVE_POINTER_FROM_LIST(t); free(t); t = p; } else { t = t->pNext; } } if(clean_cache) { for (i=1; imagic) { case MEM_BLOCK_START_MAGIC: if (!silent) { zend_debug_alloc_output("OK (allocated on %s:%d, %d bytes)\n", p->filename, p->lineno, p->size); } break; /* ok */ case MEM_BLOCK_FREED_MAGIC: if (!silent) { zend_debug_alloc_output("Freed\n"); had_problems=1; } else { return _mem_block_check(ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } break; case MEM_BLOCK_CACHED_MAGIC: if (!silent) { if (!no_cache_notice) { zend_debug_alloc_output("Cached (allocated on %s:%d, %d bytes)\n", p->filename, p->lineno, p->size); had_problems=1; } } else { if (!no_cache_notice) { return _mem_block_check(ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } } break; default: if (!silent) { zend_debug_alloc_output("Overrun (magic=0x%0.8lX, expected=0x%0.8lX)\n", p->magic, MEM_BLOCK_START_MAGIC); } else { return _mem_block_check(ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } had_problems=1; valid_beginning=0; break; } if (valid_beginning && *((long *)(((char *) p)+sizeof(zend_mem_header)+REAL_SIZE(p->size)+PLATFORM_PADDING+END_ALIGNMENT(REAL_SIZE(p->size)))) != MEM_BLOCK_END_MAGIC) { long magic_num = MEM_BLOCK_END_MAGIC; char *overflow_ptr, *magic_ptr=(char *) &magic_num; int overflows=0; int i; if (silent) { return _mem_block_check(ptr, 0 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); } had_problems=1; overflow_ptr = ((char *) p)+sizeof(zend_mem_header)+REAL_SIZE(p->size)+PLATFORM_PADDING; for (i=0; isize)+PLATFORM_PADDING+END_ALIGNMENT(REAL_SIZE(p->size)))), MEM_BLOCK_END_MAGIC); zend_debug_alloc_output("%10s\t",""); if (overflows>=sizeof(long)) { zend_debug_alloc_output("At least %d bytes overflown\n", sizeof(long)); } else { zend_debug_alloc_output("%d byte(s) overflown\n", overflows); } } else if (!silent) { zend_debug_alloc_output("%10s\t", "End:"); if (valid_beginning) { zend_debug_alloc_output("OK\n"); } else { zend_debug_alloc_output("Unknown\n"); } } if (had_problems) { int foo = 5; foo+=1; } if (!silent) { zend_debug_alloc_output("---------------------------------------\n"); } return ((!had_problems) ? 1 : 0); } ZEND_API void _full_mem_check(int silent ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { zend_mem_header *p; int errors=0; ALS_FETCH(); p = AG(head); zend_debug_alloc_output("------------------------------------------------\n"); zend_debug_alloc_output("Full Memory Check at %s:%d\n" ZEND_FILE_LINE_RELAY_CC); while (p) { if (!_mem_block_check((void *)((char *)p + sizeof(zend_mem_header) + PLATFORM_PADDING), (silent?2:3) ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC)) { errors++; } p = p->pNext; } zend_debug_alloc_output("End of full memory check %s:%d (%d errors)\n" ZEND_FILE_LINE_RELAY_CC, errors); zend_debug_alloc_output("------------------------------------------------\n"); } #endif ZEND_API int _persist_alloc(void *ptr ZEND_FILE_LINE_DC ZEND_FILE_LINE_ORIG_DC) { zend_mem_header *p = (zend_mem_header *) ((char *)ptr-sizeof(zend_mem_header)-PLATFORM_PADDING); ALS_FETCH(); #if ZEND_DEBUG _mem_block_check(ptr, 1 ZEND_FILE_LINE_RELAY_CC ZEND_FILE_LINE_ORIG_RELAY_CC); #endif HANDLE_BLOCK_INTERRUPTIONS(); /* remove the block from the non persistent list */ REMOVE_POINTER_FROM_LIST(p); p->persistent = 1; /* add the block to the persistent list */ ADD_POINTER_TO_LIST(p); HANDLE_UNBLOCK_INTERRUPTIONS(); return REAL_SIZE(p->size)+sizeof(zend_mem_header)+PLATFORM_PADDING; } /* * Local variables: * tab-width: 4 * c-basic-offset: 4 * End: */