libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

memorypool.c (29687B)


      1 /*
      2      This file is part of libmicrohttpd
      3      Copyright (C) 2007--2024 Daniel Pittman and Christian Grothoff
      4      Copyright (C) 2014--2024 Evgeny Grin (Karlson2k)
      5 
      6      This library is free software; you can redistribute it and/or
      7      modify it under the terms of the GNU Lesser General Public
      8      License as published by the Free Software Foundation; either
      9      version 2.1 of the License, or (at your option) any later version.
     10 
     11      This library is distributed in the hope that it will be useful,
     12      but WITHOUT ANY WARRANTY; without even the implied warranty of
     13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     14      Lesser General Public License for more details.
     15 
     16      You should have received a copy of the GNU Lesser General Public
     17      License along with this library; if not, write to the Free Software
     18      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
     19 */
     20 
     21 /**
     22  * @file memorypool.c
     23  * @brief memory pool
     24  * @author Christian Grothoff
     25  * @author Karlson2k (Evgeny Grin)
     26  */
     27 #include "memorypool.h"
     28 #ifdef HAVE_STDLIB_H
     29 #include <stdlib.h>
     30 #endif /* HAVE_STDLIB_H */
     31 #include <string.h>
     32 #include <stdint.h>
     33 #include "mhd_assert.h"
     34 #include "mhd_check.h"
     35 #ifdef HAVE_SYS_MMAN_H
     36 #include <sys/mman.h>
     37 #endif
     38 #ifdef _WIN32
     39 #include <windows.h>
     40 #endif
     41 #ifdef HAVE_SYSCONF
     42 #include <unistd.h>
     43 #if defined(_SC_PAGE_SIZE)
     44 #define MHD_SC_PAGESIZE _SC_PAGE_SIZE
     45 #elif defined(_SC_PAGESIZE)
     46 #define MHD_SC_PAGESIZE _SC_PAGESIZE
     47 #endif /* _SC_PAGESIZE */
     48 #endif /* HAVE_SYSCONF */
     49 #include "mhd_limits.h" /* for SIZE_MAX, PAGESIZE / PAGE_SIZE */
     50 
     51 #if defined(MHD_USE_PAGESIZE_MACRO) || defined(MHD_USE_PAGE_SIZE_MACRO)
     52 #ifndef HAVE_SYSCONF /* Avoid duplicate include */
     53 #include <unistd.h>
     54 #endif /* HAVE_SYSCONF */
     55 #ifdef HAVE_SYS_PARAM_H
     56 #include <sys/param.h>
     57 #endif /* HAVE_SYS_PARAM_H */
     58 #endif /* MHD_USE_PAGESIZE_MACRO || MHD_USE_PAGE_SIZE_MACRO */
     59 
     60 /**
     61  * Fallback value of page size
     62  */
     63 #define _MHD_FALLBACK_PAGE_SIZE (4096)
     64 
     65 #if defined(MHD_USE_PAGESIZE_MACRO)
     66 #define MHD_DEF_PAGE_SIZE_ PAGESIZE
     67 #elif defined(MHD_USE_PAGE_SIZE_MACRO)
     68 #define MHD_DEF_PAGE_SIZE_ PAGE_SIZE
     69 #else  /* ! PAGESIZE */
     70 #define MHD_DEF_PAGE_SIZE_ _MHD_FALLBACK_PAGE_SIZE
     71 #endif /* ! PAGESIZE */
     72 
     73 
     74 #ifdef MHD_ASAN_POISON_ACTIVE
     75 #include <sanitizer/asan_interface.h>
     76 #endif /* MHD_ASAN_POISON_ACTIVE */
     77 
     78 /* define MAP_ANONYMOUS for Mac OS X */
     79 #if defined(MAP_ANON) && ! defined(MAP_ANONYMOUS)
     80 #define MAP_ANONYMOUS MAP_ANON
     81 #endif
     82 #if defined(_WIN32)
     83 #define MAP_FAILED NULL
     84 #elif ! defined(MAP_FAILED)
     85 #define MAP_FAILED ((void*) -1)
     86 #endif
     87 
     88 /**
     89  * Align to 2x word size (as GNU libc does).
     90  */
     91 #define ALIGN_SIZE (2 * sizeof(void*))
     92 
     93 /**
     94  * Round up 'n' to a multiple of ALIGN_SIZE.
     95  */
     96 #define ROUND_TO_ALIGN(n) (((n) + (ALIGN_SIZE - 1)) \
     97                            / (ALIGN_SIZE) *(ALIGN_SIZE))
     98 
     99 /* ROUND_TO_ALIGN_PLUS_RED_ZONE() rounds up and, when user poisoning is
    100    active, adds a red zone, so its result is always >= its argument --
    101    unless it wrapped.  Every caller that passes a caller-supplied size
    102    must therefore reject the result with
    103 
    104      if (asize < size)
    105 
    106    and not by testing for zero.  Testing for zero is correct only in the
    107    build where the red zone is 0: ROUND_TO_ALIGN() lands exactly on 0 for
    108    the top ALIGN_SIZE-1 values of size_t, so a zero result is the only
    109    wrapped outcome there.  Adding a red zone moves that outcome to
    110    _MHD_RED_ZONE_SIZE, which is non-zero and small enough to pass the
    111    remaining-space test below it, so a zero test silently stops firing in
    112    exactly the build that has the extra instrumentation.  Sizes that are
    113    already bounded by pool->size (MHD_pool_reset(), the block_offset +
    114    block_size expressions) cannot wrap and need no such test -- but they
    115    do need the trailing red zone clamped to pool->size, see the two sites
    116    that do so. */
    117 
    118 
    119 #ifndef MHD_ASAN_POISON_ACTIVE
    120 #define _MHD_NOSANITIZE_PTRS /**/
    121 #define _MHD_RED_ZONE_SIZE (0)
    122 #define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) ROUND_TO_ALIGN(n)
    123 #define _MHD_POISON_MEMORY(pointer, size) (void)0
    124 #define _MHD_UNPOISON_MEMORY(pointer, size) (void)0
    125 /**
    126  * Boolean 'true' if the first pointer is less or equal the second pointer
    127  */
    128 #define mp_ptr_le_(p1,p2) \
    129   (((const uint8_t*)(p1)) <= ((const uint8_t*)(p2)))
    130 /**
    131  * The difference in bytes between positions of the first and
    132  * the second pointers
    133  */
    134 #define mp_ptr_diff_(p1,p2) \
    135   ((size_t)(((const uint8_t*)(p1)) - ((const uint8_t*)(p2))))
    136 #else  /* MHD_ASAN_POISON_ACTIVE */
    137 #define _MHD_RED_ZONE_SIZE (ALIGN_SIZE)
    138 #define ROUND_TO_ALIGN_PLUS_RED_ZONE(n) (ROUND_TO_ALIGN(n) + _MHD_RED_ZONE_SIZE)
    139 #define _MHD_POISON_MEMORY(pointer, size) \
    140   ASAN_POISON_MEMORY_REGION ((pointer), (size))
    141 #define _MHD_UNPOISON_MEMORY(pointer, size) \
    142   ASAN_UNPOISON_MEMORY_REGION ((pointer), (size))
    143 #if defined(FUNC_PTRCOMPARE_CAST_WORKAROUND_WORKS)
    144 /**
    145  * Boolean 'true' if the first pointer is less or equal the second pointer
    146  */
    147 #define mp_ptr_le_(p1,p2) \
    148   (((uintptr_t)((const void*)(p1))) <= ((uintptr_t)((const void*)(p2))))
    149 /**
    150  * The difference in bytes between positions of the first and
    151  * the second pointers
    152  */
    153 #define mp_ptr_diff_(p1,p2) \
    154   ((size_t)(((uintptr_t)((const uint8_t*)(p1))) - \
    155             ((uintptr_t)((const uint8_t*)(p2)))))
    156 #elif defined(FUNC_ATTR_PTRCOMPARE_WORKS) && \
    157   defined(FUNC_ATTR_PTRSUBTRACT_WORKS)
    158 #ifdef _DEBUG
    159 /**
    160  * Boolean 'true' if the first pointer is less or equal the second pointer
    161  */
    162 __attribute__((no_sanitize ("pointer-compare"))) static bool
    163 mp_ptr_le_ (const void *p1, const void *p2)
    164 {
    165   return (((const uint8_t *) p1) <= ((const uint8_t *) p2));
    166 }
    167 
    168 
    169 #endif /* _DEBUG */
    170 
    171 
    172 /**
    173  * The difference in bytes between positions of the first and
    174  * the second pointers
    175  */
    176 __attribute__((no_sanitize ("pointer-subtract"))) static size_t
    177 mp_ptr_diff_ (const void *p1, const void *p2)
    178 {
    179   return (size_t) (((const uint8_t *) p1) - ((const uint8_t *) p2));
    180 }
    181 
    182 
    183 #elif defined(FUNC_ATTR_NOSANITIZE_WORKS)
    184 #ifdef _DEBUG
    185 /**
    186  * Boolean 'true' if the first pointer is less or equal the second pointer
    187  */
    188 __attribute__((no_sanitize ("address"))) static bool
    189 mp_ptr_le_ (const void *p1, const void *p2)
    190 {
    191   return (((const uint8_t *) p1) <= ((const uint8_t *) p2));
    192 }
    193 
    194 
    195 #endif /* _DEBUG */
    196 
    197 /**
    198  * The difference in bytes between positions of the first and
    199  * the second pointers
    200  */
    201 __attribute__((no_sanitize ("address"))) static size_t
    202 mp_ptr_diff_ (const void *p1, const void *p2)
    203 {
    204   return (size_t) (((const uint8_t *) p1) - ((const uint8_t *) p2));
    205 }
    206 
    207 
    208 #else  /* ! FUNC_ATTR_NOSANITIZE_WORKS */
    209 #error User-poisoning cannot be used
    210 #endif /* ! FUNC_ATTR_NOSANITIZE_WORKS */
    211 #endif /* MHD_ASAN_POISON_ACTIVE */
    212 
    213 /**
    214  * Size of memory page
    215  */
    216 static size_t MHD_sys_page_size_ = (size_t)
    217 #if defined(MHD_USE_PAGESIZE_MACRO_STATIC)
    218                                    PAGESIZE;
    219 #elif defined(MHD_USE_PAGE_SIZE_MACRO_STATIC)
    220                                    PAGE_SIZE;
    221 #else  /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
    222                                    _MHD_FALLBACK_PAGE_SIZE; /* Default fallback value */
    223 #endif /* ! MHD_USE_PAGE_SIZE_MACRO_STATIC */
    224 
    225 /**
    226  * Initialise values for memory pools
    227  */
    228 void
    229 MHD_init_mem_pools_ (void)
    230 {
    231 #ifdef MHD_SC_PAGESIZE
    232   long result;
    233   result = sysconf (MHD_SC_PAGESIZE);
    234   if (-1 != result)
    235     MHD_sys_page_size_ = (size_t) result;
    236   else
    237     MHD_sys_page_size_ = (size_t) MHD_DEF_PAGE_SIZE_;
    238 #elif defined(_WIN32)
    239   SYSTEM_INFO si;
    240   GetSystemInfo (&si);
    241   MHD_sys_page_size_ = (size_t) si.dwPageSize;
    242 #else
    243   MHD_sys_page_size_ = (size_t) MHD_DEF_PAGE_SIZE_;
    244 #endif /* _WIN32 */
    245   mhd_assert (0 == (MHD_sys_page_size_ % ALIGN_SIZE));
    246 }
    247 
    248 
    249 /**
    250  * Handle for a memory pool.  Pools are not reentrant and must not be
    251  * used by multiple threads.
    252  */
    253 struct MemoryPool
    254 {
    255 
    256   /**
    257    * Pointer to the pool's memory
    258    */
    259   uint8_t *memory;
    260 
    261   /**
    262    * Size of the pool.
    263    */
    264   size_t size;
    265 
    266   /**
    267    * Offset of the first unallocated byte.
    268    */
    269   size_t pos;
    270 
    271   /**
    272    * Offset of the byte after the last unallocated byte.
    273    */
    274   size_t end;
    275 
    276   /**
    277    * 'false' if pool was malloc'ed, 'true' if mmapped (VirtualAlloc'ed for W32).
    278    */
    279   bool is_mmap;
    280 };
    281 
    282 
    283 /**
    284  * Create a memory pool.
    285  *
    286  * @param max maximum size of the pool
    287  * @return NULL on error
    288  */
    289 struct MemoryPool *
    290 MHD_pool_create (size_t max)
    291 {
    292   struct MemoryPool *pool;
    293   size_t alloc_size;
    294 
    295   mhd_assert (max > 0);
    296   alloc_size = 0;
    297   pool = malloc (sizeof (struct MemoryPool));
    298   if (NULL == pool)
    299     return NULL;
    300 #if defined(MAP_ANONYMOUS) || defined(_WIN32)
    301   if ( (max <= 32 * 1024) ||
    302        (max < MHD_sys_page_size_ * 4 / 3) )
    303   {
    304     pool->memory = MAP_FAILED;
    305   }
    306   else
    307   {
    308     /* Round up allocation to page granularity. */
    309     alloc_size = max + MHD_sys_page_size_ - 1;
    310     alloc_size -= alloc_size % MHD_sys_page_size_;
    311 #if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
    312     pool->memory = mmap (NULL,
    313                          alloc_size,
    314                          PROT_READ | PROT_WRITE,
    315                          MAP_PRIVATE | MAP_ANONYMOUS,
    316                          -1,
    317                          0);
    318 #elif defined(_WIN32)
    319     pool->memory = VirtualAlloc (NULL,
    320                                  alloc_size,
    321                                  MEM_COMMIT | MEM_RESERVE,
    322                                  PAGE_READWRITE);
    323 #endif /* _WIN32 */
    324   }
    325 #else  /* ! _WIN32 && ! MAP_ANONYMOUS */
    326   pool->memory = MAP_FAILED;
    327 #endif /* ! _WIN32 && ! MAP_ANONYMOUS */
    328   if (MAP_FAILED == pool->memory)
    329   {
    330     alloc_size = ROUND_TO_ALIGN (max);
    331     pool->memory = malloc (alloc_size);
    332     if (NULL == pool->memory)
    333     {
    334       free (pool);
    335       return NULL;
    336     }
    337     pool->is_mmap = false;
    338   }
    339 #if defined(MAP_ANONYMOUS) || defined(_WIN32)
    340   else
    341   {
    342     pool->is_mmap = true;
    343   }
    344 #endif /* _WIN32 || MAP_ANONYMOUS */
    345   mhd_assert (0 == (((uintptr_t) pool->memory) % ALIGN_SIZE));
    346   pool->pos = 0;
    347   pool->end = alloc_size;
    348   pool->size = alloc_size;
    349   mhd_assert (0 < alloc_size);
    350   _MHD_POISON_MEMORY (pool->memory, pool->size);
    351   return pool;
    352 }
    353 
    354 
    355 /**
    356  * Destroy a memory pool.
    357  *
    358  * @param pool memory pool to destroy
    359  */
    360 void
    361 MHD_pool_destroy (struct MemoryPool *pool)
    362 {
    363   if (NULL == pool)
    364     return;
    365 
    366   mhd_assert (pool->end >= pool->pos);
    367   mhd_assert (pool->size >= pool->end - pool->pos);
    368   mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
    369   _MHD_UNPOISON_MEMORY (pool->memory, pool->size);
    370   if (! pool->is_mmap)
    371     free (pool->memory);
    372   else
    373 #if defined(MAP_ANONYMOUS) && ! defined(_WIN32)
    374     munmap (pool->memory,
    375             pool->size);
    376 #elif defined(_WIN32)
    377     VirtualFree (pool->memory,
    378                  0,
    379                  MEM_RELEASE);
    380 #else
    381     abort ();
    382 #endif
    383   free (pool);
    384 }
    385 
    386 
    387 /**
    388  * Check how much memory is left in the @a pool
    389  *
    390  * @param pool pool to check
    391  * @return number of bytes still available in @a pool
    392  */
    393 size_t
    394 MHD_pool_get_free (struct MemoryPool *pool)
    395 {
    396   mhd_assert (pool->end >= pool->pos);
    397   mhd_assert (pool->size >= pool->end - pool->pos);
    398   mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
    399 #ifdef MHD_ASAN_POISON_ACTIVE
    400   if ((pool->end - pool->pos) <= _MHD_RED_ZONE_SIZE)
    401     return 0;
    402 #endif /* MHD_ASAN_POISON_ACTIVE */
    403   return (pool->end - pool->pos) - _MHD_RED_ZONE_SIZE;
    404 }
    405 
    406 
    407 /**
    408  * Allocate size bytes from the pool.
    409  *
    410  * @param pool memory pool to use for the operation
    411  * @param size number of bytes to allocate
    412  * @param from_end allocate from end of pool (set to 'true');
    413  *        use this for small, persistent allocations that
    414  *        will never be reallocated
    415  * @return NULL if the pool cannot support size more
    416  *         bytes
    417  */
    418 void *
    419 MHD_pool_allocate (struct MemoryPool *pool,
    420                    size_t size,
    421                    bool from_end)
    422 {
    423   void *ret;
    424   size_t asize;
    425 
    426   mhd_assert (pool->end >= pool->pos);
    427   mhd_assert (pool->size >= pool->end - pool->pos);
    428   mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
    429   asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
    430   if (asize < size)
    431     return NULL; /* Value wrap, @a size is too close to SIZE_MAX */
    432   if (asize > pool->end - pool->pos)
    433     return NULL;
    434   if (from_end)
    435   {
    436     ret = &pool->memory[pool->end - asize];
    437     pool->end -= asize;
    438   }
    439   else
    440   {
    441     ret = &pool->memory[pool->pos];
    442     pool->pos += asize;
    443   }
    444   _MHD_UNPOISON_MEMORY (ret, size);
    445   return ret;
    446 }
    447 
    448 
    449 /**
    450  * Checks whether allocated block is re-sizable in-place.
    451  * If block is not re-sizable in-place, it still could be shrunk, but freed
    452  * memory will not be re-used until reset of the pool.
    453  * @param pool the memory pool to use
    454  * @param block the pointer to the allocated block to check
    455  * @param block_size the size of the allocated @a block
    456  * @return true if block can be resized in-place in the optimal way,
    457  *         false otherwise
    458  */
    459 bool
    460 MHD_pool_is_resizable_inplace (struct MemoryPool *pool,
    461                                void *block,
    462                                size_t block_size)
    463 {
    464   mhd_assert (pool->end >= pool->pos);
    465   mhd_assert (pool->size >= pool->end - pool->pos);
    466   mhd_assert (block != NULL || block_size == 0);
    467   mhd_assert (pool->size >= block_size);
    468   if (NULL != block)
    469   {
    470     const size_t block_offset = mp_ptr_diff_ (block, pool->memory);
    471     mhd_assert (mp_ptr_le_ (pool->memory, block));
    472     mhd_assert (pool->size >= block_offset);
    473     mhd_assert (pool->size >= block_offset + block_size);
    474     return (pool->pos ==
    475             ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size));
    476   }
    477   return false; /* Unallocated blocks cannot be resized in-place */
    478 }
    479 
    480 
    481 /**
    482  * Try to allocate @a size bytes memory area from the @a pool.
    483  *
    484  * If allocation fails, @a required_bytes is updated with size required to be
    485  * freed in the @a pool from rellocatable area to allocate requested number
    486  * of bytes.
    487  * Allocated memory area is always not rellocatable ("from end").
    488  *
    489  * @param pool memory pool to use for the operation
    490  * @param size the size of memory in bytes to allocate
    491  * @param[out] required_bytes the pointer to variable to be updated with
    492  *                            the size of the required additional free
    493  *                            memory area, set to 0 if function succeeds.
    494  *                            Cannot be NULL.
    495  * @return the pointer to allocated memory area if succeed,
    496  *         NULL if the pool doesn't have enough space, required_bytes is updated
    497  *         with amount of space needed to be freed in rellocatable area or
    498  *         set to SIZE_MAX if requested size is too large for the pool.
    499  */
    500 void *
    501 MHD_pool_try_alloc (struct MemoryPool *pool,
    502                     size_t size,
    503                     size_t *required_bytes)
    504 {
    505   void *ret;
    506   size_t asize;
    507 
    508   mhd_assert (pool->end >= pool->pos);
    509   mhd_assert (pool->size >= pool->end - pool->pos);
    510   mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
    511   asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (size);
    512   if (asize < size)
    513   { /* Value wrap, @a size is too close to SIZE_MAX, very unlikely */
    514     *required_bytes = SIZE_MAX;
    515     return NULL;
    516   }
    517   if (asize > pool->end - pool->pos)
    518   {
    519     mhd_assert ((pool->end - pool->pos) == \
    520                 ROUND_TO_ALIGN (pool->end - pool->pos));
    521     if (asize <= pool->end)
    522       *required_bytes = asize - (pool->end - pool->pos);
    523     else
    524       *required_bytes = SIZE_MAX;
    525     return NULL;
    526   }
    527   *required_bytes = 0;
    528   ret = &pool->memory[pool->end - asize];
    529   pool->end -= asize;
    530   _MHD_UNPOISON_MEMORY (ret, size);
    531   return ret;
    532 }
    533 
    534 
    535 /**
    536  * Reallocate a block of memory obtained from the pool.
    537  * This is particularly efficient when growing or
    538  * shrinking the block that was last (re)allocated.
    539  * If the given block is not the most recently
    540  * (re)allocated block, the memory of the previous
    541  * allocation may be not released until the pool is
    542  * destroyed or reset.
    543  *
    544  * @param pool memory pool to use for the operation
    545  * @param old the existing block
    546  * @param old_size the size of the existing block
    547  * @param new_size the new size of the block
    548  * @return new address of the block, or
    549  *         NULL if the pool cannot support @a new_size
    550  *         bytes (old continues to be valid for @a old_size)
    551  */
    552 void *
    553 MHD_pool_reallocate (struct MemoryPool *pool,
    554                      void *old,
    555                      size_t old_size,
    556                      size_t new_size)
    557 {
    558   size_t asize;
    559   uint8_t *new_blc;
    560 
    561   mhd_assert (pool->end >= pool->pos);
    562   mhd_assert (pool->size >= pool->end - pool->pos);
    563   mhd_assert (old != NULL || old_size == 0);
    564   mhd_assert (pool->size >= old_size);
    565   mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
    566 #if defined(MHD_ASAN_POISON_ACTIVE) && defined(HAVE___ASAN_REGION_IS_POISONED)
    567   mhd_assert (NULL == __asan_region_is_poisoned (old, old_size));
    568 #endif /* MHD_ASAN_POISON_ACTIVE && HAVE___ASAN_REGION_IS_POISONED */
    569 
    570   /* No block can ever be larger than the pool, so reject that here once
    571      rather than in each of the paths below.  This is what stops a
    572      wrapping @a new_size: both remaining size tests are made on values
    573      that ROUND_TO_ALIGN_PLUS_RED_ZONE() has already wrapped back to
    574      something small and plausible, so neither of them fires and the
    575      caller is handed a block it believes is nearly SIZE_MAX bytes long.
    576      The documented contract is to return NULL when the pool cannot
    577      support @a new_size bytes. */
    578   if (new_size > pool->size)
    579     return NULL;
    580 
    581   if (NULL != old)
    582   {   /* Have previously allocated data */
    583     const size_t old_offset = mp_ptr_diff_ (old, pool->memory);
    584     const bool shrinking = (old_size > new_size);
    585 
    586     /* 'old' and 'old_size' come from the caller and bound both the
    587        memset() that shrinking performs a few lines below and the
    588        memcpy (new_blc, old, old_size) at the end of this function, so this
    589        is the precondition of two unbounded copies and must hold in release
    590        builds too.  Failing it returns NULL, aka
    591        "the pool cannot satisfy this request" and which leaves
    592        'old' valid, so the connection is failed by the caller instead of
    593        corrupting the pool.
    594        The lower bound stays an assertion because mp_ptr_le_() only exists in
    595        debug builds when user-poisoning is active; it is subsumed anyway,
    596        because mp_ptr_diff_() wraps for a pointer below pool->memory and the
    597        resulting huge 'old_offset' fails the bound below. */
    598     mhd_assert (mp_ptr_le_ (pool->memory, old));
    599     /* (pool->memory + pool->size >= (uint8_t*) old + old_size) */
    600     MHD_CHECK_RET_ ((pool->size - _MHD_RED_ZONE_SIZE) >=
    601                     (old_offset + old_size),
    602                     NULL);
    603     /* Blocks "from the end" must not be reallocated */
    604     /* (old_size == 0 || pool->memory + pool->pos > (uint8_t*) old) */
    605     mhd_assert ((old_size == 0) || \
    606                 (pool->pos > old_offset));
    607     mhd_assert ((old_size == 0) || \
    608                 ((pool->end - _MHD_RED_ZONE_SIZE) >= (old_offset + old_size)));
    609     /* Try resizing in-place */
    610     if (shrinking)
    611     {     /* Shrinking in-place, zero-out freed part */
    612       memset ((uint8_t *) old + new_size, 0, old_size - new_size);
    613       _MHD_POISON_MEMORY ((uint8_t *) old + new_size, old_size - new_size);
    614     }
    615     if (pool->pos ==
    616         ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + old_size))
    617     {     /* "old" block is the last allocated block */
    618       const size_t new_apos =
    619         ROUND_TO_ALIGN_PLUS_RED_ZONE (old_offset + new_size);
    620       if (! shrinking)
    621       {                               /* Grow in-place, check for enough space. */
    622         /* Reject a wrapping @a new_size before looking at 'new_apos'.
    623            'old_offset + new_size' can wrap all the way round and land
    624            back inside [pool->pos, pool->end], and the two tests below
    625            then both pass: the caller is handed a block it believes is
    626            nearly SIZE_MAX bytes long.  The "allocate a new block" path
    627            further down already has an explicit wrap test; this is the
    628            same check for the in-place path. */
    629         if (new_size > pool->size - old_offset)
    630           return NULL;                /* Value wrap, or beyond the pool */
    631         if ( (new_apos > pool->end) ||
    632              (new_apos < pool->pos) ) /* Value wrap */
    633           return NULL;                /* No space */
    634       }
    635       /* Resized in-place */
    636       pool->pos = new_apos;
    637       _MHD_UNPOISON_MEMORY (old, new_size);
    638       return old;
    639     }
    640     if (shrinking)
    641       return old;   /* Resized in-place, freed part remains allocated */
    642   }
    643   /* Need to allocate new block */
    644   asize = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
    645   if ( (asize < new_size) || /* Value wrap, too large new_size. */
    646        (asize > pool->end - pool->pos) ) /* Not enough space */
    647     return NULL;
    648 
    649   new_blc = pool->memory + pool->pos;
    650   pool->pos += asize;
    651 
    652   _MHD_UNPOISON_MEMORY (new_blc, new_size);
    653   if (0 != old_size)
    654   {
    655     /* Move data to new block, old block remains allocated */
    656     memcpy (new_blc, old, old_size);
    657     /* Zero-out old block */
    658     memset (old, 0, old_size);
    659     _MHD_POISON_MEMORY (old, old_size);
    660   }
    661   return new_blc;
    662 }
    663 
    664 
    665 /**
    666  * Deallocate a block of memory obtained from the pool.
    667  *
    668  * If the given block is not the most recently
    669  * (re)allocated block, the memory of the this block
    670  * allocation may be not released until the pool is
    671  * destroyed or reset.
    672  *
    673  * @param pool memory pool to use for the operation
    674  * @param block the allocated block, the NULL is tolerated
    675  * @param block_size the size of the allocated block
    676  */
    677 void
    678 MHD_pool_deallocate (struct MemoryPool *pool,
    679                      void *block,
    680                      size_t block_size)
    681 {
    682   mhd_assert (pool->end >= pool->pos);
    683   mhd_assert (pool->size >= pool->end - pool->pos);
    684   mhd_assert (block != NULL || block_size == 0);
    685   mhd_assert (pool->size >= block_size);
    686   mhd_assert (pool->pos == ROUND_TO_ALIGN (pool->pos));
    687 
    688   if (NULL != block)
    689   {   /* Have previously allocated data */
    690     const size_t block_offset = mp_ptr_diff_ (block, pool->memory);
    691     mhd_assert (mp_ptr_le_ (pool->memory, block));
    692     mhd_assert (block_offset <= pool->size);
    693     /* A block allocated "from the end" starts at or after pool->end, a
    694        "normal" block starts before it.  Those two ranges meet when the
    695        pool is exactly full (pool->pos == pool->end), so an end block may
    696        legitimately start at pool->pos; only a normal block may not. */
    697     mhd_assert ((block_offset >= pool->end) || \
    698                 (block_offset != pool->pos) || (block_size == 0));
    699     /* Zero-out deallocated region */
    700     if (0 != block_size)
    701     {
    702       memset (block, 0, block_size);
    703       _MHD_POISON_MEMORY (block, block_size);
    704     }
    705 #if ! defined(MHD_FAVOR_SMALL_CODE) && ! defined(MHD_ASAN_POISON_ACTIVE)
    706     else
    707       return; /* Zero size, no need to do anything */
    708 #endif /* ! MHD_FAVOR_SMALL_CODE && ! MHD_ASAN_POISON_ACTIVE */
    709     if (block_offset < pool->end)
    710     {
    711       /* "Normal" block, not allocated "from the end".
    712          The test is against pool->end, not pool->pos: when the pool is
    713          exactly full the two are equal, and a block allocated "from the
    714          end" then also satisfies 'block_offset <= pool->pos', so it
    715          would be mistaken for a normal block and never returned. */
    716       size_t alg_end =
    717         ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size);
    718       /* Clamped exactly as MHD_pool_reset() clamps pool->pos, and for the
    719          same reason: a block that reaches the end of the pool has no room
    720          for a trailing red zone, so its recorded end is pool->size.  The
    721          two have to agree or the "is this the last block" test below can
    722          never match such a block. */
    723       if (alg_end > pool->size)
    724         alg_end = pool->size;
    725       mhd_assert (alg_end <= pool->pos);
    726       if (alg_end == pool->pos)
    727       {
    728         /* The last allocated block, return deallocated block to the pool */
    729         size_t alg_start = ROUND_TO_ALIGN (block_offset);
    730         mhd_assert (alg_start >= block_offset);
    731 #if defined(MHD_ASAN_POISON_ACTIVE)
    732         if (alg_start != block_offset)
    733         {
    734           _MHD_POISON_MEMORY (pool->memory + block_offset, \
    735                               alg_start - block_offset);
    736         }
    737         else if (0 != alg_start)
    738         {
    739           bool need_red_zone_before;
    740           mhd_assert (_MHD_RED_ZONE_SIZE <= alg_start);
    741 #if defined(HAVE___ASAN_REGION_IS_POISONED)
    742           need_red_zone_before =
    743             (NULL == __asan_region_is_poisoned (pool->memory
    744                                                 + alg_start
    745                                                 - _MHD_RED_ZONE_SIZE,
    746                                                 _MHD_RED_ZONE_SIZE));
    747 #elif defined(HAVE___ASAN_ADDRESS_IS_POISONED)
    748           need_red_zone_before =
    749             (0 == __asan_address_is_poisoned (pool->memory + alg_start - 1));
    750 #else  /* ! HAVE___ASAN_ADDRESS_IS_POISONED */
    751           need_red_zone_before = true; /* Unknown, assume new red zone needed */
    752 #endif /* ! HAVE___ASAN_ADDRESS_IS_POISONED */
    753           if (need_red_zone_before)
    754           {
    755             _MHD_POISON_MEMORY (pool->memory + alg_start, _MHD_RED_ZONE_SIZE);
    756             alg_start += _MHD_RED_ZONE_SIZE;
    757           }
    758         }
    759 #endif /* MHD_ASAN_POISON_ACTIVE */
    760         mhd_assert (alg_start <= pool->pos);
    761         mhd_assert (alg_start == ROUND_TO_ALIGN (alg_start));
    762         pool->pos = alg_start;
    763       }
    764     }
    765     else
    766     {
    767       /* Allocated "from the end" block. */
    768       /* The size and the pointers of such block should not be manipulated by
    769          MHD code (block split is disallowed). */
    770       mhd_assert (block_offset >= pool->end);
    771       mhd_assert (ROUND_TO_ALIGN (block_offset) == block_offset);
    772       if (block_offset == pool->end)
    773       {
    774         /* The last allocated block, return deallocated block to the pool */
    775         const size_t alg_end =
    776           ROUND_TO_ALIGN_PLUS_RED_ZONE (block_offset + block_size);
    777         pool->end = alg_end;
    778       }
    779     }
    780   }
    781 }
    782 
    783 
    784 /**
    785  * Clear all entries from the memory pool except
    786  * for @a keep of the given @a copy_bytes.  The pointer
    787  * returned should be a buffer of @a new_size where
    788  * the first @a copy_bytes are from @a keep.
    789  *
    790  * @param pool memory pool to use for the operation
    791  * @param keep pointer to the entry to keep (maybe NULL)
    792  * @param copy_bytes how many bytes need to be kept at this address
    793  * @param new_size how many bytes should the allocation we return have?
    794  *                 (should be larger or equal to @a copy_bytes)
    795  * @return addr new address of @a keep (if it had to change)
    796  */
    797 void *
    798 MHD_pool_reset (struct MemoryPool *pool,
    799                 void *keep,
    800                 size_t copy_bytes,
    801                 size_t new_size)
    802 {
    803   mhd_assert (pool->end >= pool->pos);
    804   mhd_assert (pool->size >= pool->end - pool->pos);
    805   mhd_assert (copy_bytes <= new_size);
    806   mhd_assert (new_size <= pool->size);
    807   mhd_assert (copy_bytes <= pool->size);
    808   mhd_assert (keep != NULL || copy_bytes == 0);
    809   mhd_assert (keep == NULL || mp_ptr_le_ (pool->memory, keep));
    810   /* (keep == NULL || pool->memory + pool->size >= (uint8_t*) keep + copy_bytes) */
    811   mhd_assert ((keep == NULL) || \
    812               (pool->size >= mp_ptr_diff_ (keep, pool->memory) + copy_bytes));
    813 #if defined(MHD_ASAN_POISON_ACTIVE) && defined(HAVE___ASAN_REGION_IS_POISONED)
    814   mhd_assert (NULL == __asan_region_is_poisoned (keep, copy_bytes));
    815 #endif /* MHD_ASAN_POISON_ACTIVE && HAVE___ASAN_REGION_IS_POISONED */
    816   _MHD_UNPOISON_MEMORY (pool->memory, new_size);
    817   if ( (NULL != keep) &&
    818        (keep != pool->memory) )
    819   {
    820     if (0 != copy_bytes)
    821       memmove (pool->memory,
    822                keep,
    823                copy_bytes);
    824   }
    825   /* technically not needed, but safer to zero out */
    826   if (pool->size > copy_bytes)
    827   {
    828     size_t to_zero;   /** Size of area to zero-out */
    829 
    830     to_zero = pool->size - copy_bytes;
    831     _MHD_UNPOISON_MEMORY (pool->memory + copy_bytes, to_zero);
    832 #ifdef _WIN32
    833     if (pool->is_mmap)
    834     {
    835       size_t to_recommit;     /** Size of decommitted and re-committed area. */
    836       uint8_t *recommit_addr;
    837       /* Round down to page size */
    838       to_recommit = to_zero - to_zero % MHD_sys_page_size_;
    839       recommit_addr = pool->memory + pool->size - to_recommit;
    840 
    841       /* De-committing and re-committing again clear memory and make
    842        * pages free / available for other needs until accessed. */
    843       if (VirtualFree (recommit_addr,
    844                        to_recommit,
    845                        MEM_DECOMMIT))
    846       {
    847         to_zero -= to_recommit;
    848 
    849         if (recommit_addr != VirtualAlloc (recommit_addr,
    850                                            to_recommit,
    851                                            MEM_COMMIT,
    852                                            PAGE_READWRITE))
    853           abort ();      /* Serious error, must never happen */
    854       }
    855     }
    856 #endif /* _WIN32 */
    857     memset (&pool->memory[copy_bytes],
    858             0,
    859             to_zero);
    860   }
    861   pool->pos = ROUND_TO_ALIGN_PLUS_RED_ZONE (new_size);
    862   /* The red zone is a gap kept *inside* the pool so that an overrun of the
    863      block lands in poisoned bytes.  There is no room for it once the kept
    864      block reaches the end of the pool, and none is needed there either --
    865      past pool->size the allocation's own ASAN red zone takes over.  Without
    866      this clamp pool->pos would be set past pool->end for any @a new_size
    867      within a red zone of pool->size, breaking the pool->end >= pool->pos
    868      invariant that every other entry point asserts.  In the build with no
    869      user poisoning _MHD_RED_ZONE_SIZE is 0 and this can never fire. */
    870   if (pool->pos > pool->size)
    871     pool->pos = pool->size;
    872   pool->end = pool->size;
    873   _MHD_POISON_MEMORY (((uint8_t *) pool->memory) + new_size, \
    874                       pool->size - new_size);
    875   return pool->memory;
    876 }
    877 
    878 
    879 /* end of memorypool.c */