libmicrohttpd2

HTTP server C library (MHD 2.x, alpha)
Log | Files | Refs | README | LICENSE

mempool_funcs.c (24833B)


      1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */
      2 /*
      3   This file is part of GNU libmicrohttpd.
      4   Copyright (C) 2007--2024 Daniel Pittman and Christian Grothoff
      5   Copyright (C) 2014--2024 Evgeny Grin (Karlson2k)
      6 
      7   GNU libmicrohttpd is free software; you can redistribute it and/or
      8   modify it under the terms of the GNU Lesser General Public
      9   License as published by the Free Software Foundation; either
     10   version 2.1 of the License, or (at your option) any later version.
     11 
     12   GNU libmicrohttpd is distributed in the hope that it will be useful,
     13   but WITHOUT ANY WARRANTY; without even the implied warranty of
     14   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     15   Lesser General Public License for more details.
     16 
     17   Alternatively, you can redistribute GNU libmicrohttpd and/or
     18   modify it under the terms of the GNU General Public License as
     19   published by the Free Software Foundation; either version 2 of
     20   the License, or (at your option) any later version, together
     21   with the eCos exception, as follows:
     22 
     23     As a special exception, if other files instantiate templates or
     24     use macros or inline functions from this file, or you compile this
     25     file and link it with other works to produce a work based on this
     26     file, this file does not by itself cause the resulting work to be
     27     covered by the GNU General Public License. However the source code
     28     for this file must still be made available in accordance with
     29     section (3) of the GNU General Public License v2.
     30 
     31     This exception does not invalidate any other reasons why a work
     32     based on this file might be covered by the GNU General Public
     33     License.
     34 
     35   You should have received copies of the GNU Lesser General Public
     36   License and the GNU General Public License along with this library;
     37   if not, see <https://www.gnu.org/licenses/>.
     38 */
     39 
     40 /**
     41  * @file src/mhd2/mempool_funcs.c
     42  * @brief memory pool
     43  * @author Christian Grothoff
     44  * @author Karlson2k (Evgeny Grin)
     45  * TODO:
     46  * + Update code style
     47  * + Detect mmap() in configure (it is purely optional!)
     48  */
     49 #include "mhd_sys_options.h"
     50 #include "mempool_funcs.h"
     51 #include "compat_calloc.h"
     52 
     53 #ifdef HAVE_STDLIB_H
     54 #  include <stdlib.h>
     55 #endif /* HAVE_STDLIB_H */
     56 #include <string.h>
     57 #include "mhd_assert.h"
     58 #ifdef HAVE_SYS_MMAN_H
     59 #  include <sys/mman.h>
     60 #endif
     61 #ifdef _WIN32
     62 #  include <windows.h>
     63 #endif
     64 #ifdef HAVE_SYSCONF
     65 #  include <unistd.h>
     66 #  if defined(_SC_PAGE_SIZE)
     67 #    define MHD_SC_PAGESIZE _SC_PAGE_SIZE
     68 #  elif defined(_SC_PAGESIZE)
     69 #    define MHD_SC_PAGESIZE _SC_PAGESIZE
     70 #  endif /* _SC_PAGESIZE */
     71 #endif /* HAVE_SYSCONF */
     72 
     73 #if defined(MHD_USE_PAGESIZE_MACRO) || defined(MHD_USE_PAGE_SIZE_MACRO)
     74 #  ifndef HAVE_SYSCONF /* Avoid duplicate include */
     75 #    include <unistd.h>
     76 #  endif /* HAVE_SYSCONF */
     77 #  ifdef HAVE_LIMITS_H
     78 #    include <limits.h>
     79 #  endif
     80 #  ifdef HAVE_SYS_PARAM_H
     81 #    include <sys/param.h>
     82 #  endif /* HAVE_SYS_PARAM_H */
     83 #endif /* MHD_USE_PAGESIZE_MACRO || MHD_USE_PAGE_SIZE_MACRO */
     84 
     85 #include "mhd_limits.h"
     86 
     87 #ifndef mhd_FALLBACK_PAGE_SIZE
     88 /**
     89  * Fallback value of page size
     90  */
     91 #  define mhd_FALLBACK_PAGE_SIZE (4096)
     92 #endif
     93 
     94 #if defined(MHD_USE_PAGESIZE_MACRO)
     95 #  define mhd_DEF_PAGE_SIZE PAGESIZE
     96 #elif defined(MHD_USE_PAGE_SIZE_MACRO)
     97 #  define mhd_DEF_PAGE_SIZE PAGE_SIZE
     98 #else  /* ! PAGESIZE */
     99 #  define mhd_DEF_PAGE_SIZE mhd_FALLBACK_PAGE_SIZE
    100 #endif /* ! PAGESIZE */
    101 
    102 
    103 #ifdef MHD_ASAN_POISON_ACTIVE
    104 #  include <sanitizer/asan_interface.h>
    105 #endif /* MHD_ASAN_POISON_ACTIVE */
    106 
    107 #if defined(MAP_ANONYMOUS)
    108 #  define mhd_MAP_ANONYMOUS MAP_ANONYMOUS
    109 #endif
    110 
    111 #if !defined(mhd_MAP_ANONYMOUS) && defined(MAP_ANON)
    112 #  define mhd_MAP_ANONYMOUS MAP_ANON
    113 #endif
    114 
    115 #if defined(mhd_MAP_ANONYMOUS) || defined(_WIN32)
    116 #  define mhd_USE_LARGE_ALLOCS 1
    117 #endif
    118 
    119 #ifdef mhd_USE_LARGE_ALLOCS
    120 #  if defined(_WIN32)
    121 #    define mhd_MAP_FAILED NULL
    122 #  elif defined(MAP_FAILED)
    123 #    define mhd_MAP_FAILED MAP_FAILED
    124 #  else
    125 #    define mhd_MAP_FAILED ((void*) -1)
    126 #  endif
    127 #endif
    128 
    129 /**
    130  * Round up 'n' to a multiple of ALIGN_SIZE.
    131  */
    132 #define mhd_ROUND_TO_ALIGN(n) \
    133         (((n) + (mhd_MEMPOOL_ALIGN_SIZE - 1)) \
    134          / (mhd_MEMPOOL_ALIGN_SIZE) * (mhd_MEMPOOL_ALIGN_SIZE))
    135 
    136 
    137 #ifndef MHD_ASAN_POISON_ACTIVE
    138 #  define mhd_NOSANITIZE_PTRS /**/
    139 #  define mhd_RED_ZONE_SIZE (0)
    140 #  define mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE(n) mhd_ROUND_TO_ALIGN (n)
    141 #  define mhd_POISON_MEMORY(pointer, size) (void) 0
    142 #  define mhd_UNPOISON_MEMORY(pointer, size) (void) 0
    143 /**
    144  * Boolean 'true' if the first pointer is less or equal the second pointer
    145  */
    146 #  define mp_ptr_le_(p1, p2) \
    147           (((const uint8_t*) (p1)) <= ((const uint8_t*) (p2)))
    148 /**
    149  * The difference in bytes between positions of the first and
    150  * the second pointers
    151  */
    152 #  define mp_ptr_diff_(p1, p2) \
    153           ((size_t) (((const uint8_t*) (p1)) - ((const uint8_t*) (p2))))
    154 #else  /* MHD_ASAN_POISON_ACTIVE */
    155 #  define mhd_RED_ZONE_SIZE (mhd_MEMPOOL_ALIGN_SIZE)
    156 #  define mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE(n) \
    157           (mhd_ROUND_TO_ALIGN (n) + mhd_RED_ZONE_SIZE)
    158 #  define mhd_POISON_MEMORY(pointer, size) \
    159           ASAN_POISON_MEMORY_REGION ((pointer), (size))
    160 #  define mhd_UNPOISON_MEMORY(pointer, size) \
    161           ASAN_UNPOISON_MEMORY_REGION ((pointer), (size))
    162 #  if defined(FUNC_PTRCOMPARE_CAST_WORKAROUND_WORKS)
    163 /**
    164  * Boolean 'true' if the first pointer is less or equal the second pointer
    165  */
    166 #    define mp_ptr_le_(p1, p2) \
    167             (((uintptr_t) ((const void*) (p1))) <= \
    168              ((uintptr_t) ((const void*) (p2))))
    169 /**
    170  * The difference in bytes between positions of the first and
    171  * the second pointers
    172  */
    173 #    define mp_ptr_diff_(p1, p2) \
    174             ((size_t) (((uintptr_t) ((const uint8_t*) (p1))) - \
    175                        ((uintptr_t) ((const uint8_t*) (p2)))))
    176 #  elif defined(FUNC_ATTR_PTRCOMPARE_WORKS) && \
    177   defined(FUNC_ATTR_PTRSUBTRACT_WORKS)
    178 #    ifndef NDEBUG
    179 /**
    180  * Boolean 'true' if the first pointer is less or equal the second pointer
    181  */
    182 __attribute__((no_sanitize ("pointer-compare"))) static bool
    183 mp_ptr_le_ (const void *p1, const void *p2)
    184 {
    185   return (((const uint8_t *)p1) <= ((const uint8_t *)p2));
    186 }
    187 
    188 
    189 #    endif /* _DEBUG */
    190 
    191 
    192 /**
    193  * The difference in bytes between positions of the first and
    194  * the second pointers
    195  */
    196 __attribute__((no_sanitize ("pointer-subtract"))) static size_t
    197 mp_ptr_diff_ (const void *p1, const void *p2)
    198 {
    199   return (size_t)(((const uint8_t *)p1) - ((const uint8_t *)p2));
    200 }
    201 
    202 
    203 #  elif defined(FUNC_ATTR_NOSANITIZE_WORKS)
    204 #    ifndef NDEBUG
    205 /**
    206  * Boolean 'true' if the first pointer is less or equal the second pointer
    207  */
    208 __attribute__((no_sanitize ("address"))) static bool
    209 mp_ptr_le_ (const void *p1, const void *p2)
    210 {
    211   return (((const uint8_t *)p1) <= ((const uint8_t *)p2));
    212 }
    213 
    214 
    215 #    endif /* _DEBUG */
    216 
    217 /**
    218  * The difference in bytes between positions of the first and
    219  * the second pointers
    220  */
    221 __attribute__((no_sanitize ("address"))) static size_t
    222 mp_ptr_diff_ (const void *p1, const void *p2)
    223 {
    224   return (size_t)(((const uint8_t *)p1) - ((const uint8_t *)p2));
    225 }
    226 
    227 
    228 #  else  /* ! FUNC_ATTR_NOSANITIZE_WORKS */
    229 #    error User-poisoning cannot be used
    230 #  endif /* ! FUNC_ATTR_NOSANITIZE_WORKS */
    231 #endif /* MHD_ASAN_POISON_ACTIVE */
    232 
    233 #ifdef mhd_USE_LARGE_ALLOCS
    234 /**
    235  * Size of memory page
    236  */
    237 static size_t MHD_sys_page_size_ = (size_t)
    238 #  if defined(MHD_USE_PAGESIZE_MACRO_STATIC)
    239                                    PAGESIZE;
    240 #  elif defined(MHD_USE_PAGE_SIZE_MACRO_STATIC)
    241                                    PAGE_SIZE;
    242 #  else  /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
    243                                    mhd_FALLBACK_PAGE_SIZE; /* Default fallback value */
    244 #  endif /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
    245 #endif /* mhd_USE_LARGE_ALLOCS */
    246 
    247 void
    248 mhd_init_mem_pools (void)
    249 {
    250 #ifdef mhd_USE_LARGE_ALLOCS
    251 #  ifdef MHD_SC_PAGESIZE
    252   long result;
    253   result = sysconf (MHD_SC_PAGESIZE);
    254   if (-1 != result)
    255     MHD_sys_page_size_ = (size_t)result;
    256   else
    257     MHD_sys_page_size_ = (size_t)mhd_DEF_PAGE_SIZE;
    258 #  elif defined(_WIN32)
    259   SYSTEM_INFO si;
    260   GetSystemInfo (&si);
    261   MHD_sys_page_size_ = (size_t)si.dwPageSize;
    262 #  else
    263   MHD_sys_page_size_ = (size_t)mhd_DEF_PAGE_SIZE;
    264 #  endif /* _WIN32 */
    265   mhd_assert (0 == (MHD_sys_page_size_ % mhd_MEMPOOL_ALIGN_SIZE));
    266 #endif /* mhd_USE_LARGE_ALLOCS */
    267   (void)0;
    268 }
    269 
    270 
    271 /**
    272  * Handle for a memory pool.  Pools are not reentrant and must not be
    273  * used by multiple threads.
    274  */
    275 struct mhd_MemoryPool
    276 {
    277 
    278   /**
    279    * Pointer to the pool's memory
    280    */
    281   uint8_t *memory;
    282 
    283   /**
    284    * Size of the pool.
    285    */
    286   size_t size;
    287 
    288   /**
    289    * Offset of the first unallocated byte.
    290    */
    291   size_t pos;
    292 
    293   /**
    294    * Offset of the byte after the last unallocated byte.
    295    */
    296   size_t end;
    297 
    298 #ifdef mhd_USE_LARGE_ALLOCS
    299   /**
    300    * 'false' if pool was malloc'ed, 'true' if mmapped (VirtualAlloc'ed for W32).
    301    */
    302   bool is_large_alloc;
    303 #endif
    304 
    305   /**
    306    * Memory allocation zeroing mode
    307    */
    308   enum mhd_MemPoolZeroing zeroing;
    309 };
    310 
    311 
    312 MHD_INTERNAL mhd_FN_RET_UNALIASED
    313 mhd_FN_OBJ_CONSTRUCTOR (mhd_pool_destroy)
    314 struct mhd_MemoryPool *
    315 mhd_pool_create (size_t max,
    316                  enum mhd_MemPoolZeroing zeroing)
    317 {
    318   struct mhd_MemoryPool *pool;
    319   size_t alloc_size;
    320 
    321   mhd_assert (max > 0);
    322   mhd_assert (mhd_RED_ZONE_SIZE < (max + mhd_RED_ZONE_SIZE));
    323   max += mhd_RED_ZONE_SIZE;
    324   alloc_size = 0;
    325   pool = (struct mhd_MemoryPool *)malloc (sizeof (struct mhd_MemoryPool));
    326   if (NULL == pool)
    327     return NULL;
    328   pool->zeroing = zeroing;
    329 #ifdef mhd_USE_LARGE_ALLOCS
    330   pool->is_large_alloc = false;
    331   if ((max <= 32 * 1024)
    332       || (max < MHD_sys_page_size_ * 4 / 3))
    333   {
    334     pool->memory = (uint8_t *)mhd_MAP_FAILED;
    335   }
    336   else
    337   {
    338     /* Round up allocation to page granularity. */
    339     alloc_size = max + MHD_sys_page_size_ - 1;
    340     alloc_size -= alloc_size % MHD_sys_page_size_;
    341 #  if defined(mhd_MAP_ANONYMOUS)
    342     pool->memory = (uint8_t *)mmap (NULL,
    343                                     alloc_size,
    344                                     PROT_READ | PROT_WRITE,
    345                                     MAP_PRIVATE | mhd_MAP_ANONYMOUS,
    346                                     -1,
    347                                     0);
    348 #  else  /* ! mhd_MAP_ANONYMOUS */
    349     pool->memory = (uint8_t *)VirtualAlloc (NULL,
    350                                             alloc_size,
    351                                             MEM_COMMIT | MEM_RESERVE,
    352                                             PAGE_READWRITE);
    353 #  endif /* ! mhd_MAP_ANONYMOUS */
    354   }
    355   if (mhd_MAP_FAILED != pool->memory)
    356     pool->is_large_alloc = true;
    357   else
    358 #endif /* mhd_USE_LARGE_ALLOCS */
    359   if (!0)
    360   {
    361     alloc_size = mhd_ROUND_TO_ALIGN (max);
    362     if (MHD_MEMPOOL_ZEROING_NEVER == zeroing)
    363       pool->memory = (uint8_t *)malloc (alloc_size);
    364     else
    365       pool->memory = (uint8_t *)mhd_calloc (1, alloc_size);
    366     if (((uint8_t *)NULL) == pool->memory)
    367     {
    368       free (pool);
    369       return NULL;
    370     }
    371   }
    372   mhd_assert (0 == (((uintptr_t)pool->memory) % mhd_MEMPOOL_ALIGN_SIZE));
    373   pool->pos = 0;
    374   pool->end = alloc_size;
    375   pool->size = alloc_size;
    376   mhd_assert (0 < alloc_size);
    377   mhd_POISON_MEMORY (pool->memory, pool->size);
    378   return pool;
    379 }
    380 
    381 
    382 MHD_INTERNAL void
    383 mhd_pool_destroy (struct mhd_MemoryPool *restrict pool)
    384 {
    385   if (NULL == pool)
    386     return;
    387 
    388   mhd_assert (pool->end >= pool->pos);
    389   mhd_assert (pool->size >= pool->end - pool->pos);
    390   mhd_assert (pool->pos == mhd_ROUND_TO_ALIGN (pool->pos));
    391   mhd_UNPOISON_MEMORY (pool->memory, pool->size);
    392 #ifdef mhd_USE_LARGE_ALLOCS
    393   if (pool->is_large_alloc)
    394   {
    395 #  if defined(mhd_MAP_ANONYMOUS)
    396     munmap (pool->memory,
    397             pool->size);
    398 #  else
    399     VirtualFree (pool->memory,
    400                  0,
    401                  MEM_RELEASE);
    402 #  endif
    403   }
    404   else
    405 #endif /* mhd_USE_LARGE_ALLOCS*/
    406   if (!0)
    407     free (pool->memory);
    408 
    409   free (pool);
    410 }
    411 
    412 
    413 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ MHD_FN_PURE_ size_t
    414 mhd_pool_get_size (const struct mhd_MemoryPool *restrict pool)
    415 {
    416   return (pool->size - mhd_RED_ZONE_SIZE);
    417 }
    418 
    419 
    420 MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) size_t
    421 mhd_pool_get_free (struct mhd_MemoryPool *restrict pool)
    422 {
    423   mhd_assert (pool->end >= pool->pos);
    424   mhd_assert (pool->size >= pool->end - pool->pos);
    425   mhd_assert (pool->pos == mhd_ROUND_TO_ALIGN (pool->pos));
    426 #ifdef MHD_ASAN_POISON_ACTIVE
    427   if ((pool->end - pool->pos) <= mhd_RED_ZONE_SIZE)
    428     return 0;
    429 #endif /* MHD_ASAN_POISON_ACTIVE */
    430   return (pool->end - pool->pos) - mhd_RED_ZONE_SIZE;
    431 }
    432 
    433 
    434 MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) mhd_FN_RET_UNALIASED
    435 mhd_FN_RET_SIZED (2)
    436 mhd_FN_RET_ALIGNED (mhd_MEMPOOL_ALIGN_SIZE)void *
    437 mhd_pool_allocate (struct mhd_MemoryPool *restrict pool,
    438                    size_t size,
    439                    bool from_end)
    440 {
    441   void *ret;
    442   size_t asize;
    443 
    444   mhd_assert (pool->end >= pool->pos);
    445   mhd_assert (pool->size >= pool->end - pool->pos);
    446   mhd_assert (pool->pos == mhd_ROUND_TO_ALIGN (pool->pos));
    447   asize = mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
    448   if ((0 == asize) && (0 != size))
    449     return NULL; /* size too close to SIZE_MAX */
    450   if (asize > pool->end - pool->pos)
    451     return NULL;
    452   if (from_end)
    453   {
    454     ret = &pool->memory[pool->end - asize];
    455     pool->end -= asize;
    456   }
    457   else
    458   {
    459     ret = &pool->memory[pool->pos];
    460     pool->pos += asize;
    461   }
    462   mhd_UNPOISON_MEMORY (ret, size);
    463   return ret;
    464 }
    465 
    466 
    467 MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1)
    468 MHD_FN_PAR_NONNULL_IF_NONZERO_ (2, 3) bool
    469 mhd_pool_is_resizable_inplace (struct mhd_MemoryPool *restrict pool,
    470                                void *restrict block,
    471                                size_t block_size)
    472 {
    473   mhd_assert (pool->end >= pool->pos);
    474   mhd_assert (pool->size >= pool->end - pool->pos);
    475   mhd_assert (block != NULL || block_size == 0);
    476   mhd_assert (pool->size >= block_size);
    477   if (NULL != block)
    478   {
    479     const size_t block_offset = mp_ptr_diff_ (block, pool->memory);
    480     mhd_assert (mp_ptr_le_ (pool->memory, block));
    481     mhd_assert (pool->size >= block_offset);
    482     mhd_assert (pool->size >= block_offset + block_size);
    483     return (pool->pos ==
    484             mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size));
    485   }
    486   return false; /* Unallocated blocks cannot be resized in-place */
    487 }
    488 
    489 
    490 MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1) MHD_FN_PAR_NONNULL_ (3)
    491 MHD_FN_PAR_OUT_ (3) mhd_FN_RET_UNALIASED
    492 mhd_FN_RET_SIZED (2)
    493 mhd_FN_RET_ALIGNED (mhd_MEMPOOL_ALIGN_SIZE)void *
    494 mhd_pool_try_alloc (struct mhd_MemoryPool *restrict pool,
    495                     size_t size,
    496                     size_t *restrict required_bytes)
    497 {
    498   void *ret;
    499   size_t asize;
    500 
    501   mhd_assert (pool->end >= pool->pos);
    502   mhd_assert (pool->size >= pool->end - pool->pos);
    503   mhd_assert (pool->pos == mhd_ROUND_TO_ALIGN (pool->pos));
    504   asize = mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
    505   if ((0 == asize) && (0 != size))
    506   { /* size is too close to SIZE_MAX, very unlikely */
    507     *required_bytes = SIZE_MAX;
    508     return NULL;
    509   }
    510   if (asize > pool->end - pool->pos)
    511   {
    512     mhd_assert ((pool->end - pool->pos) == \
    513                 mhd_ROUND_TO_ALIGN (pool->end - pool->pos));
    514     if (asize <= pool->end)
    515       *required_bytes = asize - (pool->end - pool->pos);
    516     else
    517       *required_bytes = SIZE_MAX;
    518     return NULL;
    519   }
    520   *required_bytes = 0;
    521   ret = &pool->memory[pool->end - asize];
    522   pool->end -= asize;
    523   mhd_UNPOISON_MEMORY (ret, size);
    524   return ret;
    525 }
    526 
    527 
    528 MHD_INTERNAL
    529 MHD_FN_PAR_NONNULL_ (1)
    530 MHD_FN_PAR_NONNULL_IF_NONZERO_ (2, 3)
    531 mhd_FN_RET_SIZED (4)
    532 void *
    533 mhd_pool_reallocate (struct mhd_MemoryPool *restrict pool,
    534                      void *restrict old,
    535                      size_t old_size,
    536                      size_t new_size)
    537 {
    538   size_t asize;
    539   uint8_t *new_blc;
    540 
    541   mhd_assert (pool->end >= pool->pos);
    542   mhd_assert (pool->size >= pool->end - pool->pos);
    543   mhd_assert (old != NULL || old_size == 0);
    544   mhd_assert (pool->size >= old_size);
    545   mhd_assert (pool->pos == mhd_ROUND_TO_ALIGN (pool->pos));
    546 #if defined(MHD_ASAN_POISON_ACTIVE) && defined(HAVE___ASAN_REGION_IS_POISONED)
    547   mhd_assert (NULL == __asan_region_is_poisoned (old, old_size));
    548 #endif /* MHD_ASAN_POISON_ACTIVE && HAVE___ASAN_REGION_IS_POISONED */
    549 
    550   if (NULL != old)
    551   {   /* Have previously allocated data */
    552     const size_t old_offset = mp_ptr_diff_ (old, pool->memory);
    553     const bool shrinking = (old_size > new_size);
    554 
    555     mhd_assert (mp_ptr_le_ (pool->memory, old));
    556     /* (pool->memory + pool->size >= (uint8_t*) old + old_size) */
    557     mhd_assert ((pool->size - mhd_RED_ZONE_SIZE) >= (old_offset + old_size));
    558     /* Blocks "from the end" must not be reallocated */
    559     /* (old_size == 0 || pool->memory + pool->pos > (uint8_t*) old) */
    560     mhd_assert ((old_size == 0) \
    561                 || (pool->pos > old_offset));
    562     mhd_assert ((old_size == 0) \
    563                 || ((pool->end - mhd_RED_ZONE_SIZE) >=
    564                     (old_offset + old_size)));
    565     /* Try resizing in-place */
    566     if (shrinking)
    567     {     /* Shrinking in-place, zero-out freed part */
    568       if (MHD_MEMPOOL_ZEROING_ON_RESET < pool->zeroing)
    569         memset ((uint8_t *)old + new_size, 0, old_size - new_size);
    570       mhd_POISON_MEMORY ((uint8_t *)old + new_size, old_size - new_size);
    571     }
    572     if (pool->pos ==
    573         mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + old_size))
    574     {     /* "old" block is the last allocated block */
    575       const size_t new_apos =
    576         mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + new_size);
    577       if (!shrinking)
    578       {                               /* Grow in-place, check for enough space. */
    579         if ((new_apos > pool->end)
    580             || (new_apos < pool->pos)) /* Value wrap */
    581           return NULL;                /* No space */
    582       }
    583       /* Resized in-place */
    584       pool->pos = new_apos;
    585       mhd_UNPOISON_MEMORY (old, new_size);
    586       return old;
    587     }
    588     if (shrinking)
    589       return old;   /* Resized in-place, freed part remains allocated */
    590   }
    591   /* Need to allocate new block */
    592   asize = mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
    593   if (((0 == asize)
    594        && (0 != new_size))    /* Value wrap, too large new_size. */
    595       || (asize > pool->end - pool->pos)) /* Not enough space */
    596     return NULL;
    597 
    598   new_blc = pool->memory + pool->pos;
    599   pool->pos += asize;
    600 
    601   mhd_UNPOISON_MEMORY (new_blc, new_size);
    602   if (0 != old_size)
    603   {
    604     /* Move data to new block, old block remains allocated */
    605     memcpy (new_blc, old, old_size);
    606     /* Zero-out old block */
    607     if (MHD_MEMPOOL_ZEROING_ON_RESET < pool->zeroing)
    608       memset (old, 0, old_size);
    609     mhd_POISON_MEMORY (old, old_size);
    610   }
    611   return new_blc;
    612 }
    613 
    614 
    615 MHD_INTERNAL MHD_FN_PAR_NONNULL_ (1)
    616 MHD_FN_PAR_NONNULL_IF_NONZERO_ (2, 3) void
    617 mhd_pool_deallocate (struct mhd_MemoryPool *restrict pool,
    618                      void *restrict block,
    619                      size_t block_size)
    620 {
    621   mhd_assert (pool->end >= pool->pos);
    622   mhd_assert (pool->size >= pool->end - pool->pos);
    623   mhd_assert (block != NULL || block_size == 0);
    624   mhd_assert (pool->size >= block_size);
    625   mhd_assert (pool->pos == mhd_ROUND_TO_ALIGN (pool->pos));
    626 
    627   if (NULL != block)
    628   {   /* Have previously allocated data */
    629     const size_t block_offset = mp_ptr_diff_ (block, pool->memory);
    630     mhd_assert (mp_ptr_le_ (pool->memory, block));
    631     mhd_assert (block_offset <= pool->size);
    632     mhd_assert ((block_offset != pool->pos) || (block_size == 0));
    633     /* Zero-out deallocated region */
    634     if (0 != block_size)
    635     {
    636       if (MHD_MEMPOOL_ZEROING_ON_RESET < pool->zeroing)
    637         memset (block, 0, block_size);
    638       mhd_POISON_MEMORY (block, block_size);
    639     }
    640 #if !defined(MHD_FAVOR_SMALL_CODE) && !defined(MHD_ASAN_POISON_ACTIVE)
    641     else
    642       return; /* Zero size, no need to do anything */
    643 #endif /* ! MHD_FAVOR_SMALL_CODE && ! MHD_ASAN_POISON_ACTIVE */
    644     if (block_offset <= pool->pos)
    645     {
    646       /* "Normal" block, not allocated "from the end". */
    647       const size_t alg_end =
    648         mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size);
    649       mhd_assert (alg_end <= pool->pos);
    650       if (alg_end == pool->pos)
    651       {
    652         /* The last allocated block, return deallocated block to the pool */
    653         size_t alg_start = mhd_ROUND_TO_ALIGN (block_offset);
    654         mhd_assert (alg_start >= block_offset);
    655 #if defined(MHD_ASAN_POISON_ACTIVE)
    656         if (alg_start != block_offset)
    657         {
    658           mhd_POISON_MEMORY (pool->memory + block_offset, \
    659                              alg_start - block_offset);
    660         }
    661         else if (0 != alg_start)
    662         {
    663           bool need_red_zone_before;
    664           mhd_assert (mhd_RED_ZONE_SIZE <= alg_start);
    665 #  if defined(HAVE___ASAN_REGION_IS_POISONED)
    666           need_red_zone_before =
    667             (NULL == __asan_region_is_poisoned (pool->memory
    668                                                 + alg_start
    669                                                 - mhd_RED_ZONE_SIZE,
    670                                                 mhd_RED_ZONE_SIZE));
    671 #  elif defined(HAVE___ASAN_ADDRESS_IS_POISONED)
    672           need_red_zone_before =
    673             (0 == __asan_address_is_poisoned (pool->memory + alg_start - 1));
    674 #  else  /* ! HAVE___ASAN_ADDRESS_IS_POISONED */
    675           need_red_zone_before = true; /* Unknown, assume new red zone needed */
    676 #  endif /* ! HAVE___ASAN_ADDRESS_IS_POISONED */
    677           if (need_red_zone_before)
    678           {
    679             mhd_POISON_MEMORY (pool->memory + alg_start, mhd_RED_ZONE_SIZE);
    680             alg_start += mhd_RED_ZONE_SIZE;
    681           }
    682         }
    683 #endif /* MHD_ASAN_POISON_ACTIVE */
    684         mhd_assert (alg_start <= pool->pos);
    685         mhd_assert (alg_start == mhd_ROUND_TO_ALIGN (alg_start));
    686         pool->pos = alg_start;
    687       }
    688     }
    689     else
    690     {
    691       /* Allocated "from the end" block. */
    692       /* The size and the pointers of such block should not be manipulated by
    693          MHD code (block split is disallowed). */
    694       mhd_assert (block_offset >= pool->end);
    695       mhd_assert (mhd_ROUND_TO_ALIGN (block_offset) == block_offset);
    696       if (block_offset == pool->end)
    697       {
    698         /* The last allocated block, return deallocated block to the pool */
    699         const size_t alg_end =
    700           mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size);
    701         pool->end = alg_end;
    702       }
    703     }
    704   }
    705 }
    706 
    707 
    708 MHD_INTERNAL
    709 MHD_FN_PAR_NONNULL_ (1)
    710 MHD_FN_PAR_NONNULL_IF_NONZERO_ (2, 3)
    711 mhd_FN_RET_SIZED (4) mhd_FN_RET_ALIGNED (mhd_MEMPOOL_ALIGN_SIZE)
    712 void *
    713 mhd_pool_reset (struct mhd_MemoryPool *restrict pool,
    714                 void *restrict keep,
    715                 size_t copy_bytes,
    716                 size_t new_size)
    717 {
    718   mhd_assert (pool->end >= pool->pos);
    719   mhd_assert (pool->size >= pool->end - pool->pos);
    720   mhd_assert (copy_bytes <= new_size);
    721   mhd_assert (copy_bytes + mhd_RED_ZONE_SIZE <= pool->size);
    722   mhd_assert (keep != NULL || copy_bytes == 0);
    723   mhd_assert (keep == NULL || mp_ptr_le_ (pool->memory, keep));
    724   /* (keep == NULL || pool->memory + pool->size >= (uint8_t*) keep + copy_bytes) */
    725   mhd_assert ((keep == NULL) \
    726               || (pool->size >= mp_ptr_diff_ (keep,
    727                                               pool->memory) + copy_bytes));
    728 #if defined(MHD_ASAN_POISON_ACTIVE) && defined(HAVE___ASAN_REGION_IS_POISONED)
    729   mhd_assert (NULL == __asan_region_is_poisoned (keep, copy_bytes));
    730 #endif /* MHD_ASAN_POISON_ACTIVE && HAVE___ASAN_REGION_IS_POISONED */
    731   mhd_UNPOISON_MEMORY (pool->memory, new_size);
    732   if ((NULL != keep)
    733       && (keep != pool->memory))
    734   {
    735     if (0 != copy_bytes)
    736       memmove (pool->memory,
    737                keep,
    738                copy_bytes);
    739   }
    740   if ((MHD_MEMPOOL_ZEROING_NEVER != pool->zeroing)
    741       && (pool->size > copy_bytes))
    742   {
    743     size_t to_zero;   /** Size of area to zero-out */
    744 
    745     to_zero = pool->size - copy_bytes;
    746     mhd_UNPOISON_MEMORY (pool->memory + copy_bytes, to_zero);
    747 #if defined(mhd_USE_LARGE_ALLOCS) && defined(_WIN32)
    748     if (pool->is_large_alloc)
    749     {
    750       size_t to_recommit;     /** Size of decommitted and re-committed area. */
    751       uint8_t *recommit_addr;
    752       /* Round down to page size */
    753       to_recommit = to_zero - to_zero % MHD_sys_page_size_;
    754       recommit_addr = pool->memory + pool->size - to_recommit;
    755 
    756       /* De-committing and re-committing again clear memory and make
    757        * pages free / available for other needs until accessed. */
    758       if (VirtualFree (recommit_addr,
    759                        to_recommit,
    760                        MEM_DECOMMIT))
    761       {
    762         to_zero -= to_recommit;
    763 
    764         if (recommit_addr != VirtualAlloc (recommit_addr,
    765                                            to_recommit,
    766                                            MEM_COMMIT,
    767                                            PAGE_READWRITE))
    768           abort ();      /* Serious error, must never happen */
    769       }
    770     }
    771 #endif /* mhd_USE_LARGE_ALLOCS && _WIN32 */
    772     memset (&pool->memory[copy_bytes],
    773             0,
    774             to_zero);
    775   }
    776   pool->pos = mhd_ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
    777   pool->end = pool->size;
    778   mhd_POISON_MEMORY (((uint8_t *)pool->memory) + new_size, \
    779                      pool->size - new_size);
    780   return pool->memory;
    781 }
    782 
    783 
    784 /* end of memorypool.c */