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