LCOV - code coverage report
Current view: top level - aws-c-common/source - allocator.c (source / functions) Hit Total Coverage
Test: all_fuzz.info Lines: 65 199 32.7 %
Date: 2021-04-23 16:28:21 Functions: 4 11 36.4 %

          Line data    Source code
       1             : /**
       2             :  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
       3             :  * SPDX-License-Identifier: Apache-2.0.
       4             :  */
       5             : 
       6             : #include <aws/common/assert.h>
       7             : #include <aws/common/common.h>
       8             : #include <aws/common/logging.h>
       9             : #include <aws/common/math.h>
      10             : 
      11             : #include <stdarg.h>
      12             : #include <stdlib.h>
      13             : 
      14             : #ifdef _WIN32
      15             : #    include <Windows.h>
      16             : #endif
      17             : 
      18             : #ifdef __MACH__
      19             : #    include <CoreFoundation/CoreFoundation.h>
      20             : #endif
      21             : 
      22             : /* turn off unused named parameter warning on msvc.*/
      23             : #ifdef _MSC_VER
      24             : #    pragma warning(push)
      25             : #    pragma warning(disable : 4100)
      26             : #endif
      27             : 
      28             : #ifndef PAGE_SIZE
      29           0 : #    define PAGE_SIZE (4 * 1024)
      30             : #endif
      31             : 
      32           0 : bool aws_allocator_is_valid(const struct aws_allocator *alloc) {
      33           0 :     /* An allocator must define mem_acquire and mem_release.  All other fields are optional */
      34           0 :     return alloc && AWS_OBJECT_PTR_IS_READABLE(alloc) && alloc->mem_acquire && alloc->mem_release;
      35           0 : }
      36             : 
      37           0 : static void *s_default_malloc(struct aws_allocator *allocator, size_t size) {
      38           0 :     (void)allocator;
      39           0 :     /* larger allocations should be aligned so that AVX and friends can avoid
      40           0 :      * the extra preable during unaligned versions of memcpy/memset on big buffers
      41           0 :      * This will also accelerate hardware CRC and SHA on ARM chips
      42           0 :      *
      43           0 :      * 64 byte alignment for > page allocations on 64 bit systems
      44           0 :      * 32 byte alignment for > page allocations on 32 bit systems
      45           0 :      * 16 byte alignment for <= page allocations on 64 bit systems
      46           0 :      * 8 byte alignment for <= page allocations on 32 bit systems
      47           0 :      *
      48           0 :      * We use PAGE_SIZE as the boundary because we are not aware of any allocations of
      49           0 :      * this size or greater that are not data buffers
      50           0 :      */
      51           0 :     const size_t alignment = sizeof(void *) * (size > PAGE_SIZE ? 8 : 2);
      52           0 : #if !defined(_WIN32)
      53           0 :     void *result = NULL;
      54           0 :     return (posix_memalign(&result, alignment, size)) ? NULL : result;
      55             : #else
      56             :     return _aligned_malloc(size, alignment);
      57             : #endif
      58             : }
      59             : 
      60           0 : static void s_default_free(struct aws_allocator *allocator, void *ptr) {
      61           0 :     (void)allocator;
      62           0 : #if !defined(_WIN32)
      63           0 :     free(ptr);
      64             : #else
      65             :     _aligned_free(ptr);
      66             : #endif
      67             : }
      68             : 
      69           0 : static void *s_default_realloc(struct aws_allocator *allocator, void *ptr, size_t oldsize, size_t newsize) {
      70           0 :     (void)allocator;
      71           0 :     (void)oldsize;
      72           0 : #if !defined(_WIN32)
      73           0 :     if (newsize == 0 || !ptr) {
      74           0 :         free(ptr);
      75           0 :         return NULL;
      76           0 :     }
      77           0 : 
      78           0 :     if (newsize <= oldsize) {
      79           0 :         return ptr;
      80           0 :     }
      81           0 : 
      82           0 :     /* newsize is > oldsize, need more memory */
      83           0 :     void *new_mem = s_default_malloc(allocator, newsize);
      84           0 :     if (new_mem) {
      85           0 :         memcpy(new_mem, ptr, oldsize);
      86           0 :         s_default_free(allocator, ptr);
      87           0 :     }
      88           0 :     return new_mem;
      89             : #else
      90             :     const size_t alignment = sizeof(void *) * (newsize > PAGE_SIZE ? 8 : 2);
      91             :     return _aligned_realloc(ptr, newsize, alignment);
      92             : #endif
      93             : }
      94             : 
      95           0 : static void *s_default_calloc(struct aws_allocator *allocator, size_t num, size_t size) {
      96           0 :     void *mem = s_default_malloc(allocator, num * size);
      97           0 :     if (mem) {
      98           0 :         memset(mem, 0, num * size);
      99           0 :     }
     100           0 :     return mem;
     101           0 : }
     102             : 
     103             : static struct aws_allocator default_allocator = {
     104             :     .mem_acquire = s_default_malloc,
     105             :     .mem_release = s_default_free,
     106             :     .mem_realloc = s_default_realloc,
     107             :     .mem_calloc = s_default_calloc,
     108             : };
     109             : 
     110           0 : struct aws_allocator *aws_default_allocator(void) {
     111           0 :     return &default_allocator;
     112           0 : }
     113             : 
     114      616115 : void *aws_mem_acquire(struct aws_allocator *allocator, size_t size) {
     115      616115 :     AWS_FATAL_PRECONDITION(allocator != NULL);
     116      616115 :     AWS_FATAL_PRECONDITION(allocator->mem_acquire != NULL);
     117      616115 :     /* Protect against https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations */
     118      616115 :     AWS_FATAL_PRECONDITION(size != 0);
     119      616115 : 
     120      616115 :     void *mem = allocator->mem_acquire(allocator, size);
     121      616115 :     if (!mem) {
     122           0 :         aws_raise_error(AWS_ERROR_OOM);
     123           0 :     }
     124      616115 :     return mem;
     125      616115 : }
     126             : 
     127      148439 : void *aws_mem_calloc(struct aws_allocator *allocator, size_t num, size_t size) {
     128      148439 :     AWS_FATAL_PRECONDITION(allocator != NULL);
     129      148439 :     AWS_FATAL_PRECONDITION(allocator->mem_calloc || allocator->mem_acquire);
     130      148439 :     /* Protect against https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations */
     131      148439 :     AWS_FATAL_PRECONDITION(num != 0 && size != 0);
     132      148439 : 
     133      148439 :     /* Defensive check: never use calloc with size * num that would overflow
     134      148439 :      * https://wiki.sei.cmu.edu/confluence/display/c/MEM07-C.+Ensure+that+the+arguments+to+calloc%28%29%2C+when+multiplied%2C+do+not+wrap
     135      148439 :      */
     136      148439 :     size_t required_bytes;
     137      148439 :     if (aws_mul_size_checked(num, size, &required_bytes)) {
     138           0 :         return NULL;
     139           0 :     }
     140      148439 : 
     141      148439 :     /* If there is a defined calloc, use it */
     142      148439 :     if (allocator->mem_calloc) {
     143           0 :         void *mem = allocator->mem_calloc(allocator, num, size);
     144           0 :         if (!mem) {
     145           0 :             aws_raise_error(AWS_ERROR_OOM);
     146           0 :         }
     147           0 :         return mem;
     148           0 :     }
     149      148439 : 
     150      148439 :     /* Otherwise, emulate calloc */
     151      148439 :     void *mem = allocator->mem_acquire(allocator, required_bytes);
     152      148439 :     if (!mem) {
     153           0 :         aws_raise_error(AWS_ERROR_OOM);
     154           0 :         return NULL;
     155           0 :     }
     156      148439 :     memset(mem, 0, required_bytes);
     157      148439 :     AWS_POSTCONDITION(mem != NULL);
     158      148439 :     return mem;
     159      148439 : }
     160             : 
     161           0 : #define AWS_ALIGN_ROUND_UP(value, alignment) (((value) + ((alignment)-1)) & ~((alignment)-1))
     162             : 
     163           0 : void *aws_mem_acquire_many(struct aws_allocator *allocator, size_t count, ...) {
     164           0 : 
     165           0 :     enum { S_ALIGNMENT = sizeof(intmax_t) };
     166           0 : 
     167           0 :     va_list args_size;
     168           0 :     va_start(args_size, count);
     169           0 :     va_list args_allocs;
     170           0 :     va_copy(args_allocs, args_size);
     171           0 : 
     172           0 :     size_t total_size = 0;
     173           0 :     for (size_t i = 0; i < count; ++i) {
     174           0 : 
     175           0 :         /* Ignore the pointer argument for now */
     176           0 :         va_arg(args_size, void **);
     177           0 : 
     178           0 :         size_t alloc_size = va_arg(args_size, size_t);
     179           0 :         total_size += AWS_ALIGN_ROUND_UP(alloc_size, S_ALIGNMENT);
     180           0 :     }
     181           0 :     va_end(args_size);
     182           0 : 
     183           0 :     void *allocation = NULL;
     184           0 : 
     185           0 :     if (total_size > 0) {
     186           0 : 
     187           0 :         allocation = aws_mem_acquire(allocator, total_size);
     188           0 :         if (!allocation) {
     189           0 :             aws_raise_error(AWS_ERROR_OOM);
     190           0 :             goto cleanup;
     191           0 :         }
     192           0 : 
     193           0 :         uint8_t *current_ptr = allocation;
     194           0 : 
     195           0 :         for (size_t i = 0; i < count; ++i) {
     196           0 : 
     197           0 :             void **out_ptr = va_arg(args_allocs, void **);
     198           0 : 
     199           0 :             size_t alloc_size = va_arg(args_allocs, size_t);
     200           0 :             alloc_size = AWS_ALIGN_ROUND_UP(alloc_size, S_ALIGNMENT);
     201           0 : 
     202           0 :             *out_ptr = current_ptr;
     203           0 :             current_ptr += alloc_size;
     204           0 :         }
     205           0 :     }
     206           0 : 
     207           0 : cleanup:
     208           0 :     va_end(args_allocs);
     209           0 :     return allocation;
     210           0 : }
     211             : 
     212             : #undef AWS_ALIGN_ROUND_UP
     213             : 
     214      687040 : void aws_mem_release(struct aws_allocator *allocator, void *ptr) {
     215      687040 :     AWS_FATAL_PRECONDITION(allocator != NULL);
     216      687040 :     AWS_FATAL_PRECONDITION(allocator->mem_release != NULL);
     217      687040 : 
     218      687040 :     if (ptr != NULL) {
     219      687040 :         allocator->mem_release(allocator, ptr);
     220      687040 :     }
     221      687040 : }
     222             : 
     223      123436 : int aws_mem_realloc(struct aws_allocator *allocator, void **ptr, size_t oldsize, size_t newsize) {
     224      123436 :     AWS_FATAL_PRECONDITION(allocator != NULL);
     225      123436 :     AWS_FATAL_PRECONDITION(allocator->mem_realloc || allocator->mem_acquire);
     226      123436 :     AWS_FATAL_PRECONDITION(allocator->mem_release);
     227      123436 : 
     228      123436 :     /* Protect against https://wiki.sei.cmu.edu/confluence/display/c/MEM04-C.+Beware+of+zero-length+allocations */
     229      123436 :     if (newsize == 0) {
     230           0 :         aws_mem_release(allocator, *ptr);
     231           0 :         *ptr = NULL;
     232           0 :         return AWS_OP_SUCCESS;
     233           0 :     }
     234      123436 : 
     235      123436 :     if (allocator->mem_realloc) {
     236           0 :         void *newptr = allocator->mem_realloc(allocator, *ptr, oldsize, newsize);
     237           0 :         if (!newptr) {
     238           0 :             return aws_raise_error(AWS_ERROR_OOM);
     239           0 :         }
     240           0 :         *ptr = newptr;
     241           0 :         return AWS_OP_SUCCESS;
     242           0 :     }
     243      123436 : 
     244      123436 :     /* Since the allocator doesn't support realloc, we'll need to emulate it (inefficiently). */
     245      123436 :     if (oldsize >= newsize) {
     246           0 :         return AWS_OP_SUCCESS;
     247           0 :     }
     248      123436 : 
     249      123436 :     void *newptr = allocator->mem_acquire(allocator, newsize);
     250      123436 :     if (!newptr) {
     251           0 :         return aws_raise_error(AWS_ERROR_OOM);
     252           0 :     }
     253      123436 : 
     254      123436 :     memcpy(newptr, *ptr, oldsize);
     255      123436 :     memset((uint8_t *)newptr + oldsize, 0, newsize - oldsize);
     256      123436 : 
     257      123436 :     aws_mem_release(allocator, *ptr);
     258      123436 : 
     259      123436 :     *ptr = newptr;
     260      123436 : 
     261      123436 :     return AWS_OP_SUCCESS;
     262      123436 : }
     263             : 
     264             : /* Wraps a CFAllocator around aws_allocator. For Mac only. */
     265             : #ifdef __MACH__
     266             : 
     267             : static CFStringRef s_cf_allocator_description = CFSTR("CFAllocator wrapping aws_allocator.");
     268             : 
     269             : /* note we don't have a standard specification stating sizeof(size_t) == sizeof(void *) so we have some extra casts */
     270             : static void *s_cf_allocator_allocate(CFIndex alloc_size, CFOptionFlags hint, void *info) {
     271             :     (void)hint;
     272             : 
     273             :     struct aws_allocator *allocator = info;
     274             : 
     275             :     void *mem = aws_mem_acquire(allocator, (size_t)alloc_size + sizeof(size_t));
     276             : 
     277             :     if (!mem) {
     278             :         return NULL;
     279             :     }
     280             : 
     281             :     size_t allocation_size = (size_t)alloc_size + sizeof(size_t);
     282             :     memcpy(mem, &allocation_size, sizeof(size_t));
     283             :     return (void *)((uint8_t *)mem + sizeof(size_t));
     284             : }
     285             : 
     286             : static void s_cf_allocator_deallocate(void *ptr, void *info) {
     287             :     struct aws_allocator *allocator = info;
     288             : 
     289             :     void *original_allocation = (uint8_t *)ptr - sizeof(size_t);
     290             : 
     291             :     aws_mem_release(allocator, original_allocation);
     292             : }
     293             : 
     294             : static void *s_cf_allocator_reallocate(void *ptr, CFIndex new_size, CFOptionFlags hint, void *info) {
     295             :     (void)hint;
     296             : 
     297             :     struct aws_allocator *allocator = info;
     298             :     AWS_ASSERT(allocator->mem_realloc);
     299             : 
     300             :     void *original_allocation = (uint8_t *)ptr - sizeof(size_t);
     301             :     size_t original_size = 0;
     302             :     memcpy(&original_size, original_allocation, sizeof(size_t));
     303             : 
     304             :     if (aws_mem_realloc(allocator, &original_allocation, original_size, (size_t)new_size)) {
     305             :         return NULL;
     306             :     }
     307             : 
     308             :     size_t new_allocation_size = (size_t)new_size;
     309             :     memcpy(original_allocation, &new_allocation_size, sizeof(size_t));
     310             : 
     311             :     return (void *)((uint8_t *)original_allocation + sizeof(size_t));
     312             : }
     313             : 
     314             : static CFStringRef s_cf_allocator_copy_description(const void *info) {
     315             :     (void)info;
     316             : 
     317             :     return s_cf_allocator_description;
     318             : }
     319             : 
     320             : static CFIndex s_cf_allocator_preferred_size(CFIndex size, CFOptionFlags hint, void *info) {
     321             :     (void)hint;
     322             :     (void)info;
     323             : 
     324             :     return size + sizeof(size_t);
     325             : }
     326             : 
     327             : CFAllocatorRef aws_wrapped_cf_allocator_new(struct aws_allocator *allocator) {
     328             :     CFAllocatorRef cf_allocator = NULL;
     329             : 
     330             :     CFAllocatorReallocateCallBack reallocate_callback = NULL;
     331             : 
     332             :     if (allocator->mem_realloc) {
     333             :         reallocate_callback = s_cf_allocator_reallocate;
     334             :     }
     335             : 
     336             :     CFAllocatorContext context = {
     337             :         .allocate = s_cf_allocator_allocate,
     338             :         .copyDescription = s_cf_allocator_copy_description,
     339             :         .deallocate = s_cf_allocator_deallocate,
     340             :         .reallocate = reallocate_callback,
     341             :         .info = allocator,
     342             :         .preferredSize = s_cf_allocator_preferred_size,
     343             :         .release = NULL,
     344             :         .retain = NULL,
     345             :         .version = 0,
     346             :     };
     347             : 
     348             :     cf_allocator = CFAllocatorCreate(NULL, &context);
     349             : 
     350             :     if (!cf_allocator) {
     351             :         aws_raise_error(AWS_ERROR_OOM);
     352             :     }
     353             : 
     354             :     return cf_allocator;
     355             : }
     356             : 
     357             : void aws_wrapped_cf_allocator_destroy(CFAllocatorRef allocator) {
     358             :     CFRelease(allocator);
     359             : }
     360             : 
     361             : #endif /*__MACH__ */

Generated by: LCOV version 1.13