/* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % M M EEEEE M M OOO RRRR Y Y % % MM MM E MM MM O O R R Y Y % % M M M EEE M M M O O RRRR Y % % M M E M M O O R R Y % % M M EEEEE M M OOO R R Y % % % % % % MagickCore Memory Allocation Methods % % % % Software Design % % Cristy % % July 1998 % % % % % % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization % % dedicated to making software imaging solutions freely available. % % % % You may not use this file except in compliance with the License. You may % % obtain a copy of the License at % % % % https://imagemagick.org/script/license.php % % % % Unless required by applicable law or agreed to in writing, software % % distributed under the License is distributed on an "AS IS" BASIS, % % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % % See the License for the specific language governing permissions and % % limitations under the License. % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % We provide these memory allocators: % % AcquireCriticalMemory(): allocate a small memory request with % AcquireMagickMemory(), however, on fail throw a fatal exception and exit. % Free the memory reserve with RelinquishMagickMemory(). % AcquireAlignedMemory(): allocate a small memory request that is aligned % on a cache line. On fail, return NULL for possible recovery. % Free the memory reserve with RelinquishMagickMemory(). % AcquireMagickMemory()/ResizeMagickMemory(): allocate a small to medium % memory request, typically with malloc()/realloc(). On fail, return NULL % for possible recovery. Free the memory reserve with % RelinquishMagickMemory(). % AcquireQuantumMemory()/ResizeQuantumMemory(): allocate a small to medium % memory request. This is a secure memory allocator as it accepts two % parameters, count and quantum, to ensure the request does not overflow. % It also check to ensure the request does not exceed the maximum memory % per the security policy. Free the memory reserve with % RelinquishMagickMemory(). % AcquireVirtualMemory(): allocate a large memory request either in heap, % memory-mapped, or memory-mapped on disk depending on whether heap % allocation fails or if the request exceeds the maximum memory policy. % Free the memory reserve with RelinquishVirtualMemory(). % ResetMagickMemory(): fills the bytes of the memory area with a constant % byte. % % In addition, we provide hooks for your own memory constructor/destructors. % You can also utilize our internal custom allocator as follows: Segregate % our memory requirements from any program that calls our API. This should % help reduce the risk of others changing our program state or causing memory % corruption. % % Our custom memory allocation manager implements a best-fit allocation policy % using segregated free lists. It uses a linear distribution of size classes % for lower sizes and a power of two distribution of size classes at higher % sizes. It is based on the paper, "Fast Memory Allocation using Lazy Fits." % written by Yoo C. Chung. % % By default, C's standard library is used (e.g. malloc); use the % custom memory allocator by defining MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT % to allocate memory with private anonymous mapping rather than from the % heap. % */ /* Include declarations. */ #include "MagickCore/studio.h" #include "MagickCore/blob.h" #include "MagickCore/blob-private.h" #include "MagickCore/exception.h" #include "MagickCore/exception-private.h" #include "MagickCore/image-private.h" #include "MagickCore/memory_.h" #include "MagickCore/memory-private.h" #include "MagickCore/policy.h" #include "MagickCore/resource_.h" #include "MagickCore/semaphore.h" #include "MagickCore/string_.h" #include "MagickCore/string-private.h" #include "MagickCore/utility-private.h" /* Define declarations. */ #define BlockFooter(block,size) \ ((size_t *) ((char *) (block)+(size)-2*sizeof(size_t))) #define BlockHeader(block) ((size_t *) (block)-1) #define BlockThreshold 1024 #define MaxBlockExponent 16 #define MaxBlocks ((BlockThreshold/(4*sizeof(size_t)))+MaxBlockExponent+1) #define MaxSegments 1024 #define NextBlock(block) ((char *) (block)+SizeOfBlock(block)) #define NextBlockInList(block) (*(void **) (block)) #define PreviousBlock(block) ((char *) (block)-(*((size_t *) (block)-2))) #define PreviousBlockBit 0x01 #define PreviousBlockInList(block) (*((void **) (block)+1)) #define SegmentSize (2*1024*1024) #define SizeMask (~0x01) #define SizeOfBlock(block) (*BlockHeader(block) & SizeMask) /* Typedef declarations. */ typedef enum { UndefinedVirtualMemory, AlignedVirtualMemory, MapVirtualMemory, UnalignedVirtualMemory } VirtualMemoryType; typedef struct _DataSegmentInfo { void *allocation, *bound; MagickBooleanType mapped; size_t length; struct _DataSegmentInfo *previous, *next; } DataSegmentInfo; typedef struct _MagickMemoryMethods { AcquireMemoryHandler acquire_memory_handler; ResizeMemoryHandler resize_memory_handler; DestroyMemoryHandler destroy_memory_handler; AcquireAlignedMemoryHandler acquire_aligned_memory_handler; RelinquishAlignedMemoryHandler relinquish_aligned_memory_handler; } MagickMemoryMethods; struct _MemoryInfo { char filename[MagickPathExtent]; VirtualMemoryType type; size_t length; void *blob; size_t signature; }; typedef struct _MemoryPool { size_t allocation; void *blocks[MaxBlocks+1]; size_t number_segments; DataSegmentInfo *segments[MaxSegments], segment_pool[MaxSegments]; } MemoryPool; /* Global declarations. */ static size_t max_memory_request = 0, virtual_anonymous_memory = 0; #if defined _MSC_VER static void *MSCMalloc(size_t size) { return(malloc(size)); } static void *MSCRealloc(void* ptr, size_t size) { return(realloc(ptr,size)); } static void MSCFree(void* ptr) { free(ptr); } #endif static MagickMemoryMethods memory_methods = { #if defined _MSC_VER (AcquireMemoryHandler) MSCMalloc, (ResizeMemoryHandler) MSCRealloc, (DestroyMemoryHandler) MSCFree, #else (AcquireMemoryHandler) malloc, (ResizeMemoryHandler) realloc, (DestroyMemoryHandler) free, #endif (AcquireAlignedMemoryHandler) NULL, (RelinquishAlignedMemoryHandler) NULL }; #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT) static MemoryPool memory_pool; static SemaphoreInfo *memory_semaphore = (SemaphoreInfo *) NULL; static volatile DataSegmentInfo *free_segments = (DataSegmentInfo *) NULL; /* Forward declarations. */ static MagickBooleanType ExpandHeap(size_t); #endif /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A c q u i r e A l i g n e d M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % AcquireAlignedMemory() returns a pointer to a block of memory whose size is % at least (count*quantum) bytes, and whose address is aligned on a cache line. % % The format of the AcquireAlignedMemory method is: % % void *AcquireAlignedMemory(const size_t count,const size_t quantum) % % A description of each parameter follows: % % o count: the number of objects to allocate contiguously. % % o quantum: the size (in bytes) of each object. % */ #if defined(MAGICKCORE_HAVE_ALIGNED_MALLOC) #define AcquireAlignedMemory_Actual AcquireAlignedMemory_STDC static inline void *AcquireAlignedMemory_STDC(const size_t size) { size_t extent = CACHE_ALIGNED(size); if (extent < size) { errno=ENOMEM; return(NULL); } return(aligned_alloc(CACHE_LINE_SIZE,extent)); } #elif defined(MAGICKCORE_HAVE_POSIX_MEMALIGN) #define AcquireAlignedMemory_Actual AcquireAlignedMemory_POSIX static inline void *AcquireAlignedMemory_POSIX(const size_t size) { void *memory; if (posix_memalign(&memory,CACHE_LINE_SIZE,size)) return(NULL); return(memory); } #elif defined(MAGICKCORE_HAVE__ALIGNED_MALLOC) #define AcquireAlignedMemory_Actual AcquireAlignedMemory_WinAPI static inline void *AcquireAlignedMemory_WinAPI(const size_t size) { return(_aligned_malloc(size,CACHE_LINE_SIZE)); } #else #define ALIGNMENT_OVERHEAD \ (MAGICKCORE_MAX_ALIGNMENT_PADDING(CACHE_LINE_SIZE) + MAGICKCORE_SIZEOF_VOID_P) static inline void *reserve_space_for_actual_base_address(void *const p) { return((void **) p+1); } static inline void **pointer_to_space_for_actual_base_address(void *const p) { return((void **) p-1); } static inline void *actual_base_address(void *const p) { return(*pointer_to_space_for_actual_base_address(p)); } static inline void *align_to_cache(void *const p) { return((void *) CACHE_ALIGNED((MagickAddressType) p)); } static inline void *adjust(void *const p) { return(align_to_cache(reserve_space_for_actual_base_address(p))); } #define AcquireAlignedMemory_Actual AcquireAlignedMemory_Generic static inline void *AcquireAlignedMemory_Generic(const size_t size) { size_t extent; void *memory, *p; #if SIZE_MAX < ALIGNMENT_OVERHEAD #error "CACHE_LINE_SIZE is way too big." #endif extent=(size+ALIGNMENT_OVERHEAD); if (extent <= size) { errno=ENOMEM; return(NULL); } p=AcquireMagickMemory(extent); if (p == NULL) return(NULL); memory=adjust(p); *pointer_to_space_for_actual_base_address(memory)=p; return(memory); } #endif MagickExport void *AcquireAlignedMemory(const size_t count,const size_t quantum) { size_t size; if (HeapOverflowSanityCheckGetSize(count,quantum,&size) != MagickFalse) { errno=ENOMEM; return(NULL); } if (memory_methods.acquire_aligned_memory_handler != (AcquireAlignedMemoryHandler) NULL) return(memory_methods.acquire_aligned_memory_handler(size,CACHE_LINE_SIZE)); return(AcquireAlignedMemory_Actual(size)); } #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT) /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + A c q u i r e B l o c k % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % AcquireBlock() returns a pointer to a block of memory at least size bytes % suitably aligned for any use. % % The format of the AcquireBlock method is: % % void *AcquireBlock(const size_t size) % % A description of each parameter follows: % % o size: the size of the memory in bytes to allocate. % */ static inline size_t AllocationPolicy(size_t size) { size_t blocksize; /* The linear distribution. */ assert(size != 0); assert(size % (4*sizeof(size_t)) == 0); if (size <= BlockThreshold) return(size/(4*sizeof(size_t))); /* Check for the largest block size. */ if (size > (size_t) (BlockThreshold*(1L << (MaxBlockExponent-1L)))) return(MaxBlocks-1L); /* Otherwise use a power of two distribution. */ blocksize=BlockThreshold/(4*sizeof(size_t)); for ( ; size > BlockThreshold; size/=2) blocksize++; assert(blocksize > (BlockThreshold/(4*sizeof(size_t)))); assert(blocksize < (MaxBlocks-1L)); return(blocksize); } static inline void InsertFreeBlock(void *block,const size_t i) { void *next, *previous; size_t size; size=SizeOfBlock(block); previous=(void *) NULL; next=memory_pool.blocks[i]; while ((next != (void *) NULL) && (SizeOfBlock(next) < size)) { previous=next; next=NextBlockInList(next); } PreviousBlockInList(block)=previous; NextBlockInList(block)=next; if (previous != (void *) NULL) NextBlockInList(previous)=block; else memory_pool.blocks[i]=block; if (next != (void *) NULL) PreviousBlockInList(next)=block; } static inline void RemoveFreeBlock(void *block,const size_t i) { void *next, *previous; next=NextBlockInList(block); previous=PreviousBlockInList(block); if (previous == (void *) NULL) memory_pool.blocks[i]=next; else NextBlockInList(previous)=next; if (next != (void *) NULL) PreviousBlockInList(next)=previous; } static void *AcquireBlock(size_t size) { size_t i; void *block; /* Find free block. */ size=(size_t) (size+sizeof(size_t)+6*sizeof(size_t)-1) & -(4U*sizeof(size_t)); i=AllocationPolicy(size); block=memory_pool.blocks[i]; while ((block != (void *) NULL) && (SizeOfBlock(block) < size)) block=NextBlockInList(block); if (block == (void *) NULL) { i++; while (memory_pool.blocks[i] == (void *) NULL) i++; block=memory_pool.blocks[i]; if (i >= MaxBlocks) return((void *) NULL); } assert((*BlockHeader(NextBlock(block)) & PreviousBlockBit) == 0); assert(SizeOfBlock(block) >= size); RemoveFreeBlock(block,AllocationPolicy(SizeOfBlock(block))); if (SizeOfBlock(block) > size) { size_t blocksize; void *next; /* Split block. */ next=(char *) block+size; blocksize=SizeOfBlock(block)-size; *BlockHeader(next)=blocksize; *BlockFooter(next,blocksize)=blocksize; InsertFreeBlock(next,AllocationPolicy(blocksize)); *BlockHeader(block)=size | (*BlockHeader(block) & ~SizeMask); } assert(size == SizeOfBlock(block)); *BlockHeader(NextBlock(block))|=PreviousBlockBit; memory_pool.allocation+=size; return(block); } #endif /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A c q u i r e M a g i c k M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % AcquireMagickMemory() returns a pointer to a block of memory at least size % bytes suitably aligned for any use. % % The format of the AcquireMagickMemory method is: % % void *AcquireMagickMemory(const size_t size) % % A description of each parameter follows: % % o size: the size of the memory in bytes to allocate. % */ MagickExport void *AcquireMagickMemory(const size_t size) { void *memory; #if !defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT) memory=memory_methods.acquire_memory_handler(size == 0 ? 1UL : size); #else if (memory_semaphore == (SemaphoreInfo *) NULL) ActivateSemaphoreInfo(&memory_semaphore); if (free_segments == (DataSegmentInfo *) NULL) { LockSemaphoreInfo(memory_semaphore); if (free_segments == (DataSegmentInfo *) NULL) { ssize_t i; assert(2*sizeof(size_t) > (size_t) (~SizeMask)); (void) memset(&memory_pool,0,sizeof(memory_pool)); memory_pool.allocation=SegmentSize; memory_pool.blocks[MaxBlocks]=(void *) (-1); for (i=0; i < MaxSegments; i++) { if (i != 0) memory_pool.segment_pool[i].previous= (&memory_pool.segment_pool[i-1]); if (i != (MaxSegments-1)) memory_pool.segment_pool[i].next=(&memory_pool.segment_pool[i+1]); } free_segments=(&memory_pool.segment_pool[0]); } UnlockSemaphoreInfo(memory_semaphore); } LockSemaphoreInfo(memory_semaphore); memory=AcquireBlock(size == 0 ? 1UL : size); if (memory == (void *) NULL) { if (ExpandHeap(size == 0 ? 1UL : size) != MagickFalse) memory=AcquireBlock(size == 0 ? 1UL : size); } UnlockSemaphoreInfo(memory_semaphore); #endif return(memory); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A c q u i r e C r i t i c a l M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % AcquireCriticalMemory() is just like AcquireMagickMemory(), throws a fatal % exception if the memory cannot be acquired. % % That is, AcquireCriticalMemory() returns a pointer to a block of memory that % is at least size bytes, and that is suitably aligned for any use; however, % if this is not possible, it throws an exception and terminates the program % as unceremoniously as possible. % % The format of the AcquireCriticalMemory method is: % % void *AcquireCriticalMemory(const size_t size) % % A description of each parameter follows: % % o size: the size (in bytes) of the memory to allocate. % */ MagickExport void *AcquireCriticalMemory(const size_t size) { void *memory; /* Fail if memory request cannot be fulfilled. */ memory=AcquireMagickMemory(size); if (memory == (void *) NULL) ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); return(memory); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A c q u i r e Q u a n t u m M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % AcquireQuantumMemory() returns a pointer to a block of memory at least % count * quantum bytes suitably aligned for any use. % % The format of the AcquireQuantumMemory method is: % % void *AcquireQuantumMemory(const size_t count,const size_t quantum) % % A description of each parameter follows: % % o count: the number of objects to allocate contiguously. % % o quantum: the size (in bytes) of each object. % */ MagickExport void *AcquireQuantumMemory(const size_t count,const size_t quantum) { size_t size; if ((HeapOverflowSanityCheckGetSize(count,quantum,&size) != MagickFalse) || (size > GetMaxMemoryRequest())) { errno=ENOMEM; return(NULL); } return(AcquireMagickMemory(size)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % A c q u i r e V i r t u a l M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % AcquireVirtualMemory() allocates a pointer to a block of memory at least % size bytes suitably aligned for any use. In addition to heap, it also % supports memory-mapped and file-based memory-mapped memory requests. % % The format of the AcquireVirtualMemory method is: % % MemoryInfo *AcquireVirtualMemory(const size_t count,const size_t quantum) % % A description of each parameter follows: % % o count: the number of objects to allocate contiguously. % % o quantum: the size (in bytes) of each object. % */ MagickExport MemoryInfo *AcquireVirtualMemory(const size_t count, const size_t quantum) { char *value; MemoryInfo *memory_info; size_t size; if (HeapOverflowSanityCheckGetSize(count,quantum,&size) != MagickFalse) { errno=ENOMEM; return((MemoryInfo *) NULL); } if (virtual_anonymous_memory == 0) { virtual_anonymous_memory=1; value=GetPolicyValue("system:memory-map"); if (LocaleCompare(value,"anonymous") == 0) { /* The security policy sets anonymous mapping for the memory request. */ #if defined(MAGICKCORE_HAVE_MMAP) && defined(MAP_ANONYMOUS) virtual_anonymous_memory=2; #endif } value=DestroyString(value); } memory_info=(MemoryInfo *) MagickAssumeAligned(AcquireAlignedMemory(1, sizeof(*memory_info))); if (memory_info == (MemoryInfo *) NULL) ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); (void) memset(memory_info,0,sizeof(*memory_info)); memory_info->length=size; memory_info->signature=MagickCoreSignature; if ((virtual_anonymous_memory == 1) && (size <= GetMaxMemoryRequest())) { memory_info->blob=AcquireAlignedMemory(1,size); if (memory_info->blob != NULL) memory_info->type=AlignedVirtualMemory; } if (memory_info->blob == NULL) { /* Acquire anonymous memory map. */ memory_info->blob=NULL; if (size <= GetMaxMemoryRequest()) memory_info->blob=MapBlob(-1,IOMode,0,size); if (memory_info->blob != NULL) memory_info->type=MapVirtualMemory; else { int file; /* Anonymous memory mapping failed, try file-backed memory mapping. */ file=AcquireUniqueFileResource(memory_info->filename); if (file != -1) { MagickOffsetType offset; offset=(MagickOffsetType) lseek(file,size-1,SEEK_SET); if ((offset == (MagickOffsetType) (size-1)) && (write(file,"",1) == 1)) { #if !defined(MAGICKCORE_HAVE_POSIX_FALLOCATE) memory_info->blob=MapBlob(file,IOMode,0,size); #else if (posix_fallocate(file,0,(MagickOffsetType) size) == 0) memory_info->blob=MapBlob(file,IOMode,0,size); #endif if (memory_info->blob != NULL) memory_info->type=MapVirtualMemory; else { (void) RelinquishUniqueFileResource( memory_info->filename); *memory_info->filename='\0'; } } (void) close(file); } } } if (memory_info->blob == NULL) { memory_info->blob=AcquireQuantumMemory(1,size); if (memory_info->blob != NULL) memory_info->type=UnalignedVirtualMemory; } if (memory_info->blob == NULL) memory_info=RelinquishVirtualMemory(memory_info); return(memory_info); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % C o p y M a g i c k M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % CopyMagickMemory() copies size bytes from memory area source to the % destination. Copying between objects that overlap will take place % correctly. It returns destination. % % The format of the CopyMagickMemory method is: % % void *CopyMagickMemory(void *magick_restrict destination, % const void *magick_restrict source,const size_t size) % % A description of each parameter follows: % % o destination: the destination. % % o source: the source. % % o size: the size of the memory in bytes to allocate. % */ MagickExport void *CopyMagickMemory(void *magick_restrict destination, const void *magick_restrict source,const size_t size) { const unsigned char *p; unsigned char *q; assert(destination != (void *) NULL); assert(source != (const void *) NULL); p=(const unsigned char *) source; q=(unsigned char *) destination; if (((q+size) < p) || (q > (p+size))) switch (size) { default: return(memcpy(destination,source,size)); case 8: *q++=(*p++); case 7: *q++=(*p++); case 6: *q++=(*p++); case 5: *q++=(*p++); case 4: *q++=(*p++); case 3: *q++=(*p++); case 2: *q++=(*p++); case 1: *q++=(*p++); case 0: return(destination); } return(memmove(destination,source,size)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + D e s t r o y M a g i c k M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % DestroyMagickMemory() deallocates memory associated with the memory manager. % % The format of the DestroyMagickMemory method is: % % DestroyMagickMemory(void) % */ MagickExport void DestroyMagickMemory(void) { #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT) ssize_t i; if (memory_semaphore == (SemaphoreInfo *) NULL) ActivateSemaphoreInfo(&memory_semaphore); LockSemaphoreInfo(memory_semaphore); for (i=0; i < (ssize_t) memory_pool.number_segments; i++) if (memory_pool.segments[i]->mapped == MagickFalse) memory_methods.destroy_memory_handler( memory_pool.segments[i]->allocation); else (void) UnmapBlob(memory_pool.segments[i]->allocation, memory_pool.segments[i]->length); free_segments=(DataSegmentInfo *) NULL; (void) memset(&memory_pool,0,sizeof(memory_pool)); UnlockSemaphoreInfo(memory_semaphore); RelinquishSemaphoreInfo(&memory_semaphore); #endif } #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT) /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + E x p a n d H e a p % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ExpandHeap() get more memory from the system. It returns MagickTrue on % success otherwise MagickFalse. % % The format of the ExpandHeap method is: % % MagickBooleanType ExpandHeap(size_t size) % % A description of each parameter follows: % % o size: the size of the memory in bytes we require. % */ static MagickBooleanType ExpandHeap(size_t size) { DataSegmentInfo *segment_info; MagickBooleanType mapped; ssize_t i; void *block; size_t blocksize; void *segment; blocksize=((size+12*sizeof(size_t))+SegmentSize-1) & -SegmentSize; assert(memory_pool.number_segments < MaxSegments); segment=MapBlob(-1,IOMode,0,blocksize); mapped=segment != (void *) NULL ? MagickTrue : MagickFalse; if (segment == (void *) NULL) segment=(void *) memory_methods.acquire_memory_handler(blocksize); if (segment == (void *) NULL) return(MagickFalse); segment_info=(DataSegmentInfo *) free_segments; free_segments=segment_info->next; segment_info->mapped=mapped; segment_info->length=blocksize; segment_info->allocation=segment; segment_info->bound=(char *) segment+blocksize; i=(ssize_t) memory_pool.number_segments-1; for ( ; (i >= 0) && (memory_pool.segments[i]->allocation > segment); i--) memory_pool.segments[i+1]=memory_pool.segments[i]; memory_pool.segments[i+1]=segment_info; memory_pool.number_segments++; size=blocksize-12*sizeof(size_t); block=(char *) segment_info->allocation+4*sizeof(size_t); *BlockHeader(block)=size | PreviousBlockBit; *BlockFooter(block,size)=size; InsertFreeBlock(block,AllocationPolicy(size)); block=NextBlock(block); assert(block < segment_info->bound); *BlockHeader(block)=2*sizeof(size_t); *BlockHeader(NextBlock(block))=PreviousBlockBit; return(MagickTrue); } #endif /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t M a g i c k M e m o r y M e t h o d s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetMagickMemoryMethods() gets the methods to acquire, resize, and destroy % memory. % % The format of the GetMagickMemoryMethods() method is: % % void GetMagickMemoryMethods(AcquireMemoryHandler *acquire_memory_handler, % ResizeMemoryHandler *resize_memory_handler, % DestroyMemoryHandler *destroy_memory_handler) % % A description of each parameter follows: % % o acquire_memory_handler: method to acquire memory (e.g. malloc). % % o resize_memory_handler: method to resize memory (e.g. realloc). % % o destroy_memory_handler: method to destroy memory (e.g. free). % */ MagickExport void GetMagickMemoryMethods( AcquireMemoryHandler *acquire_memory_handler, ResizeMemoryHandler *resize_memory_handler, DestroyMemoryHandler *destroy_memory_handler) { assert(acquire_memory_handler != (AcquireMemoryHandler *) NULL); assert(resize_memory_handler != (ResizeMemoryHandler *) NULL); assert(destroy_memory_handler != (DestroyMemoryHandler *) NULL); *acquire_memory_handler=memory_methods.acquire_memory_handler; *resize_memory_handler=memory_methods.resize_memory_handler; *destroy_memory_handler=memory_methods.destroy_memory_handler; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + G e t M a x M e m o r y R e q u e s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetMaxMemoryRequest() returns the max_memory_request value. % % The format of the GetMaxMemoryRequest method is: % % size_t GetMaxMemoryRequest(void) % */ MagickExport size_t GetMaxMemoryRequest(void) { #define MinMemoryRequest "16MiB" if (max_memory_request == 0) { char *value; max_memory_request=(size_t) MagickULLConstant(~0); value=GetPolicyValue("system:max-memory-request"); if (value != (char *) NULL) { /* The security policy sets a max memory request limit. */ max_memory_request=MagickMax(StringToSizeType(value,100.0), StringToSizeType(MinMemoryRequest,100.0)); value=DestroyString(value); } } return(MagickMin(max_memory_request,MAGICK_SSIZE_MAX)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % G e t V i r t u a l M e m o r y B l o b % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % GetVirtualMemoryBlob() returns the virtual memory blob associated with the % specified MemoryInfo structure. % % The format of the GetVirtualMemoryBlob method is: % % void *GetVirtualMemoryBlob(const MemoryInfo *memory_info) % % A description of each parameter follows: % % o memory_info: The MemoryInfo structure. */ MagickExport void *GetVirtualMemoryBlob(const MemoryInfo *memory_info) { assert(memory_info != (const MemoryInfo *) NULL); assert(memory_info->signature == MagickCoreSignature); return(memory_info->blob); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e l i n q u i s h A l i g n e d M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % RelinquishAlignedMemory() frees memory acquired with AcquireAlignedMemory() % or reuse. % % The format of the RelinquishAlignedMemory method is: % % void *RelinquishAlignedMemory(void *memory) % % A description of each parameter follows: % % o memory: A pointer to a block of memory to free for reuse. % */ MagickExport void *RelinquishAlignedMemory(void *memory) { if (memory == (void *) NULL) return((void *) NULL); if (memory_methods.relinquish_aligned_memory_handler != (RelinquishAlignedMemoryHandler) NULL) { memory_methods.relinquish_aligned_memory_handler(memory); return(NULL); } #if defined(MAGICKCORE_HAVE_ALIGNED_MALLOC) || defined(MAGICKCORE_HAVE_POSIX_MEMALIGN) free(memory); #elif defined(MAGICKCORE_HAVE__ALIGNED_MALLOC) _aligned_free(memory); #else RelinquishMagickMemory(actual_base_address(memory)); #endif return(NULL); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e l i n q u i s h M a g i c k M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % RelinquishMagickMemory() frees memory acquired with AcquireMagickMemory() % or AcquireQuantumMemory() for reuse. % % The format of the RelinquishMagickMemory method is: % % void *RelinquishMagickMemory(void *memory) % % A description of each parameter follows: % % o memory: A pointer to a block of memory to free for reuse. % */ MagickExport void *RelinquishMagickMemory(void *memory) { if (memory == (void *) NULL) return((void *) NULL); #if !defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT) memory_methods.destroy_memory_handler(memory); #else LockSemaphoreInfo(memory_semaphore); assert((SizeOfBlock(memory) % (4*sizeof(size_t))) == 0); assert((*BlockHeader(NextBlock(memory)) & PreviousBlockBit) != 0); if ((*BlockHeader(memory) & PreviousBlockBit) == 0) { void *previous; /* Coalesce with previous adjacent block. */ previous=PreviousBlock(memory); RemoveFreeBlock(previous,AllocationPolicy(SizeOfBlock(previous))); *BlockHeader(previous)=(SizeOfBlock(previous)+SizeOfBlock(memory)) | (*BlockHeader(previous) & ~SizeMask); memory=previous; } if ((*BlockHeader(NextBlock(NextBlock(memory))) & PreviousBlockBit) == 0) { void *next; /* Coalesce with next adjacent block. */ next=NextBlock(memory); RemoveFreeBlock(next,AllocationPolicy(SizeOfBlock(next))); *BlockHeader(memory)=(SizeOfBlock(memory)+SizeOfBlock(next)) | (*BlockHeader(memory) & ~SizeMask); } *BlockFooter(memory,SizeOfBlock(memory))=SizeOfBlock(memory); *BlockHeader(NextBlock(memory))&=(~PreviousBlockBit); InsertFreeBlock(memory,AllocationPolicy(SizeOfBlock(memory))); UnlockSemaphoreInfo(memory_semaphore); #endif return((void *) NULL); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e l i n q u i s h V i r t u a l M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % RelinquishVirtualMemory() frees memory acquired with AcquireVirtualMemory(). % % The format of the RelinquishVirtualMemory method is: % % MemoryInfo *RelinquishVirtualMemory(MemoryInfo *memory_info) % % A description of each parameter follows: % % o memory_info: A pointer to a block of memory to free for reuse. % */ MagickExport MemoryInfo *RelinquishVirtualMemory(MemoryInfo *memory_info) { assert(memory_info != (MemoryInfo *) NULL); assert(memory_info->signature == MagickCoreSignature); if (memory_info->blob != (void *) NULL) switch (memory_info->type) { case AlignedVirtualMemory: { memory_info->blob=RelinquishAlignedMemory(memory_info->blob); break; } case MapVirtualMemory: { (void) UnmapBlob(memory_info->blob,memory_info->length); memory_info->blob=NULL; if (*memory_info->filename != '\0') (void) RelinquishUniqueFileResource(memory_info->filename); break; } case UnalignedVirtualMemory: default: { memory_info->blob=RelinquishMagickMemory(memory_info->blob); break; } } memory_info->signature=(~MagickCoreSignature); memory_info=(MemoryInfo *) RelinquishAlignedMemory(memory_info); return(memory_info); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e s e t M a g i c k M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ResetMagickMemory() fills the first size bytes of the memory area pointed to % by memory with the constant byte c. We use a volatile pointer when % updating the byte string. Most compilers will avoid optimizing away access % to a volatile pointer, even if the pointer appears to be unused after the % call. % % The format of the ResetMagickMemory method is: % % void *ResetMagickMemory(void *memory,int c,const size_t size) % % A description of each parameter follows: % % o memory: a pointer to a memory allocation. % % o c: set the memory to this value. % % o size: size of the memory to reset. % */ MagickExport void *ResetMagickMemory(void *memory,int c,const size_t size) { volatile unsigned char *p = (volatile unsigned char *) memory; size_t n = size; assert(memory != (void *) NULL); while (n-- != 0) *p++=(unsigned char) c; return(memory); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + R e s e t M a x M e m o r y R e q u e s t % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ResetMaxMemoryRequest() resets the max_memory_request value. % % The format of the ResetMaxMemoryRequest method is: % % void ResetMaxMemoryRequest(void) % */ MagickPrivate void ResetMaxMemoryRequest(void) { max_memory_request=0; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % + R e s e t V i r t u a l A n o n y m o u s M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ResetVirtualAnonymousMemory() resets the virtual_anonymous_memory value. % % The format of the ResetVirtualAnonymousMemory method is: % % void ResetVirtualAnonymousMemory(void) % */ MagickPrivate void ResetVirtualAnonymousMemory(void) { virtual_anonymous_memory=0; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e s i z e M a g i c k M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ResizeMagickMemory() changes the size of the memory and returns a pointer to % the (possibly moved) block. The contents will be unchanged up to the % lesser of the new and old sizes. % % The format of the ResizeMagickMemory method is: % % void *ResizeMagickMemory(void *memory,const size_t size) % % A description of each parameter follows: % % o memory: A pointer to a memory allocation. % % o size: the new size of the allocated memory. % */ #if defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT) static inline void *ResizeBlock(void *block,size_t size) { void *memory; if (block == (void *) NULL) return(AcquireBlock(size)); memory=AcquireBlock(size); if (memory == (void *) NULL) return((void *) NULL); if (size <= (SizeOfBlock(block)-sizeof(size_t))) (void) memcpy(memory,block,size); else (void) memcpy(memory,block,SizeOfBlock(block)-sizeof(size_t)); memory_pool.allocation+=size; return(memory); } #endif MagickExport void *ResizeMagickMemory(void *memory,const size_t size) { void *block; if (memory == (void *) NULL) return(AcquireMagickMemory(size)); #if !defined(MAGICKCORE_ANONYMOUS_MEMORY_SUPPORT) block=memory_methods.resize_memory_handler(memory,size == 0 ? 1UL : size); if (block == (void *) NULL) memory=RelinquishMagickMemory(memory); #else LockSemaphoreInfo(memory_semaphore); block=ResizeBlock(memory,size == 0 ? 1UL : size); if (block == (void *) NULL) { if (ExpandHeap(size == 0 ? 1UL : size) == MagickFalse) { UnlockSemaphoreInfo(memory_semaphore); memory=RelinquishMagickMemory(memory); ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); } block=ResizeBlock(memory,size == 0 ? 1UL : size); assert(block != (void *) NULL); } UnlockSemaphoreInfo(memory_semaphore); memory=RelinquishMagickMemory(memory); #endif return(block); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % R e s i z e Q u a n t u m M e m o r y % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % ResizeQuantumMemory() changes the size of the memory and returns a pointer % to the (possibly moved) block. The contents will be unchanged up to the % lesser of the new and old sizes. % % The format of the ResizeQuantumMemory method is: % % void *ResizeQuantumMemory(void *memory,const size_t count, % const size_t quantum) % % A description of each parameter follows: % % o memory: A pointer to a memory allocation. % % o count: the number of objects to allocate contiguously. % % o quantum: the size (in bytes) of each object. % */ MagickExport void *ResizeQuantumMemory(void *memory,const size_t count, const size_t quantum) { size_t size; if ((HeapOverflowSanityCheckGetSize(count,quantum,&size) != MagickFalse) || (size > GetMaxMemoryRequest())) { errno=ENOMEM; memory=RelinquishMagickMemory(memory); return(NULL); } return(ResizeMagickMemory(memory,size)); } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S e t M a g i c k A l i g n e d M e m o r y M e t h o d s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % SetMagickAlignedMemoryMethods() sets the methods to acquire and relinquish % aligned memory. % % The format of the SetMagickAlignedMemoryMethods() method is: % % SetMagickAlignedMemoryMethods( % AcquireAlignedMemoryHandler acquire_aligned_memory_handler, % RelinquishAlignedMemoryHandler relinquish_aligned_memory_handler) % % A description of each parameter follows: % % o acquire_memory_handler: method to acquire aligned memory. % % o relinquish_aligned_memory_handler: method to relinquish aligned memory. % */ MagickExport void SetMagickAlignedMemoryMethods( AcquireAlignedMemoryHandler acquire_aligned_memory_handler, RelinquishAlignedMemoryHandler relinquish_aligned_memory_handler) { memory_methods.acquire_aligned_memory_handler=acquire_aligned_memory_handler; memory_methods.relinquish_aligned_memory_handler= relinquish_aligned_memory_handler; } /* %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % % % % % % S e t M a g i c k M e m o r y M e t h o d s % % % % % % % %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % % SetMagickMemoryMethods() sets the methods to acquire, resize, and destroy % memory. Your custom memory methods must be set prior to the % MagickCoreGenesis() method. % % The format of the SetMagickMemoryMethods() method is: % % SetMagickMemoryMethods(AcquireMemoryHandler acquire_memory_handler, % ResizeMemoryHandler resize_memory_handler, % DestroyMemoryHandler destroy_memory_handler) % % A description of each parameter follows: % % o acquire_memory_handler: method to acquire memory (e.g. malloc). % % o resize_memory_handler: method to resize memory (e.g. realloc). % % o destroy_memory_handler: method to destroy memory (e.g. free). % */ MagickExport void SetMagickMemoryMethods( AcquireMemoryHandler acquire_memory_handler, ResizeMemoryHandler resize_memory_handler, DestroyMemoryHandler destroy_memory_handler) { /* Set memory methods. */ if (acquire_memory_handler != (AcquireMemoryHandler) NULL) memory_methods.acquire_memory_handler=acquire_memory_handler; if (resize_memory_handler != (ResizeMemoryHandler) NULL) memory_methods.resize_memory_handler=resize_memory_handler; if (destroy_memory_handler != (DestroyMemoryHandler) NULL) memory_methods.destroy_memory_handler=destroy_memory_handler; }