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 */